Reorganize the output to "svnserve --help".
[svn.git] / subversion / svn / propedit-cmd.c
blobb2a651d33baeb1a8e85f219a370811c9faef5194
1 /*
2 * propedit-cmd.c -- Edit properties of files/dirs using $EDITOR
4 * ====================================================================
5 * Copyright (c) 2000-2004 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 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include "svn_cmdline.h"
26 #include "svn_wc.h"
27 #include "svn_pools.h"
28 #include "svn_client.h"
29 #include "svn_string.h"
30 #include "svn_path.h"
31 #include "svn_error.h"
32 #include "svn_utf.h"
33 #include "svn_props.h"
34 #include "cl.h"
36 #include "svn_private_config.h"
39 /*** Code. ***/
41 /* This implements the `svn_opt_subcommand_t' interface. */
42 svn_error_t *
43 svn_cl__propedit(apr_getopt_t *os,
44 void *baton,
45 apr_pool_t *pool)
47 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
48 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
49 const char *pname, *pname_utf8;
50 apr_array_header_t *args, *targets;
51 int i;
53 /* Validate the input and get the property's name (and a UTF-8
54 version of that name). */
55 SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
56 pname = APR_ARRAY_IDX(args, 0, const char *);
57 SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
58 if (! svn_prop_name_is_valid(pname_utf8))
59 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
60 _("'%s' is not a valid Subversion property name"),
61 pname_utf8);
62 if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
63 return svn_error_create
64 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
65 _("--encoding option applies only to textual"
66 " Subversion-controlled properties"));
68 /* Suck up all the remaining arguments into a targets array */
69 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
70 opt_state->targets,
71 pool));
73 if (opt_state->revprop) /* operate on a revprop */
75 svn_revnum_t rev;
76 const char *URL;
77 svn_string_t *propval;
78 const char *temp_dir;
80 /* Implicit "." is okay for revision properties; it just helps
81 us find the right repository. */
82 svn_opt_push_implicit_dot_target(targets, pool);
84 SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
85 &URL, pool));
87 /* Fetch the current property. */
88 SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
89 URL, &(opt_state->start_revision),
90 &rev, ctx, pool));
91 if (! propval)
92 propval = svn_string_create("", pool);
94 /* Run the editor on a temporary file which contains the
95 original property value... */
96 SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
97 SVN_ERR(svn_cl__edit_string_externally
98 (&propval, NULL,
99 opt_state->editor_cmd, temp_dir,
100 propval, "svn-prop",
101 ctx->config,
102 svn_prop_needs_translation(pname_utf8),
103 opt_state->encoding, pool));
105 /* ...and re-set the property's value accordingly. */
106 if (propval)
108 SVN_ERR(svn_client_revprop_set(pname_utf8, propval,
109 URL, &(opt_state->start_revision),
110 &rev, opt_state->force, ctx, pool));
112 SVN_ERR
113 (svn_cmdline_printf
114 (pool,
115 _("Set new value for property '%s' on revision %ld\n"),
116 pname_utf8, rev));
118 else
120 SVN_ERR(svn_cmdline_printf
121 (pool, _("No changes to property '%s' on revision %ld\n"),
122 pname_utf8, rev));
125 else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
127 return svn_error_createf
128 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
129 _("Cannot specify revision for editing versioned property '%s'"),
130 pname_utf8);
132 else /* operate on a normal, versioned property (not a revprop) */
134 apr_pool_t *subpool = svn_pool_create(pool);
136 /* The customary implicit dot rule has been prone to user error
137 * here. For example, Jon Trowbridge <trow@gnu.og> did
139 * $ svn propedit HACKING
141 * and then when he closed his editor, he was surprised to see
143 * Set new value for property 'HACKING' on ''
145 * ...meaning that the property named 'HACKING' had been set on
146 * the current working directory, with the value taken from the
147 * editor. So we don't do the implicit dot thing anymore; an
148 * explicit target is always required when editing a versioned
149 * property.
151 if (targets->nelts == 0)
153 return svn_error_create
154 (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
155 _("Explicit target argument required"));
158 /* For each target, edit the property PNAME. */
159 for (i = 0; i < targets->nelts; i++)
161 apr_hash_t *props;
162 const char *target = APR_ARRAY_IDX(targets, i, const char *);
163 svn_string_t *propval, *edited_propval;
164 const char *base_dir = target;
165 const char *target_local;
166 svn_wc_adm_access_t *adm_access;
167 const svn_wc_entry_t *entry;
168 svn_opt_revision_t peg_revision;
169 svn_revnum_t base_rev = SVN_INVALID_REVNUM;
171 svn_pool_clear(subpool);
172 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
174 /* Propedits can only happen on HEAD or the working copy, so
175 the peg revision can be as unspecified. */
176 peg_revision.kind = svn_opt_revision_unspecified;
178 /* Fetch the current property. */
179 SVN_ERR(svn_client_propget3(&props, pname_utf8, target,
180 &peg_revision,
181 &(opt_state->start_revision),
182 &base_rev, svn_depth_empty,
183 NULL, ctx, subpool));
185 /* Get the property value. */
186 propval = apr_hash_get(props, target, APR_HASH_KEY_STRING);
187 if (! propval)
188 propval = svn_string_create("", subpool);
190 if (svn_path_is_url(target))
192 /* For URLs, put the temporary file in the current directory. */
193 base_dir = ".";
195 else
197 if (opt_state->message || opt_state->filedata ||
198 opt_state->revprop_table)
200 return svn_error_create
201 (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
202 _("Local, non-commit operations do not take a log message "
203 "or revision properties"));
206 /* Split the path if it is a file path. */
207 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target,
208 FALSE, 0, ctx->cancel_func,
209 ctx->cancel_baton, subpool));
210 SVN_ERR(svn_wc_entry(&entry, target, adm_access, FALSE, subpool));
211 if (! entry)
212 return svn_error_createf
213 (SVN_ERR_ENTRY_NOT_FOUND, NULL,
214 _("'%s' does not appear to be a working copy path"), target);
215 if (entry->kind == svn_node_file)
216 svn_path_split(target, &base_dir, NULL, subpool);
219 /* Run the editor on a temporary file which contains the
220 original property value... */
221 SVN_ERR(svn_cl__edit_string_externally(&edited_propval, NULL,
222 opt_state->editor_cmd,
223 base_dir,
224 propval,
225 "svn-prop",
226 ctx->config,
227 svn_prop_needs_translation
228 (pname_utf8),
229 opt_state->encoding,
230 subpool));
232 target_local = svn_path_is_url(target) ? target
233 : svn_path_local_style(target, subpool);
235 /* ...and re-set the property's value accordingly. */
236 if (edited_propval && !svn_string_compare(propval, edited_propval))
238 svn_commit_info_t *commit_info = NULL;
239 svn_error_t *err = SVN_NO_ERROR;
241 svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data,
242 subpool);
244 if (ctx->log_msg_func3)
245 SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
246 opt_state, NULL, ctx->config,
247 subpool));
249 ctx->revprop_table = opt_state->revprop_table;
251 err = svn_client_propset3(&commit_info,
252 pname_utf8, edited_propval, target,
253 svn_depth_empty, opt_state->force,
254 base_rev, NULL,
255 ctx, subpool);
256 if (ctx->log_msg_func3)
257 SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err));
258 else if (err)
259 return err;
261 /* Print a message if we successfully committed or if it
262 was just a wc propset (but not if the user aborted an URL
263 propedit). */
264 if (commit_info || ! svn_path_is_url(target))
265 SVN_ERR
266 (svn_cmdline_printf
267 (subpool, _("Set new value for property '%s' on '%s'\n"),
268 pname_utf8, target_local));
270 if (commit_info && ! opt_state->quiet)
271 SVN_ERR(svn_cl__print_commit_info(commit_info, subpool));
273 else
275 SVN_ERR
276 (svn_cmdline_printf
277 (subpool, _("No changes to property '%s' on '%s'\n"),
278 pname_utf8, target_local));
281 svn_pool_destroy(subpool);
284 return SVN_NO_ERROR;