Hi, Currently the svnrdump command line tool does something quite different compared to other programs like svn, svnadmin and svnauthz. By "quite different" I mean that svnrdump does exactly the following:
- Inconsistently uses (svn_cmdline_handle_exit_error) and SVNRDUMP_ERR, which actually is (svn_handle_error2 + svn_error_clear) to handle errors. - Sometimes destroys the top-level pool and sometimes does not destroy it, e.g.: * Running 'svnrdump --non-interactive --force-interactive' explicitly destroys the pool. * Running 'svnrdump dump INVALID-URL' does not destroy the pool thus leaving it to be destroyed during apr_terminate. - Prefixes every possible exit path with the svn_pool_destroy call instead of making that call in a single place. - Inconsistently uses SVN_INT_ERR instead of SVNRDUMP_ERR in one place: [[[ if (subcommand->name[0] == '-') SVN_INT_ERR(help_cmd(NULL, NULL, pool)); ]]] We can avoid all these inconsistencies by utilizing the (main / submain / SVN_INT_ERR / EXIT_ERROR / single svn_pool_destroy call) pattern. That is the way things are done in in svn / svnadmin and svnauthz. See the attached patch. Another thing is worth mentioning here. This patch prevents svnrdump from crashing in the SSL + error-out case, which I recently reported to serf-dev [1]. That is *just a lucky coincidence*, because the problem is in serf, not in svnrdump. So, this patch should in no way be considered a workaround for the serf issue, it is simply about making things more consistent and better in general. [1]: https://groups.google.com/d/msg/serf-dev/gOn9HTUN98U/pz0_AqdrmJYJ Thanks and regards, Evgeny Kotkov
Index: subversion/svnrdump/svnrdump.c =================================================================== --- subversion/svnrdump/svnrdump.c (revision 1514184) +++ subversion/svnrdump/svnrdump.c (working copy) @@ -666,23 +666,21 @@ version(const char *progname, } -/* A statement macro, similar to @c SVN_ERR, but returns an integer. - * Evaluate @a expr. If it yields an error, handle that error and - * return @c EXIT_FAILURE. - */ -#define SVNRDUMP_ERR(expr) \ - do \ - { \ - svn_error_t *svn_err__temp = (expr); \ - if (svn_err__temp) \ - { \ - svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \ - svn_error_clear(svn_err__temp); \ - return EXIT_FAILURE; \ - } \ - } \ - while (0) +/* Report and clear the error ERR, and return EXIT_FAILURE. */ +#define EXIT_ERROR(err) \ + svn_cmdline_handle_exit_error(err, NULL, "svnrdump: ") +/* A redefinition of the public SVN_INT_ERR macro, that suppresses the + * error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR, and with the + * program name 'svnrdump' instead of 'svn'. */ +#undef SVN_INT_ERR +#define SVN_INT_ERR(expr) \ + do { \ + svn_error_t *svn_err__temp = (expr); \ + if (svn_err__temp) \ + return EXIT_ERROR(svn_err__temp); \ + } while (0) + /* Handle the "dump" subcommand. Implements `svn_opt_subcommand_t'. */ static svn_error_t * dump_cmd(apr_getopt_t *os, @@ -830,14 +828,13 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba return SVN_NO_ERROR; } -int -main(int argc, const char **argv) +static int +sub_main(int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; const svn_opt_subcommand_desc2_t *subcommand = NULL; opt_baton_t *opt_baton; svn_revnum_t latest_revision = SVN_INVALID_REVNUM; - apr_pool_t *pool = NULL; const char *config_dir = NULL; const char *username = NULL; const char *password = NULL; @@ -851,20 +848,12 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba apr_array_header_t *received_opts; int i; - if (svn_cmdline_init ("svnrdump", 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)); - opt_baton = apr_pcalloc(pool, sizeof(*opt_baton)); opt_baton->start_revision.kind = svn_opt_revision_unspecified; opt_baton->end_revision.kind = svn_opt_revision_unspecified; opt_baton->url = NULL; - SVNRDUMP_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = TRUE; /* Options and arguments can be interleaved */ @@ -904,8 +893,8 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba break; if (status != APR_SUCCESS) { - SVNRDUMP_ERR(usage(argv[0], pool)); - exit(EXIT_FAILURE); + SVN_INT_ERR(usage(argv[0], pool)); + return EXIT_FAILURE; } /* Stash the option code in an array before parsing it. */ @@ -922,7 +911,7 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba _("Multiple revision arguments " "encountered; try '-r N:M' instead " "of '-r N -r M'")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return EXIT_ERROR(err); } /* Parse the -r argument. */ if (svn_opt_parse_revision(&(opt_baton->start_revision), @@ -935,7 +924,7 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error in revision " "argument '%s'"), utf8_opt_arg); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return EXIT_ERROR(err); } } break; @@ -952,10 +941,10 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba opt_baton->help = TRUE; break; case opt_auth_username: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); + SVN_INT_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); break; case opt_auth_password: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); + SVN_INT_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); break; case opt_auth_nocache: no_auth_cache = TRUE; @@ -978,9 +967,9 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba apr_array_make(pool, 1, sizeof(svn_cmdline__config_argument_t*)); - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); - SVNRDUMP_ERR(svn_cmdline__parse_config_option(config_options, - opt_arg, pool)); + SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_INT_ERR(svn_cmdline__parse_config_option(config_options, + opt_arg, pool)); } } @@ -991,7 +980,7 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("--non-interactive and --force-interactive " "are mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return EXIT_ERROR(err); } if (opt_baton->help) @@ -1016,9 +1005,8 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba else { - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_INT_ERR(help_cmd(NULL, NULL, pool)); + return EXIT_FAILURE; } } else @@ -1032,14 +1020,13 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba const char *first_arg_utf8; err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool); if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return EXIT_ERROR(err); svn_error_clear( svn_cmdline_fprintf(stderr, pool, _("Unknown subcommand: '%s'\n"), first_arg_utf8)); - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_INT_ERR(help_cmd(NULL, NULL, pool)); + return EXIT_FAILURE; } } } @@ -1071,7 +1058,6 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba _("Subcommand '%s' doesn't accept option '%s'\n" "Type 'svnrdump help %s' for usage.\n"), subcommand->name, optstr, subcommand->name)); - svn_pool_destroy(pool); return EXIT_FAILURE; } } @@ -1078,16 +1064,14 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba if (strcmp(subcommand->name, "--version") == 0) { - SVNRDUMP_ERR(version(argv[0], opt_baton->quiet, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_INT_ERR(version(argv[0], opt_baton->quiet, pool)); + return EXIT_SUCCESS; } if (strcmp(subcommand->name, "help") == 0) { - SVNRDUMP_ERR(help_cmd(os, opt_baton, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_INT_ERR(help_cmd(os, opt_baton, pool)); + return EXIT_SUCCESS; } /* --trust-server-cert can only be used with --non-interactive */ @@ -1096,30 +1080,27 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("--trust-server-cert requires " "--non-interactive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return EXIT_ERROR(err); } /* Expect one more non-option argument: the repository URL. */ if (os->ind != os->argc - 1) { - SVNRDUMP_ERR(usage(argv[0], pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_INT_ERR(usage(argv[0], pool)); + return EXIT_FAILURE; } else { const char *repos_url; - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&repos_url, - os->argv[os->ind], pool)); + SVN_INT_ERR(svn_utf_cstring_to_utf8(&repos_url, + os->argv[os->ind], pool)); if (! svn_path_is_url(repos_url)) { err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, "Target '%s' is not a URL", repos_url); - SVNRDUMP_ERR(err); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + return EXIT_ERROR(err); } opt_baton->url = svn_uri_canonicalize(repos_url, pool); } @@ -1140,16 +1121,16 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba non_interactive = !svn_cmdline__be_interactive(non_interactive, force_interactive); - SVNRDUMP_ERR(init_client_context(&(opt_baton->ctx), - non_interactive, - username, - password, - config_dir, - opt_baton->url, - no_auth_cache, - trust_server_cert, - config_options, - pool)); + SVN_INT_ERR(init_client_context(&(opt_baton->ctx), + non_interactive, + username, + password, + config_dir, + opt_baton->url, + no_auth_cache, + trust_server_cert, + config_options, + pool)); err = svn_client_open_ra_session2(&(opt_baton->session), opt_baton->url, NULL, @@ -1174,11 +1155,31 @@ validate_and_resolve_revisions(opt_baton_t *opt_ba _("Authentication failed and interactive" " prompting is disabled; see the" " --force-interactive option")); + return EXIT_ERROR(err); } + else if (err) + return EXIT_ERROR(err); + else + return EXIT_SUCCESS; +} - SVNRDUMP_ERR(err); +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code; + /* Initialize the app. */ + if (svn_cmdline_init("svnrdump", 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)); + + exit_code = sub_main(argc, argv, pool); + svn_pool_destroy(pool); - - return EXIT_SUCCESS; + return exit_code; }
svnrdump: Handle errors the svn / svnadmin / svnauthz way. * subversion/svnrdump/svnrdump.c (SVNRDUMP_ERR): Remove this macro. (EXIT_ERROR, SVN_INT_ERR): New macros, same as those defined in subversion/svn/svn.c, but for svnrdump. (submain): Extract most of 'main' into this new function. Use SVN_INT_ERR / EXIT_ERROR to handle errors. Remove all svn_pool_destroy calls, because the top-level pool is now always destroyed in 'main'. (main): Now just a wrapper, that creates the top-level pool, calls 'submain' and cleans everything up. Patch by: Evgeny Kotkov <evgeny.kotkov{_AT_}visualsvn.com>