props.c revision 299742
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 52svn_error_t * 53svn_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 87void 88svn_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 115static const char* 116force_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 140static const char* 141wrong_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 165svn_error_t * 166svn_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