1/* 2 * properties.c: stuff related to Subversion properties 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#include <apr_pools.h> 27#include <apr_hash.h> 28#include <apr_tables.h> 29#include <string.h> /* for strncmp() */ 30#include "svn_hash.h" 31#include "svn_string.h" 32#include "svn_props.h" 33#include "svn_error.h" 34#include "svn_ctype.h" 35#include "private/svn_subr_private.h" 36 37 38/* All Subversion-specific versioned node properties 39 * known to this client, that are applicable to both a file and a dir. 40 */ 41#define SVN_PROP__NODE_COMMON_PROPS SVN_PROP_MERGEINFO, \ 42 SVN_PROP_TEXT_TIME, \ 43 SVN_PROP_OWNER, \ 44 SVN_PROP_GROUP, \ 45 SVN_PROP_UNIX_MODE, 46 47/* All Subversion-specific versioned node properties 48 * known to this client, that are applicable to a dir only. 49 */ 50#define SVN_PROP__NODE_DIR_ONLY_PROPS SVN_PROP_IGNORE, \ 51 SVN_PROP_INHERITABLE_IGNORES, \ 52 SVN_PROP_INHERITABLE_AUTO_PROPS, \ 53 SVN_PROP_EXTERNALS, 54 55/* All Subversion-specific versioned node properties 56 * known to this client, that are applicable to a file only. 57 */ 58#define SVN_PROP__NODE_FILE_ONLY_PROPS SVN_PROP_MIME_TYPE, \ 59 SVN_PROP_EOL_STYLE, \ 60 SVN_PROP_KEYWORDS, \ 61 SVN_PROP_EXECUTABLE, \ 62 SVN_PROP_NEEDS_LOCK, \ 63 SVN_PROP_SPECIAL, 64 65static const char *const known_rev_props[] 66 = { SVN_PROP_REVISION_ALL_PROPS 67 NULL }; 68 69static const char *const known_node_props[] 70 = { SVN_PROP__NODE_COMMON_PROPS 71 SVN_PROP__NODE_DIR_ONLY_PROPS 72 SVN_PROP__NODE_FILE_ONLY_PROPS 73 NULL }; 74 75static const char *const known_dir_props[] 76 = { SVN_PROP__NODE_COMMON_PROPS 77 SVN_PROP__NODE_DIR_ONLY_PROPS 78 NULL }; 79 80static const char *const known_file_props[] 81 = { SVN_PROP__NODE_COMMON_PROPS 82 SVN_PROP__NODE_FILE_ONLY_PROPS 83 NULL }; 84 85static svn_boolean_t 86is_known_prop(const char *prop_name, 87 const char *const *known_props) 88{ 89 while (*known_props) 90 { 91 if (strcmp(prop_name, *known_props++) == 0) 92 return TRUE; 93 } 94 return FALSE; 95} 96 97svn_boolean_t 98svn_prop_is_known_svn_rev_prop(const char *prop_name) 99{ 100 return is_known_prop(prop_name, known_rev_props); 101} 102 103svn_boolean_t 104svn_prop_is_known_svn_node_prop(const char *prop_name) 105{ 106 return is_known_prop(prop_name, known_node_props); 107} 108 109svn_boolean_t 110svn_prop_is_known_svn_file_prop(const char *prop_name) 111{ 112 return is_known_prop(prop_name, known_file_props); 113} 114 115svn_boolean_t 116svn_prop_is_known_svn_dir_prop(const char *prop_name) 117{ 118 return is_known_prop(prop_name, known_dir_props); 119} 120 121 122svn_boolean_t 123svn_prop_is_svn_prop(const char *prop_name) 124{ 125 return strncmp(prop_name, SVN_PROP_PREFIX, (sizeof(SVN_PROP_PREFIX) - 1)) 126 == 0; 127} 128 129 130svn_boolean_t 131svn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool) 132{ 133 apr_hash_index_t *hi; 134 const void *prop_name; 135 136 if (! props) 137 return FALSE; 138 139 for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi; 140 hi = apr_hash_next(hi)) 141 { 142 apr_hash_this(hi, &prop_name, NULL, NULL); 143 if (svn_prop_is_svn_prop((const char *) prop_name)) 144 return TRUE; 145 } 146 147 return FALSE; 148} 149 150 151#define SIZEOF_WC_PREFIX (sizeof(SVN_PROP_WC_PREFIX) - 1) 152#define SIZEOF_ENTRY_PREFIX (sizeof(SVN_PROP_ENTRY_PREFIX) - 1) 153 154svn_prop_kind_t 155svn_property_kind2(const char *prop_name) 156{ 157 158 if (strncmp(prop_name, SVN_PROP_WC_PREFIX, SIZEOF_WC_PREFIX) == 0) 159 return svn_prop_wc_kind; 160 161 if (strncmp(prop_name, SVN_PROP_ENTRY_PREFIX, SIZEOF_ENTRY_PREFIX) == 0) 162 return svn_prop_entry_kind; 163 164 return svn_prop_regular_kind; 165} 166 167 168/* NOTE: this function is deprecated, but we cannot move it to deprecated.c 169 because we need the SIZEOF_*_PREFIX constant symbols defined above. */ 170svn_prop_kind_t 171svn_property_kind(int *prefix_len, 172 const char *prop_name) 173{ 174 svn_prop_kind_t kind = svn_property_kind2(prop_name); 175 176 if (prefix_len) 177 { 178 if (kind == svn_prop_wc_kind) 179 *prefix_len = SIZEOF_WC_PREFIX; 180 else if (kind == svn_prop_entry_kind) 181 *prefix_len = SIZEOF_ENTRY_PREFIX; 182 else 183 *prefix_len = 0; 184 } 185 186 return kind; 187} 188 189 190svn_error_t * 191svn_categorize_props(const apr_array_header_t *proplist, 192 apr_array_header_t **entry_props, 193 apr_array_header_t **wc_props, 194 apr_array_header_t **regular_props, 195 apr_pool_t *pool) 196{ 197 int i; 198 if (entry_props) 199 *entry_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); 200 if (wc_props) 201 *wc_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); 202 if (regular_props) 203 *regular_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); 204 205 for (i = 0; i < proplist->nelts; i++) 206 { 207 svn_prop_t *prop, *newprop; 208 enum svn_prop_kind kind; 209 210 prop = &APR_ARRAY_IDX(proplist, i, svn_prop_t); 211 kind = svn_property_kind2(prop->name); 212 newprop = NULL; 213 214 if (kind == svn_prop_regular_kind) 215 { 216 if (regular_props) 217 newprop = apr_array_push(*regular_props); 218 } 219 else if (kind == svn_prop_wc_kind) 220 { 221 if (wc_props) 222 newprop = apr_array_push(*wc_props); 223 } 224 else if (kind == svn_prop_entry_kind) 225 { 226 if (entry_props) 227 newprop = apr_array_push(*entry_props); 228 } 229 else 230 /* Technically this can't happen, but might as well have the 231 code ready in case that ever changes. */ 232 return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL, 233 "Bad property kind for property '%s'", 234 prop->name); 235 236 if (newprop) 237 { 238 newprop->name = prop->name; 239 newprop->value = prop->value; 240 } 241 } 242 243 return SVN_NO_ERROR; 244} 245 246 247svn_error_t * 248svn_prop_diffs(apr_array_header_t **propdiffs, 249 const apr_hash_t *target_props, 250 const apr_hash_t *source_props, 251 apr_pool_t *pool) 252{ 253 apr_hash_index_t *hi; 254 apr_array_header_t *ary = apr_array_make(pool, 1, sizeof(svn_prop_t)); 255 256 /* Note: we will be storing the pointers to the keys (from the hashes) 257 into the propdiffs array. It is acceptable for us to 258 reference the same memory as the base/target_props hash. */ 259 260 /* Loop over SOURCE_PROPS and examine each key. This will allow us to 261 detect any `deletion' events or `set-modification' events. */ 262 for (hi = apr_hash_first(pool, (apr_hash_t *)source_props); hi; 263 hi = apr_hash_next(hi)) 264 { 265 const void *key; 266 apr_ssize_t klen; 267 void *val; 268 const svn_string_t *propval1, *propval2; 269 270 /* Get next property */ 271 apr_hash_this(hi, &key, &klen, &val); 272 propval1 = val; 273 274 /* Does property name exist in TARGET_PROPS? */ 275 propval2 = apr_hash_get((apr_hash_t *)target_props, key, klen); 276 277 if (propval2 == NULL) 278 { 279 /* Add a delete event to the array */ 280 svn_prop_t *p = apr_array_push(ary); 281 p->name = key; 282 p->value = NULL; 283 } 284 else if (! svn_string_compare(propval1, propval2)) 285 { 286 /* Add a set (modification) event to the array */ 287 svn_prop_t *p = apr_array_push(ary); 288 p->name = key; 289 p->value = svn_string_dup(propval2, pool); 290 } 291 } 292 293 /* Loop over TARGET_PROPS and examine each key. This allows us to 294 detect `set-creation' events */ 295 for (hi = apr_hash_first(pool, (apr_hash_t *)target_props); hi; 296 hi = apr_hash_next(hi)) 297 { 298 const void *key; 299 apr_ssize_t klen; 300 void *val; 301 const svn_string_t *propval; 302 303 /* Get next property */ 304 apr_hash_this(hi, &key, &klen, &val); 305 propval = val; 306 307 /* Does property name exist in SOURCE_PROPS? */ 308 if (NULL == apr_hash_get((apr_hash_t *)source_props, key, klen)) 309 { 310 /* Add a set (creation) event to the array */ 311 svn_prop_t *p = apr_array_push(ary); 312 p->name = key; 313 p->value = svn_string_dup(propval, pool); 314 } 315 } 316 317 /* Done building our array of user events. */ 318 *propdiffs = ary; 319 320 return SVN_NO_ERROR; 321} 322 323apr_hash_t * 324svn_prop__patch(const apr_hash_t *original_props, 325 const apr_array_header_t *prop_changes, 326 apr_pool_t *pool) 327{ 328 apr_hash_t *props = apr_hash_copy(pool, original_props); 329 int i; 330 331 for (i = 0; i < prop_changes->nelts; i++) 332 { 333 const svn_prop_t *p = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t); 334 335 svn_hash_sets(props, p->name, p->value); 336 } 337 return props; 338} 339 340/** 341 * Reallocate the members of PROP using POOL. 342 */ 343static void 344svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool) 345{ 346 if (prop->name) 347 prop->name = apr_pstrdup(pool, prop->name); 348 if (prop->value) 349 prop->value = svn_string_dup(prop->value, pool); 350} 351 352svn_prop_t * 353svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool) 354{ 355 svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop)); 356 357 *new_prop = *prop; 358 359 svn_prop__members_dup(new_prop, pool); 360 361 return new_prop; 362} 363 364apr_array_header_t * 365svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool) 366{ 367 int i; 368 apr_array_header_t *new_array = apr_array_copy(pool, array); 369 for (i = 0; i < new_array->nelts; ++i) 370 { 371 svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t); 372 svn_prop__members_dup(elt, pool); 373 } 374 return new_array; 375} 376 377apr_array_header_t * 378svn_prop_hash_to_array(const apr_hash_t *hash, 379 apr_pool_t *pool) 380{ 381 apr_hash_index_t *hi; 382 apr_array_header_t *array = apr_array_make(pool, 383 apr_hash_count((apr_hash_t *)hash), 384 sizeof(svn_prop_t)); 385 386 for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi; 387 hi = apr_hash_next(hi)) 388 { 389 const void *key; 390 void *val; 391 svn_prop_t prop; 392 393 apr_hash_this(hi, &key, NULL, &val); 394 prop.name = key; 395 prop.value = val; 396 APR_ARRAY_PUSH(array, svn_prop_t) = prop; 397 } 398 399 return array; 400} 401 402apr_hash_t * 403svn_prop_hash_dup(const apr_hash_t *hash, 404 apr_pool_t *pool) 405{ 406 apr_hash_index_t *hi; 407 apr_hash_t *new_hash = apr_hash_make(pool); 408 409 for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi; 410 hi = apr_hash_next(hi)) 411 { 412 const void *key; 413 apr_ssize_t klen; 414 void *prop; 415 416 apr_hash_this(hi, &key, &klen, &prop); 417 apr_hash_set(new_hash, apr_pstrmemdup(pool, key, klen), klen, 418 svn_string_dup(prop, pool)); 419 } 420 return new_hash; 421} 422 423apr_hash_t * 424svn_prop_array_to_hash(const apr_array_header_t *properties, 425 apr_pool_t *pool) 426{ 427 int i; 428 apr_hash_t *prop_hash = apr_hash_make(pool); 429 430 for (i = 0; i < properties->nelts; i++) 431 { 432 const svn_prop_t *prop = &APR_ARRAY_IDX(properties, i, svn_prop_t); 433 svn_hash_sets(prop_hash, prop->name, prop->value); 434 } 435 436 return prop_hash; 437} 438 439svn_boolean_t 440svn_prop_is_boolean(const char *prop_name) 441{ 442 /* If we end up with more than 3 of these, we should probably put 443 them in a table and use bsearch. With only three, it doesn't 444 make any speed difference. */ 445 if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0 446 || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0 447 || strcmp(prop_name, SVN_PROP_SPECIAL) == 0) 448 return TRUE; 449 return FALSE; 450} 451 452 453svn_boolean_t 454svn_prop_needs_translation(const char *propname) 455{ 456 /* ### Someday, we may want to be picky and choosy about which 457 properties require UTF8 and EOL conversion. For now, all "svn:" 458 props need it. */ 459 460 return svn_prop_is_svn_prop(propname); 461} 462 463 464svn_boolean_t 465svn_prop_name_is_valid(const char *prop_name) 466{ 467 const char *p = prop_name; 468 469 /* The characters we allow use identical representations in UTF8 470 and ASCII, so we can just test for the appropriate ASCII codes. 471 But we can't use standard C character notation ('A', 'B', etc) 472 because there's no guarantee that this C environment is using 473 ASCII. */ 474 475 if (!(svn_ctype_isalpha(*p) 476 || *p == SVN_CTYPE_ASCII_COLON 477 || *p == SVN_CTYPE_ASCII_UNDERSCORE)) 478 return FALSE; 479 p++; 480 for (; *p; p++) 481 { 482 if (!(svn_ctype_isalnum(*p) 483 || *p == SVN_CTYPE_ASCII_MINUS 484 || *p == SVN_CTYPE_ASCII_DOT 485 || *p == SVN_CTYPE_ASCII_COLON 486 || *p == SVN_CTYPE_ASCII_UNDERSCORE)) 487 return FALSE; 488 } 489 return TRUE; 490} 491 492const char * 493svn_prop_get_value(const apr_hash_t *props, 494 const char *prop_name) 495{ 496 svn_string_t *str; 497 498 if (!props) 499 return NULL; 500 501 str = svn_hash_gets((apr_hash_t *)props, prop_name); 502 503 if (str) 504 return str->data; 505 506 return NULL; 507} 508