2 * opt.c : option and argument parsing for Subversion command lines
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 * ====================================================================
26 #define APR_WANT_STRFUNC
32 #include <apr_pools.h>
33 #include <apr_general.h>
35 #include <apr_file_info.h>
38 #include "svn_cmdline.h"
39 #include "svn_version.h"
40 #include "svn_types.h"
42 #include "svn_error.h"
43 #include "svn_dirent_uri.h"
47 #include "svn_props.h"
48 #include "svn_ctype.h"
50 #include "private/svn_opt_private.h"
53 #include "svn_private_config.h"
58 const svn_opt_subcommand_desc2_t *
59 svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t *table,
67 while (table[i].name) {
69 if (strcmp(cmd_name, table[i].name) == 0)
71 for (j = 0; (j < SVN_OPT_MAX_ALIASES) && table[i].aliases[j]; j++)
72 if (strcmp(cmd_name, table[i].aliases[j]) == 0)
78 /* If we get here, there was no matching subcommand name or alias. */
82 const apr_getopt_option_t *
83 svn_opt_get_option_from_code2(int code,
84 const apr_getopt_option_t *option_table,
85 const svn_opt_subcommand_desc2_t *command,
90 for (i = 0; option_table[i].optch; i++)
91 if (option_table[i].optch == code)
97 for (j = 0; ((j < SVN_OPT_MAX_OPTIONS) &&
98 command->desc_overrides[j].optch); j++)
99 if (command->desc_overrides[j].optch == code)
101 apr_getopt_option_t *tmpopt =
102 apr_palloc(pool, sizeof(*tmpopt));
103 *tmpopt = option_table[i];
104 tmpopt->description = command->desc_overrides[j].desc;
108 return &(option_table[i]);
115 const apr_getopt_option_t *
116 svn_opt_get_option_from_code(int code,
117 const apr_getopt_option_t *option_table)
121 for (i = 0; option_table[i].optch; i++)
122 if (option_table[i].optch == code)
123 return &(option_table[i]);
129 /* Like svn_opt_get_option_from_code2(), but also, if CODE appears a second
130 * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
131 * second name, else set it to NULL. */
132 static const apr_getopt_option_t *
133 get_option_from_code(const char **long_alias,
135 const apr_getopt_option_t *option_table,
136 const svn_opt_subcommand_desc2_t *command,
139 const apr_getopt_option_t *i;
140 const apr_getopt_option_t *opt
141 = svn_opt_get_option_from_code2(code, option_table, command, pool);
143 /* Find a long alias in the table, if there is one. */
145 for (i = option_table; i->optch; i++)
147 if (i->optch == code && i->name != opt->name)
149 *long_alias = i->name;
158 /* Print an option OPT nicely into a STRING allocated in POOL.
159 * If OPT has a single-character short form, then print OPT->name (if not
160 * NULL) as an alias, else print LONG_ALIAS (if not NULL) as an alias.
161 * If DOC is set, include the generic documentation string of OPT,
162 * localized to the current locale if a translation is available.
165 format_option(const char **string,
166 const apr_getopt_option_t *opt,
167 const char *long_alias,
179 /* We have a valid option which may or may not have a "short
180 name" (a single-character alias for the long option). */
181 if (opt->optch <= 255)
182 opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
184 opts = apr_psprintf(pool, "--%s [--%s]", opt->name, long_alias);
186 opts = apr_psprintf(pool, "--%s", opt->name);
189 opts = apr_pstrcat(pool, opts, _(" ARG"), SVN_VA_NULL);
192 opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
198 svn_opt_format_option(const char **string,
199 const apr_getopt_option_t *opt,
203 format_option(string, opt, NULL, doc, pool);
208 svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
210 const int *global_options)
214 for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
215 if (command->valid_options[i] == option_code)
219 for (i = 0; global_options[i]; i++)
220 if (global_options[i] == option_code)
227 svn_opt_subcommand_takes_option2(const svn_opt_subcommand_desc2_t *command,
230 return svn_opt_subcommand_takes_option3(command,
237 svn_opt_subcommand_takes_option(const svn_opt_subcommand_desc_t *command,
242 for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
243 if (command->valid_options[i] == option_code)
250 /* Print the canonical command name for CMD, and all its aliases, to
251 STREAM. If HELP is set, print CMD's help string too, in which case
252 obtain option usage from OPTIONS_TABLE. */
254 print_command_info2(const svn_opt_subcommand_desc2_t *cmd,
255 const apr_getopt_option_t *options_table,
256 const int *global_options,
261 svn_boolean_t first_time;
264 /* Print the canonical command name. */
265 SVN_ERR(svn_cmdline_fputs(cmd->name, stream, pool));
267 /* Print the list of aliases. */
269 for (i = 0; i < SVN_OPT_MAX_ALIASES; i++)
271 if (cmd->aliases[i] == NULL)
275 SVN_ERR(svn_cmdline_fputs(" (", stream, pool));
279 SVN_ERR(svn_cmdline_fputs(", ", stream, pool));
281 SVN_ERR(svn_cmdline_fputs(cmd->aliases[i], stream, pool));
285 SVN_ERR(svn_cmdline_fputs(")", stream, pool));
289 const apr_getopt_option_t *option;
290 const char *long_alias;
291 svn_boolean_t have_options = FALSE;
293 SVN_ERR(svn_cmdline_fprintf(stream, pool, ": %s", _(cmd->help)));
295 /* Loop over all valid option codes attached to the subcommand */
296 for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
298 if (cmd->valid_options[i])
302 SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
307 /* convert each option code into an option */
308 option = get_option_from_code(&long_alias, cmd->valid_options[i],
309 options_table, cmd, pool);
311 /* print the option's docstring */
312 if (option && option->description)
315 format_option(&optstr, option, long_alias, TRUE, pool);
316 SVN_ERR(svn_cmdline_fprintf(stream, pool, " %s\n",
321 /* And global options too */
322 if (global_options && *global_options)
324 SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
328 for (i = 0; global_options[i]; i++)
331 /* convert each option code into an option */
332 option = get_option_from_code(&long_alias, global_options[i],
333 options_table, cmd, pool);
335 /* print the option's docstring */
336 if (option && option->description)
339 format_option(&optstr, option, long_alias, TRUE, pool);
340 SVN_ERR(svn_cmdline_fprintf(stream, pool, " %s\n",
347 SVN_ERR(svn_cmdline_fprintf(stream, pool, "\n"));
353 /* The body for svn_opt_print_generic_help2() function with standard error
354 * handling semantic. Handling of errors implemented at caller side. */
356 print_generic_help_body(const char *header,
357 const svn_opt_subcommand_desc2_t *cmd_table,
358 const apr_getopt_option_t *opt_table,
360 apr_pool_t *pool, FILE *stream)
365 SVN_ERR(svn_cmdline_fputs(header, stream, pool));
367 while (cmd_table[i].name)
369 SVN_ERR(svn_cmdline_fputs(" ", stream, pool));
370 SVN_ERR(print_command_info2(cmd_table + i, opt_table,
373 SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
377 SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
380 SVN_ERR(svn_cmdline_fputs(footer, stream, pool));
386 svn_opt_print_generic_help2(const char *header,
387 const svn_opt_subcommand_desc2_t *cmd_table,
388 const apr_getopt_option_t *opt_table,
390 apr_pool_t *pool, FILE *stream)
394 err = print_generic_help_body(header, cmd_table, opt_table, footer, pool,
398 * Don't print anything on broken pipes. The pipe was likely
399 * closed by the process at the other end. We expect that
400 * process to perform error reporting as necessary.
402 * ### This assumes that there is only one error in a chain for
403 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
404 if (err && err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
405 svn_handle_error2(err, stderr, FALSE, "svn: ");
406 svn_error_clear(err);
411 svn_opt_subcommand_help3(const char *subcommand,
412 const svn_opt_subcommand_desc2_t *table,
413 const apr_getopt_option_t *options_table,
414 const int *global_options,
417 const svn_opt_subcommand_desc2_t *cmd =
418 svn_opt_get_canonical_subcommand2(table, subcommand);
422 err = print_command_info2(cmd, options_table, global_options,
425 err = svn_cmdline_fprintf(stderr, pool,
426 _("\"%s\": unknown command.\n\n"), subcommand);
429 /* Issue #3014: Don't print anything on broken pipes. */
430 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
431 svn_handle_error2(err, stderr, FALSE, "svn: ");
432 svn_error_clear(err);
438 /*** Parsing revision and date options. ***/
441 /** Parsing "X:Y"-style arguments. **/
443 /* If WORD matches one of the special revision descriptors,
444 * case-insensitively, set *REVISION accordingly:
446 * - For "head", set REVISION->kind to svn_opt_revision_head.
448 * - For "prev", set REVISION->kind to svn_opt_revision_previous.
450 * - For "base", set REVISION->kind to svn_opt_revision_base.
452 * - For "committed", set REVISION->kind to svn_opt_revision_committed.
454 * If match, return 0, else return -1 and don't touch REVISION.
457 revision_from_word(svn_opt_revision_t *revision, const char *word)
459 if (svn_cstring_casecmp(word, "head") == 0)
461 revision->kind = svn_opt_revision_head;
463 else if (svn_cstring_casecmp(word, "prev") == 0)
465 revision->kind = svn_opt_revision_previous;
467 else if (svn_cstring_casecmp(word, "base") == 0)
469 revision->kind = svn_opt_revision_base;
471 else if (svn_cstring_casecmp(word, "committed") == 0)
473 revision->kind = svn_opt_revision_committed;
482 /* Parse one revision specification. Return pointer to character
483 after revision, or NULL if the revision is invalid. Modifies
484 str, so make sure to pass a copy of anything precious. Uses
485 POOL for temporary allocation. */
486 static char *parse_one_rev(svn_opt_revision_t *revision, char *str,
491 /* Allow any number of 'r's to prefix a revision number, because
492 that way if a script pastes svn output into another svn command
493 (like "svn log -r${REV_COPIED_FROM_OUTPUT}"), it'll Just Work,
494 even when compounded.
496 As it happens, none of our special revision words begins with
497 "r". If any ever do, then this code will have to get smarter.
499 Incidentally, this allows "r{DATE}". We could avoid that with
500 some trivial code rearrangement, but it's not clear what would
501 be gained by doing so. */
507 svn_boolean_t matched;
511 /* Brackets denote a date. */
513 end = strchr(str, '}');
517 err = svn_parse_date(&matched, &tm, str, apr_time_now(), pool);
520 svn_error_clear(err);
525 revision->kind = svn_opt_revision_date;
526 revision->value.date = tm;
529 else if (svn_ctype_isdigit(*str))
533 while (svn_ctype_isdigit(*end))
537 revision->kind = svn_opt_revision_number;
538 revision->value.number = SVN_STR_TO_REV(str);
542 else if (svn_ctype_isalpha(*str))
545 while (svn_ctype_isalpha(*end))
549 if (revision_from_word(revision, str) != 0)
560 svn_opt_parse_revision(svn_opt_revision_t *start_revision,
561 svn_opt_revision_t *end_revision,
565 char *left_rev, *right_rev, *end;
567 /* Operate on a copy of the argument. */
568 left_rev = apr_pstrdup(pool, arg);
570 right_rev = parse_one_rev(start_revision, left_rev, pool);
571 if (right_rev && *right_rev == ':')
574 end = parse_one_rev(end_revision, right_rev, pool);
575 if (!end || *end != '\0')
578 else if (!right_rev || *right_rev != '\0')
586 svn_opt_parse_revision_to_range(apr_array_header_t *opt_ranges,
590 svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
592 range->start.kind = svn_opt_revision_unspecified;
593 range->end.kind = svn_opt_revision_unspecified;
595 if (svn_opt_parse_revision(&(range->start), &(range->end),
599 APR_ARRAY_PUSH(opt_ranges, svn_opt_revision_range_t *) = range;
604 svn_opt_resolve_revisions(svn_opt_revision_t *peg_rev,
605 svn_opt_revision_t *op_rev,
606 svn_boolean_t is_url,
607 svn_boolean_t notice_local_mods,
610 if (peg_rev->kind == svn_opt_revision_unspecified)
614 peg_rev->kind = svn_opt_revision_head;
618 if (notice_local_mods)
619 peg_rev->kind = svn_opt_revision_working;
621 peg_rev->kind = svn_opt_revision_base;
625 if (op_rev->kind == svn_opt_revision_unspecified)
632 svn_opt__revision_to_string(const svn_opt_revision_t *revision,
633 apr_pool_t *result_pool)
635 switch (revision->kind)
637 case svn_opt_revision_unspecified:
638 return "unspecified";
639 case svn_opt_revision_number:
640 return apr_psprintf(result_pool, "%ld", revision->value.number);
641 case svn_opt_revision_date:
642 /* ### svn_time_to_human_cstring()? */
643 return svn_time_to_cstring(revision->value.date, result_pool);
644 case svn_opt_revision_committed:
646 case svn_opt_revision_previous:
648 case svn_opt_revision_base:
650 case svn_opt_revision_working:
652 case svn_opt_revision_head:
659 svn_opt_revision_range_t *
660 svn_opt__revision_range_create(const svn_opt_revision_t *start_revision,
661 const svn_opt_revision_t *end_revision,
662 apr_pool_t *result_pool)
664 svn_opt_revision_range_t *range = apr_palloc(result_pool, sizeof(*range));
666 range->start = *start_revision;
667 range->end = *end_revision;
671 svn_opt_revision_range_t *
672 svn_opt__revision_range_from_revnums(svn_revnum_t start_revnum,
673 svn_revnum_t end_revnum,
674 apr_pool_t *result_pool)
676 svn_opt_revision_range_t *range = apr_palloc(result_pool, sizeof(*range));
678 range->start.kind = svn_opt_revision_number;
679 range->start.value.number = start_revnum;
680 range->end.kind = svn_opt_revision_number;
681 range->end.value.number = end_revnum;
687 /*** Parsing arguments. ***/
688 #define DEFAULT_ARRAY_SIZE 5
691 /* Copy STR into POOL and push the copy onto ARRAY. */
693 array_push_str(apr_array_header_t *array,
697 /* ### Not sure if this function is still necessary. It used to
698 convert str to svn_stringbuf_t * and push it, but now it just
699 dups str in pool and pushes the copy. So its only effect is
700 transfer str's lifetime to pool. Is that something callers are
703 APR_ARRAY_PUSH(array, const char *) = apr_pstrdup(pool, str);
708 svn_opt_push_implicit_dot_target(apr_array_header_t *targets,
711 if (targets->nelts == 0)
712 APR_ARRAY_PUSH(targets, const char *) = ""; /* Ha! "", not ".", is the canonical */
713 assert(targets->nelts);
718 svn_opt_parse_num_args(apr_array_header_t **args_p,
724 apr_array_header_t *args
725 = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
727 /* loop for num_args and add each arg to the args array */
728 for (i = 0; i < num_args; i++)
730 if (os->ind >= os->argc)
732 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
734 array_push_str(args, os->argv[os->ind++], pool);
742 svn_opt_parse_all_args(apr_array_header_t **args_p,
746 apr_array_header_t *args
747 = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
749 if (os->ind > os->argc)
751 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
753 while (os->ind < os->argc)
755 array_push_str(args, os->argv[os->ind++], pool);
764 svn_opt_parse_path(svn_opt_revision_t *rev,
765 const char **truepath,
766 const char *path /* UTF-8! */,
771 SVN_ERR(svn_opt__split_arg_at_peg_revision(truepath, &peg_rev, path, pool));
773 /* Parse the peg revision, if one was found */
777 svn_opt_revision_t start_revision, end_revision;
779 end_revision.kind = svn_opt_revision_unspecified;
781 if (peg_rev[1] == '\0') /* looking at empty peg revision */
784 start_revision.kind = svn_opt_revision_unspecified;
785 start_revision.value.number = 0;
787 else /* looking at non-empty peg revision */
789 const char *rev_str = &peg_rev[1];
791 /* URLs get treated differently from wc paths. */
792 if (svn_path_is_url(path))
794 /* URLs are URI-encoded, so we look for dates with
795 URI-encoded delimiters. */
796 size_t rev_len = strlen(rev_str);
800 && (rev_str[2] == 'B'
801 || rev_str[2] == 'b')
802 && rev_str[rev_len-3] == '%'
803 && rev_str[rev_len-2] == '7'
804 && (rev_str[rev_len-1] == 'D'
805 || rev_str[rev_len-1] == 'd'))
807 rev_str = svn_path_uri_decode(rev_str, pool);
810 ret = svn_opt_parse_revision(&start_revision,
815 if (ret || end_revision.kind != svn_opt_revision_unspecified)
817 /* If an svn+ssh URL was used and it contains only one @,
818 * provide an error message that presents a possible solution
819 * to the parsing error (issue #2349). */
820 if (strncmp(path, "svn+ssh://", 10) == 0)
824 at = strchr(path, '@');
825 if (at && strrchr(path, '@') == at)
826 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
827 _("Syntax error parsing peg revision "
828 "'%s'; did you mean '%s@'?"),
832 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
833 _("Syntax error parsing peg revision '%s'"),
836 rev->kind = start_revision.kind;
837 rev->value = start_revision.value;
841 /* Didn't find a peg revision. */
842 rev->kind = svn_opt_revision_unspecified;
849 /* Note: This is substantially copied into svn_client_args_to_target_array() in
850 * order to move to libsvn_client while maintaining backward compatibility. */
852 svn_opt__args_to_target_array(apr_array_header_t **targets_p,
854 const apr_array_header_t *known_targets,
858 svn_error_t *err = SVN_NO_ERROR;
859 apr_array_header_t *input_targets =
860 apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
861 apr_array_header_t *output_targets =
862 apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
864 /* Step 1: create a master array of targets that are in UTF-8
865 encoding, and come from concatenating the targets left by apr_getopt,
866 plus any extra targets (e.g., from the --targets switch.) */
868 for (; os->ind < os->argc; os->ind++)
870 /* The apr_getopt targets are still in native encoding. */
871 const char *raw_target = os->argv[os->ind];
872 SVN_ERR(svn_utf_cstring_to_utf8
873 ((const char **) apr_array_push(input_targets),
879 for (i = 0; i < known_targets->nelts; i++)
881 /* The --targets array have already been converted to UTF-8,
882 because we needed to split up the list with svn_cstring_split. */
883 const char *utf8_target = APR_ARRAY_IDX(known_targets,
885 APR_ARRAY_PUSH(input_targets, const char *) = utf8_target;
889 /* Step 2: process each target. */
891 for (i = 0; i < input_targets->nelts; i++)
893 const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *);
894 const char *true_target;
895 const char *target; /* after all processing is finished */
899 * This is needed so that the target can be properly canonicalized,
900 * otherwise the canonicalization does not treat a ".@BASE" as a "."
901 * with a BASE peg revision, and it is not canonicalized to "@BASE".
902 * If any peg revision exists, it is appended to the final
903 * canonicalized path or URL. Do not use svn_opt_parse_path()
904 * because the resulting peg revision is a structure that would have
905 * to be converted back into a string. Converting from a string date
906 * to the apr_time_t field in the svn_opt_revision_value_t and back to
907 * a string would not necessarily preserve the exact bytes of the
908 * input date, so its easier just to keep it in string form.
910 SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev,
913 /* URLs and wc-paths get treated differently. */
914 if (svn_path_is_url(true_target))
916 SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, true_target,
919 else /* not a url, so treat as a path */
921 const char *base_name;
923 SVN_ERR(svn_opt__arg_canonicalize_path(&true_target, true_target,
926 /* If the target has the same name as a Subversion
927 working copy administrative dir, skip it. */
928 base_name = svn_dirent_basename(true_target, pool);
931 The canonical list of administrative directory names is
932 maintained in libsvn_wc/adm_files.c:svn_wc_set_adm_dir().
933 That list can't be used here, because that use would
934 create a circular dependency between libsvn_wc and
935 libsvn_subr. Make sure changes to the lists are always
937 if (0 == strcmp(base_name, ".svn")
938 || 0 == strcmp(base_name, "_svn"))
940 err = svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED,
941 err, _("'%s' ends in a reserved name"),
947 target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL);
949 APR_ARRAY_PUSH(output_targets, const char *) = target;
953 /* kff todo: need to remove redundancies from targets before
954 passing it to the cmd_func. */
956 *targets_p = output_targets;
962 svn_opt_parse_revprop(apr_hash_t **revprop_table_p, const char *revprop_spec,
965 const char *sep, *propname;
966 svn_string_t *propval;
969 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
970 _("Revision property pair is empty"));
972 if (! *revprop_table_p)
973 *revprop_table_p = apr_hash_make(pool);
975 sep = strchr(revprop_spec, '=');
978 propname = apr_pstrndup(pool, revprop_spec, sep - revprop_spec);
979 SVN_ERR(svn_utf_cstring_to_utf8(&propname, propname, pool));
980 propval = svn_string_create(sep + 1, pool);
984 SVN_ERR(svn_utf_cstring_to_utf8(&propname, revprop_spec, pool));
985 propval = svn_string_create_empty(pool);
988 if (!svn_prop_name_is_valid(propname))
989 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
990 _("'%s' is not a valid Subversion property name"),
993 svn_hash_sets(*revprop_table_p, propname, propval);
999 svn_opt__split_arg_at_peg_revision(const char **true_target,
1000 const char **peg_revision,
1001 const char *utf8_target,
1004 const char *peg_start = NULL; /* pointer to the peg revision, if any */
1007 for (ptr = (utf8_target + strlen(utf8_target) - 1); ptr >= utf8_target;
1010 /* If we hit a path separator, stop looking. This is OK
1011 only because our revision specifiers can't contain '/'. */
1024 *true_target = apr_pstrmemdup(pool, utf8_target, ptr - utf8_target);
1026 *peg_revision = apr_pstrdup(pool, peg_start);
1030 *true_target = utf8_target;
1035 return SVN_NO_ERROR;
1039 svn_opt__arg_canonicalize_url(const char **url_out, const char *url_in,
1044 /* Convert to URI. */
1045 target = svn_path_uri_from_iri(url_in, pool);
1046 /* Auto-escape some ASCII characters. */
1047 target = svn_path_uri_autoescape(target, pool);
1049 #if '/' != SVN_PATH_LOCAL_SEPARATOR
1050 /* Allow using file:///C:\users\me/repos on Windows, like we did in 1.6 */
1051 if (strchr(target, SVN_PATH_LOCAL_SEPARATOR))
1053 char *p = apr_pstrdup(pool, target);
1056 /* Convert all local-style separators to the canonical ones. */
1057 for (; *p != '\0'; ++p)
1058 if (*p == SVN_PATH_LOCAL_SEPARATOR)
1063 /* Verify that no backpaths are present in the URL. */
1064 if (svn_path_is_backpath_present(target))
1065 return svn_error_createf(SVN_ERR_BAD_URL, 0,
1066 _("URL '%s' contains a '..' element"),
1069 /* Strip any trailing '/' and collapse other redundant elements. */
1070 target = svn_uri_canonicalize(target, pool);
1073 return SVN_NO_ERROR;
1077 svn_opt__arg_canonicalize_path(const char **path_out, const char *path_in,
1080 const char *apr_target;
1081 char *truenamed_target; /* APR-encoded */
1082 apr_status_t apr_err;
1084 /* canonicalize case, and change all separators to '/'. */
1085 SVN_ERR(svn_path_cstring_from_utf8(&apr_target, path_in, pool));
1086 apr_err = apr_filepath_merge(&truenamed_target, "", apr_target,
1087 APR_FILEPATH_TRUENAME, pool);
1090 /* We have a canonicalized APR-encoded target now. */
1091 apr_target = truenamed_target;
1092 else if (APR_STATUS_IS_ENOENT(apr_err))
1093 /* It's okay for the file to not exist, that just means we
1094 have to accept the case given to the client. We'll use
1095 the original APR-encoded target. */
1098 return svn_error_createf(apr_err, NULL,
1099 _("Error resolving case of '%s'"),
1100 svn_dirent_local_style(path_in, pool));
1102 /* convert back to UTF-8. */
1103 SVN_ERR(svn_path_cstring_to_utf8(path_out, apr_target, pool));
1104 *path_out = svn_dirent_canonicalize(*path_out, pool);
1106 return SVN_NO_ERROR;
1111 svn_opt__print_version_info(const char *pgm_name,
1113 const svn_version_extended_t *info,
1114 svn_boolean_t quiet,
1115 svn_boolean_t verbose,
1119 return svn_cmdline_printf(pool, "%s\n", SVN_VER_NUMBER);
1121 SVN_ERR(svn_cmdline_printf(pool, _("%s, version %s\n"
1122 " compiled on %s\n\n"),
1123 pgm_name, SVN_VERSION,
1124 svn_version_ext_build_host(info)));
1125 SVN_ERR(svn_cmdline_printf(pool, "%s\n", svn_version_ext_copyright(info)));
1129 SVN_ERR(svn_cmdline_printf(pool, "%s\n", footer));
1134 const apr_array_header_t *libs;
1136 SVN_ERR(svn_cmdline_fputs(_("System information:\n\n"), stdout, pool));
1137 SVN_ERR(svn_cmdline_printf(pool, _("* running on %s\n"),
1138 svn_version_ext_runtime_host(info)));
1139 if (svn_version_ext_runtime_osname(info))
1141 SVN_ERR(svn_cmdline_printf(pool, _(" - %s\n"),
1142 svn_version_ext_runtime_osname(info)));
1145 libs = svn_version_ext_linked_libs(info);
1146 if (libs && libs->nelts)
1148 const svn_version_ext_linked_lib_t *lib;
1151 SVN_ERR(svn_cmdline_fputs(_("* linked dependencies:\n"),
1153 for (i = 0; i < libs->nelts; ++i)
1155 lib = &APR_ARRAY_IDX(libs, i, svn_version_ext_linked_lib_t);
1156 if (lib->runtime_version)
1157 SVN_ERR(svn_cmdline_printf(pool,
1158 " - %s %s (compiled with %s)\n",
1160 lib->runtime_version,
1161 lib->compiled_version));
1163 SVN_ERR(svn_cmdline_printf(pool,
1164 " - %s %s (static)\n",
1166 lib->compiled_version));
1170 libs = svn_version_ext_loaded_libs(info);
1171 if (libs && libs->nelts)
1173 const svn_version_ext_loaded_lib_t *lib;
1176 SVN_ERR(svn_cmdline_fputs(_("* loaded shared libraries:\n"),
1178 for (i = 0; i < libs->nelts; ++i)
1180 lib = &APR_ARRAY_IDX(libs, i, svn_version_ext_loaded_lib_t);
1182 SVN_ERR(svn_cmdline_printf(pool,
1184 lib->name, lib->version));
1186 SVN_ERR(svn_cmdline_printf(pool, " - %s\n", lib->name));
1191 return SVN_NO_ERROR;
1195 svn_opt_print_help4(apr_getopt_t *os,
1196 const char *pgm_name,
1197 svn_boolean_t print_version,
1198 svn_boolean_t quiet,
1199 svn_boolean_t verbose,
1200 const char *version_footer,
1202 const svn_opt_subcommand_desc2_t *cmd_table,
1203 const apr_getopt_option_t *option_table,
1204 const int *global_options,
1208 apr_array_header_t *targets = NULL;
1211 SVN_ERR(svn_opt_parse_all_args(&targets, os, pool));
1213 if (os && targets->nelts) /* help on subcommand(s) requested */
1217 for (i = 0; i < targets->nelts; i++)
1219 svn_opt_subcommand_help3(APR_ARRAY_IDX(targets, i, const char *),
1220 cmd_table, option_table,
1221 global_options, pool);
1224 else if (print_version) /* just --version */
1226 SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
1227 svn_version_extended(verbose, pool),
1228 quiet, verbose, pool));
1230 else if (os && !targets->nelts) /* `-h', `--help', or `help' */
1231 svn_opt_print_generic_help2(header,
1237 else /* unknown option or cmd */
1238 SVN_ERR(svn_cmdline_fprintf(stderr, pool,
1239 _("Type '%s help' for usage.\n"), pgm_name));
1241 return SVN_NO_ERROR;