2 * opt.c : option and argument parsing for Subversion command lines
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #define APR_WANT_STRFUNC
26 #include <apr_pools.h>
27 #include <apr_general.h>
29 #include <apr_file_info.h>
31 #include "svn_cmdline.h"
32 #include "svn_version.h"
33 #include "svn_types.h"
35 #include "svn_error.h"
40 #include "svn_private_config.h"
45 const svn_opt_subcommand_desc2_t
*
46 svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t
*table
,
54 while (table
[i
].name
) {
56 if (strcmp(cmd_name
, table
[i
].name
) == 0)
58 for (j
= 0; (j
< SVN_OPT_MAX_ALIASES
) && table
[i
].aliases
[j
]; j
++)
59 if (strcmp(cmd_name
, table
[i
].aliases
[j
]) == 0)
65 /* If we get here, there was no matching subcommand name or alias. */
70 const svn_opt_subcommand_desc_t
*
71 svn_opt_get_canonical_subcommand(const svn_opt_subcommand_desc_t
*table
,
79 while (table
[i
].name
) {
81 if (strcmp(cmd_name
, table
[i
].name
) == 0)
83 for (j
= 0; (j
< SVN_OPT_MAX_ALIASES
) && table
[i
].aliases
[j
]; j
++)
84 if (strcmp(cmd_name
, table
[i
].aliases
[j
]) == 0)
90 /* If we get here, there was no matching subcommand name or alias. */
95 const apr_getopt_option_t
*
96 svn_opt_get_option_from_code2(int code
,
97 const apr_getopt_option_t
*option_table
,
98 const svn_opt_subcommand_desc2_t
*command
,
103 for (i
= 0; option_table
[i
].optch
; i
++)
104 if (option_table
[i
].optch
== code
)
108 for (j
= 0; ((j
< SVN_OPT_MAX_OPTIONS
) &&
109 command
->desc_overrides
[j
].optch
); j
++)
110 if (command
->desc_overrides
[j
].optch
== code
)
112 apr_getopt_option_t
*tmpopt
=
113 apr_palloc(pool
, sizeof(*tmpopt
));
114 *tmpopt
= option_table
[i
];
115 tmpopt
->description
= command
->desc_overrides
[j
].desc
;
118 return &(option_table
[i
]);
125 const apr_getopt_option_t
*
126 svn_opt_get_option_from_code(int code
,
127 const apr_getopt_option_t
*option_table
)
131 for (i
= 0; option_table
[i
].optch
; i
++)
132 if (option_table
[i
].optch
== code
)
133 return &(option_table
[i
]);
140 svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t
*command
,
142 const int *global_options
)
146 for (i
= 0; i
< SVN_OPT_MAX_OPTIONS
; i
++)
147 if (command
->valid_options
[i
] == option_code
)
151 for (i
= 0; global_options
[i
]; i
++)
152 if (global_options
[i
] == option_code
)
159 svn_opt_subcommand_takes_option2(const svn_opt_subcommand_desc2_t
*command
,
162 return svn_opt_subcommand_takes_option3(command
,
169 svn_opt_subcommand_takes_option(const svn_opt_subcommand_desc_t
*command
,
174 for (i
= 0; i
< SVN_OPT_MAX_OPTIONS
; i
++)
175 if (command
->valid_options
[i
] == option_code
)
182 /* Print the canonical command name for CMD, and all its aliases, to
183 STREAM. If HELP is set, print CMD's help string too, in which case
184 obtain option usage from OPTIONS_TABLE. */
186 print_command_info2(const svn_opt_subcommand_desc2_t
*cmd
,
187 const apr_getopt_option_t
*options_table
,
188 const int *global_options
,
193 svn_boolean_t first_time
;
196 /* Print the canonical command name. */
197 SVN_ERR(svn_cmdline_fputs(cmd
->name
, stream
, pool
));
199 /* Print the list of aliases. */
201 for (i
= 0; i
< SVN_OPT_MAX_ALIASES
; i
++)
203 if (cmd
->aliases
[i
] == NULL
)
207 SVN_ERR(svn_cmdline_fputs(" (", stream
, pool
));
211 SVN_ERR(svn_cmdline_fputs(", ", stream
, pool
));
213 SVN_ERR(svn_cmdline_fputs(cmd
->aliases
[i
], stream
, pool
));
217 SVN_ERR(svn_cmdline_fputs(")", stream
, pool
));
221 const apr_getopt_option_t
*option
;
222 svn_boolean_t have_options
= FALSE
;
224 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, ": %s", _(cmd
->help
)));
226 /* Loop over all valid option codes attached to the subcommand */
227 for (i
= 0; i
< SVN_OPT_MAX_OPTIONS
; i
++)
229 if (cmd
->valid_options
[i
])
231 if (have_options
== FALSE
)
233 SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
238 /* convert each option code into an option */
240 svn_opt_get_option_from_code2(cmd
->valid_options
[i
],
244 /* print the option's docstring */
248 svn_opt_format_option(&optstr
, option
, TRUE
, pool
);
249 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, " %s\n",
254 /* And global options too */
255 if (global_options
&& *global_options
)
257 SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
261 for (i
= 0; global_options
[i
]; i
++)
264 /* convert each option code into an option */
266 svn_opt_get_option_from_code2(global_options
[i
],
270 /* print the option's docstring */
274 svn_opt_format_option(&optstr
, option
, TRUE
, pool
);
275 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, " %s\n",
282 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, "\n"));
289 /* Same as print_command_info2(), but with deprecated struct revision. */
291 print_command_info(const svn_opt_subcommand_desc_t
*cmd
,
292 const apr_getopt_option_t
*options_table
,
297 svn_boolean_t first_time
;
300 /* Print the canonical command name. */
301 SVN_ERR(svn_cmdline_fputs(cmd
->name
, stream
, pool
));
303 /* Print the list of aliases. */
305 for (i
= 0; i
< SVN_OPT_MAX_ALIASES
; i
++)
307 if (cmd
->aliases
[i
] == NULL
)
311 SVN_ERR(svn_cmdline_fputs(" (", stream
, pool
));
315 SVN_ERR(svn_cmdline_fputs(", ", stream
, pool
));
317 SVN_ERR(svn_cmdline_fputs(cmd
->aliases
[i
], stream
, pool
));
321 SVN_ERR(svn_cmdline_fputs(")", stream
, pool
));
325 const apr_getopt_option_t
*option
;
326 svn_boolean_t have_options
= FALSE
;
328 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, ": %s", _(cmd
->help
)));
330 /* Loop over all valid option codes attached to the subcommand */
331 for (i
= 0; i
< SVN_OPT_MAX_OPTIONS
; i
++)
333 if (cmd
->valid_options
[i
])
335 if (have_options
== FALSE
)
337 SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
342 /* convert each option code into an option */
344 svn_opt_get_option_from_code(cmd
->valid_options
[i
],
347 /* print the option's docstring */
351 svn_opt_format_option(&optstr
, option
, TRUE
, pool
);
352 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, " %s\n",
359 SVN_ERR(svn_cmdline_fprintf(stream
, pool
, "\n"));
367 svn_opt_print_generic_help2(const char *header
,
368 const svn_opt_subcommand_desc2_t
*cmd_table
,
369 const apr_getopt_option_t
*opt_table
,
371 apr_pool_t
*pool
, FILE *stream
)
377 if ((err
= svn_cmdline_fputs(header
, stream
, pool
)))
380 while (cmd_table
[i
].name
)
382 if ((err
= svn_cmdline_fputs(" ", stream
, pool
))
383 || (err
= print_command_info2(cmd_table
+ i
, opt_table
,
386 || (err
= svn_cmdline_fputs("\n", stream
, pool
)))
391 if ((err
= svn_cmdline_fputs("\n", stream
, pool
)))
395 if ((err
= svn_cmdline_fputs(footer
, stream
, pool
)))
401 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
402 svn_error_clear(err
);
407 svn_opt_print_generic_help(const char *header
,
408 const svn_opt_subcommand_desc_t
*cmd_table
,
409 const apr_getopt_option_t
*opt_table
,
411 apr_pool_t
*pool
, FILE *stream
)
417 if ((err
= svn_cmdline_fputs(header
, stream
, pool
)))
420 while (cmd_table
[i
].name
)
422 if ((err
= svn_cmdline_fputs(" ", stream
, pool
))
423 || (err
= print_command_info(cmd_table
+ i
, opt_table
, FALSE
,
425 || (err
= svn_cmdline_fputs("\n", stream
, pool
)))
430 if ((err
= svn_cmdline_fputs("\n", stream
, pool
)))
434 if ((err
= svn_cmdline_fputs(footer
, stream
, pool
)))
440 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
441 svn_error_clear(err
);
446 svn_opt_format_option(const char **string
,
447 const apr_getopt_option_t
*opt
,
459 /* We have a valid option which may or may not have a "short
460 name" (a single-character alias for the long option). */
461 if (opt
->optch
<= 255)
462 opts
= apr_psprintf(pool
, "-%c [--%s]", opt
->optch
, opt
->name
);
464 opts
= apr_psprintf(pool
, "--%s", opt
->name
);
467 opts
= apr_pstrcat(pool
, opts
, _(" ARG"), NULL
);
470 opts
= apr_psprintf(pool
, "%-24s : %s", opts
, _(opt
->description
));
477 svn_opt_subcommand_help3(const char *subcommand
,
478 const svn_opt_subcommand_desc2_t
*table
,
479 const apr_getopt_option_t
*options_table
,
480 const int *global_options
,
483 const svn_opt_subcommand_desc2_t
*cmd
=
484 svn_opt_get_canonical_subcommand2(table
, subcommand
);
488 err
= print_command_info2(cmd
, options_table
, global_options
,
491 err
= svn_cmdline_fprintf(stderr
, pool
,
492 _("\"%s\": unknown command.\n\n"), subcommand
);
495 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
496 svn_error_clear(err
);
501 svn_opt_subcommand_help2(const char *subcommand
,
502 const svn_opt_subcommand_desc2_t
*table
,
503 const apr_getopt_option_t
*options_table
,
506 svn_opt_subcommand_help3(subcommand
, table
, options_table
,
512 svn_opt_subcommand_help(const char *subcommand
,
513 const svn_opt_subcommand_desc_t
*table
,
514 const apr_getopt_option_t
*options_table
,
517 const svn_opt_subcommand_desc_t
*cmd
=
518 svn_opt_get_canonical_subcommand(table
, subcommand
);
522 err
= print_command_info(cmd
, options_table
, TRUE
, pool
, stdout
);
524 err
= svn_cmdline_fprintf(stderr
, pool
,
525 _("\"%s\": unknown command.\n\n"), subcommand
);
528 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
529 svn_error_clear(err
);
535 /*** Parsing revision and date options. ***/
538 /** Parsing "X:Y"-style arguments. **/
540 /* If WORD matches one of the special revision descriptors,
541 * case-insensitively, set *REVISION accordingly:
543 * - For "head", set REVISION->kind to svn_opt_revision_head.
545 * - For "prev", set REVISION->kind to svn_opt_revision_previous.
547 * - For "base", set REVISION->kind to svn_opt_revision_base.
549 * - For "committed", set REVISION->kind to svn_opt_revision_committed.
551 * If match, return 0, else return -1 and don't touch REVISION.
554 revision_from_word(svn_opt_revision_t
*revision
, const char *word
)
556 if (svn_cstring_casecmp(word
, "head") == 0)
558 revision
->kind
= svn_opt_revision_head
;
560 else if (svn_cstring_casecmp(word
, "prev") == 0)
562 revision
->kind
= svn_opt_revision_previous
;
564 else if (svn_cstring_casecmp(word
, "base") == 0)
566 revision
->kind
= svn_opt_revision_base
;
568 else if (svn_cstring_casecmp(word
, "committed") == 0)
570 revision
->kind
= svn_opt_revision_committed
;
579 /* Parse one revision specification. Return pointer to character
580 after revision, or NULL if the revision is invalid. Modifies
581 str, so make sure to pass a copy of anything precious. Uses
582 POOL for temporary allocation. */
583 static char *parse_one_rev(svn_opt_revision_t
*revision
, char *str
,
590 svn_boolean_t matched
;
594 /* Brackets denote a date. */
596 end
= strchr(str
, '}');
600 err
= svn_parse_date(&matched
, &tm
, str
, apr_time_now(), pool
);
603 svn_error_clear(err
);
608 revision
->kind
= svn_opt_revision_date
;
609 revision
->value
.date
= tm
;
612 else if (apr_isdigit(*str
))
616 while (apr_isdigit(*end
))
620 revision
->kind
= svn_opt_revision_number
;
621 revision
->value
.number
= SVN_STR_TO_REV(str
);
625 else if (apr_isalpha(*str
))
628 while (apr_isalpha(*end
))
632 if (revision_from_word(revision
, str
) != 0)
643 svn_opt_parse_revision(svn_opt_revision_t
*start_revision
,
644 svn_opt_revision_t
*end_revision
,
648 char *left_rev
, *right_rev
, *end
;
650 /* Operate on a copy of the argument. */
651 left_rev
= apr_pstrdup(pool
, arg
);
653 right_rev
= parse_one_rev(start_revision
, left_rev
, pool
);
654 if (right_rev
&& *right_rev
== ':')
657 end
= parse_one_rev(end_revision
, right_rev
, pool
);
658 if (!end
|| *end
!= '\0')
661 else if (!right_rev
|| *right_rev
!= '\0')
669 svn_opt_parse_revision_to_range(apr_array_header_t
*opt_ranges
,
673 svn_opt_revision_range_t
*range
= apr_palloc(pool
, sizeof(*range
));
675 range
->start
.kind
= svn_opt_revision_unspecified
;
676 range
->end
.kind
= svn_opt_revision_unspecified
;
678 if (svn_opt_parse_revision(&(range
->start
), &(range
->end
),
682 APR_ARRAY_PUSH(opt_ranges
, svn_opt_revision_range_t
*) = range
;
687 svn_opt_resolve_revisions(svn_opt_revision_t
*peg_rev
,
688 svn_opt_revision_t
*op_rev
,
689 svn_boolean_t is_url
,
690 svn_boolean_t notice_local_mods
,
693 if (peg_rev
->kind
== svn_opt_revision_unspecified
)
697 peg_rev
->kind
= svn_opt_revision_head
;
701 if (notice_local_mods
)
702 peg_rev
->kind
= svn_opt_revision_working
;
704 peg_rev
->kind
= svn_opt_revision_base
;
708 if (op_rev
->kind
== svn_opt_revision_unspecified
)
715 /*** Parsing arguments. ***/
716 #define DEFAULT_ARRAY_SIZE 5
719 /* Copy STR into POOL and push the copy onto ARRAY. */
721 array_push_str(apr_array_header_t
*array
,
725 /* ### Not sure if this function is still necessary. It used to
726 convert str to svn_stringbuf_t * and push it, but now it just
727 dups str in pool and pushes the copy. So its only effect is
728 transfer str's lifetime to pool. Is that something callers are
731 APR_ARRAY_PUSH(array
, const char *) = apr_pstrdup(pool
, str
);
736 svn_opt_push_implicit_dot_target(apr_array_header_t
*targets
,
739 if (targets
->nelts
== 0)
740 array_push_str(targets
, "", pool
); /* Ha! "", not ".", is the canonical */
741 assert(targets
->nelts
);
746 svn_opt_parse_num_args(apr_array_header_t
**args_p
,
752 apr_array_header_t
*args
753 = apr_array_make(pool
, DEFAULT_ARRAY_SIZE
, sizeof(const char *));
755 /* loop for num_args and add each arg to the args array */
756 for (i
= 0; i
< num_args
; i
++)
758 if (os
->ind
>= os
->argc
)
760 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS
, 0, NULL
);
762 array_push_str(args
, os
->argv
[os
->ind
++], pool
);
770 svn_opt_parse_all_args(apr_array_header_t
**args_p
,
774 apr_array_header_t
*args
775 = apr_array_make(pool
, DEFAULT_ARRAY_SIZE
, sizeof(const char *));
777 if (os
->ind
> os
->argc
)
779 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, 0, NULL
);
781 while (os
->ind
< os
->argc
)
783 array_push_str(args
, os
->argv
[os
->ind
++], pool
);
792 svn_opt_parse_path(svn_opt_revision_t
*rev
,
793 const char **truepath
,
794 const char *path
/* UTF-8! */,
799 /* scanning from right to left, just to be friendly to any
800 screwed-up filenames that might *actually* contain @-signs. :-) */
801 for (i
= (strlen(path
) - 1); i
>= 0; i
--)
803 /* If we hit a path separator, stop looking. */
804 /* This is OK only because our revision specifiers can't contain '/'. */
811 svn_opt_revision_t start_revision
, end_revision
;
813 end_revision
.kind
= svn_opt_revision_unspecified
;
815 if (path
[i
+ 1] == '\0') /* looking at empty peg revision */
818 start_revision
.kind
= svn_opt_revision_unspecified
;
820 else /* looking at non-empty peg revision */
822 const char *rev_str
= path
+ i
+ 1;
824 /* URLs get treated differently from wc paths. */
825 if (svn_path_is_url(path
))
827 /* URLs are URI-encoded, so we look for dates with
828 URI-encoded delimeters. */
829 int rev_len
= strlen(rev_str
);
833 && (rev_str
[2] == 'B'
834 || rev_str
[2] == 'b')
835 && rev_str
[rev_len
-3] == '%'
836 && rev_str
[rev_len
-2] == '7'
837 && (rev_str
[rev_len
-1] == 'D'
838 || rev_str
[rev_len
-1] == 'd'))
840 rev_str
= svn_path_uri_decode(rev_str
, pool
);
843 ret
= svn_opt_parse_revision(&start_revision
,
848 if (ret
|| end_revision
.kind
!= svn_opt_revision_unspecified
)
849 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
850 _("Syntax error parsing revision '%s'"),
853 *truepath
= apr_pstrmemdup(pool
, path
, i
);
854 rev
->kind
= start_revision
.kind
;
855 rev
->value
= start_revision
.value
;
861 /* Didn't find an @-sign. */
863 rev
->kind
= svn_opt_revision_unspecified
;
870 svn_opt_args_to_target_array2(apr_array_header_t
**targets_p
,
872 apr_array_header_t
*known_targets
,
875 svn_error_t
*err
= svn_opt_args_to_target_array3(targets_p
, os
,
876 known_targets
, pool
);
878 if (err
&& err
->apr_err
== SVN_ERR_RESERVED_FILENAME_SPECIFIED
)
880 svn_error_clear(err
);
889 svn_opt_args_to_target_array3(apr_array_header_t
**targets_p
,
891 apr_array_header_t
*known_targets
,
895 svn_error_t
*err
= SVN_NO_ERROR
;
896 apr_array_header_t
*input_targets
=
897 apr_array_make(pool
, DEFAULT_ARRAY_SIZE
, sizeof(const char *));
898 apr_array_header_t
*output_targets
=
899 apr_array_make(pool
, DEFAULT_ARRAY_SIZE
, sizeof(const char *));
901 /* Step 1: create a master array of targets that are in UTF-8
902 encoding, and come from concatenating the targets left by apr_getopt,
903 plus any extra targets (e.g., from the --targets switch.) */
905 for (; os
->ind
< os
->argc
; os
->ind
++)
907 /* The apr_getopt targets are still in native encoding. */
908 const char *raw_target
= os
->argv
[os
->ind
];
909 SVN_ERR(svn_utf_cstring_to_utf8
910 ((const char **) apr_array_push(input_targets
),
916 for (i
= 0; i
< known_targets
->nelts
; i
++)
918 /* The --targets array have already been converted to UTF-8,
919 because we needed to split up the list with svn_cstring_split. */
920 const char *utf8_target
= APR_ARRAY_IDX(known_targets
,
922 APR_ARRAY_PUSH(input_targets
, const char *) = utf8_target
;
926 /* Step 2: process each target. */
928 for (i
= 0; i
< input_targets
->nelts
; i
++)
930 const char *utf8_target
= APR_ARRAY_IDX(input_targets
, i
, const char *);
931 const char *peg_start
= NULL
; /* pointer to the peg revision, if any */
932 const char *target
; /* after all processing is finished */
935 /* Remove a peg revision, if any, in the target so that it can
936 be properly canonicalized, otherwise the canonicalization
937 does not treat a ".@BASE" as a "." with a BASE peg revision,
938 and it is not canonicalized to "@BASE". If any peg revision
939 exists, it is appended to the final canonicalized path or
940 URL. Do not use svn_opt_parse_path() because the resulting
941 peg revision is a structure that would have to be converted
942 back into a string. Converting from a string date to the
943 apr_time_t field in the svn_opt_revision_value_t and back to
944 a string would not necessarily preserve the exact bytes of
945 the input date, so its easier just to keep it in string
947 for (j
= (strlen(utf8_target
) - 1); j
>= 0; --j
)
949 /* If we hit a path separator, stop looking. This is OK
950 only because our revision specifiers can't contain
952 if (utf8_target
[j
] == '/')
954 if (utf8_target
[j
] == '@')
956 peg_start
= utf8_target
+ j
;
961 utf8_target
= apr_pstrmemdup(pool
,
963 peg_start
- utf8_target
);
965 /* URLs and wc-paths get treated differently. */
966 if (svn_path_is_url(utf8_target
))
968 /* No need to canonicalize a URL's case or path separators. */
970 /* Convert to URI. */
971 target
= svn_path_uri_from_iri(utf8_target
, pool
);
972 /* Auto-escape some ASCII characters. */
973 target
= svn_path_uri_autoescape(target
, pool
);
975 /* The above doesn't guarantee a valid URI. */
976 if (! svn_path_is_uri_safe(target
))
977 return svn_error_createf(SVN_ERR_BAD_URL
, 0,
978 _("URL '%s' is not properly URI-encoded"),
981 /* Verify that no backpaths are present in the URL. */
982 if (svn_path_is_backpath_present(target
))
983 return svn_error_createf(SVN_ERR_BAD_URL
, 0,
984 _("URL '%s' contains a '..' element"),
987 /* strip any trailing '/' */
988 target
= svn_path_canonicalize(target
, pool
);
990 else /* not a url, so treat as a path */
992 const char *apr_target
;
993 const char *base_name
;
994 char *truenamed_target
; /* APR-encoded */
995 apr_status_t apr_err
;
997 /* canonicalize case, and change all separators to '/'. */
998 SVN_ERR(svn_path_cstring_from_utf8(&apr_target
, utf8_target
,
1000 apr_err
= apr_filepath_merge(&truenamed_target
, "", apr_target
,
1001 APR_FILEPATH_TRUENAME
, pool
);
1004 /* We have a canonicalized APR-encoded target now. */
1005 apr_target
= truenamed_target
;
1006 else if (APR_STATUS_IS_ENOENT(apr_err
))
1007 /* It's okay for the file to not exist, that just means we
1008 have to accept the case given to the client. We'll use
1009 the original APR-encoded target. */
1012 return svn_error_createf(apr_err
, NULL
,
1013 _("Error resolving case of '%s'"),
1014 svn_path_local_style(utf8_target
,
1017 /* convert back to UTF-8. */
1018 SVN_ERR(svn_path_cstring_to_utf8(&target
, apr_target
, pool
));
1019 target
= svn_path_canonicalize(target
, pool
);
1021 /* If the target has the same name as a Subversion
1022 working copy administrative dir, skip it. */
1023 base_name
= svn_path_basename(target
, pool
);
1025 The canonical list of administrative directory names is
1026 maintained in libsvn_wc/adm_files.c:svn_wc_set_adm_dir().
1027 That list can't be used here, because that use would
1028 create a circular dependency between libsvn_wc and
1029 libsvn_subr. Make sure changes to the lists are always
1031 if (0 == strcmp(base_name
, ".svn")
1032 || 0 == strcmp(base_name
, "_svn"))
1034 err
= svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED
,
1035 err
, _("'%s' ends in a reserved name"),
1041 /* Append the peg revision back to the canonicalized target if
1042 there was a peg revision. */
1044 target
= apr_pstrcat(pool
, target
, peg_start
, NULL
);
1046 APR_ARRAY_PUSH(output_targets
, const char *) = target
;
1050 /* kff todo: need to remove redundancies from targets before
1051 passing it to the cmd_func. */
1053 *targets_p
= output_targets
;
1060 svn_opt_args_to_target_array(apr_array_header_t
**targets_p
,
1062 apr_array_header_t
*known_targets
,
1063 svn_opt_revision_t
*start_revision
,
1064 svn_opt_revision_t
*end_revision
,
1065 svn_boolean_t extract_revisions
,
1068 apr_array_header_t
*output_targets
;
1070 SVN_ERR(svn_opt_args_to_target_array2(&output_targets
, os
,
1071 known_targets
, pool
));
1073 if (extract_revisions
)
1075 svn_opt_revision_t temprev
;
1078 if (output_targets
->nelts
> 0)
1080 path
= APR_ARRAY_IDX(output_targets
, 0, const char *);
1081 SVN_ERR(svn_opt_parse_path(&temprev
, &path
, path
, pool
));
1082 if (temprev
.kind
!= svn_opt_revision_unspecified
)
1084 APR_ARRAY_IDX(output_targets
, 0, const char *) = path
;
1085 start_revision
->kind
= temprev
.kind
;
1086 start_revision
->value
= temprev
.value
;
1089 if (output_targets
->nelts
> 1)
1091 path
= APR_ARRAY_IDX(output_targets
, 1, const char *);
1092 SVN_ERR(svn_opt_parse_path(&temprev
, &path
, path
, pool
));
1093 if (temprev
.kind
!= svn_opt_revision_unspecified
)
1095 APR_ARRAY_IDX(output_targets
, 1, const char *) = path
;
1096 end_revision
->kind
= temprev
.kind
;
1097 end_revision
->value
= temprev
.value
;
1102 *targets_p
= output_targets
;
1103 return SVN_NO_ERROR
;
1108 svn_opt_parse_revprop(apr_hash_t
**revprop_table_p
, const char *revprop_spec
,
1111 const char *sep
, *propname
;
1112 svn_string_t
*propval
;
1114 if (! *revprop_spec
)
1115 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
1116 _("Revision property pair is empty"));
1118 if (! *revprop_table_p
)
1119 *revprop_table_p
= apr_hash_make(pool
);
1121 sep
= strchr(revprop_spec
, '=');
1124 propname
= apr_pstrndup(pool
, revprop_spec
, sep
- revprop_spec
);
1125 SVN_ERR(svn_utf_cstring_to_utf8(&propname
, propname
, pool
));
1126 propval
= svn_string_create(sep
+ 1, pool
);
1130 SVN_ERR(svn_utf_cstring_to_utf8(&propname
, revprop_spec
, pool
));
1131 propval
= svn_string_create("", pool
);
1134 if (!svn_prop_name_is_valid(propname
))
1135 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME
, NULL
,
1136 _("'%s' is not a valid Subversion property name"),
1139 apr_hash_set(*revprop_table_p
, propname
, APR_HASH_KEY_STRING
, propval
);
1141 return SVN_NO_ERROR
;
1145 /* Print version info for PGM_NAME. If QUIET is true, print in
1146 * brief. Else if QUIET is not true, print the version more
1147 * verbosely, and if FOOTER is non-null, print it following the
1148 * version information.
1150 * Use POOL for temporary allocations.
1152 static svn_error_t
*
1153 print_version_info(const char *pgm_name
,
1155 svn_boolean_t quiet
,
1160 SVN_ERR(svn_cmdline_printf(pool
, "%s\n", SVN_VER_NUMBER
));
1161 return SVN_NO_ERROR
;
1164 SVN_ERR(svn_cmdline_printf(pool
, _("%s, version %s\n"
1165 " compiled %s, %s\n\n"), pgm_name
,
1166 SVN_VERSION
, __DATE__
, __TIME__
));
1167 SVN_ERR(svn_cmdline_fputs(_("Copyright (C) 2000-2008 CollabNet.\n"
1168 "Subversion is open source software, see"
1169 " http://subversion.tigris.org/\n"
1170 "This product includes software developed by "
1171 "CollabNet (http://www.Collab.Net/).\n\n"),
1176 SVN_ERR(svn_cmdline_printf(pool
, "%s\n", footer
));
1179 return SVN_NO_ERROR
;
1184 svn_opt_print_help3(apr_getopt_t
*os
,
1185 const char *pgm_name
,
1186 svn_boolean_t print_version
,
1187 svn_boolean_t quiet
,
1188 const char *version_footer
,
1190 const svn_opt_subcommand_desc2_t
*cmd_table
,
1191 const apr_getopt_option_t
*option_table
,
1192 const int *global_options
,
1196 apr_array_header_t
*targets
= NULL
;
1200 SVN_ERR(svn_opt_parse_all_args(&targets
, os
, pool
));
1202 if (os
&& targets
->nelts
) /* help on subcommand(s) requested */
1203 for (i
= 0; i
< targets
->nelts
; i
++)
1205 svn_opt_subcommand_help3(APR_ARRAY_IDX(targets
, i
, const char *),
1206 cmd_table
, option_table
,
1207 global_options
, pool
);
1209 else if (print_version
) /* just --version */
1210 SVN_ERR(print_version_info(pgm_name
, version_footer
, quiet
, pool
));
1211 else if (os
&& !targets
->nelts
) /* `-h', `--help', or `help' */
1212 svn_opt_print_generic_help2(header
,
1218 else /* unknown option or cmd */
1219 SVN_ERR(svn_cmdline_fprintf(stderr
, pool
,
1220 _("Type '%s help' for usage.\n"), pgm_name
));
1222 return SVN_NO_ERROR
;
1227 svn_opt_print_help2(apr_getopt_t
*os
,
1228 const char *pgm_name
,
1229 svn_boolean_t print_version
,
1230 svn_boolean_t quiet
,
1231 const char *version_footer
,
1233 const svn_opt_subcommand_desc2_t
*cmd_table
,
1234 const apr_getopt_option_t
*option_table
,
1238 return svn_opt_print_help3(os
,
1253 svn_opt_print_help(apr_getopt_t
*os
,
1254 const char *pgm_name
,
1255 svn_boolean_t print_version
,
1256 svn_boolean_t quiet
,
1257 const char *version_footer
,
1259 const svn_opt_subcommand_desc_t
*cmd_table
,
1260 const apr_getopt_option_t
*option_table
,
1264 apr_array_header_t
*targets
= NULL
;
1268 SVN_ERR(svn_opt_parse_all_args(&targets
, os
, pool
));
1270 if (os
&& targets
->nelts
) /* help on subcommand(s) requested */
1271 for (i
= 0; i
< targets
->nelts
; i
++)
1273 svn_opt_subcommand_help(APR_ARRAY_IDX(targets
, i
, const char *),
1274 cmd_table
, option_table
, pool
);
1276 else if (print_version
) /* just --version */
1277 SVN_ERR(print_version_info(pgm_name
, version_footer
, quiet
, pool
));
1278 else if (os
&& !targets
->nelts
) /* `-h', `--help', or `help' */
1279 svn_opt_print_generic_help(header
,
1285 else /* unknown option or cmd */
1286 SVN_ERR(svn_cmdline_fprintf(stderr
, pool
,
1287 _("Type '%s help' for usage.\n"), pgm_name
));
1289 return SVN_NO_ERROR
;