]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svn/props.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svn / props.c
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 \f
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 \f
51
52 svn_error_t *
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
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*
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*
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 *
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 }