blob: 4ccc5cd4b1eacd3ee4297e29840966b27ea67a95 [file] [log] [blame]
/*
* 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;
if (! props)
return FALSE;
for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi;
hi = apr_hash_next(hi))
{
const char *prop_name = apr_hash_this_key(hi);
if (svn_prop_is_svn_prop(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;
}