* subversion/svnrdump/load_editor.c: Code formatting/comment changes.
[svnrdump.git] / svnrdump.c
blobb9417a5f9995179f031e1a6019cc7dbea122b08e
1 /*
2 * svnrdump.c: Produce a dumpfile of a local or remote repository
3 * without touching the filesystem, but for temporary files.
5 * ====================================================================
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
21 * under the License.
22 * ====================================================================
25 #include <apr_signal.h>
27 #include "svn_pools.h"
28 #include "svn_cmdline.h"
29 #include "svn_client.h"
30 #include "svn_hash.h"
31 #include "svn_ra.h"
32 #include "svn_repos.h"
33 #include "svn_path.h"
34 #include "svn_utf.h"
35 #include "svn_private_config.h"
36 #include "svn_string.h"
37 #include "svn_props.h"
39 #include "dump_editor.h"
40 #include "load_editor.h"
42 #include "private/svn_cmdline_private.h"
46 /*** Cancellation ***/
48 /* A flag to see if we've been cancelled by the client or not. */
49 static volatile sig_atomic_t cancelled = FALSE;
51 /* A signal handler to support cancellation. */
52 static void
53 signal_handler(int signum)
55 apr_signal(signum, SIG_IGN);
56 cancelled = TRUE;
59 /* Our cancellation callback. */
60 static svn_error_t *
61 check_cancel(void *baton)
63 if (cancelled)
64 return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
65 else
66 return SVN_NO_ERROR;
72 static svn_opt_subcommand_t dump_cmd, load_cmd;
74 enum svn_svnrdump__longopt_t
76 opt_config_dir = SVN_OPT_FIRST_LONGOPT_ID,
77 opt_auth_username,
78 opt_auth_password,
79 opt_non_interactive,
80 opt_auth_nocache,
81 opt_version,
82 opt_config_option,
85 static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] =
87 { "dump", dump_cmd, { 0 },
88 N_("usage: svnrdump dump URL [-r LOWER[:UPPER]]\n\n"
89 "Dump revisions LOWER to UPPER of repository at remote URL "
90 "to stdout in a 'dumpfile' portable format.\n"
91 "If only LOWER is given, dump that one revision.\n"),
92 { 0 } },
93 { "load", load_cmd, { 0 },
94 N_("usage: svnrdump load URL\n\n"
95 "Load a 'dumpfile' given on stdin to a repository "
96 "at remote URL.\n"),
97 { 0 } },
98 { "help", 0, { "?", "h" },
99 N_("usage: svnrdump help [SUBCOMMAND...]\n\n"
100 "Describe the usage of this program or its subcommands.\n"),
101 { 0 } },
102 { NULL, NULL, { 0 }, NULL, { 0 } }
105 static const apr_getopt_option_t svnrdump__options[] =
107 {"revision", 'r', 1,
108 N_("specify revision number ARG (or X:Y range)")},
109 {"quiet", 'q', 0,
110 N_("no progress (only errors) to stderr")},
111 {"config-dir", opt_config_dir, 1,
112 N_("read user configuration files from directory ARG")},
113 {"username", opt_auth_username, 1,
114 N_("specify a username ARG")},
115 {"password", opt_auth_password, 1,
116 N_("specify a password ARG")},
117 {"non-interactive", opt_non_interactive, 0,
118 N_("do no interactive prompting")},
119 {"no-auth-cache", opt_auth_nocache, 0,
120 N_("do not cache authentication tokens")},
121 {"help", 'h', 0,
122 N_("display this help")},
123 {"version", opt_version, 0,
124 N_("show program version information")},
125 {"config-option", opt_config_option, 1,
126 N_("set user configuration option in the format:\n"
128 " FILE:SECTION:OPTION=[VALUE]\n"
130 "For example:\n"
132 " servers:global:http-library=serf")},
133 {0, 0, 0, 0}
136 /* Baton for the RA replay session. */
137 struct replay_baton {
138 /* The editor producing diffs. */
139 const svn_delta_editor_t *editor;
141 /* Baton for the editor. */
142 void *edit_baton;
144 /* Whether to be quiet. */
145 svn_boolean_t quiet;
148 /* Option set */
149 typedef struct opt_baton_t {
150 svn_ra_session_t *session;
151 const char *url;
152 svn_revnum_t start_revision;
153 svn_revnum_t end_revision;
154 svn_boolean_t quiet;
155 } opt_baton_t;
157 /* Print dumpstream-formatted information about REVISION.
158 * Implements the `svn_ra_replay_revstart_callback_t' interface.
160 static svn_error_t *
161 replay_revstart(svn_revnum_t revision,
162 void *replay_baton,
163 const svn_delta_editor_t **editor,
164 void **edit_baton,
165 apr_hash_t *rev_props,
166 apr_pool_t *pool)
168 struct replay_baton *rb = replay_baton;
169 svn_stringbuf_t *propstring;
170 svn_stream_t *stdout_stream;
171 svn_stream_t *revprop_stream;
173 svn_stream_for_stdout(&stdout_stream, pool);
175 /* Revision-number: 19 */
176 SVN_ERR(svn_stream_printf(stdout_stream, pool,
177 SVN_REPOS_DUMPFILE_REVISION_NUMBER
178 ": %ld\n", revision));
179 SVN_ERR(normalize_props(rev_props, pool));
180 propstring = svn_stringbuf_create_ensure(0, pool);
181 revprop_stream = svn_stream_from_stringbuf(propstring, pool);
182 SVN_ERR(svn_hash_write2(rev_props, revprop_stream, "PROPS-END", pool));
183 SVN_ERR(svn_stream_close(revprop_stream));
185 /* Prop-content-length: 13 */
186 SVN_ERR(svn_stream_printf(stdout_stream, pool,
187 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
188 ": %" APR_SIZE_T_FMT "\n", propstring->len));
190 /* Content-length: 29 */
191 SVN_ERR(svn_stream_printf(stdout_stream, pool,
192 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
193 ": %" APR_SIZE_T_FMT "\n\n", propstring->len));
195 /* Property data. */
196 SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
197 &(propstring->len)));
199 SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
200 SVN_ERR(svn_stream_close(stdout_stream));
202 /* Extract editor and editor_baton from the replay_baton and
203 set them so that the editor callbacks can use them. */
204 *editor = rb->editor;
205 *edit_baton = rb->edit_baton;
207 return SVN_NO_ERROR;
210 /* Print progress information about the dump of REVISION.
211 Implements the `svn_ra_replay_revfinish_callback_t' interface. */
212 static svn_error_t *
213 replay_revend(svn_revnum_t revision,
214 void *replay_baton,
215 const svn_delta_editor_t *editor,
216 void *edit_baton,
217 apr_hash_t *rev_props,
218 apr_pool_t *pool)
220 /* No resources left to free. */
221 struct replay_baton *rb = replay_baton;
222 if (! rb->quiet)
223 svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", revision);
224 return SVN_NO_ERROR;
227 /* Set *SESSION to a new RA session opened to URL. Allocate *SESSION
228 * and related data structures in POOL. Use CONFIG_DIR and pass
229 * USERNAME, PASSWORD, CONFIG_DIR and NO_AUTH_CACHE to initialize the
230 * authorization baton. CONFIG_OPTIONS (if not NULL) is a list of
231 * configuration overrides.
233 static svn_error_t *
234 open_connection(svn_ra_session_t **session,
235 const char *url,
236 svn_boolean_t non_interactive,
237 const char *username,
238 const char *password,
239 const char *config_dir,
240 svn_boolean_t no_auth_cache,
241 apr_array_header_t *config_options,
242 apr_pool_t *pool)
244 svn_client_ctx_t *ctx = NULL;
245 svn_config_t *cfg_config;
247 SVN_ERR(svn_ra_initialize(pool));
249 SVN_ERR(svn_config_ensure(config_dir, pool));
250 SVN_ERR(svn_client_create_context(&ctx, pool));
252 SVN_ERR(svn_config_get_config(&(ctx->config), config_dir, pool));
254 if (config_options)
255 SVN_ERR(svn_cmdline__apply_config_options(ctx->config, config_options,
256 "svnrdump: ", "--config-option"));
258 cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
259 APR_HASH_KEY_STRING);
261 /* Set up our cancellation support. */
262 ctx->cancel_func = check_cancel;
264 /* Default authentication providers for non-interactive use */
265 SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), non_interactive,
266 username, password, config_dir,
267 no_auth_cache, FALSE, cfg_config,
268 ctx->cancel_func, ctx->cancel_baton,
269 pool));
270 SVN_ERR(svn_client_open_ra_session(session, url, ctx, pool));
271 return SVN_NO_ERROR;
274 /* Replay revisions START_REVISION thru END_REVISION (inclusive) of
275 * the repository located at URL, using callbacks which generate
276 * Subversion repository dumpstreams describing the changes made in
277 * those revisions. If QUIET is set, don't generate progress
278 * messages.
280 static svn_error_t *
281 replay_revisions(svn_ra_session_t *session,
282 const char *url,
283 svn_revnum_t start_revision,
284 svn_revnum_t end_revision,
285 svn_boolean_t quiet,
286 apr_pool_t *pool)
288 const svn_delta_editor_t *dump_editor;
289 struct replay_baton *replay_baton;
290 void *dump_baton;
291 const char *uuid;
292 svn_stream_t *stdout_stream;
294 SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
296 SVN_ERR(get_dump_editor(&dump_editor, &dump_baton, stdout_stream,
297 check_cancel, NULL, pool));
299 replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
300 replay_baton->editor = dump_editor;
301 replay_baton->edit_baton = dump_baton;
302 replay_baton->quiet = quiet;
304 /* Write the magic header and UUID */
305 SVN_ERR(svn_stream_printf(stdout_stream, pool,
306 SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
307 SVN_REPOS_DUMPFILE_FORMAT_VERSION));
308 SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool));
309 SVN_ERR(svn_stream_printf(stdout_stream, pool,
310 SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
312 /* Fake revision 0 if necessary */
313 if (start_revision == 0)
315 apr_hash_t *prophash;
316 svn_stringbuf_t *propstring;
317 svn_stream_t *propstream;
318 SVN_ERR(svn_stream_printf(stdout_stream, pool,
319 SVN_REPOS_DUMPFILE_REVISION_NUMBER
320 ": %ld\n", start_revision));
322 prophash = apr_hash_make(pool);
323 propstring = svn_stringbuf_create("", pool);
325 SVN_ERR(svn_ra_rev_proplist(session, start_revision,
326 &prophash, pool));
328 propstream = svn_stream_from_stringbuf(propstring, pool);
329 SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool));
330 SVN_ERR(svn_stream_close(propstream));
332 /* Property-content-length: 14; Content-length: 14 */
333 SVN_ERR(svn_stream_printf(stdout_stream, pool,
334 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
335 ": %" APR_SIZE_T_FMT "\n",
336 propstring->len));
337 SVN_ERR(svn_stream_printf(stdout_stream, pool,
338 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
339 ": %" APR_SIZE_T_FMT "\n\n",
340 propstring->len));
341 /* The properties */
342 SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
343 &(propstring->len)));
344 SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
345 if (! quiet)
346 svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n",
347 start_revision);
349 start_revision++;
352 SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
353 0, TRUE, replay_revstart, replay_revend,
354 replay_baton, pool));
355 SVN_ERR(svn_stream_close(stdout_stream));
356 return SVN_NO_ERROR;
359 /* Read a dumpstream from stdin, and use it to feed a loader capable
360 * of transmitting that information to the repository located at URL
361 * (to which SESSION has been opened).
363 static svn_error_t *
364 load_revisions(svn_ra_session_t *session,
365 const char *url,
366 svn_boolean_t quiet,
367 apr_pool_t *pool)
369 apr_file_t *stdin_file;
370 svn_stream_t *stdin_stream;
371 const svn_repos_parse_fns2_t *parser;
372 void *parse_baton;
374 apr_file_open_stdin(&stdin_file, pool);
375 stdin_stream = svn_stream_from_aprfile2(stdin_file, FALSE, pool);
377 SVN_ERR(get_dumpstream_loader(&parser, &parse_baton, session, pool));
378 SVN_ERR(drive_dumpstream_loader(stdin_stream, parser, parse_baton,
379 session, check_cancel, NULL, pool));
381 svn_stream_close(stdin_stream);
383 return SVN_NO_ERROR;
386 /* Return a program name for this program, the basename of the path
387 * represented by PROGNAME if not NULL; use "svnrdump" otherwise.
389 static const char *
390 ensure_appname(const char *progname,
391 apr_pool_t *pool)
393 if (!progname)
394 return "svnrdump";
396 return svn_dirent_basename(svn_dirent_internal_style(progname, pool), NULL);
399 /* Print a simple usage string. */
400 static svn_error_t *
401 usage(const char *progname,
402 apr_pool_t *pool)
404 return svn_cmdline_fprintf(stderr, pool,
405 _("Type '%s help' for usage.\n"),
406 ensure_appname(progname, pool));
409 /* Print information about the version of this program and dependent
410 * modules.
412 static svn_error_t *
413 version(const char *progname,
414 apr_pool_t *pool)
416 svn_stringbuf_t *version_footer =
417 svn_stringbuf_create(_("The following repository access (RA) modules "
418 "are available:\n\n"),
419 pool);
421 SVN_ERR(svn_ra_print_modules(version_footer, pool));
422 return svn_opt_print_help3(NULL, ensure_appname(progname, pool),
423 TRUE, FALSE, version_footer->data,
424 NULL, NULL, NULL, NULL, NULL, pool);
428 /* A statement macro, similar to @c SVN_ERR, but returns an integer.
429 * Evaluate @a expr. If it yields an error, handle that error and
430 * return @c EXIT_FAILURE.
432 #define SVNRDUMP_ERR(expr) \
433 do \
435 svn_error_t *svn_err__temp = (expr); \
436 if (svn_err__temp) \
438 svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \
439 svn_error_clear(svn_err__temp); \
440 return EXIT_FAILURE; \
443 while (0)
445 /* Handle the "dump" subcommand. Implements `svn_opt_subcommand_t'. */
446 static svn_error_t *
447 dump_cmd(apr_getopt_t *os,
448 void *baton,
449 apr_pool_t *pool)
451 opt_baton_t *opt_baton = baton;
452 return replay_revisions(opt_baton->session, opt_baton->url,
453 opt_baton->start_revision, opt_baton->end_revision,
454 opt_baton->quiet, pool);
457 /* Handle the "load" subcommand. Implements `svn_opt_subcommand_t'. */
458 static svn_error_t *
459 load_cmd(apr_getopt_t *os,
460 void *baton,
461 apr_pool_t *pool)
463 opt_baton_t *opt_baton = baton;
464 return load_revisions(opt_baton->session, opt_baton->url,
465 opt_baton->quiet, pool);
468 /* Handle the "help" subcommand. Implements `svn_opt_subcommand_t'. */
469 static svn_error_t *
470 help_cmd(apr_getopt_t *os,
471 void *baton,
472 apr_pool_t *pool)
474 const char *header =
475 _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n"
476 "Type 'svnrdump help <subcommand>' for help on a specific subcommand.\n"
477 "\n"
478 "Available subcommands:\n");
480 return svn_opt_print_help3(os, "svnrdump", FALSE, FALSE, NULL, header,
481 svnrdump__cmd_table, svnrdump__options, NULL,
482 NULL, pool);
486 main(int argc, const char **argv)
488 const svn_opt_subcommand_desc2_t *subcommand = NULL;
489 opt_baton_t *opt_baton;
490 char *revision_cut = NULL;
491 svn_revnum_t latest_revision = svn_opt_revision_unspecified;
492 apr_pool_t *pool = NULL;
493 const char *config_dir = NULL;
494 const char *username = NULL;
495 const char *password = NULL;
496 svn_boolean_t no_auth_cache = FALSE;
497 svn_boolean_t non_interactive = FALSE;
498 apr_array_header_t *config_options = NULL;
499 apr_getopt_t *os;
500 const char *first_arg;
502 if (svn_cmdline_init ("svnrdump", stderr) != EXIT_SUCCESS)
503 return EXIT_FAILURE;
505 pool = svn_pool_create(NULL);
506 opt_baton = apr_pcalloc(pool, sizeof(*opt_baton));
507 opt_baton->start_revision = svn_opt_revision_unspecified;
508 opt_baton->end_revision = svn_opt_revision_unspecified;
509 opt_baton->url = NULL;
511 SVNRDUMP_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
513 os->interleave = TRUE; /* Options and arguments can be interleaved */
515 /* Set up our cancellation support. */
516 apr_signal(SIGINT, signal_handler);
517 #ifdef SIGBREAK
518 /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
519 apr_signal(SIGBREAK, signal_handler);
520 #endif
521 #ifdef SIGHUP
522 apr_signal(SIGHUP, signal_handler);
523 #endif
524 #ifdef SIGTERM
525 apr_signal(SIGTERM, signal_handler);
526 #endif
527 #ifdef SIGPIPE
528 /* Disable SIGPIPE generation for the platforms that have it. */
529 apr_signal(SIGPIPE, SIG_IGN);
530 #endif
531 #ifdef SIGXFSZ
532 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
533 * working with large files when compiled against an APR that doesn't have
534 * large file support will crash the program, which is uncool. */
535 apr_signal(SIGXFSZ, SIG_IGN);
536 #endif
538 while (1)
540 int opt;
541 const char *opt_arg;
542 apr_status_t status = apr_getopt_long(os, svnrdump__options, &opt,
543 &opt_arg);
545 if (APR_STATUS_IS_EOF(status))
546 break;
547 if (status != APR_SUCCESS)
549 SVNRDUMP_ERR(usage(argv[0], pool));
550 exit(EXIT_FAILURE);
553 switch(opt)
555 case 'r':
557 revision_cut = strchr(opt_arg, ':');
558 if (revision_cut)
560 opt_baton->start_revision =
561 (svn_revnum_t)strtoul(opt_arg, &revision_cut, 10);
562 opt_baton->end_revision =
563 (svn_revnum_t)strtoul(revision_cut + 1, NULL, 10);
565 else
567 opt_baton->start_revision =
568 (svn_revnum_t)strtoul(opt_arg, NULL, 10);
569 opt_baton->end_revision = opt_baton->start_revision;
572 break;
573 case 'q':
574 opt_baton->quiet = TRUE;
575 break;
576 case opt_config_dir:
577 config_dir = opt_arg;
578 break;
579 case opt_version:
580 SVNRDUMP_ERR(version(argv[0], pool));
581 exit(EXIT_SUCCESS);
582 break;
583 case 'h':
584 SVNRDUMP_ERR(help_cmd(os, opt_baton, pool));
585 exit(EXIT_SUCCESS);
586 break;
587 case opt_auth_username:
588 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool));
589 break;
590 case opt_auth_password:
591 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool));
592 break;
593 case opt_auth_nocache:
594 no_auth_cache = TRUE;
595 break;
596 case opt_non_interactive:
597 non_interactive = TRUE;
598 break;
599 case opt_config_option:
600 if (!config_options)
601 config_options =
602 apr_array_make(pool, 1,
603 sizeof(svn_cmdline__config_argument_t*));
605 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool));
606 SVNRDUMP_ERR(svn_cmdline__parse_config_option(config_options,
607 opt_arg, pool));
611 if (os->ind >= os->argc)
613 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
614 _("Subcommand argument required\n")));
615 SVNRDUMP_ERR(help_cmd(NULL, NULL, pool));
616 svn_pool_destroy(pool);
617 exit(EXIT_FAILURE);
620 first_arg = os->argv[os->ind++];
622 subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table,
623 first_arg);
625 if (subcommand == NULL)
627 const char *first_arg_utf8;
628 svn_error_t *err = svn_utf_cstring_to_utf8(&first_arg_utf8,
629 first_arg, pool);
630 if (err)
631 return svn_cmdline_handle_exit_error(err, pool, "svnrdump: ");
632 svn_error_clear(svn_cmdline_fprintf(stderr, pool,
633 _("Unknown command: '%s'\n"),
634 first_arg_utf8));
635 SVNRDUMP_ERR(help_cmd(NULL, NULL, pool));
636 svn_pool_destroy(pool);
637 exit(EXIT_FAILURE);
640 if (subcommand && strcmp(subcommand->name, "help") == 0)
642 SVNRDUMP_ERR(help_cmd(os, opt_baton, pool));
643 exit(EXIT_SUCCESS);
646 /* Only continue if the only not option argument is a url */
647 if ((os->ind != os->argc-1)
648 || !svn_path_is_url(os->argv[os->ind]))
650 SVNRDUMP_ERR(usage(argv[0], pool));
651 exit(EXIT_FAILURE);
654 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&(opt_baton->url),
655 os->argv[os->ind], pool));
657 opt_baton->url = svn_uri_canonicalize(os->argv[os->ind], pool);
659 SVNRDUMP_ERR(open_connection(&(opt_baton->session),
660 opt_baton->url,
661 non_interactive,
662 username,
663 password,
664 config_dir,
665 no_auth_cache,
666 config_options,
667 pool));
669 /* Have sane opt_baton->start_revision and end_revision defaults if
670 unspecified. */
671 SVNRDUMP_ERR(svn_ra_get_latest_revnum(opt_baton->session,
672 &latest_revision, pool));
673 if (opt_baton->start_revision == svn_opt_revision_unspecified)
674 opt_baton->start_revision = 0;
675 if (opt_baton->end_revision == svn_opt_revision_unspecified)
676 opt_baton->end_revision = latest_revision;
677 if (opt_baton->end_revision > latest_revision)
679 SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
680 _("Revision %ld does not exist.\n"),
681 opt_baton->end_revision));
682 exit(EXIT_FAILURE);
684 if (opt_baton->end_revision < opt_baton->start_revision)
686 SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
687 _("LOWER cannot be greater "
688 "than UPPER.\n")));
689 exit(EXIT_FAILURE);
692 /* Dispatch the subcommand */
693 SVNRDUMP_ERR((*subcommand->cmd_func)(os, opt_baton, pool));
695 svn_pool_destroy(pool);
697 return EXIT_SUCCESS;