diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/svn/info-cmd.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/svn/info-cmd.c')
-rw-r--r-- | subversion/svn/info-cmd.c | 429 |
1 files changed, 375 insertions, 54 deletions
diff --git a/subversion/svn/info-cmd.c b/subversion/svn/info-cmd.c index 56833f6..fafc398 100644 --- a/subversion/svn/info-cmd.c +++ b/subversion/svn/info-cmd.c @@ -76,6 +76,168 @@ schedule_str(svn_wc_schedule_t schedule) } } +/* Return a relative URL from information in INFO using POOL for + temporary allocation. */ +static const char* +relative_url(const svn_client_info2_t *info, apr_pool_t *pool) +{ + return apr_pstrcat(pool, "^/", + svn_path_uri_encode( + svn_uri_skip_ancestor(info->repos_root_URL, + info->URL, pool), + pool), SVN_VA_NULL); +} + + +/* The kinds of items for print_info_item(). */ +typedef enum +{ + /* Entry kind */ + info_item_kind, + + /* Repository location. */ + info_item_url, + info_item_relative_url, + info_item_repos_root_url, + info_item_repos_uuid, + + /* Working copy revision or repository HEAD revision */ + info_item_revision, + + /* Commit details. */ + info_item_last_changed_rev, + info_item_last_changed_date, + info_item_last_changed_author, + + /* Working copy information */ + info_item_wc_root +} info_item_t; + +/* Mapping between option keywords and info_item_t. */ +typedef struct info_item_map_t +{ + const svn_string_t keyword; + const info_item_t print_what; +} info_item_map_t; + +#define MAKE_STRING(x) { x, sizeof(x) - 1 } +static const info_item_map_t info_item_map[] = + { + { MAKE_STRING("kind"), info_item_kind }, + { MAKE_STRING("url"), info_item_url }, + { MAKE_STRING("relative-url"), info_item_relative_url }, + { MAKE_STRING("repos-root-url"), info_item_repos_root_url }, + { MAKE_STRING("repos-uuid"), info_item_repos_uuid }, + { MAKE_STRING("revision"), info_item_revision }, + { MAKE_STRING("last-changed-revision"), + info_item_last_changed_rev }, + { MAKE_STRING("last-changed-date"), info_item_last_changed_date }, + { MAKE_STRING("last-changed-author"), info_item_last_changed_author }, + { MAKE_STRING("wc-root"), info_item_wc_root } + }; +#undef MAKE_STRING + +static const apr_size_t info_item_map_len = + (sizeof(info_item_map) / sizeof(info_item_map[0])); + + +/* The baton type used by the info receiver functions. */ +typedef struct print_info_baton_t +{ + /* The path prefix that output paths should be normalized to. */ + const char *path_prefix; + + /* + * The following fields are used by print_info_item(). + */ + + /* Which item to print. */ + info_item_t print_what; + + /* Do we expect to show info for multiple targets? */ + svn_boolean_t multiple_targets; + + /* TRUE iff the current is a local path. */ + svn_boolean_t target_is_path; + + /* Did we already print a line of output? */ + svn_boolean_t start_new_line; +} print_info_baton_t; + + +/* Find the appropriate info_item_t for KEYWORD and initialize + * RECEIVER_BATON for print_info_item(). Use SCRATCH_POOL for + * temporary allocation. + */ +static svn_error_t * +find_print_what(const char *keyword, + print_info_baton_t *receiver_baton, + apr_pool_t *scratch_pool) +{ + svn_cl__simcheck_t **keywords = apr_palloc( + scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t*)); + svn_cl__simcheck_t *kwbuf = apr_palloc( + scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t)); + apr_size_t i; + + for (i = 0; i < info_item_map_len; ++i) + { + keywords[i] = &kwbuf[i]; + kwbuf[i].token.data = info_item_map[i].keyword.data; + kwbuf[i].token.len = info_item_map[i].keyword.len; + kwbuf[i].data = &info_item_map[i]; + } + + switch (svn_cl__similarity_check(keyword, keywords, + info_item_map_len, scratch_pool)) + { + const info_item_map_t *kw0; + const info_item_map_t *kw1; + const info_item_map_t *kw2; + + case 0: /* Exact match. */ + kw0 = keywords[0]->data; + receiver_baton->print_what = kw0->print_what; + return SVN_NO_ERROR; + + case 1: + /* The best alternative isn't good enough */ + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item"), + keyword); + + case 2: + /* There is only one good candidate */ + kw0 = keywords[0]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s'?"), + keyword, kw0->keyword.data); + + case 3: + /* Suggest a list of the most likely candidates */ + kw0 = keywords[0]->data; + kw1 = keywords[1]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s' or '%s'?"), + keyword, kw0->keyword.data, kw1->keyword.data); + + default: + /* Never suggest more than three candidates */ + kw0 = keywords[0]->data; + kw1 = keywords[1]->data; + kw2 = keywords[2]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s', '%s' or '%s'?"), + keyword, kw0->keyword.data, kw1->keyword.data, kw2->keyword.data); + } +} /* A callback of type svn_client_info_receiver2_t. Prints svn info in xml mode to standard out */ @@ -87,7 +249,7 @@ print_info_xml(void *baton, { svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); const char *rev_str; - const char *path_prefix = baton; + print_info_baton_t *const receiver_baton = baton; if (SVN_IS_VALID_REVNUM(info->rev)) rev_str = apr_psprintf(pool, "%ld", info->rev); @@ -97,10 +259,10 @@ print_info_xml(void *baton, /* "<entry ...>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "path", svn_cl__local_style_skip_ancestor( - path_prefix, target, pool), + receiver_baton->path_prefix, target, pool), "kind", svn_cl__node_kind_str_xml(info->kind), "revision", rev_str, - NULL); + SVN_VA_NULL); /* "<url> xx </url>" */ svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL); @@ -109,19 +271,14 @@ print_info_xml(void *baton, { /* "<relative-url> xx </relative-url>" */ svn_cl__xml_tagged_cdata(&sb, pool, "relative-url", - apr_pstrcat(pool, "^/", - svn_path_uri_encode( - svn_uri_skip_ancestor( - info->repos_root_URL, - info->URL, pool), - pool), - NULL)); + relative_url(info, pool)); } if (info->repos_root_URL || info->repos_UUID) { /* "<repository>" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", + SVN_VA_NULL); /* "<root> xx </root>" */ svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL); @@ -136,7 +293,8 @@ print_info_xml(void *baton, if (info->wc_info) { /* "<wc-info>" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", + SVN_VA_NULL); /* "<wcroot-abspath> xx </wcroot-abspath>" */ if (info->wc_info->wcroot_abspath) @@ -261,11 +419,11 @@ print_info(void *baton, const svn_client_info2_t *info, apr_pool_t *pool) { - const char *path_prefix = baton; + print_info_baton_t *const receiver_baton = baton; SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, target, pool))); + receiver_baton->path_prefix, target, pool))); /* ### remove this someday: it's only here for cmdline output compatibility with svn 1.1 and older. */ @@ -283,11 +441,8 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL)); if (info->URL && info->repos_root_URL) - SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: ^/%s\n"), - svn_path_uri_encode( - svn_uri_skip_ancestor(info->repos_root_URL, - info->URL, pool), - pool))); + SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: %s\n"), + relative_url(info, pool))); if (info->repos_root_URL) SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"), @@ -387,30 +542,18 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"), info->wc_info->copyfrom_rev)); if (info->wc_info->moved_from_abspath) - { - const char *relpath; - - relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath, - info->wc_info->moved_from_abspath); - if (relpath && relpath[0] != '\0') - SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), relpath)); - else - SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), - info->wc_info->moved_from_abspath)); - } + SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + info->wc_info->moved_from_abspath, + pool))); if (info->wc_info->moved_to_abspath) - { - const char *relpath; - - relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath, - info->wc_info->moved_to_abspath); - if (relpath && relpath[0] != '\0') - SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), relpath)); - else - SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), - info->wc_info->moved_to_abspath)); - } + SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + info->wc_info->moved_to_abspath, + pool))); } if (info->last_changed_author) @@ -439,6 +582,7 @@ print_info(void *baton, if (info->wc_info->conflicts) { svn_boolean_t printed_prop_conflict_file = FALSE; + svn_boolean_t printed_tc = FALSE; int i; for (i = 0; i < info->wc_info->conflicts->nelts; i++) @@ -455,21 +599,24 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("Conflict Previous Base File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->base_abspath, + receiver_baton->path_prefix, + conflict->base_abspath, pool))); if (conflict->my_abspath) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Previous Working File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->my_abspath, + receiver_baton->path_prefix, + conflict->my_abspath, pool))); if (conflict->their_abspath) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Current Base File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->their_abspath, + receiver_baton->path_prefix, + conflict->their_abspath, pool))); break; @@ -477,12 +624,15 @@ print_info(void *baton, if (! printed_prop_conflict_file) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Properties File: %s\n"), - svn_dirent_local_style(conflict->their_abspath, - pool))); + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + conflict->prop_reject_abspath, + pool))); printed_prop_conflict_file = TRUE; break; case svn_wc_conflict_kind_tree: + printed_tc = TRUE; SVN_ERR( svn_cl__get_human_readable_tree_conflict_description( &desc, conflict, pool)); @@ -504,6 +654,19 @@ print_info(void *baton, APR_ARRAY_IDX(info->wc_info->conflicts, 0, const svn_wc_conflict_description2_t *); + if (!printed_tc) + { + const char *desc; + + SVN_ERR(svn_cl__get_human_readable_action_description(&desc, + svn_wc_conflict_action_edit, + conflict->operation, + conflict->node_kind, pool)); + + SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n", + _("Conflict Details"), desc)); + } + src_left_version = svn_cl__node_description(conflict->src_left_version, info->repos_root_URL, pool); @@ -570,6 +733,123 @@ print_info(void *baton, } +/* Helper for print_info_item(): Print the value TEXT for TARGET_PATH, + either of which may be NULL. Use POOL for temporary allocation. */ +static svn_error_t * +print_info_item_string(const char *text, const char *target_path, + apr_pool_t *pool) +{ + if (text) + { + if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", text, target_path)); + else + SVN_ERR(svn_cmdline_fputs(text, stdout, pool)); + } + else if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path)); + + return SVN_NO_ERROR; +} + +/* Helper for print_info_item(): Print the revision number REV, which + may be SVN_INVALID_REVNUM, for TARGET_PATH, which may be NULL. Use + POOL for temporary allocation. */ +static svn_error_t * +print_info_item_revision(svn_revnum_t rev, const char *target_path, + apr_pool_t *pool) +{ + if (SVN_IS_VALID_REVNUM(rev)) + { + if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10ld %s", rev, target_path)); + else + SVN_ERR(svn_cmdline_printf(pool, "%ld", rev)); + } + else if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path)); + + return SVN_NO_ERROR; +} + +/* A callback of type svn_client_info_receiver2_t. */ +static svn_error_t * +print_info_item(void *baton, + const char *target, + const svn_client_info2_t *info, + apr_pool_t *pool) +{ + print_info_baton_t *const receiver_baton = baton; + const char *const target_path = + (!receiver_baton->multiple_targets ? NULL + : (!receiver_baton->target_is_path ? info->URL + : svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, target, pool))); + + if (receiver_baton->start_new_line) + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); + + switch (receiver_baton->print_what) + { + case info_item_kind: + SVN_ERR(print_info_item_string(svn_node_kind_to_word(info->kind), + target_path, pool)); + break; + + case info_item_url: + SVN_ERR(print_info_item_string(info->URL, target_path, pool)); + break; + + case info_item_relative_url: + SVN_ERR(print_info_item_string(relative_url(info, pool), + target_path, pool)); + break; + + case info_item_repos_root_url: + SVN_ERR(print_info_item_string(info->repos_root_URL, target_path, pool)); + break; + + case info_item_repos_uuid: + SVN_ERR(print_info_item_string(info->repos_UUID, target_path, pool)); + break; + + case info_item_revision: + SVN_ERR(print_info_item_revision(info->rev, target_path, pool)); + break; + + case info_item_last_changed_rev: + SVN_ERR(print_info_item_revision(info->last_changed_rev, + target_path, pool)); + break; + + case info_item_last_changed_date: + SVN_ERR(print_info_item_string( + (!info->last_changed_date ? NULL + : svn_time_to_cstring(info->last_changed_date, pool)), + target_path, pool)); + break; + + case info_item_last_changed_author: + SVN_ERR(print_info_item_string(info->last_changed_author, + target_path, pool)); + break; + + case info_item_wc_root: + SVN_ERR(print_info_item_string( + (info->wc_info && info->wc_info->wcroot_abspath + ? info->wc_info->wcroot_abspath : NULL), + target_path, pool)); + break; + + default: + SVN_ERR_MALFUNCTION(); + } + + receiver_baton->start_new_line = TRUE; + return SVN_NO_ERROR; +} + + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__info(apr_getopt_t *os, @@ -585,7 +865,7 @@ svn_cl__info(apr_getopt_t *os, svn_boolean_t seen_nonexistent_target = FALSE; svn_opt_revision_t peg_revision; svn_client_info_receiver2_t receiver; - const char *path_prefix; + print_info_baton_t receiver_baton = { 0 }; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, @@ -598,26 +878,59 @@ svn_cl__info(apr_getopt_t *os, { receiver = print_info_xml; + if (opt_state->show_item) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--show-item is not valid in --xml mode")); + if (opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline is not valid in --xml mode")); + /* If output is not incremental, output the XML header and wrap everything in a top-level element. This makes the output in its entirety a well-formed XML document. */ if (! opt_state->incremental) SVN_ERR(svn_cl__xml_print_header("info", pool)); } + else if (opt_state->show_item) + { + receiver = print_info_item; + + if (opt_state->incremental) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--incremental is only valid in --xml mode")); + + receiver_baton.multiple_targets = (opt_state->depth > svn_depth_empty + || targets->nelts > 1); + if (receiver_baton.multiple_targets && opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline is only available for single-target," + " non-recursive info operations")); + + SVN_ERR(find_print_what(opt_state->show_item, &receiver_baton, pool)); + receiver_baton.start_new_line = FALSE; + } else { receiver = print_info; if (opt_state->incremental) - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("'incremental' option only valid in XML " - "mode")); + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--incremental is only valid in --xml mode")); + if (opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline' is only valid with --show-item")); } if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_empty; - SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool)); + SVN_ERR(svn_dirent_get_absolute(&receiver_baton.path_prefix, "", pool)); for (i = 0; i < targets->nelts; i++) { @@ -635,17 +948,22 @@ svn_cl__info(apr_getopt_t *os, { if (peg_revision.kind == svn_opt_revision_unspecified) peg_revision.kind = svn_opt_revision_head; + receiver_baton.target_is_path = FALSE; } else { SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool)); + receiver_baton.target_is_path = TRUE; } - err = svn_client_info3(truepath, + err = svn_client_info4(truepath, &peg_revision, &(opt_state->start_revision), - opt_state->depth, TRUE, TRUE, + opt_state->depth, + TRUE /* fetch_excluded */, + TRUE /* fetch_actual_only */, + opt_state->include_externals, opt_state->changelists, - receiver, (void *) path_prefix, + receiver, &receiver_baton, ctx, subpool); if (err) @@ -672,6 +990,9 @@ svn_cl__info(apr_getopt_t *os, if (opt_state->xml && (! opt_state->incremental)) SVN_ERR(svn_cl__xml_print_footer("info", pool)); + else if (opt_state->show_item && !opt_state->no_newline + && receiver_baton.start_new_line) + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); if (seen_nonexistent_target) return svn_error_create( |