2 * props.c: Utility functions for property handling
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
34 #include "svn_cmdline.h"
35 #include "svn_string.h"
36 #include "svn_error.h"
37 #include "svn_sorts.h"
38 #include "svn_subst.h"
39 #include "svn_props.h"
40 #include "svn_string.h"
43 #include "svn_base64.h"
46 #include "private/svn_string_private.h"
47 #include "private/svn_cmdline_private.h"
49 #include "svn_private_config.h"
53 svn_cl__revprop_prepare(const svn_opt_revision_t *revision,
54 const apr_array_header_t *targets,
56 svn_client_ctx_t *ctx,
61 if (revision->kind != svn_opt_revision_number
62 && revision->kind != svn_opt_revision_date
63 && revision->kind != svn_opt_revision_head)
64 return svn_error_create
65 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
66 _("Must specify the revision as a number, a date or 'HEAD' "
67 "when operating on a revision property"));
69 /* There must be exactly one target at this point. If it was optional and
70 unspecified by the user, the caller has already added the implicit '.'. */
71 if (targets->nelts != 1)
72 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
73 _("Wrong number of targets specified"));
75 /* (The docs say the target must be either a URL or implicit '.', but
76 explicit WC targets are also accepted.) */
77 target = APR_ARRAY_IDX(targets, 0, const char *);
78 SVN_ERR(svn_client_url_from_path2(URL, target, ctx, pool, pool));
80 return svn_error_create
81 (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
82 _("Either a URL or versioned item is required"));
88 svn_cl__check_boolean_prop_val(const char *propname, const char *propval,
91 svn_stringbuf_t *propbuf;
93 if (!svn_prop_is_boolean(propname))
96 propbuf = svn_stringbuf_create(propval, pool);
97 svn_stringbuf_strip_whitespace(propbuf);
99 if (propbuf->data[0] == '\0'
100 || svn_cstring_casecmp(propbuf->data, "0") == 0
101 || svn_cstring_casecmp(propbuf->data, "no") == 0
102 || svn_cstring_casecmp(propbuf->data, "off") == 0
103 || svn_cstring_casecmp(propbuf->data, "false") == 0)
105 svn_error_t *err = svn_error_createf
106 (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
107 _("To turn off the %s property, use 'svn propdel';\n"
108 "setting the property to '%s' will not turn it off."),
110 svn_handle_warning2(stderr, err, "svn: ");
111 svn_error_clear(err);
116 force_prop_option_message(svn_cl__prop_use_t prop_use, const char *prop_name,
117 apr_pool_t *scratch_pool)
121 case svn_cl__prop_use_set:
124 _("Use '--force' to set the '%s' property."),
126 case svn_cl__prop_use_edit:
129 _("Use '--force' to edit the '%s' property."),
131 case svn_cl__prop_use_use:
135 _("Use '--force' to use the '%s' property'."),
141 wrong_prop_error_message(svn_cl__prop_use_t prop_use, const char *prop_name,
142 apr_pool_t *scratch_pool)
146 case svn_cl__prop_use_set:
149 _("'%s' is not a valid %s property name; use '--force' to set it"),
150 prop_name, SVN_PROP_PREFIX);
151 case svn_cl__prop_use_edit:
154 _("'%s' is not a valid %s property name; use '--force' to edit it"),
155 prop_name, SVN_PROP_PREFIX);
156 case svn_cl__prop_use_use:
160 _("'%s' is not a valid %s property name; use '--force' to use it"),
161 prop_name, SVN_PROP_PREFIX);
166 svn_cl__check_svn_prop_name(const char *propname,
167 svn_boolean_t revprop,
168 svn_cl__prop_use_t prop_use,
169 apr_pool_t *scratch_pool)
171 static const char *const nodeprops[] =
173 SVN_PROP_NODE_ALL_PROPS
175 static const apr_size_t nodeprops_len = sizeof(nodeprops)/sizeof(*nodeprops);
177 static const char *const revprops[] =
179 SVN_PROP_REVISION_ALL_PROPS
181 static const apr_size_t revprops_len = sizeof(revprops)/sizeof(*revprops);
183 const char *const *const proplist = (revprop ? revprops : nodeprops);
184 const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len);
186 svn_cl__simcheck_t **propkeys;
187 svn_cl__simcheck_t *propbuf;
190 svn_string_t propstring;
194 propstring.data = propname;
195 propstring.len = strlen(propname);
196 prefix.data = SVN_PROP_PREFIX;
197 prefix.len = strlen(SVN_PROP_PREFIX);
199 svn_membuf__create(&buffer, 0, scratch_pool);
201 /* First, check if the name is even close to being in the svn: namespace.
202 It must contain a colon in the right place, and we only allow
203 one-char typos or a single transposition. */
204 if (propstring.len < prefix.len
205 || propstring.data[prefix.len - 1] != prefix.data[prefix.len - 1])
206 return SVN_NO_ERROR; /* Wrong prefix, ignore */
210 const apr_size_t name_len = propstring.len;
211 propstring.len = prefix.len; /* Only check up to the prefix length */
212 svn_string__similarity(&propstring, &prefix, &buffer, &lcs);
213 propstring.len = name_len; /* Restore the original propname length */
214 if (lcs < prefix.len - 1)
215 return SVN_NO_ERROR; /* Wrong prefix, ignore */
217 /* If the prefix is slightly different, the rest must be
218 identical in order to trigger the error. */
219 if (lcs == prefix.len - 1)
221 for (i = 0; i < numprops; ++i)
223 if (0 == strcmp(proplist[i] + prefix.len, propname + prefix.len))
224 return svn_error_quick_wrap(svn_error_createf(
225 SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
226 _("'%s' is not a valid %s property name;"
227 " did you mean '%s'?"),
228 propname, SVN_PROP_PREFIX, proplist[i]),
229 force_prop_option_message(prop_use, propname, scratch_pool));
235 /* Now find the closest match from amongst the set of reserved
236 node or revision property names. Skip the prefix while matching,
237 we already know that it's the same and looking at it would only
239 propkeys = apr_palloc(scratch_pool,
240 numprops * sizeof(svn_cl__simcheck_t*));
241 propbuf = apr_palloc(scratch_pool,
242 numprops * sizeof(svn_cl__simcheck_t));
243 propstring.data += prefix.len;
244 propstring.len -= prefix.len;
245 for (i = 0; i < numprops; ++i)
247 propkeys[i] = &propbuf[i];
248 propbuf[i].token.data = proplist[i] + prefix.len;
249 propbuf[i].token.len = strlen(propbuf[i].token.data);
250 propbuf[i].data = proplist[i];
253 switch (svn_cl__similarity_check(
254 propstring.data, propkeys, numprops, scratch_pool))
257 return SVN_NO_ERROR; /* We found an exact match. */
260 /* The best alternative isn't good enough */
261 return svn_error_create(
262 SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
263 wrong_prop_error_message(prop_use, propname, scratch_pool));
266 /* There is only one good candidate */
267 return svn_error_quick_wrap(svn_error_createf(
268 SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
269 _("'%s' is not a valid %s property name; did you mean '%s'?"),
270 propname, SVN_PROP_PREFIX,
271 (const char *)propkeys[0]->data),
272 force_prop_option_message(prop_use, propname, scratch_pool));
275 /* Suggest a list of the most likely candidates */
276 return svn_error_quick_wrap(svn_error_createf(
277 SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
278 _("'%s' is not a valid %s property name; "
279 "did you mean '%s' or '%s'?"),
280 propname, SVN_PROP_PREFIX,
281 (const char *)propkeys[0]->data, (const char *)propkeys[1]->data),
282 force_prop_option_message(prop_use, propname, scratch_pool));
285 /* Never suggest more than three candidates */
286 return svn_error_quick_wrap(svn_error_createf(
287 SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
288 _("'%s' is not a valid %s property name; "
289 "did you mean '%s', '%s' or '%s'?"),
290 propname, SVN_PROP_PREFIX,
291 (const char *)propkeys[0]->data,
292 (const char *)propkeys[1]->data, (const char *)propkeys[2]->data),
293 force_prop_option_message(prop_use, propname, scratch_pool));