#include "svn_time.h"
#include "svn_xml.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_ctype.h"
#include "svn_pools.h"
#include "wc.h"
#include "adm_files.h"
#include "entries.h"
#include "lock.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
#define ENTRIES_BOOL_COPIED "copied"
#define ENTRIES_BOOL_DELETED "deleted"
#define ENTRIES_BOOL_ABSENT "absent"
#define ENTRIES_BOOL_INCOMPLETE "incomplete"
#define ENTRIES_BOOL_KEEP_LOCAL "keep-local"
#define ENTRIES_TAG_ENTRY "entry"
#define ENTRIES_ATTR_NAME "name"
#define ENTRIES_ATTR_REPOS "repos"
#define ENTRIES_ATTR_UUID "uuid"
#define ENTRIES_ATTR_INCOMPLETE "incomplete"
#define ENTRIES_ATTR_LOCK_TOKEN "lock-token"
#define ENTRIES_ATTR_LOCK_OWNER "lock-owner"
#define ENTRIES_ATTR_LOCK_COMMENT "lock-comment"
#define ENTRIES_ATTR_LOCK_CREATION_DATE "lock-creation-date"
#define ENTRIES_ATTR_DELETED "deleted"
#define ENTRIES_ATTR_ABSENT "absent"
#define ENTRIES_ATTR_CMT_REV "committed-rev"
#define ENTRIES_ATTR_CMT_DATE "committed-date"
#define ENTRIES_ATTR_CMT_AUTHOR "last-author"
#define ENTRIES_ATTR_REVISION "revision"
#define ENTRIES_ATTR_URL "url"
#define ENTRIES_ATTR_KIND "kind"
#define ENTRIES_ATTR_SCHEDULE "schedule"
#define ENTRIES_ATTR_COPIED "copied"
#define ENTRIES_ATTR_COPYFROM_URL "copyfrom-url"
#define ENTRIES_ATTR_COPYFROM_REV "copyfrom-rev"
#define ENTRIES_ATTR_CHECKSUM "checksum"
#define ENTRIES_ATTR_WORKING_SIZE "working-size"
#define ENTRIES_ATTR_TEXT_TIME "text-time"
#define ENTRIES_ATTR_CONFLICT_OLD "conflict-old"
#define ENTRIES_ATTR_CONFLICT_NEW "conflict-new"
#define ENTRIES_ATTR_CONFLICT_WRK "conflict-wrk"
#define ENTRIES_ATTR_PREJFILE "prop-reject-file"
#define ENTRIES_VALUE_FILE "file"
#define ENTRIES_VALUE_DIR "dir"
#define ENTRIES_VALUE_ADD "add"
#define ENTRIES_VALUE_DELETE "delete"
#define ENTRIES_VALUE_REPLACE "replace"
static svn_wc_entry_t *
alloc_entry(apr_pool_t *pool)
{
svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
entry->revision = SVN_INVALID_REVNUM;
entry->copyfrom_rev = SVN_INVALID_REVNUM;
entry->cmt_rev = SVN_INVALID_REVNUM;
entry->kind = svn_node_none;
entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
entry->depth = svn_depth_infinity;
entry->file_external_path = NULL;
entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
entry->file_external_rev.kind = svn_opt_revision_unspecified;
return entry;
}
static svn_error_t *
read_escaped(char *result, char **buf, const char *end)
{
apr_uint64_t val;
char digits[3];
if (end - *buf < 3 || **buf != 'x' || ! svn_ctype_isxdigit((*buf)[1])
|| ! svn_ctype_isxdigit((*buf)[2]))
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Invalid escape sequence"));
(*buf)++;
digits[0] = *((*buf)++);
digits[1] = *((*buf)++);
digits[2] = 0;
if ((val = apr_strtoi64(digits, NULL, 16)) == 0)
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Invalid escaped character"));
*result = (char) val;
return SVN_NO_ERROR;
}
static svn_error_t *
read_str(const char **result,
char **buf, const char *end,
apr_pool_t *pool)
{
svn_stringbuf_t *s = NULL;
const char *start;
if (*buf == end)
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Unexpected end of entry"));
if (**buf == '\n')
{
*result = NULL;
(*buf)++;
return SVN_NO_ERROR;
}
start = *buf;
while (*buf != end && **buf != '\n')
{
if (**buf == '\\')
{
char c;
if (! s)
s = svn_stringbuf_ncreate(start, *buf - start, pool);
else
svn_stringbuf_appendbytes(s, start, *buf - start);
(*buf)++;
SVN_ERR(read_escaped(&c, buf, end));
svn_stringbuf_appendbyte(s, c);
start = *buf;
}
else
(*buf)++;
}
if (*buf == end)
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Unexpected end of entry"));
if (s)
{
svn_stringbuf_appendbytes(s, start, *buf - start);
*result = s->data;
}
else
*result = apr_pstrndup(pool, start, *buf - start);
(*buf)++;
return SVN_NO_ERROR;
}
static svn_error_t *
read_path(const char **result,
char **buf, const char *end,
apr_pool_t *pool)
{
SVN_ERR(read_str(result, buf, end, pool));
if (*result && **result && !svn_relpath_is_canonical(*result))
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Entry contains non-canonical path '%s'"),
*result);
return SVN_NO_ERROR;
}
static svn_error_t *
read_url(const char **result,
char **buf, const char *end,
int wc_format,
apr_pool_t *pool)
{
SVN_ERR(read_str(result, buf, end, pool));
if (*result && **result)
*result = svn_uri_canonicalize(*result, pool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_val(const char **result,
char **buf, const char *end)
{
const char *start = *buf;
if (*buf == end)
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Unexpected end of entry"));
if (**buf == '\n')
{
(*buf)++;
*result = NULL;
return SVN_NO_ERROR;
}
while (*buf != end && **buf != '\n')
(*buf)++;
if (*buf == end)
return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Unexpected end of entry"));
**buf = '\0';
*result = start;
(*buf)++;
return SVN_NO_ERROR;
}
static svn_error_t *
read_bool(svn_boolean_t *result, const char *field_name,
char **buf, const char *end)
{
const char *val;
SVN_ERR(read_val(&val, buf, end));
if (val)
{
if (strcmp(val, field_name) != 0)
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Invalid value for field '%s'"),
field_name);
*result = TRUE;
}
else
*result = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
read_revnum(svn_revnum_t *result,
char **buf,
const char *end,
apr_pool_t *pool)
{
const char *val;
SVN_ERR(read_val(&val, buf, end));
if (val)
*result = SVN_STR_TO_REV(val);
else
*result = SVN_INVALID_REVNUM;
return SVN_NO_ERROR;
}
static svn_error_t *
read_time(apr_time_t *result,
char **buf, const char *end,
apr_pool_t *pool)
{
const char *val;
SVN_ERR(read_val(&val, buf, end));
if (val)
SVN_ERR(svn_time_from_cstring(result, val, pool));
else
*result = 0;
return SVN_NO_ERROR;
}
static svn_error_t *
string_to_opt_revision(svn_opt_revision_t *opt_rev,
const char **str,
apr_pool_t *pool)
{
const char *s = *str;
SVN_ERR_ASSERT(opt_rev);
while (*s && *s != ':')
++s;
if (!*s)
return svn_error_createf
(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Found an unexpected \\0 in the file external '%s'"), *str);
if (0 == strncmp(*str, "HEAD:", 5))
{
opt_rev->kind = svn_opt_revision_head;
}
else
{
svn_revnum_t rev;
const char *endptr;
SVN_ERR(svn_revnum_parse(&rev, *str, &endptr));
SVN_ERR_ASSERT(endptr == s);
opt_rev->kind = svn_opt_revision_number;
opt_rev->value.number = rev;
}
*str = s + 1;
return SVN_NO_ERROR;
}
static svn_error_t *
opt_revision_to_string(const char **str,
const char *path,
const svn_opt_revision_t *rev,
apr_pool_t *pool)
{
switch (rev->kind)
{
case svn_opt_revision_head:
*str = apr_pstrmemdup(pool, "HEAD", 4);
break;
case svn_opt_revision_number:
*str = apr_itoa(pool, rev->value.number);
break;
default:
return svn_error_createf
(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Illegal file external revision kind %d for path '%s'"),
rev->kind, path);
break;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__unserialize_file_external(const char **path_result,
svn_opt_revision_t *peg_rev_result,
svn_opt_revision_t *rev_result,
const char *str,
apr_pool_t *pool)
{
if (str)
{
svn_opt_revision_t peg_rev;
svn_opt_revision_t op_rev;
const char *s = str;
SVN_ERR(string_to_opt_revision(&peg_rev, &s, pool));
SVN_ERR(string_to_opt_revision(&op_rev, &s, pool));
*path_result = apr_pstrdup(pool, s);
*peg_rev_result = peg_rev;
*rev_result = op_rev;
}
else
{
*path_result = NULL;
peg_rev_result->kind = svn_opt_revision_unspecified;
rev_result->kind = svn_opt_revision_unspecified;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__serialize_file_external(const char **str,
const char *path,
const svn_opt_revision_t *peg_rev,
const svn_opt_revision_t *rev,
apr_pool_t *pool)
{
const char *s;
if (path)
{
const char *s1;
const char *s2;
SVN_ERR(opt_revision_to_string(&s1, path, peg_rev, pool));
SVN_ERR(opt_revision_to_string(&s2, path, rev, pool));
s = apr_pstrcat(pool, s1, ":", s2, ":", path, (char *)NULL);
}
else
s = NULL;
*str = s;
return SVN_NO_ERROR;
}
static svn_error_t *
read_entry(svn_wc_entry_t **new_entry,
char **buf, const char *end,
int entries_format,
apr_pool_t *pool)
{
svn_wc_entry_t *entry = alloc_entry(pool);
const char *name;
#define MAYBE_DONE if (**buf == '\f') goto done
SVN_ERR(read_path(&name, buf, end, pool));
entry->name = name ? name : SVN_WC_ENTRY_THIS_DIR;
{
const char *kindstr;
SVN_ERR(read_val(&kindstr, buf, end));
if (kindstr)
{
if (strcmp(kindstr, ENTRIES_VALUE_FILE) == 0)
entry->kind = svn_node_file;
else if (strcmp(kindstr, ENTRIES_VALUE_DIR) == 0)
entry->kind = svn_node_dir;
else
return svn_error_createf
(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Entry '%s' has invalid node kind"),
(name ? name : SVN_WC_ENTRY_THIS_DIR));
}
else
entry->kind = svn_node_none;
}
MAYBE_DONE;
SVN_ERR(read_revnum(&entry->revision, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_url(&entry->url, buf, end, entries_format, pool));
MAYBE_DONE;
SVN_ERR(read_url(&entry->repos, buf, end, entries_format, pool));
if (entry->repos && entry->url
&& ! svn_uri__is_ancestor(entry->repos, entry->url))
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Entry for '%s' has invalid repository "
"root"),
name ? name : SVN_WC_ENTRY_THIS_DIR);
MAYBE_DONE;
{
const char *schedulestr;
SVN_ERR(read_val(&schedulestr, buf, end));
entry->schedule = svn_wc_schedule_normal;
if (schedulestr)
{
if (strcmp(schedulestr, ENTRIES_VALUE_ADD) == 0)
entry->schedule = svn_wc_schedule_add;
else if (strcmp(schedulestr, ENTRIES_VALUE_DELETE) == 0)
entry->schedule = svn_wc_schedule_delete;
else if (strcmp(schedulestr, ENTRIES_VALUE_REPLACE) == 0)
entry->schedule = svn_wc_schedule_replace;
else
return svn_error_createf(
SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL,
_("Entry '%s' has invalid 'schedule' value"),
name ? name : SVN_WC_ENTRY_THIS_DIR);
}
}
MAYBE_DONE;
SVN_ERR(read_time(&entry->text_time, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->checksum, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_time(&entry->cmt_date, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_revnum(&entry->cmt_rev, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->cmt_author, buf, end, pool));
MAYBE_DONE;
{
const char *unused_value;
SVN_ERR(read_val(&unused_value, buf, end));
entry->has_props = FALSE;
MAYBE_DONE;
SVN_ERR(read_val(&unused_value, buf, end));
entry->has_prop_mods = FALSE;
MAYBE_DONE;
SVN_ERR(read_val(&unused_value, buf, end));
entry->cachable_props = "";
MAYBE_DONE;
SVN_ERR(read_val(&unused_value, buf, end));
entry->present_props = "";
MAYBE_DONE;
}
{
SVN_ERR(read_path(&entry->prejfile, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_path(&entry->conflict_old, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_path(&entry->conflict_new, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_path(&entry->conflict_wrk, buf, end, pool));
MAYBE_DONE;
}
SVN_ERR(read_bool(&entry->copied, ENTRIES_BOOL_COPIED, buf, end));
MAYBE_DONE;
SVN_ERR(read_url(&entry->copyfrom_url, buf, end, entries_format, pool));
MAYBE_DONE;
SVN_ERR(read_revnum(&entry->copyfrom_rev, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_bool(&entry->deleted, ENTRIES_BOOL_DELETED, buf, end));
MAYBE_DONE;
SVN_ERR(read_bool(&entry->absent, ENTRIES_BOOL_ABSENT, buf, end));
MAYBE_DONE;
SVN_ERR(read_bool(&entry->incomplete, ENTRIES_BOOL_INCOMPLETE, buf, end));
MAYBE_DONE;
SVN_ERR(read_str(&entry->uuid, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->lock_token, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->lock_owner, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->lock_comment, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_time(&entry->lock_creation_date, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_str(&entry->changelist, buf, end, pool));
MAYBE_DONE;
SVN_ERR(read_bool(&entry->keep_local, ENTRIES_BOOL_KEEP_LOCAL, buf, end));
MAYBE_DONE;
{
const char *val;
SVN_ERR(read_val(&val, buf, end));
if (val)
entry->working_size = (apr_off_t)apr_strtoi64(val, NULL, 0);
}
MAYBE_DONE;
{
const char *result;
SVN_ERR(read_val(&result, buf, end));
if (result)
{
svn_boolean_t invalid;
svn_boolean_t is_this_dir;
entry->depth = svn_depth_from_word(result);
is_this_dir = !name;
invalid = is_this_dir != (entry->depth != svn_depth_exclude);
if (entry->depth != svn_depth_infinity && invalid)
return svn_error_createf(
SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL,
_("Entry '%s' has invalid 'depth' value"),
name ? name : SVN_WC_ENTRY_THIS_DIR);
}
else
entry->depth = svn_depth_infinity;
}
MAYBE_DONE;
SVN_ERR(read_str(&entry->tree_conflict_data, buf, end, pool));
MAYBE_DONE;
{
const char *str;
SVN_ERR(read_str(&str, buf, end, pool));
SVN_ERR(svn_wc__unserialize_file_external(&entry->file_external_path,
&entry->file_external_peg_rev,
&entry->file_external_rev,
str,
pool));
}
MAYBE_DONE;
done:
*new_entry = entry;
return SVN_NO_ERROR;
}
static svn_error_t *
do_bool_attr(svn_boolean_t *entry_flag,
apr_hash_t *atts, const char *attr_name,
const char *entry_name)
{
const char *str = apr_hash_get(atts, attr_name, APR_HASH_KEY_STRING);
*entry_flag = FALSE;
if (str)
{
if (strcmp(str, "true") == 0)
*entry_flag = TRUE;
else if (strcmp(str, "false") == 0 || strcmp(str, "") == 0)
*entry_flag = FALSE;
else
return svn_error_createf
(SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL,
_("Entry '%s' has invalid '%s' value"),
(entry_name ? entry_name : SVN_WC_ENTRY_THIS_DIR), attr_name);
}
return SVN_NO_ERROR;
}
static const char *
extract_string(apr_hash_t *atts,
const char *att_name,
apr_pool_t *result_pool)
{
const char *value = apr_hash_get(atts, att_name, APR_HASH_KEY_STRING);
if (value == NULL)
return NULL;
return apr_pstrdup(result_pool, value);
}
static const char *
extract_string_normalize(apr_hash_t *atts,
const char *att_name,
apr_pool_t *result_pool)
{
const char *value = apr_hash_get(atts, att_name, APR_HASH_KEY_STRING);
if (value == NULL)
return NULL;
if (*value == '\0')
return NULL;
return apr_pstrdup(result_pool, value);
}
static svn_error_t *
atts_to_entry(svn_wc_entry_t **new_entry,
apr_hash_t *atts,
apr_pool_t *pool)
{
svn_wc_entry_t *entry = alloc_entry(pool);
const char *name;
name = apr_hash_get(atts, ENTRIES_ATTR_NAME, APR_HASH_KEY_STRING);
entry->name = name ? apr_pstrdup(pool, name) : SVN_WC_ENTRY_THIS_DIR;
{
const char *revision_str
= apr_hash_get(atts, ENTRIES_ATTR_REVISION, APR_HASH_KEY_STRING);
if (revision_str)
entry->revision = SVN_STR_TO_REV(revision_str);
else
entry->revision = SVN_INVALID_REVNUM;
}
entry->url = extract_string(atts, ENTRIES_ATTR_URL, pool);
if (entry->url)
entry->url = svn_uri_canonicalize(entry->url, pool);
entry->repos = extract_string(atts, ENTRIES_ATTR_REPOS, pool);
if (entry->repos)
entry->repos = svn_uri_canonicalize(entry->repos, pool);
if (entry->url && entry->repos
&& !svn_uri__is_ancestor(entry->repos, entry->url))
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Entry for '%s' has invalid repository "
"root"),
name ? name : SVN_WC_ENTRY_THIS_DIR);
{
const char *kindstr
= apr_hash_get(atts, ENTRIES_ATTR_KIND, APR_HASH_KEY_STRING);
entry->kind = svn_node_none;
if (kindstr)
{
if (strcmp(kindstr, ENTRIES_VALUE_FILE) == 0)
entry->kind = svn_node_file;
else if (strcmp(kindstr, ENTRIES_VALUE_DIR) == 0)
entry->kind = svn_node_dir;
else
return svn_error_createf
(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Entry '%s' has invalid node kind"),
(name ? name : SVN_WC_ENTRY_THIS_DIR));
}
}
{
const char *schedulestr
= apr_hash_get(atts, ENTRIES_ATTR_SCHEDULE, APR_HASH_KEY_STRING);
entry->schedule = svn_wc_schedule_normal;
if (schedulestr)
{
if (strcmp(schedulestr, ENTRIES_VALUE_ADD) == 0)
entry->schedule = svn_wc_schedule_add;
else if (strcmp(schedulestr, ENTRIES_VALUE_DELETE) == 0)
entry->schedule = svn_wc_schedule_delete;
else if (strcmp(schedulestr, ENTRIES_VALUE_REPLACE) == 0)
entry->schedule = svn_wc_schedule_replace;
else if (strcmp(schedulestr, "") == 0)
entry->schedule = svn_wc_schedule_normal;
else
return svn_error_createf(
SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL,
_("Entry '%s' has invalid 'schedule' value"),
(name ? name : SVN_WC_ENTRY_THIS_DIR));
}
}
entry->prejfile = extract_string_normalize(atts,
ENTRIES_ATTR_PREJFILE,
pool);
entry->conflict_old = extract_string_normalize(atts,
ENTRIES_ATTR_CONFLICT_OLD,
pool);
entry->conflict_new = extract_string_normalize(atts,
ENTRIES_ATTR_CONFLICT_NEW,
pool);
entry->conflict_wrk = extract_string_normalize(atts,
ENTRIES_ATTR_CONFLICT_WRK,
pool);
SVN_ERR(do_bool_attr(&entry->copied, atts, ENTRIES_ATTR_COPIED, name));
entry->copyfrom_url = extract_string(atts, ENTRIES_ATTR_COPYFROM_URL, pool);
{
const char *revstr;
revstr = apr_hash_get(atts, ENTRIES_ATTR_COPYFROM_REV,
APR_HASH_KEY_STRING);
if (revstr)
entry->copyfrom_rev = SVN_STR_TO_REV(revstr);
}
SVN_ERR(do_bool_attr(&entry->deleted, atts, ENTRIES_ATTR_DELETED, name));
SVN_ERR(do_bool_attr(&entry->absent, atts, ENTRIES_ATTR_ABSENT, name));
SVN_ERR(do_bool_attr(&entry->incomplete, atts, ENTRIES_ATTR_INCOMPLETE,
name));
{
const char *text_timestr;
text_timestr = apr_hash_get(atts, ENTRIES_ATTR_TEXT_TIME,
APR_HASH_KEY_STRING);
if (text_timestr)
SVN_ERR(svn_time_from_cstring(&entry->text_time, text_timestr, pool));
}
entry->checksum = extract_string(atts, ENTRIES_ATTR_CHECKSUM, pool);
entry->uuid = extract_string(atts, ENTRIES_ATTR_UUID, pool);
{
const char *cmt_datestr, *cmt_revstr;
cmt_datestr = apr_hash_get(atts, ENTRIES_ATTR_CMT_DATE,
APR_HASH_KEY_STRING);
if (cmt_datestr)
{
SVN_ERR(svn_time_from_cstring(&entry->cmt_date, cmt_datestr, pool));
}
else
entry->cmt_date = 0;
cmt_revstr = apr_hash_get(atts, ENTRIES_ATTR_CMT_REV, APR_HASH_KEY_STRING);
if (cmt_revstr)
{
entry->cmt_rev = SVN_STR_TO_REV(cmt_revstr);
}
else
entry->cmt_rev = SVN_INVALID_REVNUM;
entry->cmt_author = extract_string(atts, ENTRIES_ATTR_CMT_AUTHOR, pool);
}
entry->lock_token = extract_string(atts, ENTRIES_ATTR_LOCK_TOKEN, pool);
entry->lock_owner = extract_string(atts, ENTRIES_ATTR_LOCK_OWNER, pool);
entry->lock_comment = extract_string(atts, ENTRIES_ATTR_LOCK_COMMENT, pool);
{
const char *cdate_str =
apr_hash_get(atts, ENTRIES_ATTR_LOCK_CREATION_DATE, APR_HASH_KEY_STRING);
if (cdate_str)
{
SVN_ERR(svn_time_from_cstring(&entry->lock_creation_date,
cdate_str, pool));
}
}
{
const char *val = apr_hash_get(atts, ENTRIES_ATTR_WORKING_SIZE,
APR_HASH_KEY_STRING);
if (val)
{
entry->working_size = (apr_off_t)apr_strtoi64(val, NULL, 0);
}
}
*new_entry = entry;
return SVN_NO_ERROR;
}
struct entries_accumulator
{
apr_hash_t *entries;
svn_xml_parser_t *parser;
apr_pool_t *pool;
apr_pool_t *scratch_pool;
};
static void
handle_start_tag(void *userData, const char *tagname, const char **atts)
{
struct entries_accumulator *accum = userData;
apr_hash_t *attributes;
svn_wc_entry_t *entry;
svn_error_t *err;
if (strcmp(tagname, ENTRIES_TAG_ENTRY))
return;
svn_pool_clear(accum->scratch_pool);
attributes = svn_xml_make_att_hash(atts, accum->scratch_pool);
err = atts_to_entry(&entry, attributes, accum->pool);
if (err)
{
svn_xml_signal_bailout(err, accum->parser);
return;
}
apr_hash_set(accum->entries, entry->name, APR_HASH_KEY_STRING, entry);
}
static svn_error_t *
parse_entries_xml(const char *dir_abspath,
apr_hash_t *entries,
const char *buf,
apr_size_t size,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_xml_parser_t *svn_parser;
struct entries_accumulator accum;
accum.entries = entries;
accum.pool = result_pool;
accum.scratch_pool = svn_pool_create(scratch_pool);
svn_parser = svn_xml_make_parser(&accum,
handle_start_tag,
NULL,
NULL,
scratch_pool);
accum.parser = svn_parser;
SVN_ERR_W(svn_xml_parse(svn_parser, buf, size, TRUE),
apr_psprintf(scratch_pool,
_("XML parser failed in '%s'"),
svn_dirent_local_style(dir_abspath, scratch_pool)));
svn_pool_destroy(accum.scratch_pool);
svn_xml_free_parser(svn_parser);
return SVN_NO_ERROR;
}
static void
take_from_entry(const svn_wc_entry_t *src,
svn_wc_entry_t *dst,
apr_pool_t *pool)
{
if ((dst->revision == SVN_INVALID_REVNUM) && (dst->kind != svn_node_dir))
dst->revision = src->revision;
if (! dst->url)
dst->url = svn_path_url_add_component2(src->url, dst->name, pool);
if (! dst->repos)
dst->repos = src->repos;
if ((! dst->uuid)
&& (! ((dst->schedule == svn_wc_schedule_add)
|| (dst->schedule == svn_wc_schedule_replace))))
{
dst->uuid = src->uuid;
}
}
static svn_error_t *
resolve_to_defaults(apr_hash_t *entries,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
svn_wc_entry_t *default_entry
= apr_hash_get(entries, SVN_WC_ENTRY_THIS_DIR, APR_HASH_KEY_STRING);
if (! default_entry)
return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND,
NULL,
_("Missing default entry"));
if (default_entry->revision == SVN_INVALID_REVNUM)
return svn_error_create(SVN_ERR_ENTRY_MISSING_REVISION,
NULL,
_("Default entry has no revision number"));
if (! default_entry->url)
return svn_error_create(SVN_ERR_ENTRY_MISSING_URL,
NULL,
_("Default entry is missing URL"));
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
if (this_entry == default_entry)
continue;
if (this_entry->kind == svn_node_dir)
continue;
if (this_entry->kind == svn_node_file)
take_from_entry(default_entry, this_entry, pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__read_entries_old(apr_hash_t **entries,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
char *curp;
const char *endp;
svn_wc_entry_t *entry;
svn_stream_t *stream;
svn_string_t *buf;
*entries = apr_hash_make(result_pool);
SVN_ERR(svn_wc__open_adm_stream(&stream, dir_abspath, SVN_WC__ADM_ENTRIES,
scratch_pool, scratch_pool));
SVN_ERR(svn_string_from_stream(&buf, stream, scratch_pool, scratch_pool));
curp = (char *)buf->data;
endp = buf->data + buf->len;
if (curp != endp && !svn_ctype_isdigit(*curp))
SVN_ERR(parse_entries_xml(dir_abspath, *entries, buf->data, buf->len,
result_pool, scratch_pool));
else
{
int entryno, entries_format;
const char *val;
SVN_ERR(read_val(&val, &curp, endp));
if (val)
entries_format = (int)apr_strtoi64(val, NULL, 0);
else
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Invalid version line in entries file "
"of '%s'"),
svn_dirent_local_style(dir_abspath,
scratch_pool));
entryno = 1;
while (curp != endp)
{
svn_error_t *err = read_entry(&entry, &curp, endp,
entries_format, result_pool);
if (! err)
{
curp = memchr(curp, '\f', endp - curp);
if (! curp)
err = svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Missing entry terminator"));
if (! err && (curp == endp || *(++curp) != '\n'))
err = svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
_("Invalid entry terminator"));
}
if (err)
return svn_error_createf(err->apr_err, err,
_("Error at entry %d in entries file for "
"'%s':"),
entryno,
svn_dirent_local_style(dir_abspath,
scratch_pool));
++curp;
++entryno;
apr_hash_set(*entries, entry->name, APR_HASH_KEY_STRING, entry);
}
}
return resolve_to_defaults(*entries, result_pool);
}
svn_error_t *
svn_wc_entry(const svn_wc_entry_t **entry,
const char *path,
svn_wc_adm_access_t *adm_access,
svn_boolean_t show_hidden,
apr_pool_t *pool)
{
svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
const char *local_abspath;
svn_wc_adm_access_t *dir_access;
const char *entry_name;
apr_hash_t *entries;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
dir_access = svn_wc__adm_retrieve_internal2(db, local_abspath, pool);
if (dir_access == NULL)
{
const char *dir_abspath;
svn_dirent_split(&dir_abspath, &entry_name, local_abspath, pool);
dir_access = svn_wc__adm_retrieve_internal2(db, dir_abspath, pool);
}
else
{
entry_name = "";
}
if (dir_access == NULL)
{
*entry = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc_entries_read(&entries, dir_access, TRUE, pool));
*entry = apr_hash_get(entries, entry_name, APR_HASH_KEY_STRING);
if (!show_hidden && *entry != NULL)
{
svn_boolean_t hidden;
SVN_ERR(svn_wc__entry_is_hidden(&hidden, *entry));
if (hidden)
*entry = NULL;
}
return SVN_NO_ERROR;
}