summaryrefslogtreecommitdiffstats
path: root/subversion/svn/propedit-cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/propedit-cmd.c')
-rw-r--r--subversion/svn/propedit-cmd.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/subversion/svn/propedit-cmd.c b/subversion/svn/propedit-cmd.c
new file mode 100644
index 0000000..520fe6c
--- /dev/null
+++ b/subversion/svn/propedit-cmd.c
@@ -0,0 +1,356 @@
+/*
+ * propedit-cmd.c -- Edit properties of files/dirs using $EDITOR
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_hash.h"
+#include "svn_cmdline.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_props.h"
+#include "cl.h"
+
+#include "private/svn_cmdline_private.h"
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+struct commit_info_baton
+{
+ const char *pname_utf8;
+ const char *target_local;
+};
+
+static svn_error_t *
+commit_info_handler(const svn_commit_info_t *commit_info,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct commit_info_baton *cib = baton;
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Set new value for property '%s' on '%s'\n"),
+ cib->pname_utf8, cib->target_local));
+ SVN_ERR(svn_cl__print_commit_info(commit_info, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__propedit(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *pname, *pname_utf8;
+ apr_array_header_t *args, *targets;
+
+ /* Validate the input and get the property's name (and a UTF-8
+ version of that name). */
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
+ pname = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
+ if (! svn_prop_name_is_valid(pname_utf8))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("'%s' is not a valid Subversion property name"),
+ pname_utf8);
+ if (!opt_state->force)
+ SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop,
+ svn_cl__prop_use_edit, pool));
+
+ if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
+ return svn_error_create
+ (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("--encoding option applies only to textual"
+ " Subversion-controlled properties"));
+
+ /* Suck up all the remaining arguments into a targets array */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We do our own notifications */
+ ctx->notify_func2 = NULL;
+
+ if (opt_state->revprop) /* operate on a revprop */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+ svn_string_t *propval;
+ svn_string_t original_propval;
+ const char *temp_dir;
+
+ /* Implicit "." is okay for revision properties; it just helps
+ us find the right repository. */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, pool));
+
+ /* Fetch the current property. */
+ SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
+ URL, &(opt_state->start_revision),
+ &rev, ctx, pool));
+
+ if (! propval)
+ {
+ propval = svn_string_create_empty(pool);
+ /* This is how we signify to svn_client_revprop_set2() that
+ we want it to check that the original value hasn't
+ changed, but that that original value was non-existent: */
+ original_propval.data = NULL; /* and .len is ignored */
+ }
+ else
+ {
+ original_propval = *propval;
+ }
+
+ /* Run the editor on a temporary file which contains the
+ original property value... */
+ SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
+ SVN_ERR(svn_cmdline__edit_string_externally(
+ &propval, NULL,
+ opt_state->editor_cmd, temp_dir,
+ propval, "svn-prop",
+ ctx->config,
+ svn_prop_needs_translation(pname_utf8),
+ opt_state->encoding, pool));
+
+ /* ...and re-set the property's value accordingly. */
+ if (propval)
+ {
+ SVN_ERR(svn_client_revprop_set2(pname_utf8,
+ propval, &original_propval,
+ URL, &(opt_state->start_revision),
+ &rev, opt_state->force, ctx, pool));
+
+ SVN_ERR
+ (svn_cmdline_printf
+ (pool,
+ _("Set new value for property '%s' on revision %ld\n"),
+ pname_utf8, rev));
+ }
+ else
+ {
+ SVN_ERR(svn_cmdline_printf
+ (pool, _("No changes to property '%s' on revision %ld\n"),
+ pname_utf8, rev));
+ }
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_createf
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot specify revision for editing versioned property '%s'"),
+ pname_utf8);
+ }
+ else /* operate on a normal, versioned property (not a revprop) */
+ {
+ apr_pool_t *subpool = svn_pool_create(pool);
+ struct commit_info_baton cib;
+ int i;
+
+ /* The customary implicit dot rule has been prone to user error
+ * here. For example, Jon Trowbridge <trow@gnu.og> did
+ *
+ * $ svn propedit HACKING
+ *
+ * and then when he closed his editor, he was surprised to see
+ *
+ * Set new value for property 'HACKING' on ''
+ *
+ * ...meaning that the property named 'HACKING' had been set on
+ * the current working directory, with the value taken from the
+ * editor. So we don't do the implicit dot thing anymore; an
+ * explicit target is always required when editing a versioned
+ * property.
+ */
+ if (targets->nelts == 0)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Explicit target argument required"));
+ }
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ cib.pname_utf8 = pname_utf8;
+
+ /* For each target, edit the property PNAME. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ apr_hash_t *props;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_string_t *propval, *edited_propval;
+ const char *base_dir = target;
+ const char *target_local;
+ const char *abspath_or_url;
+ svn_node_kind_t kind;
+ svn_opt_revision_t peg_revision;
+ svn_revnum_t base_rev = SVN_INVALID_REVNUM;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ if (!svn_path_is_url(target))
+ SVN_ERR(svn_dirent_get_absolute(&abspath_or_url, target, subpool));
+ else
+ abspath_or_url = target;
+
+ /* Propedits can only happen on HEAD or the working copy, so
+ the peg revision can be as unspecified. */
+ peg_revision.kind = svn_opt_revision_unspecified;
+
+ /* Fetch the current property. */
+ SVN_ERR(svn_client_propget5(&props, NULL, pname_utf8, abspath_or_url,
+ &peg_revision,
+ &(opt_state->start_revision),
+ &base_rev, svn_depth_empty,
+ NULL, ctx, subpool, subpool));
+
+ /* Get the property value. */
+ propval = svn_hash_gets(props, abspath_or_url);
+ if (! propval)
+ propval = svn_string_create_empty(subpool);
+
+ if (svn_path_is_url(target))
+ {
+ /* For URLs, put the temporary file in the current directory. */
+ base_dir = ".";
+ }
+ else
+ {
+ if (opt_state->message || opt_state->filedata ||
+ opt_state->revprop_table)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+
+ /* Split the path if it is a file path. */
+ SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath_or_url,
+ FALSE, FALSE, subpool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(
+ SVN_ERR_ENTRY_NOT_FOUND, NULL,
+ _("'%s' does not appear to be a working copy path"), target);
+ if (kind == svn_node_file)
+ base_dir = svn_dirent_dirname(target, subpool);
+ }
+
+ /* Run the editor on a temporary file which contains the
+ original property value... */
+ SVN_ERR(svn_cmdline__edit_string_externally(&edited_propval, NULL,
+ opt_state->editor_cmd,
+ base_dir,
+ propval,
+ "svn-prop",
+ ctx->config,
+ svn_prop_needs_translation
+ (pname_utf8),
+ opt_state->encoding,
+ subpool));
+
+ target_local = svn_path_is_url(target) ? target
+ : svn_dirent_local_style(target, subpool);
+ cib.target_local = target_local;
+
+ /* ...and re-set the property's value accordingly. */
+ if (edited_propval && !svn_string_compare(propval, edited_propval))
+ {
+ svn_error_t *err = SVN_NO_ERROR;
+
+ svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data,
+ subpool);
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
+ opt_state, NULL, ctx->config,
+ subpool));
+ if (svn_path_is_url(target))
+ {
+ err = svn_client_propset_remote(pname_utf8, edited_propval,
+ target, opt_state->force,
+ base_rev,
+ opt_state->revprop_table,
+ commit_info_handler, &cib,
+ ctx, subpool);
+ }
+ else
+ {
+ apr_array_header_t *targs = apr_array_make(subpool, 1,
+ sizeof(const char *));
+
+ APR_ARRAY_PUSH(targs, const char *) = target;
+
+ SVN_ERR(svn_cl__propset_print_binary_mime_type_warning(
+ targs, pname_utf8, propval, subpool));
+
+ err = svn_client_propset_local(pname_utf8, edited_propval,
+ targs, svn_depth_empty,
+ opt_state->force, NULL,
+ ctx, subpool);
+ }
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
+ err, pool));
+ else if (err)
+ return svn_error_trace(err);
+
+ /* Print a message if we successfully committed or if it
+ was just a wc propset (but not if the user aborted a URL
+ propedit). */
+ if (!svn_path_is_url(target))
+ SVN_ERR(svn_cmdline_printf(
+ subpool, _("Set new value for property '%s' on '%s'\n"),
+ pname_utf8, target_local));
+ }
+ else
+ {
+ SVN_ERR
+ (svn_cmdline_printf
+ (subpool, _("No changes to property '%s' on '%s'\n"),
+ pname_utf8, target_local));
+ }
+ }
+ svn_pool_destroy(subpool);
+ }
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud