1 /*
2 * props.c: Utility functions for property handling
3 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27
28 /*** Includes. ***/
29
30 #include <stdlib.h>
31
32 #include <apr_hash.h>
33 #include "svn_hash.h"
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"
41 #include "svn_opt.h"
42 #include "svn_xml.h"
43 #include "svn_base64.h"
44 #include "cl.h"
45
46 #include "private/svn_string_private.h"
47 #include "private/svn_cmdline_private.h"
48
49 #include "svn_private_config.h"
50
51
52 svn_error_t *
svn_cl__revprop_prepare(const svn_opt_revision_t * revision,const apr_array_header_t * targets,const char ** URL,svn_client_ctx_t * ctx,apr_pool_t * pool)53 svn_cl__revprop_prepare(const svn_opt_revision_t *revision,
54 const apr_array_header_t *targets,
55 const char **URL,
56 svn_client_ctx_t *ctx,
57 apr_pool_t *pool)
58 {
59 const char *target;
60
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"));
68
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"));
74
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));
79 if (*URL == NULL)
80 return svn_error_create
81 (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
82 _("Either a URL or versioned item is required"));
83
84 return SVN_NO_ERROR;
85 }
86
87 void
svn_cl__check_boolean_prop_val(const char * propname,const char * propval,apr_pool_t * pool)88 svn_cl__check_boolean_prop_val(const char *propname, const char *propval,
89 apr_pool_t *pool)
90 {
91 svn_stringbuf_t *propbuf;
92
93 if (!svn_prop_is_boolean(propname))
94 return;
95
96 propbuf = svn_stringbuf_create(propval, pool);
97 svn_stringbuf_strip_whitespace(propbuf);
98
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)
104 {
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."),
109 propname, propval);
110 svn_handle_warning2(stderr, err, "svn: ");
111 svn_error_clear(err);
112 }
113 }
114
115 static const char*
force_prop_option_message(svn_cl__prop_use_t prop_use,const char * prop_name,apr_pool_t * scratch_pool)116 force_prop_option_message(svn_cl__prop_use_t prop_use, const char *prop_name,
117 apr_pool_t *scratch_pool)
118 {
119 switch (prop_use)
120 {
121 case svn_cl__prop_use_set:
122 return apr_psprintf(
123 scratch_pool,
124 _("Use '--force' to set the '%s' property."),
125 prop_name);
126 case svn_cl__prop_use_edit:
127 return apr_psprintf(
128 scratch_pool,
129 _("Use '--force' to edit the '%s' property."),
130 prop_name);
131 case svn_cl__prop_use_use:
132 default:
133 return apr_psprintf(
134 scratch_pool,
135 _("Use '--force' to use the '%s' property'."),
136 prop_name);
137 }
138 }
139
140 static const char*
wrong_prop_error_message(svn_cl__prop_use_t prop_use,const char * prop_name,apr_pool_t * scratch_pool)141 wrong_prop_error_message(svn_cl__prop_use_t prop_use, const char *prop_name,
142 apr_pool_t *scratch_pool)
143 {
144 switch (prop_use)
145 {
146 case svn_cl__prop_use_set:
147 return apr_psprintf(
148 scratch_pool,
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:
152 return apr_psprintf(
153 scratch_pool,
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:
157 default:
158 return apr_psprintf(
159 scratch_pool,
160 _("'%s' is not a valid %s property name; use '--force' to use it"),
161 prop_name, SVN_PROP_PREFIX);
162 }
163 }
164
165 svn_error_t *
svn_cl__check_svn_prop_name(const char * propname,svn_boolean_t revprop,svn_cl__prop_use_t prop_use,apr_pool_t * scratch_pool)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)
170 {
171 static const char *const nodeprops[] =
172 {
173 SVN_PROP_NODE_ALL_PROPS
174 };
175 static const apr_size_t nodeprops_len = sizeof(nodeprops)/sizeof(*nodeprops);
176
177 static const char *const revprops[] =
178 {
179 SVN_PROP_REVISION_ALL_PROPS
180 };
181 static const apr_size_t revprops_len = sizeof(revprops)/sizeof(*revprops);
182
183 const char *const *const proplist = (revprop ? revprops : nodeprops);
184 const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len);
185
186 svn_cl__simcheck_t **propkeys;
187 svn_cl__simcheck_t *propbuf;
188 apr_size_t i;
189
190 svn_string_t propstring;
191 svn_string_t prefix;
192 svn_membuf_t buffer;
193
194 propstring.data = propname;
195 propstring.len = strlen(propname);
196 prefix.data = SVN_PROP_PREFIX;
197 prefix.len = strlen(SVN_PROP_PREFIX);
198
199 svn_membuf__create(&buffer, 0, scratch_pool);
200
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 */
207 else
208 {
209 apr_size_t lcs;
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 */
216
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)
220 {
221 for (i = 0; i < numprops; ++i)
222 {
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));
230 }
231 return SVN_NO_ERROR;
232 }
233 }
234
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
238 skew the results. */
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)
246 {
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];
251 }
252
253 switch (svn_cl__similarity_check(
254 propstring.data, propkeys, numprops, scratch_pool))
255 {
256 case 0:
257 return SVN_NO_ERROR; /* We found an exact match. */
258
259 case 1:
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));
264
265 case 2:
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));
273
274 case 3:
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));
283
284 default:
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));
294 }
295 }
296