Modified: subversion/branches/tree-read-api/subversion/svn/props.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svn/props.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svn/props.c (original) +++ subversion/branches/tree-read-api/subversion/svn/props.c Sun Jan 6 02:33:34 2013 @@ -83,121 +83,6 @@ svn_cl__revprop_prepare(const svn_opt_re return SVN_NO_ERROR; } - -svn_error_t * -svn_cl__print_prop_hash(svn_stream_t *out, - apr_hash_t *prop_hash, - svn_boolean_t names_only, - apr_pool_t *pool) -{ - apr_array_header_t *sorted_props; - int i; - - sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, - pool); - for (i = 0; i < sorted_props->nelts; i++) - { - svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); - const char *pname = item.key; - svn_string_t *propval = item.value; - const char *pname_stdout; - - if (svn_prop_needs_translation(pname)) - SVN_ERR(svn_subst_detranslate_string(&propval, propval, - TRUE, pool)); - - SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool)); - - if (out) - { - pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout); - SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout, - APR_EOL_STR, /* 'native' eol */ - FALSE, /* no repair */ - NULL, /* no keywords */ - FALSE, /* no expansion */ - pool)); - - SVN_ERR(svn_stream_puts(out, pname_stdout)); - } - else - { - /* ### We leave these printfs for now, since if propval wasn't - translated above, we don't know anything about its encoding. - In fact, it might be binary data... */ - printf(" %s\n", pname_stdout); - } - - if (!names_only) - { - /* Add an extra newline to the value before indenting, so that - * every line of output has the indentation whether the value - * already ended in a newline or not. */ - const char *newval = apr_psprintf(pool, "%s\n", propval->data); - const char *indented_newval = svn_cl__indent_string(newval, - " ", - pool); - if (out) - { - SVN_ERR(svn_stream_puts(out, indented_newval)); - } - else - { - printf("%s", indented_newval); - } - } - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cl__print_xml_prop_hash(svn_stringbuf_t **outstr, - apr_hash_t *prop_hash, - svn_boolean_t names_only, - svn_boolean_t inherited_props, - apr_pool_t *pool) -{ - apr_array_header_t *sorted_props; - int i; - - if (*outstr == NULL) - *outstr = svn_stringbuf_create_empty(pool); - - sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, - pool); - for (i = 0; i < sorted_props->nelts; i++) - { - svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); - const char *pname = item.key; - svn_string_t *propval = item.value; - - if (names_only) - { - svn_xml_make_open_tag( - outstr, pool, svn_xml_self_closing, - inherited_props ? "inherited_property" : "property", - "name", pname, NULL); - } - else - { - const char *pname_out; - - if (svn_prop_needs_translation(pname)) - SVN_ERR(svn_subst_detranslate_string(&propval, propval, - TRUE, pool)); - - SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool)); - - svn_cmdline__print_xml_prop(outstr, pname_out, propval, - inherited_props, pool); - } - } - - return SVN_NO_ERROR; -} - - void svn_cl__check_boolean_prop_val(const char *propname, const char *propval, apr_pool_t *pool)
Modified: subversion/branches/tree-read-api/subversion/svn/status.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svn/status.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svn/status.c (original) +++ subversion/branches/tree-read-api/subversion/svn/status.c Sun Jan 6 02:33:34 2013 @@ -377,7 +377,7 @@ print_status(const char *cwd_abspath, co SVN_ERR (svn_cmdline_printf(pool, - "%c%c%c%c%c%c%c %c %6s %6s %-12s %s%s%s%s\n", + "%c%c%c%c%c%c%c %c %8s %8s %-12s %s%s%s%s\n", generate_status_code(combined_status(status)), generate_status_code(prop_status), status->wc_is_locked ? 'L' : ' ', @@ -396,7 +396,7 @@ print_status(const char *cwd_abspath, co } else SVN_ERR( - svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %6s %s%s%s%s\n", + svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %8s %s%s%s%s\n", generate_status_code(combined_status(status)), generate_status_code(prop_status), status->wc_is_locked ? 'L' : ' ', Modified: subversion/branches/tree-read-api/subversion/svn/svn.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svn/svn.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svn/svn.c (original) +++ subversion/branches/tree-read-api/subversion/svn/svn.c Sun Jan 6 02:33:34 2013 @@ -1,5 +1,5 @@ /* - * main.c: Subversion command line client. + * svn.c: Subversion command line client main file. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -101,6 +101,7 @@ typedef enum svn_cl__longopt_t { opt_no_ignore, opt_no_unlock, opt_non_interactive, + opt_force_interactive, opt_old_cmd, opt_record_only, opt_relocate, @@ -229,7 +230,13 @@ const apr_getopt_option_t svn_cl__option " " "with '--non-interactive')") }, {"non-interactive", opt_non_interactive, 0, - N_("do no interactive prompting")}, + N_("do no interactive prompting (default is to prompt\n" + " " + "only if standard input is a terminal device)")}, + {"force-interactive", opt_force_interactive, 0, + N_("do interactive prompting even if standard input\n" + " " + "is not a terminal device")}, {"dry-run", opt_dry_run, 0, N_("try operation but make no changes")}, {"ignore-ancestry", opt_ignore_ancestry, 0, @@ -401,7 +408,8 @@ const apr_getopt_option_t svn_cl__option willy-nilly to every invocation of 'svn') . */ const int svn_cl__global_options[] = { opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive, - opt_trust_server_cert, opt_config_dir, opt_config_options, 0 + opt_force_interactive, opt_trust_server_cert, opt_config_dir, + opt_config_options, 0 }; /* Options for giving a log message. (Some of these also have other uses.) @@ -730,7 +738,7 @@ const svn_opt_subcommand_desc2_t svn_cl_ " (the 'automatic' merge)\n" " 2. merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n" " (the 'cherry-pick' merge)\n" -" 3. merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n" +" 3. merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n" " (the '2-URL' merge)\n" "\n" " 1. This form, with one source path and no revision range, is called\n" @@ -915,26 +923,27 @@ const svn_opt_subcommand_desc2_t svn_cl_ "\n" " 3. This form is called a '2-URL merge':\n" "\n" -" svn merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n" -"\n" -" Two source URLs are specified, together with two revisions N and M.\n" -" The two sources are compared at the specified revisions, and the\n" -" difference is applied to TARGET_WCPATH, which is a path to a working\n" -" copy of another branch. The three branches involved can be completely\n" -" unrelated.\n" +" svn merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n" "\n" " You should use this merge variant only if the other variants do not\n" " apply to your situation, as this variant can be quite complex to\n" " master.\n" "\n" +" Two source URLs are specified, identifying two trees on the same\n" +" branch or on different branches. The trees are compared and the\n" +" difference from SOURCE1@REV1 to SOURCE2@REV2 is applied to the\n" +" working copy of the target branch at TARGET_WCPATH. The target\n" +" branch may be the same as one or both sources, or different again.\n" +" The three branches involved can be completely unrelated.\n" +"\n" " If TARGET_WCPATH is omitted, a default value of '.' is assumed.\n" " However, in the special case where both sources refer to a file node\n" -" with the same basename and a similarly named file is also found within\n" -" '.', the differences will be applied to that local file. The source\n" -" revisions default to HEAD if omitted.\n" +" with the same name and a file with the same name is also found within\n" +" '.', the differences will be applied to that local file. The source\n" +" revisions REV1 and REV2 default to HEAD if omitted.\n" "\n" -" The sources can also be specified as working copy paths, in which case\n" -" the URLs of the merge sources are derived from the working copies.\n" +" SOURCE1 and/or SOURCE2 can also be specified as a working copy path,\n" +" in which case the merge source URL is derived from the working copy.\n" "\n" " - 2-URL Merge Example -\n" "\n" @@ -1036,7 +1045,7 @@ const svn_opt_subcommand_desc2_t svn_cl_ " repositories.\n"), {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd, opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate, - opt_allow_mixed_revisions} }, + opt_allow_mixed_revisions, 'v'} }, { "mergeinfo", svn_cl__mergeinfo, {0}, N_ ("Display merge-related information.\n" @@ -1659,6 +1668,7 @@ sub_main(int argc, const char *argv[], a svn_config_t *cfg_config; svn_boolean_t descend = TRUE; svn_boolean_t interactive_conflicts = FALSE; + svn_boolean_t force_interactive = FALSE; svn_boolean_t use_notifier = TRUE; apr_hash_t *changelists; const char *sqlite_exclusive; @@ -1982,6 +1992,9 @@ sub_main(int argc, const char *argv[], a case opt_non_interactive: opt_state.non_interactive = TRUE; break; + case opt_force_interactive: + force_interactive = TRUE; + break; case opt_trust_server_cert: opt_state.trust_server_cert = TRUE; break; @@ -2083,6 +2096,12 @@ sub_main(int argc, const char *argv[], a break; case opt_changelist: opt_state.changelist = apr_pstrdup(pool, opt_arg); + if (opt_state.changelist[0] == '\0') + { + err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Changelist names must not be empty")); + return svn_cmdline_handle_exit_error(err, pool, "svn: "); + } apr_hash_set(changelists, opt_state.changelist, APR_HASH_KEY_STRING, (void *)1); break; @@ -2191,6 +2210,20 @@ sub_main(int argc, const char *argv[], a } } + /* The --non-interactive and --force-interactive options are mutually + * exclusive. */ + if (opt_state.non_interactive && force_interactive) + { + err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); + return EXIT_ERROR(err); + } + else + opt_state.non_interactive = !svn_cmdline__be_interactive( + opt_state.non_interactive, + force_interactive); + /* Turn our hash of changelists into an array of unique ones. */ SVN_INT_ERR(svn_hash_keys(&(opt_state.changelists), changelists, pool)); Modified: subversion/branches/tree-read-api/subversion/svn/util.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svn/util.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svn/util.c (original) +++ subversion/branches/tree-read-api/subversion/svn/util.c Sun Jan 6 02:33:34 2013 @@ -62,6 +62,7 @@ #include "private/svn_token.h" #include "private/svn_opt_private.h" #include "private/svn_client_private.h" +#include "private/svn_cmdline_private.h" #include "private/svn_string_private.h" @@ -93,125 +94,6 @@ svn_cl__print_commit_info(const svn_comm } -/* Helper for the next two functions. Set *EDITOR to some path to an - editor binary. Sources to search include: the EDITOR_CMD argument - (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG - is not NULL), $VISUAL, $EDITOR. Return - SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */ -static svn_error_t * -find_editor_binary(const char **editor, - const char *editor_cmd, - apr_hash_t *config) -{ - const char *e; - struct svn_config_t *cfg; - - /* Use the editor specified on the command line via --editor-cmd, if any. */ - e = editor_cmd; - - /* Otherwise look for the Subversion-specific environment variable. */ - if (! e) - e = getenv("SVN_EDITOR"); - - /* If not found then fall back on the config file. */ - if (! e) - { - cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING) : NULL; - svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS, - SVN_CONFIG_OPTION_EDITOR_CMD, NULL); - } - - /* If not found yet then try general purpose environment variables. */ - if (! e) - e = getenv("VISUAL"); - if (! e) - e = getenv("EDITOR"); - -#ifdef SVN_CLIENT_EDITOR - /* If still not found then fall back on the hard-coded default. */ - if (! e) - e = SVN_CLIENT_EDITOR; -#endif - - /* Error if there is no editor specified */ - if (e) - { - const char *c; - - for (c = e; *c; c++) - if (!svn_ctype_isspace(*c)) - break; - - if (! *c) - return svn_error_create - (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL, - _("The EDITOR, SVN_EDITOR or VISUAL environment variable or " - "'editor-cmd' run-time configuration option is empty or " - "consists solely of whitespace. Expected a shell command.")); - } - else - return svn_error_create - (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL, - _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are " - "set, and no 'editor-cmd' run-time configuration option was found")); - - *editor = e; - return SVN_NO_ERROR; -} - - -/* Use the visual editor to edit files. This requires that the file name itself - be shell-safe, although the path to reach that file need not be shell-safe. - */ -svn_error_t * -svn_cl__edit_file_externally(const char *path, - const char *editor_cmd, - apr_hash_t *config, - apr_pool_t *pool) -{ - const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr; - char *old_cwd; - int sys_err; - apr_status_t apr_err; - - svn_dirent_split(&base_dir, &file_name, path, pool); - - SVN_ERR(find_editor_binary(&editor, editor_cmd, config)); - - apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool); - if (apr_err) - return svn_error_wrap_apr(apr_err, _("Can't get working directory")); - - /* APR doesn't like "" directories */ - if (base_dir[0] == '\0') - base_dir_apr = "."; - else - SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool)); - - apr_err = apr_filepath_set(base_dir_apr, pool); - if (apr_err) - return svn_error_wrap_apr - (apr_err, _("Can't change working directory to '%s'"), base_dir); - - cmd = apr_psprintf(pool, "%s %s", editor, file_name); - sys_err = system(cmd); - - apr_err = apr_filepath_set(old_cwd, pool); - if (apr_err) - svn_handle_error2(svn_error_wrap_apr - (apr_err, _("Can't restore working directory")), - stderr, TRUE /* fatal */, "svn: "); - - if (sys_err) - /* Extracting any meaning from sys_err is platform specific, so just - use the raw value. */ - return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, - _("system('%s') returned %d"), cmd, sys_err); - - return SVN_NO_ERROR; -} - svn_error_t * svn_cl__merge_file_externally(const char *base_path, const char *their_path, @@ -290,248 +172,6 @@ svn_cl__merge_file_externally(const char return SVN_NO_ERROR; } -svn_error_t * -svn_cl__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, - const char **tmpfile_left /* UTF-8! */, - const char *editor_cmd, - const char *base_dir /* UTF-8! */, - const svn_string_t *contents /* UTF-8! */, - const char *filename, - apr_hash_t *config, - svn_boolean_t as_text, - const char *encoding, - apr_pool_t *pool) -{ - const char *editor; - const char *cmd; - apr_file_t *tmp_file; - const char *tmpfile_name; - const char *tmpfile_native; - const char *tmpfile_apr, *base_dir_apr; - svn_string_t *translated_contents; - apr_status_t apr_err, apr_err2; - apr_size_t written; - apr_finfo_t finfo_before, finfo_after; - svn_error_t *err = SVN_NO_ERROR, *err2; - char *old_cwd; - int sys_err; - svn_boolean_t remove_file = TRUE; - - SVN_ERR(find_editor_binary(&editor, editor_cmd, config)); - - /* Convert file contents from UTF-8/LF if desired. */ - if (as_text) - { - const char *translated; - SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated, - APR_EOL_STR, FALSE, - NULL, FALSE, pool)); - translated_contents = svn_string_create_empty(pool); - if (encoding) - SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data, - translated, encoding, pool)); - else - SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data, - translated, pool)); - translated_contents->len = strlen(translated_contents->data); - } - else - translated_contents = svn_string_dup(contents, pool); - - /* Move to BASE_DIR to avoid getting characters that need quoting - into tmpfile_name */ - apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool); - if (apr_err) - return svn_error_wrap_apr(apr_err, _("Can't get working directory")); - - /* APR doesn't like "" directories */ - if (base_dir[0] == '\0') - base_dir_apr = "."; - else - SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool)); - apr_err = apr_filepath_set(base_dir_apr, pool); - if (apr_err) - { - return svn_error_wrap_apr - (apr_err, _("Can't change working directory to '%s'"), base_dir); - } - - /*** From here on, any problems that occur require us to cd back!! ***/ - - /* Ask the working copy for a temporary file named FILENAME-something. */ - err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name, - "" /* dirpath */, - filename, - ".tmp", - svn_io_file_del_none, pool, pool); - - if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS)) - { - const char *temp_dir_apr; - - svn_error_clear(err); - - SVN_ERR(svn_io_temp_dir(&base_dir, pool)); - - SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool)); - apr_err = apr_filepath_set(temp_dir_apr, pool); - if (apr_err) - { - return svn_error_wrap_apr - (apr_err, _("Can't change working directory to '%s'"), base_dir); - } - - err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name, - "" /* dirpath */, - filename, - ".tmp", - svn_io_file_del_none, pool, pool); - } - - if (err) - goto cleanup2; - - /*** From here on, any problems that occur require us to cleanup - the file we just created!! ***/ - - /* Dump initial CONTENTS to TMP_FILE. */ - apr_err = apr_file_write_full(tmp_file, translated_contents->data, - translated_contents->len, &written); - - apr_err2 = apr_file_close(tmp_file); - if (! apr_err) - apr_err = apr_err2; - - /* Make sure the whole CONTENTS were written, else return an error. */ - if (apr_err) - { - err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"), - tmpfile_name); - goto cleanup; - } - - err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool); - if (err) - goto cleanup; - - /* Get information about the temporary file before the user has - been allowed to edit its contents. */ - apr_err = apr_stat(&finfo_before, tmpfile_apr, - APR_FINFO_MTIME, pool); - if (apr_err) - { - err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); - goto cleanup; - } - - /* Backdate the file a little bit in case the editor is very fast - and doesn't change the size. (Use two seconds, since some - filesystems have coarse granularity.) It's OK if this call - fails, so we don't check its return value.*/ - apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool); - - /* Stat it again to get the mtime we actually set. */ - apr_err = apr_stat(&finfo_before, tmpfile_apr, - APR_FINFO_MTIME | APR_FINFO_SIZE, pool); - if (apr_err) - { - err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); - goto cleanup; - } - - /* Prepare the editor command line. */ - err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool); - if (err) - goto cleanup; - cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native); - - /* If the caller wants us to leave the file around, return the path - of the file we'll use, and make a note not to destroy it. */ - if (tmpfile_left) - { - *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool); - remove_file = FALSE; - } - - /* Now, run the editor command line. */ - sys_err = system(cmd); - if (sys_err != 0) - { - /* Extracting any meaning from sys_err is platform specific, so just - use the raw value. */ - err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, - _("system('%s') returned %d"), cmd, sys_err); - goto cleanup; - } - - /* Get information about the temporary file after the assumed editing. */ - apr_err = apr_stat(&finfo_after, tmpfile_apr, - APR_FINFO_MTIME | APR_FINFO_SIZE, pool); - if (apr_err) - { - err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); - goto cleanup; - } - - /* If the file looks changed... */ - if ((finfo_before.mtime != finfo_after.mtime) || - (finfo_before.size != finfo_after.size)) - { - svn_stringbuf_t *edited_contents_s; - err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool); - if (err) - goto cleanup; - - *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s); - - /* Translate back to UTF8/LF if desired. */ - if (as_text) - { - err = svn_subst_translate_string2(edited_contents, FALSE, FALSE, - *edited_contents, encoding, FALSE, - pool, pool); - if (err) - { - err = svn_error_quick_wrap - (err, - _("Error normalizing edited contents to internal format")); - goto cleanup; - } - } - } - else - { - /* No edits seem to have been made */ - *edited_contents = NULL; - } - - cleanup: - if (remove_file) - { - /* Remove the file from disk. */ - err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool); - - /* Only report remove error if there was no previous error. */ - if (! err && err2) - err = err2; - else - svn_error_clear(err2); - } - - cleanup2: - /* If we against all probability can't cd back, all further relative - file references would be screwed up, so we have to abort. */ - apr_err = apr_filepath_set(old_cwd, pool); - if (apr_err) - { - svn_handle_error2(svn_error_wrap_apr - (apr_err, _("Can't restore working directory")), - stderr, TRUE /* fatal */, "svn: "); - } - - return svn_error_trace(err); -} - /* A svn_client_ctx_t's log_msg_baton3, for use with svn_cl__make_log_msg_baton(). */ @@ -732,8 +372,8 @@ svn_cl__get_log_message(const char **log while (! message) { /* We still don't have a valid commit message. Use $EDITOR to - get one. Note that svn_cl__edit_externally will still return - a UTF-8'ized log message. */ + get one. Note that svn_cl__edit_string_externally will still + return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; @@ -793,12 +433,12 @@ svn_cl__get_log_message(const char **log /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { - err = svn_cl__edit_string_externally(&msg_string, &lmb->tmpfile_left, - lmb->editor_cmd, lmb->base_dir, - msg_string, "svn-commit", - lmb->config, TRUE, - lmb->message_encoding, - pool); + err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, + lmb->editor_cmd, lmb->base_dir, + msg_string, "svn-commit", + lmb->config, TRUE, + lmb->message_encoding, + pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { @@ -1264,56 +904,6 @@ svn_cl__time_cstring_to_human_cstring(co return SVN_NO_ERROR; } - -/* Return a copy, allocated in POOL, of the next line of text from *STR - * up to and including a CR and/or an LF. Change *STR to point to the - * remainder of the string after the returned part. If there are no - * characters to be returned, return NULL; never return an empty string. - */ -static const char * -next_line(const char **str, apr_pool_t *pool) -{ - const char *start = *str; - const char *p = *str; - - /* n.b. Throughout this fn, we never read any character after a '\0'. */ - /* Skip over all non-EOL characters, if any. */ - while (*p != '\r' && *p != '\n' && *p != '\0') - p++; - /* Skip over \r\n or \n\r or \r or \n, if any. */ - if (*p == '\r' || *p == '\n') - { - char c = *p++; - - if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r')) - p++; - } - - /* Now p points after at most one '\n' and/or '\r'. */ - *str = p; - - if (p == start) - return NULL; - - return svn_string_ncreate(start, p - start, pool)->data; -} - -const char * -svn_cl__indent_string(const char *str, - const char *indent, - apr_pool_t *pool) -{ - svn_stringbuf_t *out = svn_stringbuf_create_empty(pool); - const char *line; - - while ((line = next_line(&str, pool))) - { - svn_stringbuf_appendcstr(out, indent); - svn_stringbuf_appendcstr(out, line); - } - return out->data; -} - const char * svn_cl__node_description(const svn_wc_conflict_version_t *node, const char *wc_repos_root_URL, Modified: subversion/branches/tree-read-api/subversion/svnadmin/svnadmin.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnadmin/svnadmin.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svnadmin/svnadmin.c (original) +++ subversion/branches/tree-read-api/subversion/svnadmin/svnadmin.c Sun Jan 6 02:33:34 2013 @@ -1,5 +1,5 @@ /* - * main.c: Subversion server administration tool. + * svnadmin.c: Subversion server administration tool main file. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one Modified: subversion/branches/tree-read-api/subversion/svndumpfilter/svndumpfilter.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svndumpfilter/svndumpfilter.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svndumpfilter/svndumpfilter.c (original) +++ subversion/branches/tree-read-api/subversion/svndumpfilter/svndumpfilter.c Sun Jan 6 02:33:34 2013 @@ -1,5 +1,5 @@ /* - * main.c: Subversion dump stream filtering tool. + * svndumpfilter.c: Subversion dump stream filtering tool main file. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one Modified: subversion/branches/tree-read-api/subversion/svnlook/svnlook.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnlook/svnlook.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svnlook/svnlook.c (original) +++ subversion/branches/tree-read-api/subversion/svnlook/svnlook.c Sun Jan 6 02:33:34 2013 @@ -1,5 +1,5 @@ /* - * main.c: Subversion server inspection tool. + * svnlook.c: Subversion server inspection tool main file. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -97,7 +97,8 @@ enum svnlook__xml_opt, svnlook__ignore_properties, svnlook__properties_only, - svnlook__diff_cmd + svnlook__diff_cmd, + svnlook__show_inherited_props }; /* @@ -150,6 +151,9 @@ static const apr_getopt_option_t options {"show-ids", svnlook__show_ids, 0, N_("show node revision ids for each path")}, + {"show-inherited-props", svnlook__show_inherited_props, 0, + N_("show path's inherited properties")}, + {"transaction", 't', 1, N_("specify transaction name ARG")}, @@ -265,7 +269,7 @@ static const svn_opt_subcommand_desc2_t " 2. svnlook propget --revprop REPOS_PATH PROPNAME\n\n" "Print the raw value of a property on a path in the repository.\n" "With --revprop, print the raw value of a revision property.\n"), - {'r', 't', svnlook__revprop_opt} }, + {'r', 't', 'v', svnlook__revprop_opt, svnlook__show_inherited_props} }, {"proplist", subcommand_plist, {"plist", "pl"}, N_("usage: 1. svnlook proplist REPOS_PATH PATH_IN_REPOS\n" @@ -275,7 +279,8 @@ static const svn_opt_subcommand_desc2_t "List the properties of a path in the repository, or\n" "with the --revprop option, revision properties.\n" "With -v, show the property values too.\n"), - {'r', 't', 'v', svnlook__revprop_opt, svnlook__xml_opt} }, + {'r', 't', 'v', svnlook__revprop_opt, svnlook__xml_opt, + svnlook__show_inherited_props} }, {"tree", subcommand_tree, {0}, N_("usage: svnlook tree REPOS_PATH [PATH_IN_REPOS]\n\n" @@ -323,6 +328,7 @@ struct svnlook_opt_state svn_boolean_t ignore_properties; /* --ignore_properties */ svn_boolean_t properties_only; /* --properties-only */ const char *diff_cmd; /* --diff-cmd */ + svn_boolean_t show_inherited_props; /* --show-inherited-props */ }; @@ -1628,13 +1634,23 @@ do_history(svnlook_ctxt_t *c, /* Print the value of property PROPNAME on PATH in the repository. - Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist, or with - SVN_ERR_PROPERTY_NOT_FOUND if no such property on PATH. + + If VERBOSE, print their values too. If SHOW_INHERITED_PROPS, print + PATH's inherited props too. + + Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist. If + SHOW_INHERITED_PROPS is FALSE,then error with SVN_ERR_PROPERTY_NOT_FOUND + if there is no such property on PATH. If SHOW_INHERITED_PROPS is TRUE, + then error with SVN_ERR_PROPERTY_NOT_FOUND only if there is no such + property on PATH nor inherited by path. + If PATH is NULL, operate on a revision property. */ static svn_error_t * do_pget(svnlook_ctxt_t *c, const char *propname, const char *path, + svn_boolean_t verbose, + svn_boolean_t show_inherited_props, apr_pool_t *pool) { svn_fs_root_t *root; @@ -1642,17 +1658,30 @@ do_pget(svnlook_ctxt_t *c, svn_node_kind_t kind; svn_stream_t *stdout_stream; apr_size_t len; + apr_array_header_t *inherited_props = NULL; SVN_ERR(get_root(&root, c, pool)); if (path != NULL) { + path = svn_fspath__canonicalize(path, pool); SVN_ERR(verify_path(&kind, root, path, pool)); SVN_ERR(svn_fs_node_prop(&prop, root, path, propname, pool)); + + if (show_inherited_props) + { + SVN_ERR(svn_repos_fs_get_inherited_props(&inherited_props, root, + path, propname, NULL, + NULL, pool, pool)); + } + } + else /* --revprop */ + { + SVN_ERR(get_property(&prop, c, propname, pool)); } - else - SVN_ERR(get_property(&prop, c, propname, pool)); - if (prop == NULL) + /* Did we find nothing? */ + if (prop == NULL + && (!show_inherited_props || inherited_props->nelts == 0)) { const char *err_msg; if (path == NULL) @@ -1665,61 +1694,162 @@ do_pget(svnlook_ctxt_t *c, else { if (SVN_IS_VALID_REVNUM(c->rev_id)) - err_msg = apr_psprintf(pool, - _("Property '%s' not found on path '%s' " - "in revision %ld"), - propname, path, c->rev_id); + { + if (show_inherited_props) + err_msg = apr_psprintf(pool, + _("Property '%s' not found on path '%s' " + "or inherited from a parent " + "in revision %ld"), + propname, path, c->rev_id); + else + err_msg = apr_psprintf(pool, + _("Property '%s' not found on path '%s' " + "in revision %ld"), + propname, path, c->rev_id); + } else - err_msg = apr_psprintf(pool, - _("Property '%s' not found on path '%s' " - "in transaction %s"), - propname, path, c->txn_name); + { + if (show_inherited_props) + err_msg = apr_psprintf(pool, + _("Property '%s' not found on path '%s' " + "or inherited from a parent " + "in transaction %s"), + propname, path, c->txn_name); + else + err_msg = apr_psprintf(pool, + _("Property '%s' not found on path '%s' " + "in transaction %s"), + propname, path, c->txn_name); + } } return svn_error_create(SVN_ERR_PROPERTY_NOT_FOUND, NULL, err_msg); } - /* Else. */ - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Unlike the command line client, we don't translate the property - value or print a trailing newline here. We just output the raw - bytes of whatever's in the repository, as svnlook is more likely - to be used for automated inspections. */ - len = prop->len; - SVN_ERR(svn_stream_write(stdout_stream, prop->data, &len)); + if (verbose || show_inherited_props) + { + if (inherited_props) + { + int i; + + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *elt = + APR_ARRAY_IDX(inherited_props, i, + svn_prop_inherited_item_t *); + + if (verbose) + { + SVN_ERR(svn_stream_printf(stdout_stream, pool, + _("Inherited properties on '%s',\nfrom '%s':\n"), + path, svn_fspath__canonicalize(elt->path_or_url, + pool))); + SVN_ERR(svn_cmdline__print_prop_hash(stdout_stream, + elt->prop_hash, + !verbose, pool)); + } + else + { + svn_string_t *propval = + svn__apr_hash_index_val(apr_hash_first(pool, + elt->prop_hash)); + + SVN_ERR(svn_stream_printf( + stdout_stream, pool, "%s - ", + svn_fspath__canonicalize(elt->path_or_url, pool))); + len = propval->len; + SVN_ERR(svn_stream_write(stdout_stream, propval->data, &len)); + /* If we have more than one property to write, then add a newline*/ + if (inherited_props->nelts > 1 || prop) + { + len = strlen(APR_EOL_STR); + SVN_ERR(svn_stream_write(stdout_stream, APR_EOL_STR, &len)); + } + } + } + } + + if (prop) + { + if (verbose) + { + apr_hash_t *hash = apr_hash_make(pool); + + apr_hash_set(hash, propname, APR_HASH_KEY_STRING, prop); + SVN_ERR(svn_stream_printf(stdout_stream, pool, + _("Properties on '%s':\n"), path)); + SVN_ERR(svn_cmdline__print_prop_hash(stdout_stream, hash, + FALSE, pool)); + } + else + { + SVN_ERR(svn_stream_printf(stdout_stream, pool, "%s - ", path)); + len = prop->len; + SVN_ERR(svn_stream_write(stdout_stream, prop->data, &len)); + } + } + } + else /* Raw single prop output, i.e. non-verbose output with no + inherited props. */ + { + /* Unlike the command line client, we don't translate the property + value or print a trailing newline here. We just output the raw + bytes of whatever's in the repository, as svnlook is more likely + to be used for automated inspections. */ + len = prop->len; + SVN_ERR(svn_stream_write(stdout_stream, prop->data, &len)); + } return SVN_NO_ERROR; } /* Print the property names of all properties on PATH in the repository. - If VERBOSE, print their values too. - If XML, print as XML rather than as plain text. - Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist, or with - SVN_ERR_PROPERTY_NOT_FOUND if no such property on PATH. + + If VERBOSE, print their values too. If XML, print as XML rather than as + plain text. If SHOW_INHERITED_PROPS, print PATH's inherited props too. + + Error with SVN_ERR_FS_NOT_FOUND if PATH does not exist. + If PATH is NULL, operate on a revision properties. */ static svn_error_t * do_plist(svnlook_ctxt_t *c, const char *path, svn_boolean_t verbose, svn_boolean_t xml, + svn_boolean_t show_inherited_props, apr_pool_t *pool) { - svn_stream_t *stdout_stream; svn_fs_root_t *root; apr_hash_t *props; apr_hash_index_t *hi; svn_node_kind_t kind; svn_stringbuf_t *sb = NULL; svn_boolean_t revprop = FALSE; + apr_array_header_t *inherited_props = NULL; - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); if (path != NULL) { + /* PATH might be the root of the repsository and we accept both + "" and "/". But to avoid the somewhat cryptic output like this: + + >svnlook pl repos-path "" + Properties on '': + svn:auto-props + svn:global-ignores + + We canonicalize PATH so that is has a leading slash. */ + path = svn_fspath__canonicalize(path, pool); + SVN_ERR(get_root(&root, c, pool)); SVN_ERR(verify_path(&kind, root, path, pool)); SVN_ERR(svn_fs_node_proplist(&props, root, path, pool)); + + if (show_inherited_props) + SVN_ERR(svn_repos_fs_get_inherited_props(&inherited_props, root, + path, NULL, NULL, NULL, + pool, pool)); } else if (c->is_revision) { @@ -1734,18 +1864,55 @@ do_plist(svnlook_ctxt_t *c, if (xml) { - char *revstr = apr_psprintf(pool, "%ld", c->rev_id); /* <?xml version="1.0" encoding="UTF-8"?> */ svn_xml_make_header2(&sb, "UTF-8", pool); /* "<properties>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "properties", NULL); + } + if (inherited_props) + { + int i; + + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *elt = + APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); + + /* Canonicalize the inherited parent paths for consistency + with PATH. */ + if (xml) + { + svn_xml_make_open_tag( + &sb, pool, svn_xml_normal, "target", "path", + svn_fspath__canonicalize(elt->path_or_url, pool), + NULL); + SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, elt->prop_hash, + !verbose, TRUE, + pool)); + svn_xml_make_close_tag(&sb, pool, "target"); + } + else + { + SVN_ERR(svn_cmdline_printf( + pool, _("Inherited properties on '%s',\nfrom '%s':\n"), + path, svn_fspath__canonicalize(elt->path_or_url, pool))); + SVN_ERR(svn_cmdline__print_prop_hash(NULL, elt->prop_hash, + !verbose, pool)); + } + } + } + + if (xml) + { if (revprop) { /* "<revprops ...>" */ if (c->is_revision) { + char *revstr = apr_psprintf(pool, "%ld", c->rev_id); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", "rev", revstr, NULL); } @@ -1763,6 +1930,9 @@ do_plist(svnlook_ctxt_t *c, } } + if (!xml && path /* Not a --revprop */) + SVN_ERR(svn_cmdline_printf(pool, _("Properties on '%s':\n"), path)); + for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { const char *pname = svn__apr_hash_index_key(hi); @@ -1786,10 +1956,19 @@ do_plist(svnlook_ctxt_t *c, else { const char *pname_stdout; + const char *indented_newval; SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool)); - printf(" %s : %s\n", pname_stdout, propval->data); + printf(" %s\n", pname_stdout); + /* Add an extra newline to the value before indenting, so that + every line of output has the indentation whether the value + already ended in a newline or not. */ + indented_newval = + svn_cmdline__indent_string(apr_psprintf(pool, "%s\n", + propval->data), + " ", pool); + printf("%s", indented_newval); } } else if (xml) @@ -2171,7 +2350,9 @@ subcommand_pget(apr_getopt_t *os, void * SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); SVN_ERR(do_pget(c, opt_state->arg1, - opt_state->revprop ? NULL : opt_state->arg2, pool)); + opt_state->revprop ? NULL : opt_state->arg2, + opt_state->verbose, opt_state->show_inherited_props, + pool)); return SVN_NO_ERROR; } @@ -2186,7 +2367,8 @@ subcommand_plist(apr_getopt_t *os, void SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); SVN_ERR(do_plist(c, opt_state->revprop ? NULL : opt_state->arg1, - opt_state->verbose, opt_state->xml, pool)); + opt_state->verbose, opt_state->xml, + opt_state->show_inherited_props, pool)); return SVN_NO_ERROR; } @@ -2419,6 +2601,10 @@ main(int argc, const char *argv[]) opt_state.diff_cmd = opt_arg; break; + case svnlook__show_inherited_props: + opt_state.show_inherited_props = TRUE; + break; + default: SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); svn_pool_destroy(pool); @@ -2434,6 +2620,13 @@ main(int argc, const char *argv[]) _("The '--transaction' (-t) and '--revision' (-r) arguments " "cannot co-exist"))); + /* The --show-inherited-props and --revprop options may not co-exist. */ + if (opt_state.show_inherited_props && opt_state.revprop) + SVN_INT_ERR(svn_error_create + (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("Cannot use the '--show-inherited-props' option with the " + "'--revprop' option"))); + /* If the user asked for help, then the rest of the arguments are the names of subcommands to get help on (if any), or else they're just typos/mistakes. Whatever the case, the subcommand to Modified: subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c?rev=1429457&r1=1429456&r2=1429457&view=diff ============================================================================== --- subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c (original) +++ subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c Sun Jan 6 02:33:34 2013 @@ -55,6 +55,9 @@ #include "private/svn_cmdline_private.h" #include "private/svn_ra_private.h" +#include "private/svn_string_private.h" + +#include "svn_private_config.h" static void handle_error(svn_error_t *err, apr_pool_t *pool) { @@ -750,6 +753,29 @@ execute(const apr_array_header_t *action "svnmucc: ", "--config-option")); cfg_config = apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING); + + if (! apr_hash_get(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING)) + { + svn_string_t *msg = svn_string_create("", pool); + + /* If we can do so, try to pop up $EDITOR to fetch a log message. */ + if (non_interactive) + { + return svn_error_create + (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, + _("Cannot invoke editor to get log message " + "when non-interactive")); + } + else + { + SVN_ERR(svn_cmdline__edit_string_externally( + &msg, NULL, NULL, "", msg, "svnmucc-commit", config, + TRUE, NULL, apr_hash_pool_get(revprops))); + } + + apr_hash_set(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, msg); + } + SVN_ERR(create_ra_callbacks(&ra_callbacks, username, password, config_dir, cfg_config, non_interactive, no_auth_cache, pool)); @@ -899,38 +925,43 @@ static void usage(apr_pool_t *pool, int exit_val) { FILE *stream = exit_val == EXIT_SUCCESS ? stdout : stderr; - static const char *msg = - "Multiple URL Command Client (for Subversion)\n" - "\nUsage: svnmucc [OPTION]... [ACTION]...\n" - "\nActions:\n" - " cp REV URL1 URL2 copy URL1@REV to URL2\n" - " mkdir URL create new directory URL\n" - " mv URL1 URL2 move URL1 to URL2\n" - " rm URL delete URL\n" - " put SRC-FILE URL add or modify file URL with contents copied from\n" - " SRC-FILE (use \"-\" to read from standard input)\n" - " propset NAME VAL URL set property NAME on URL to value VAL\n" - " propsetf NAME VAL URL set property NAME on URL to value from file VAL\n" - " propdel NAME URL delete property NAME from URL\n" - "\nOptions:\n" - " -h, --help, -? display this text\n" - " -m, --message ARG use ARG as a log message\n" - " -F, --file ARG read log message from file ARG\n" - " -u, --username ARG commit the changes as username ARG\n" - " -p, --password ARG use ARG as the password\n" - " -U, --root-url ARG interpret all action URLs are relative to ARG\n" - " -r, --revision ARG use revision ARG as baseline for changes\n" - " --with-revprop A[=B] set revision property A in new revision to B\n" - " if specified, else to the empty string\n" - " -n, --non-interactive don't prompt the user about anything\n" - " -X, --extra-args ARG append arguments from file ARG (one per line;\n" - " use \"-\" to read from standard input)\n" - " --config-dir ARG use ARG to override the config directory\n" - " --config-option ARG use ARG to override a configuration option\n" - " --no-auth-cache do not cache authentication tokens\n" - " --version print version information\n"; - svn_error_clear(svn_cmdline_fputs(msg, stream, pool)); - apr_pool_destroy(pool); + svn_error_clear(svn_cmdline_fputs( + _("Subversion multiple URL command client\n" + "usage: svnmucc [OPTION]... [ACTION]...\n" + "\n" + " Perform one or more Subversion repository URL-based ACTIONs, committing\n" + " the result as a (single) new revision.\n" + "\n" + "Actions:\n" + " cp REV URL1 URL2 : copy URL1@REV to URL2\n" + " mkdir URL : create new directory URL\n" + " mv URL1 URL2 : move URL1 to URL2\n" + " rm URL : delete URL\n" + " put SRC-FILE URL : add or modify file URL with contents copied from\n" + " SRC-FILE (use \"-\" to read from standard input)\n" + " propset NAME VAL URL : set property NAME on URL to value VAL\n" + " propsetf NAME VAL URL : set property NAME on URL to value from file VAL\n" + " propdel NAME URL : delete property NAME from URL\n" + "\n" + "Valid options:\n" + " -h, -? [--help] : display this text\n" + " -m [--message] ARG : use ARG as a log message\n" + " -F [--file] ARG : read log message from file ARG\n" + " -u [--username] ARG : commit the changes as username ARG\n" + " -p [--password] ARG : use ARG as the password\n" + " -U [--root-url] ARG : interpret all action URLs relative to ARG\n" + " -r [--revision] ARG : use revision ARG as baseline for changes\n" + " --with-revprop ARG : set revision property in the following format:\n" + " NAME[=VALUE]\n" + " -n [--non-interactive] : don't prompt the user about anything\n" + " -X [--extra-args] ARG : append arguments from file ARG (one per line;\n" + " : use \"-\" to read from standard input)\n" + " --config-dir ARG : use ARG to override the config directory\n" + " --config-option ARG : use ARG to override a configuration option\n" + " --no-auth-cache : do not cache authentication tokens\n" + " --version : print version information\n"), + stream, pool)); + svn_pool_destroy(pool); exit(exit_val); } @@ -959,6 +990,53 @@ display_version(apr_getopt_t *os, apr_po return SVN_NO_ERROR; } +/* Return an error about the mutual exclusivity of the -m, -F, and + --with-revprop=svn:log command-line options. */ +static svn_error_t * +mutually_exclusive_logs_error(void) +{ + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--message (-m), --file (-F), and " + "--with-revprop=svn:log are mutually " + "exclusive")); +} + +/* Ensure that the REVPROPS hash contains a command-line-provided log + message, if any, and that there was but one source of such a thing + provided on that command-line. */ +static svn_error_t * +sanitize_log_sources(apr_hash_t *revprops, + const char *message, + svn_stringbuf_t *filedata) +{ + apr_pool_t *hash_pool = apr_hash_pool_get(revprops); + + /* If we already have a log message in the revprop hash, then just + make sure the user didn't try to also use -m or -F. Otherwise, + we need to consult -m or -F to find a log message, if any. */ + if (apr_hash_get(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING)) + { + if (filedata || message) + return mutually_exclusive_logs_error(); + } + else if (filedata) + { + if (message) + return mutually_exclusive_logs_error(); + + SVN_ERR(svn_utf_cstring_to_utf8(&message, filedata->data, hash_pool)); + apr_hash_set(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, + svn_stringbuf__morph_into_string(filedata)); + } + else if (message) + { + apr_hash_set(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, + svn_string_create(message, hash_pool)); + } + + return SVN_NO_ERROR; +} + int main(int argc, const char **argv) { @@ -994,6 +1072,7 @@ main(int argc, const char **argv) {NULL, 0, 0, NULL} }; const char *message = NULL; + svn_stringbuf_t *filedata = NULL; const char *username = NULL, *password = NULL; const char *root_url = NULL, *extra_args_file = NULL; const char *config_dir = NULL; @@ -1031,12 +1110,9 @@ main(int argc, const char **argv) case 'F': { const char *arg_utf8; - svn_stringbuf_t *contents; err = svn_utf_cstring_to_utf8(&arg_utf8, arg, pool); if (! err) - err = svn_stringbuf_from_file2(&contents, arg, pool); - if (! err) - err = svn_utf_cstring_to_utf8(&message, contents->data, pool); + err = svn_stringbuf_from_file2(&filedata, arg, pool); if (err) handle_error(err, pool); } @@ -1109,6 +1185,11 @@ main(int argc, const char **argv) } } + /* Make sure we have a log message to use. */ + err = sanitize_log_sources(revprops, message, filedata); + if (err) + handle_error(err, pool); + /* Copy the rest of our command-line arguments to an array, UTF-8-ing them along the way. */ action_args = apr_array_make(pool, opts->argc, sizeof(const char *)); @@ -1324,21 +1405,6 @@ main(int argc, const char **argv) if (! actions->nelts) usage(pool, EXIT_FAILURE); - if (message == NULL) - { - if (apr_hash_get(revprops, SVN_PROP_REVISION_LOG, - APR_HASH_KEY_STRING) == NULL) - /* None of -F, -m, or --with-revprop=svn:log specified; default. */ - apr_hash_set(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, - svn_string_create("committed using svnmucc", pool)); - } - else - { - /* -F or -m specified; use that even if --with-revprop=svn:log. */ - apr_hash_set(revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, - svn_string_create(message, pool)); - } - if ((err = execute(actions, anchor, revprops, username, password, config_dir, config_options, non_interactive, no_auth_cache, base_revision, pool)))
