diff options
Diffstat (limited to 'subversion/libsvn_subr/properties.c')
-rw-r--r-- | subversion/libsvn_subr/properties.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/properties.c b/subversion/libsvn_subr/properties.c new file mode 100644 index 0000000..738d00f --- /dev/null +++ b/subversion/libsvn_subr/properties.c @@ -0,0 +1,507 @@ +/* + * properties.c: stuff related to Subversion properties + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + + + +#include <apr_pools.h> +#include <apr_hash.h> +#include <apr_tables.h> +#include <string.h> /* for strncmp() */ +#include "svn_hash.h" +#include "svn_string.h" +#include "svn_props.h" +#include "svn_error.h" +#include "svn_ctype.h" +#include "private/svn_subr_private.h" + + +/* All Subversion-specific versioned node properties + * known to this client, that are applicable to both a file and a dir. + */ +#define SVN_PROP__NODE_COMMON_PROPS SVN_PROP_MERGEINFO, \ + SVN_PROP_TEXT_TIME, \ + SVN_PROP_OWNER, \ + SVN_PROP_GROUP, \ + SVN_PROP_UNIX_MODE, + +/* All Subversion-specific versioned node properties + * known to this client, that are applicable to a dir only. + */ +#define SVN_PROP__NODE_DIR_ONLY_PROPS SVN_PROP_IGNORE, \ + SVN_PROP_INHERITABLE_IGNORES, \ + SVN_PROP_INHERITABLE_AUTO_PROPS, \ + SVN_PROP_EXTERNALS, + +/* All Subversion-specific versioned node properties + * known to this client, that are applicable to a file only. + */ +#define SVN_PROP__NODE_FILE_ONLY_PROPS SVN_PROP_MIME_TYPE, \ + SVN_PROP_EOL_STYLE, \ + SVN_PROP_KEYWORDS, \ + SVN_PROP_EXECUTABLE, \ + SVN_PROP_NEEDS_LOCK, \ + SVN_PROP_SPECIAL, + +static const char *const known_rev_props[] + = { SVN_PROP_REVISION_ALL_PROPS + NULL }; + +static const char *const known_node_props[] + = { SVN_PROP__NODE_COMMON_PROPS + SVN_PROP__NODE_DIR_ONLY_PROPS + SVN_PROP__NODE_FILE_ONLY_PROPS + NULL }; + +static const char *const known_dir_props[] + = { SVN_PROP__NODE_COMMON_PROPS + SVN_PROP__NODE_DIR_ONLY_PROPS + NULL }; + +static const char *const known_file_props[] + = { SVN_PROP__NODE_COMMON_PROPS + SVN_PROP__NODE_FILE_ONLY_PROPS + NULL }; + +static svn_boolean_t +is_known_prop(const char *prop_name, + const char *const *known_props) +{ + while (*known_props) + { + if (strcmp(prop_name, *known_props++) == 0) + return TRUE; + } + return FALSE; +} + +svn_boolean_t +svn_prop_is_known_svn_rev_prop(const char *prop_name) +{ + return is_known_prop(prop_name, known_rev_props); +} + +svn_boolean_t +svn_prop_is_known_svn_node_prop(const char *prop_name) +{ + return is_known_prop(prop_name, known_node_props); +} + +svn_boolean_t +svn_prop_is_known_svn_file_prop(const char *prop_name) +{ + return is_known_prop(prop_name, known_file_props); +} + +svn_boolean_t +svn_prop_is_known_svn_dir_prop(const char *prop_name) +{ + return is_known_prop(prop_name, known_dir_props); +} + + +svn_boolean_t +svn_prop_is_svn_prop(const char *prop_name) +{ + return strncmp(prop_name, SVN_PROP_PREFIX, (sizeof(SVN_PROP_PREFIX) - 1)) + == 0; +} + + +svn_boolean_t +svn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool) +{ + apr_hash_index_t *hi; + const void *prop_name; + + if (! props) + return FALSE; + + for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi; + hi = apr_hash_next(hi)) + { + apr_hash_this(hi, &prop_name, NULL, NULL); + if (svn_prop_is_svn_prop((const char *) prop_name)) + return TRUE; + } + + return FALSE; +} + + +#define SIZEOF_WC_PREFIX (sizeof(SVN_PROP_WC_PREFIX) - 1) +#define SIZEOF_ENTRY_PREFIX (sizeof(SVN_PROP_ENTRY_PREFIX) - 1) + +svn_prop_kind_t +svn_property_kind2(const char *prop_name) +{ + + if (strncmp(prop_name, SVN_PROP_WC_PREFIX, SIZEOF_WC_PREFIX) == 0) + return svn_prop_wc_kind; + + if (strncmp(prop_name, SVN_PROP_ENTRY_PREFIX, SIZEOF_ENTRY_PREFIX) == 0) + return svn_prop_entry_kind; + + return svn_prop_regular_kind; +} + + +/* NOTE: this function is deprecated, but we cannot move it to deprecated.c + because we need the SIZEOF_*_PREFIX constant symbols defined above. */ +svn_prop_kind_t +svn_property_kind(int *prefix_len, + const char *prop_name) +{ + svn_prop_kind_t kind = svn_property_kind2(prop_name); + + if (prefix_len) + { + if (kind == svn_prop_wc_kind) + *prefix_len = SIZEOF_WC_PREFIX; + else if (kind == svn_prop_entry_kind) + *prefix_len = SIZEOF_ENTRY_PREFIX; + else + *prefix_len = 0; + } + + return kind; +} + + +svn_error_t * +svn_categorize_props(const apr_array_header_t *proplist, + apr_array_header_t **entry_props, + apr_array_header_t **wc_props, + apr_array_header_t **regular_props, + apr_pool_t *pool) +{ + int i; + if (entry_props) + *entry_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); + if (wc_props) + *wc_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); + if (regular_props) + *regular_props = apr_array_make(pool, 1, sizeof(svn_prop_t)); + + for (i = 0; i < proplist->nelts; i++) + { + svn_prop_t *prop, *newprop; + enum svn_prop_kind kind; + + prop = &APR_ARRAY_IDX(proplist, i, svn_prop_t); + kind = svn_property_kind2(prop->name); + newprop = NULL; + + if (kind == svn_prop_regular_kind) + { + if (regular_props) + newprop = apr_array_push(*regular_props); + } + else if (kind == svn_prop_wc_kind) + { + if (wc_props) + newprop = apr_array_push(*wc_props); + } + else if (kind == svn_prop_entry_kind) + { + if (entry_props) + newprop = apr_array_push(*entry_props); + } + else + /* Technically this can't happen, but might as well have the + code ready in case that ever changes. */ + return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL, + "Bad property kind for property '%s'", + prop->name); + + if (newprop) + { + newprop->name = prop->name; + newprop->value = prop->value; + } + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_prop_diffs(apr_array_header_t **propdiffs, + const apr_hash_t *target_props, + const apr_hash_t *source_props, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + apr_array_header_t *ary = apr_array_make(pool, 1, sizeof(svn_prop_t)); + + /* Note: we will be storing the pointers to the keys (from the hashes) + into the propdiffs array. It is acceptable for us to + reference the same memory as the base/target_props hash. */ + + /* Loop over SOURCE_PROPS and examine each key. This will allow us to + detect any `deletion' events or `set-modification' events. */ + for (hi = apr_hash_first(pool, (apr_hash_t *)source_props); hi; + hi = apr_hash_next(hi)) + { + const void *key; + apr_ssize_t klen; + void *val; + const svn_string_t *propval1, *propval2; + + /* Get next property */ + apr_hash_this(hi, &key, &klen, &val); + propval1 = val; + + /* Does property name exist in TARGET_PROPS? */ + propval2 = apr_hash_get((apr_hash_t *)target_props, key, klen); + + if (propval2 == NULL) + { + /* Add a delete event to the array */ + svn_prop_t *p = apr_array_push(ary); + p->name = key; + p->value = NULL; + } + else if (! svn_string_compare(propval1, propval2)) + { + /* Add a set (modification) event to the array */ + svn_prop_t *p = apr_array_push(ary); + p->name = key; + p->value = svn_string_dup(propval2, pool); + } + } + + /* Loop over TARGET_PROPS and examine each key. This allows us to + detect `set-creation' events */ + for (hi = apr_hash_first(pool, (apr_hash_t *)target_props); hi; + hi = apr_hash_next(hi)) + { + const void *key; + apr_ssize_t klen; + void *val; + const svn_string_t *propval; + + /* Get next property */ + apr_hash_this(hi, &key, &klen, &val); + propval = val; + + /* Does property name exist in SOURCE_PROPS? */ + if (NULL == apr_hash_get((apr_hash_t *)source_props, key, klen)) + { + /* Add a set (creation) event to the array */ + svn_prop_t *p = apr_array_push(ary); + p->name = key; + p->value = svn_string_dup(propval, pool); + } + } + + /* Done building our array of user events. */ + *propdiffs = ary; + + return SVN_NO_ERROR; +} + +apr_hash_t * +svn_prop__patch(const apr_hash_t *original_props, + const apr_array_header_t *prop_changes, + apr_pool_t *pool) +{ + apr_hash_t *props = apr_hash_copy(pool, original_props); + int i; + + for (i = 0; i < prop_changes->nelts; i++) + { + const svn_prop_t *p = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t); + + svn_hash_sets(props, p->name, p->value); + } + return props; +} + +/** + * Reallocate the members of PROP using POOL. + */ +static void +svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool) +{ + if (prop->name) + prop->name = apr_pstrdup(pool, prop->name); + if (prop->value) + prop->value = svn_string_dup(prop->value, pool); +} + +svn_prop_t * +svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool) +{ + svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop)); + + *new_prop = *prop; + + svn_prop__members_dup(new_prop, pool); + + return new_prop; +} + +apr_array_header_t * +svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool) +{ + int i; + apr_array_header_t *new_array = apr_array_copy(pool, array); + for (i = 0; i < new_array->nelts; ++i) + { + svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t); + svn_prop__members_dup(elt, pool); + } + return new_array; +} + +apr_array_header_t * +svn_prop_hash_to_array(const apr_hash_t *hash, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + apr_array_header_t *array = apr_array_make(pool, + apr_hash_count((apr_hash_t *)hash), + sizeof(svn_prop_t)); + + for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi; + hi = apr_hash_next(hi)) + { + const void *key; + void *val; + svn_prop_t prop; + + apr_hash_this(hi, &key, NULL, &val); + prop.name = key; + prop.value = val; + APR_ARRAY_PUSH(array, svn_prop_t) = prop; + } + + return array; +} + +apr_hash_t * +svn_prop_hash_dup(const apr_hash_t *hash, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + apr_hash_t *new_hash = apr_hash_make(pool); + + for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi; + hi = apr_hash_next(hi)) + { + const void *key; + apr_ssize_t klen; + void *prop; + + apr_hash_this(hi, &key, &klen, &prop); + apr_hash_set(new_hash, apr_pstrmemdup(pool, key, klen), klen, + svn_string_dup(prop, pool)); + } + return new_hash; +} + +apr_hash_t * +svn_prop_array_to_hash(const apr_array_header_t *properties, + apr_pool_t *pool) +{ + int i; + apr_hash_t *prop_hash = apr_hash_make(pool); + + for (i = 0; i < properties->nelts; i++) + { + const svn_prop_t *prop = &APR_ARRAY_IDX(properties, i, svn_prop_t); + svn_hash_sets(prop_hash, prop->name, prop->value); + } + + return prop_hash; +} + +svn_boolean_t +svn_prop_is_boolean(const char *prop_name) +{ + /* If we end up with more than 3 of these, we should probably put + them in a table and use bsearch. With only three, it doesn't + make any speed difference. */ + if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0 + || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0 + || strcmp(prop_name, SVN_PROP_SPECIAL) == 0) + return TRUE; + return FALSE; +} + + +svn_boolean_t +svn_prop_needs_translation(const char *propname) +{ + /* ### Someday, we may want to be picky and choosy about which + properties require UTF8 and EOL conversion. For now, all "svn:" + props need it. */ + + return svn_prop_is_svn_prop(propname); +} + + +svn_boolean_t +svn_prop_name_is_valid(const char *prop_name) +{ + const char *p = prop_name; + + /* The characters we allow use identical representations in UTF8 + and ASCII, so we can just test for the appropriate ASCII codes. + But we can't use standard C character notation ('A', 'B', etc) + because there's no guarantee that this C environment is using + ASCII. */ + + if (!(svn_ctype_isalpha(*p) + || *p == SVN_CTYPE_ASCII_COLON + || *p == SVN_CTYPE_ASCII_UNDERSCORE)) + return FALSE; + p++; + for (; *p; p++) + { + if (!(svn_ctype_isalnum(*p) + || *p == SVN_CTYPE_ASCII_MINUS + || *p == SVN_CTYPE_ASCII_DOT + || *p == SVN_CTYPE_ASCII_COLON + || *p == SVN_CTYPE_ASCII_UNDERSCORE)) + return FALSE; + } + return TRUE; +} + +const char * +svn_prop_get_value(const apr_hash_t *props, + const char *prop_name) +{ + svn_string_t *str; + + if (!props) + return NULL; + + str = svn_hash_gets((apr_hash_t *)props, prop_name); + + if (str) + return str->data; + + return NULL; +} |