diff options
Diffstat (limited to 'subversion/libsvn_fs_base/util/fs_skels.c')
-rw-r--r-- | subversion/libsvn_fs_base/util/fs_skels.c | 1515 |
1 files changed, 1515 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_base/util/fs_skels.c b/subversion/libsvn_fs_base/util/fs_skels.c new file mode 100644 index 0000000..f3466b9 --- /dev/null +++ b/subversion/libsvn_fs_base/util/fs_skels.c @@ -0,0 +1,1515 @@ +/* fs_skels.c --- conversion between fs native types and skeletons + * + * ==================================================================== + * 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 <string.h> + +#include <apr_md5.h> +#include <apr_sha1.h> + +#include "svn_error.h" +#include "svn_string.h" +#include "svn_types.h" +#include "svn_time.h" + +#include "private/svn_skel.h" +#include "private/svn_dep_compat.h" +#include "private/svn_subr_private.h" + +#include "svn_checksum.h" +#include "fs_skels.h" +#include "../id.h" + + +static svn_error_t * +skel_err(const char *skel_type) +{ + return svn_error_createf(SVN_ERR_FS_MALFORMED_SKEL, NULL, + "Malformed%s%s skeleton", + skel_type ? " " : "", + skel_type ? skel_type : ""); +} + + + +/*** Validity Checking ***/ + +static svn_boolean_t +is_valid_checksum_skel(svn_skel_t *skel) +{ + if (svn_skel__list_length(skel) != 2) + return FALSE; + + if (svn_skel__matches_atom(skel->children, "md5") + && skel->children->next->is_atom) + return TRUE; + + if (svn_skel__matches_atom(skel->children, "sha1") + && skel->children->next->is_atom) + return TRUE; + + return FALSE; +} + + +static svn_boolean_t +is_valid_revision_skel(svn_skel_t *skel) +{ + int len = svn_skel__list_length(skel); + + if ((len == 2) + && svn_skel__matches_atom(skel->children, "revision") + && skel->children->next->is_atom) + return TRUE; + + return FALSE; +} + + +static svn_boolean_t +is_valid_transaction_skel(svn_skel_t *skel, transaction_kind_t *kind) +{ + int len = svn_skel__list_length(skel); + + if (len != 5) + return FALSE; + + /* Determine (and verify) the kind. */ + if (svn_skel__matches_atom(skel->children, "transaction")) + *kind = transaction_kind_normal; + else if (svn_skel__matches_atom(skel->children, "committed")) + *kind = transaction_kind_committed; + else if (svn_skel__matches_atom(skel->children, "dead")) + *kind = transaction_kind_dead; + else + return FALSE; + + if (skel->children->next->is_atom + && skel->children->next->next->is_atom + && (! skel->children->next->next->next->is_atom) + && (! skel->children->next->next->next->next->is_atom)) + return TRUE; + + return FALSE; +} + + +static svn_boolean_t +is_valid_rep_delta_chunk_skel(svn_skel_t *skel) +{ + int len; + svn_skel_t *window; + svn_skel_t *diff; + + /* check the delta skel. */ + if ((svn_skel__list_length(skel) != 2) + || (! skel->children->is_atom)) + return FALSE; + + /* check the window. */ + window = skel->children->next; + len = svn_skel__list_length(window); + if ((len < 3) || (len > 4)) + return FALSE; + if (! ((! window->children->is_atom) + && (window->children->next->is_atom) + && (window->children->next->next->is_atom))) + return FALSE; + if ((len == 4) + && (! window->children->next->next->next->is_atom)) + return FALSE; + + /* check the diff. ### currently we support only svndiff version + 0 delta data. */ + diff = window->children; + if ((svn_skel__list_length(diff) == 3) + && (svn_skel__matches_atom(diff->children, "svndiff")) + && ((svn_skel__matches_atom(diff->children->next, "0")) + || (svn_skel__matches_atom(diff->children->next, "1"))) + && (diff->children->next->next->is_atom)) + return TRUE; + + return FALSE; +} + + +static svn_boolean_t +is_valid_representation_skel(svn_skel_t *skel) +{ + int len = svn_skel__list_length(skel); + svn_skel_t *header; + int header_len; + + /* the rep has at least two items in it, a HEADER list, and at least + one piece of kind-specific data. */ + if (len < 2) + return FALSE; + + /* check the header. it must have KIND and TXN atoms, and + optionally 1 or 2 checksums (which is a list form). */ + header = skel->children; + header_len = svn_skel__list_length(header); + if (! (((header_len == 2) /* 2 means old repository, checksum absent */ + && (header->children->is_atom) + && (header->children->next->is_atom)) + || ((header_len == 3) /* 3 means md5 checksum present */ + && (header->children->is_atom) + && (header->children->next->is_atom) + && (is_valid_checksum_skel(header->children->next->next))) + || ((header_len == 4) /* 3 means md5 and sha1 checksums present */ + && (header->children->is_atom) + && (header->children->next->is_atom) + && (is_valid_checksum_skel(header->children->next->next)) + && (is_valid_checksum_skel(header->children->next->next->next))))) + return FALSE; + + /* check for fulltext rep. */ + if ((len == 2) + && (svn_skel__matches_atom(header->children, "fulltext"))) + return TRUE; + + /* check for delta rep. */ + if ((len >= 2) + && (svn_skel__matches_atom(header->children, "delta"))) + { + /* it's a delta rep. check the validity. */ + svn_skel_t *chunk = skel->children->next; + + /* loop over chunks, checking each one. */ + while (chunk) + { + if (! is_valid_rep_delta_chunk_skel(chunk)) + return FALSE; + chunk = chunk->next; + } + + /* all good on this delta rep. */ + return TRUE; + } + + return FALSE; +} + + +static svn_boolean_t +is_valid_node_revision_header_skel(svn_skel_t *skel, svn_skel_t **kind_p) +{ + int len = svn_skel__list_length(skel); + + if (len < 2) + return FALSE; + + /* set the *KIND_P pointer. */ + *kind_p = skel->children; + + /* check for valid lengths. */ + if (! ((len == 2) || (len == 3) || (len == 4) || (len == 6))) + return FALSE; + + /* got mergeinfo stuff? */ + if ((len > 4) + && (! (skel->children->next->next->next->next->is_atom + && skel->children->next->next->next->next->next->is_atom))) + return FALSE; + + /* got predecessor count? */ + if ((len > 3) + && (! skel->children->next->next->next->is_atom)) + return FALSE; + + /* got predecessor? */ + if ((len > 2) + && (! skel->children->next->next->is_atom)) + return FALSE; + + /* got the basics? */ + if (! (skel->children->is_atom + && skel->children->next->is_atom + && (skel->children->next->data[0] == '/'))) + return FALSE; + + return TRUE; +} + + +static svn_boolean_t +is_valid_node_revision_skel(svn_skel_t *skel) +{ + int len = svn_skel__list_length(skel); + svn_skel_t *header = skel->children; + svn_skel_t *kind; + + if (len < 1) + return FALSE; + + if (! is_valid_node_revision_header_skel(header, &kind)) + return FALSE; + + if (svn_skel__matches_atom(kind, "dir")) + { + if (! ((len == 3) + && header->next->is_atom + && header->next->next->is_atom)) + return FALSE; + } + else if (svn_skel__matches_atom(kind, "file")) + { + if (len < 3) + return FALSE; + + if (! header->next->is_atom) + return FALSE; + + /* As of SVN_FS_BASE__MIN_REP_SHARING_FORMAT version, the + DATA-KEY slot can be a 2-tuple. */ + if (! header->next->next->is_atom) + { + if (! ((svn_skel__list_length(header->next->next) == 2) + && header->next->next->children->is_atom + && header->next->next->children->len + && header->next->next->children->next->is_atom + && header->next->next->children->next->len)) + return FALSE; + } + + if ((len > 3) && (! header->next->next->next->is_atom)) + return FALSE; + + if (len > 4) + return FALSE; + } + + return TRUE; +} + + +static svn_boolean_t +is_valid_copy_skel(svn_skel_t *skel) +{ + return ((svn_skel__list_length(skel) == 4) + && (svn_skel__matches_atom(skel->children, "copy") + || svn_skel__matches_atom(skel->children, "soft-copy")) + && skel->children->next->is_atom + && skel->children->next->next->is_atom + && skel->children->next->next->next->is_atom); +} + + +static svn_boolean_t +is_valid_change_skel(svn_skel_t *skel, svn_fs_path_change_kind_t *kind) +{ + if ((svn_skel__list_length(skel) == 6) + && svn_skel__matches_atom(skel->children, "change") + && skel->children->next->is_atom + && skel->children->next->next->is_atom + && skel->children->next->next->next->is_atom + && skel->children->next->next->next->next->is_atom + && skel->children->next->next->next->next->next->is_atom) + { + svn_skel_t *kind_skel = skel->children->next->next->next; + + /* check the kind (and return it) */ + if (svn_skel__matches_atom(kind_skel, "reset")) + { + if (kind) + *kind = svn_fs_path_change_reset; + return TRUE; + } + if (svn_skel__matches_atom(kind_skel, "add")) + { + if (kind) + *kind = svn_fs_path_change_add; + return TRUE; + } + if (svn_skel__matches_atom(kind_skel, "delete")) + { + if (kind) + *kind = svn_fs_path_change_delete; + return TRUE; + } + if (svn_skel__matches_atom(kind_skel, "replace")) + { + if (kind) + *kind = svn_fs_path_change_replace; + return TRUE; + } + if (svn_skel__matches_atom(kind_skel, "modify")) + { + if (kind) + *kind = svn_fs_path_change_modify; + return TRUE; + } + } + return FALSE; +} + + +static svn_boolean_t +is_valid_lock_skel(svn_skel_t *skel) +{ + if ((svn_skel__list_length(skel) == 8) + && svn_skel__matches_atom(skel->children, "lock") + && skel->children->next->is_atom + && skel->children->next->next->is_atom + && skel->children->next->next->next->is_atom + && skel->children->next->next->next->next->is_atom + && skel->children->next->next->next->next->next->is_atom + && skel->children->next->next->next->next->next->next->is_atom + && skel->children->next->next->next->next->next->next->next->is_atom) + return TRUE; + + return FALSE; +} + + + +/*** Parsing (conversion from skeleton to native FS type) ***/ + +svn_error_t * +svn_fs_base__parse_revision_skel(revision_t **revision_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + revision_t *revision; + + /* Validate the skel. */ + if (! is_valid_revision_skel(skel)) + return skel_err("revision"); + + /* Create the returned structure */ + revision = apr_pcalloc(pool, sizeof(*revision)); + revision->txn_id = apr_pstrmemdup(pool, skel->children->next->data, + skel->children->next->len); + + /* Return the structure. */ + *revision_p = revision; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_transaction_skel(transaction_t **transaction_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + transaction_t *transaction; + transaction_kind_t kind; + svn_skel_t *root_id, *base_id_or_rev, *proplist, *copies; + int len; + + /* Validate the skel. */ + if (! is_valid_transaction_skel(skel, &kind)) + return skel_err("transaction"); + + root_id = skel->children->next; + base_id_or_rev = skel->children->next->next; + proplist = skel->children->next->next->next; + copies = skel->children->next->next->next->next; + + /* Create the returned structure */ + transaction = apr_pcalloc(pool, sizeof(*transaction)); + + /* KIND */ + transaction->kind = kind; + + /* REVISION or BASE-ID */ + if (kind == transaction_kind_committed) + { + /* Committed transactions have a revision number... */ + transaction->base_id = NULL; + transaction->revision = + SVN_STR_TO_REV(apr_pstrmemdup(pool, base_id_or_rev->data, + base_id_or_rev->len)); + if (! SVN_IS_VALID_REVNUM(transaction->revision)) + return skel_err("transaction"); + + } + else + { + /* ...where unfinished transactions have a base node-revision-id. */ + transaction->revision = SVN_INVALID_REVNUM; + transaction->base_id = svn_fs_base__id_parse(base_id_or_rev->data, + base_id_or_rev->len, pool); + } + + /* ROOT-ID */ + transaction->root_id = svn_fs_base__id_parse(root_id->data, + root_id->len, pool); + + /* PROPLIST */ + SVN_ERR(svn_skel__parse_proplist(&(transaction->proplist), + proplist, pool)); + + /* COPIES */ + if ((len = svn_skel__list_length(copies))) + { + const char *copy_id; + apr_array_header_t *txncopies; + svn_skel_t *cpy = copies->children; + + txncopies = apr_array_make(pool, len, sizeof(copy_id)); + while (cpy) + { + copy_id = apr_pstrmemdup(pool, cpy->data, cpy->len); + APR_ARRAY_PUSH(txncopies, const char *) = copy_id; + cpy = cpy->next; + } + transaction->copies = txncopies; + } + + /* Return the structure. */ + *transaction_p = transaction; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_representation_skel(representation_t **rep_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + representation_t *rep; + svn_skel_t *header_skel; + + /* Validate the skel. */ + if (! is_valid_representation_skel(skel)) + return skel_err("representation"); + header_skel = skel->children; + + /* Create the returned structure */ + rep = apr_pcalloc(pool, sizeof(*rep)); + + /* KIND */ + if (svn_skel__matches_atom(header_skel->children, "fulltext")) + rep->kind = rep_kind_fulltext; + else + rep->kind = rep_kind_delta; + + /* TXN */ + rep->txn_id = apr_pstrmemdup(pool, header_skel->children->next->data, + header_skel->children->next->len); + + /* MD5 */ + if (header_skel->children->next->next) + { + svn_skel_t *checksum_skel = header_skel->children->next->next; + rep->md5_checksum = + svn_checksum__from_digest_md5((const unsigned char *) + (checksum_skel->children->next->data), + pool); + + /* SHA1 */ + if (header_skel->children->next->next->next) + { + checksum_skel = header_skel->children->next->next->next; + rep->sha1_checksum = + svn_checksum__from_digest_sha1( + (const unsigned char *)(checksum_skel->children->next->data), + pool); + } + } + + /* KIND-SPECIFIC stuff */ + if (rep->kind == rep_kind_fulltext) + { + /* "fulltext"-specific. */ + rep->contents.fulltext.string_key + = apr_pstrmemdup(pool, + skel->children->next->data, + skel->children->next->len); + } + else + { + /* "delta"-specific. */ + svn_skel_t *chunk_skel = skel->children->next; + rep_delta_chunk_t *chunk; + apr_array_header_t *chunks; + + /* Alloc the chunk array. */ + chunks = apr_array_make(pool, svn_skel__list_length(skel) - 1, + sizeof(chunk)); + + /* Process the chunks. */ + while (chunk_skel) + { + svn_skel_t *window_skel = chunk_skel->children->next; + svn_skel_t *diff_skel = window_skel->children; + apr_int64_t val; + apr_uint64_t uval; + const char *str; + + /* Allocate a chunk and its window */ + chunk = apr_palloc(pool, sizeof(*chunk)); + + /* Populate the window */ + str = apr_pstrmemdup(pool, diff_skel->children->next->data, + diff_skel->children->next->len); + SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, 255, 10)); + chunk->version = (apr_byte_t)uval; + + chunk->string_key + = apr_pstrmemdup(pool, + diff_skel->children->next->next->data, + diff_skel->children->next->next->len); + + str = apr_pstrmemdup(pool, window_skel->children->next->data, + window_skel->children->next->len); + SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, APR_SIZE_MAX, 10)); + chunk->size = (apr_size_t)uval; + + chunk->rep_key + = apr_pstrmemdup(pool, + window_skel->children->next->next->data, + window_skel->children->next->next->len); + + str = apr_pstrmemdup(pool, chunk_skel->children->data, + chunk_skel->children->len); + SVN_ERR(svn_cstring_strtoi64(&val, str, 0, APR_INT64_MAX, 10)); + chunk->offset = (svn_filesize_t)val; + + /* Add this chunk to the array. */ + APR_ARRAY_PUSH(chunks, rep_delta_chunk_t *) = chunk; + + /* Next... */ + chunk_skel = chunk_skel->next; + } + + /* Add the chunks array to the representation. */ + rep->contents.delta.chunks = chunks; + } + + /* Return the structure. */ + *rep_p = rep; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_node_revision_skel(node_revision_t **noderev_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + node_revision_t *noderev; + svn_skel_t *header_skel, *cur_skel; + + /* Validate the skel. */ + if (! is_valid_node_revision_skel(skel)) + return skel_err("node-revision"); + header_skel = skel->children; + + /* Create the returned structure */ + noderev = apr_pcalloc(pool, sizeof(*noderev)); + + /* KIND */ + if (svn_skel__matches_atom(header_skel->children, "dir")) + noderev->kind = svn_node_dir; + else + noderev->kind = svn_node_file; + + /* CREATED-PATH */ + noderev->created_path = apr_pstrmemdup(pool, + header_skel->children->next->data, + header_skel->children->next->len); + + /* PREDECESSOR-ID */ + if (header_skel->children->next->next) + { + cur_skel = header_skel->children->next->next; + if (cur_skel->len) + noderev->predecessor_id = svn_fs_base__id_parse(cur_skel->data, + cur_skel->len, pool); + + /* PREDECESSOR-COUNT */ + noderev->predecessor_count = -1; + if (cur_skel->next) + { + const char *str; + + cur_skel = cur_skel->next; + if (cur_skel->len) + { + str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len); + SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, str)); + } + + /* HAS-MERGEINFO and MERGEINFO-COUNT */ + if (cur_skel->next) + { + int val; + + cur_skel = cur_skel->next; + str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len); + SVN_ERR(svn_cstring_atoi(&val, str)); + noderev->has_mergeinfo = (val != 0); + + str = apr_pstrmemdup(pool, cur_skel->next->data, + cur_skel->next->len); + SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, str)); + } + } + } + + /* PROP-KEY */ + if (skel->children->next->len) + noderev->prop_key = apr_pstrmemdup(pool, skel->children->next->data, + skel->children->next->len); + + /* DATA-KEY */ + if (skel->children->next->next->is_atom) + { + /* This is a real data rep key. */ + if (skel->children->next->next->len) + noderev->data_key = apr_pstrmemdup(pool, + skel->children->next->next->data, + skel->children->next->next->len); + noderev->data_key_uniquifier = NULL; + } + else + { + /* This is a 2-tuple with a data rep key and a uniquifier. */ + noderev->data_key = + apr_pstrmemdup(pool, + skel->children->next->next->children->data, + skel->children->next->next->children->len); + noderev->data_key_uniquifier = + apr_pstrmemdup(pool, + skel->children->next->next->children->next->data, + skel->children->next->next->children->next->len); + } + + /* EDIT-DATA-KEY (optional, files only) */ + if ((noderev->kind == svn_node_file) + && skel->children->next->next->next + && skel->children->next->next->next->len) + noderev->edit_key + = apr_pstrmemdup(pool, skel->children->next->next->next->data, + skel->children->next->next->next->len); + + /* Return the structure. */ + *noderev_p = noderev; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_copy_skel(copy_t **copy_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + copy_t *copy; + + /* Validate the skel. */ + if (! is_valid_copy_skel(skel)) + return skel_err("copy"); + + /* Create the returned structure */ + copy = apr_pcalloc(pool, sizeof(*copy)); + + /* KIND */ + if (svn_skel__matches_atom(skel->children, "soft-copy")) + copy->kind = copy_kind_soft; + else + copy->kind = copy_kind_real; + + /* SRC-PATH */ + copy->src_path = apr_pstrmemdup(pool, + skel->children->next->data, + skel->children->next->len); + + /* SRC-TXN-ID */ + copy->src_txn_id = apr_pstrmemdup(pool, + skel->children->next->next->data, + skel->children->next->next->len); + + /* DST-NODE-ID */ + copy->dst_noderev_id + = svn_fs_base__id_parse(skel->children->next->next->next->data, + skel->children->next->next->next->len, pool); + + /* Return the structure. */ + *copy_p = copy; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_entries_skel(apr_hash_t **entries_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + apr_hash_t *entries = NULL; + int len = svn_skel__list_length(skel); + svn_skel_t *elt; + + if (! (len >= 0)) + return skel_err("entries"); + + if (len > 0) + { + /* Else, allocate a hash and populate it. */ + entries = apr_hash_make(pool); + + /* Check entries are well-formed as we go along. */ + for (elt = skel->children; elt; elt = elt->next) + { + const char *name; + svn_fs_id_t *id; + + /* ENTRY must be a list of two elements. */ + if (svn_skel__list_length(elt) != 2) + return skel_err("entries"); + + /* Get the entry's name and ID. */ + name = apr_pstrmemdup(pool, elt->children->data, + elt->children->len); + id = svn_fs_base__id_parse(elt->children->next->data, + elt->children->next->len, pool); + + /* Add the entry to the hash. */ + apr_hash_set(entries, name, elt->children->len, id); + } + } + + /* Return the structure. */ + *entries_p = entries; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_change_skel(change_t **change_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + change_t *change; + svn_fs_path_change_kind_t kind; + + /* Validate the skel. */ + if (! is_valid_change_skel(skel, &kind)) + return skel_err("change"); + + /* Create the returned structure */ + change = apr_pcalloc(pool, sizeof(*change)); + + /* PATH */ + change->path = apr_pstrmemdup(pool, skel->children->next->data, + skel->children->next->len); + + /* NODE-REV-ID */ + if (skel->children->next->next->len) + change->noderev_id = svn_fs_base__id_parse + (skel->children->next->next->data, skel->children->next->next->len, + pool); + + /* KIND */ + change->kind = kind; + + /* TEXT-MOD */ + if (skel->children->next->next->next->next->len) + change->text_mod = TRUE; + + /* PROP-MOD */ + if (skel->children->next->next->next->next->next->len) + change->prop_mod = TRUE; + + /* Return the structure. */ + *change_p = change; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__parse_lock_skel(svn_lock_t **lock_p, + svn_skel_t *skel, + apr_pool_t *pool) +{ + svn_lock_t *lock; + const char *timestr; + + /* Validate the skel. */ + if (! is_valid_lock_skel(skel)) + return skel_err("lock"); + + /* Create the returned structure */ + lock = apr_pcalloc(pool, sizeof(*lock)); + + /* PATH */ + lock->path = apr_pstrmemdup(pool, skel->children->next->data, + skel->children->next->len); + + /* LOCK-TOKEN */ + lock->token = apr_pstrmemdup(pool, + skel->children->next->next->data, + skel->children->next->next->len); + + /* OWNER */ + lock->owner = apr_pstrmemdup(pool, + skel->children->next->next->next->data, + skel->children->next->next->next->len); + + /* COMMENT (could be just an empty atom) */ + if (skel->children->next->next->next->next->len) + lock->comment = + apr_pstrmemdup(pool, + skel->children->next->next->next->next->data, + skel->children->next->next->next->next->len); + + /* XML_P */ + if (svn_skel__matches_atom + (skel->children->next->next->next->next->next, "1")) + lock->is_dav_comment = TRUE; + else + lock->is_dav_comment = FALSE; + + /* CREATION-DATE */ + timestr = apr_pstrmemdup + (pool, + skel->children->next->next->next->next->next->next->data, + skel->children->next->next->next->next->next->next->len); + SVN_ERR(svn_time_from_cstring(&(lock->creation_date), + timestr, pool)); + + /* EXPIRATION-DATE (could be just an empty atom) */ + if (skel->children->next->next->next->next->next->next->next->len) + { + timestr = + apr_pstrmemdup + (pool, + skel->children->next->next->next->next->next->next->next->data, + skel->children->next->next->next->next->next->next->next->len); + SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), + timestr, pool)); + } + + /* Return the structure. */ + *lock_p = lock; + return SVN_NO_ERROR; +} + + + +/*** Unparsing (conversion from native FS type to skeleton) ***/ + +svn_error_t * +svn_fs_base__unparse_revision_skel(svn_skel_t **skel_p, + const revision_t *revision, + apr_pool_t *pool) +{ + svn_skel_t *skel; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + + /* TXN_ID */ + svn_skel__prepend(svn_skel__str_atom(revision->txn_id, pool), skel); + + /* "revision" */ + svn_skel__prepend(svn_skel__str_atom("revision", pool), skel); + + /* Validate and return the skel. */ + if (! is_valid_revision_skel(skel)) + return skel_err("revision"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_transaction_skel(svn_skel_t **skel_p, + const transaction_t *transaction, + apr_pool_t *pool) +{ + svn_skel_t *skel; + svn_skel_t *proplist_skel, *copies_skel, *header_skel; + svn_string_t *id_str; + transaction_kind_t kind; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + + switch (transaction->kind) + { + case transaction_kind_committed: + header_skel = svn_skel__str_atom("committed", pool); + if ((transaction->base_id) + || (! SVN_IS_VALID_REVNUM(transaction->revision))) + return skel_err("transaction"); + break; + case transaction_kind_dead: + header_skel = svn_skel__str_atom("dead", pool); + if ((! transaction->base_id) + || (SVN_IS_VALID_REVNUM(transaction->revision))) + return skel_err("transaction"); + break; + case transaction_kind_normal: + header_skel = svn_skel__str_atom("transaction", pool); + if ((! transaction->base_id) + || (SVN_IS_VALID_REVNUM(transaction->revision))) + return skel_err("transaction"); + break; + default: + return skel_err("transaction"); + } + + + /* COPIES */ + copies_skel = svn_skel__make_empty_list(pool); + if (transaction->copies && transaction->copies->nelts) + { + int i; + for (i = transaction->copies->nelts - 1; i >= 0; i--) + { + svn_skel__prepend(svn_skel__str_atom( + APR_ARRAY_IDX(transaction->copies, i, + const char *), + pool), + copies_skel); + } + } + svn_skel__prepend(copies_skel, skel); + + /* PROPLIST */ + SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, + transaction->proplist, pool)); + svn_skel__prepend(proplist_skel, skel); + + /* REVISION or BASE-ID */ + if (transaction->kind == transaction_kind_committed) + { + /* Committed transactions have a revision number... */ + svn_skel__prepend(svn_skel__str_atom(apr_psprintf(pool, "%ld", + transaction->revision), + pool), skel); + } + else + { + /* ...where other transactions have a base node revision ID. */ + id_str = svn_fs_base__id_unparse(transaction->base_id, pool); + svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool), + skel); + } + + /* ROOT-ID */ + id_str = svn_fs_base__id_unparse(transaction->root_id, pool); + svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool), skel); + + /* KIND (see above) */ + svn_skel__prepend(header_skel, skel); + + /* Validate and return the skel. */ + if (! is_valid_transaction_skel(skel, &kind)) + return skel_err("transaction"); + if (kind != transaction->kind) + return skel_err("transaction"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +/* Construct a skel representing CHECKSUM, allocated in POOL, and prepend + * it onto the existing skel SKEL. */ +static svn_error_t * +prepend_checksum(svn_skel_t *skel, + svn_checksum_t *checksum, + apr_pool_t *pool) +{ + svn_skel_t *checksum_skel = svn_skel__make_empty_list(pool); + + switch (checksum->kind) + { + case svn_checksum_md5: + svn_skel__prepend(svn_skel__mem_atom(checksum->digest, + APR_MD5_DIGESTSIZE, pool), + checksum_skel); + svn_skel__prepend(svn_skel__str_atom("md5", pool), checksum_skel); + break; + + case svn_checksum_sha1: + svn_skel__prepend(svn_skel__mem_atom(checksum->digest, + APR_SHA1_DIGESTSIZE, pool), + checksum_skel); + svn_skel__prepend(svn_skel__str_atom("sha1", pool), checksum_skel); + break; + + default: + return skel_err("checksum"); + } + svn_skel__prepend(checksum_skel, skel); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_representation_skel(svn_skel_t **skel_p, + const representation_t *rep, + int format, + apr_pool_t *pool) +{ + svn_skel_t *skel = svn_skel__make_empty_list(pool); + svn_skel_t *header_skel = svn_skel__make_empty_list(pool); + + /** Some parts of the header are common to all representations; do + those parts first. **/ + + /* SHA1 */ + if ((format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) && rep->sha1_checksum) + SVN_ERR(prepend_checksum(header_skel, rep->sha1_checksum, pool)); + + /* MD5 */ + { + svn_checksum_t *md5_checksum = rep->md5_checksum; + if (! md5_checksum) + md5_checksum = svn_checksum_create(svn_checksum_md5, pool); + SVN_ERR(prepend_checksum(header_skel, md5_checksum, pool)); + } + + /* TXN */ + if (rep->txn_id) + svn_skel__prepend(svn_skel__str_atom(rep->txn_id, pool), header_skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel); + + /** Do the kind-specific stuff. **/ + + if (rep->kind == rep_kind_fulltext) + { + /*** Fulltext Representation. ***/ + + /* STRING-KEY */ + if ((! rep->contents.fulltext.string_key) + || (! *rep->contents.fulltext.string_key)) + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + else + svn_skel__prepend(svn_skel__str_atom(rep->contents.fulltext.string_key, + pool), skel); + + /* "fulltext" */ + svn_skel__prepend(svn_skel__str_atom("fulltext", pool), header_skel); + + /* header */ + svn_skel__prepend(header_skel, skel); + } + else if (rep->kind == rep_kind_delta) + { + /*** Delta Representation. ***/ + int i; + apr_array_header_t *chunks = rep->contents.delta.chunks; + + /* Loop backwards through the windows, creating and prepending skels. */ + for (i = chunks->nelts; i > 0; i--) + { + svn_skel_t *window_skel = svn_skel__make_empty_list(pool); + svn_skel_t *chunk_skel = svn_skel__make_empty_list(pool); + svn_skel_t *diff_skel = svn_skel__make_empty_list(pool); + const char *size_str, *offset_str, *version_str; + rep_delta_chunk_t *chunk = APR_ARRAY_IDX(chunks, i - 1, + rep_delta_chunk_t *); + + /* OFFSET */ + offset_str = apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT, + chunk->offset); + + /* SIZE */ + size_str = apr_psprintf(pool, "%" APR_SIZE_T_FMT, chunk->size); + + /* VERSION */ + version_str = apr_psprintf(pool, "%d", chunk->version); + + /* DIFF */ + if ((! chunk->string_key) || (! *chunk->string_key)) + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), diff_skel); + else + svn_skel__prepend(svn_skel__str_atom(chunk->string_key, pool), + diff_skel); + svn_skel__prepend(svn_skel__str_atom(version_str, pool), diff_skel); + svn_skel__prepend(svn_skel__str_atom("svndiff", pool), diff_skel); + + /* REP-KEY */ + if ((! chunk->rep_key) || (! *(chunk->rep_key))) + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), + window_skel); + else + svn_skel__prepend(svn_skel__str_atom(chunk->rep_key, pool), + window_skel); + svn_skel__prepend(svn_skel__str_atom(size_str, pool), window_skel); + svn_skel__prepend(diff_skel, window_skel); + + /* window header. */ + svn_skel__prepend(window_skel, chunk_skel); + svn_skel__prepend(svn_skel__str_atom(offset_str, pool), + chunk_skel); + + /* Add this window item to the main skel. */ + svn_skel__prepend(chunk_skel, skel); + } + + /* "delta" */ + svn_skel__prepend(svn_skel__str_atom("delta", pool), header_skel); + + /* header */ + svn_skel__prepend(header_skel, skel); + } + else /* unknown kind */ + SVN_ERR_MALFUNCTION(); + + /* Validate and return the skel. */ + if (! is_valid_representation_skel(skel)) + return skel_err("representation"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_node_revision_skel(svn_skel_t **skel_p, + const node_revision_t *noderev, + int format, + apr_pool_t *pool) +{ + svn_skel_t *skel; + svn_skel_t *header_skel; + const char *num_str; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + header_skel = svn_skel__make_empty_list(pool); + + /* Store mergeinfo stuffs only if the schema level supports it. */ + if (format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT) + { + /* MERGEINFO-COUNT */ + num_str = apr_psprintf(pool, "%" APR_INT64_T_FMT, + noderev->mergeinfo_count); + svn_skel__prepend(svn_skel__str_atom(num_str, pool), header_skel); + + /* HAS-MERGEINFO */ + svn_skel__prepend(svn_skel__mem_atom(noderev->has_mergeinfo ? "1" : "0", + 1, pool), header_skel); + + /* PREDECESSOR-COUNT padding (only if we *don't* have a valid + value; if we do, we'll pick that up below) */ + if (noderev->predecessor_count == -1) + { + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel); + } + } + + /* PREDECESSOR-COUNT */ + if (noderev->predecessor_count != -1) + { + const char *count_str = apr_psprintf(pool, "%d", + noderev->predecessor_count); + svn_skel__prepend(svn_skel__str_atom(count_str, pool), header_skel); + } + + /* PREDECESSOR-ID */ + if (noderev->predecessor_id) + { + svn_string_t *id_str = svn_fs_base__id_unparse(noderev->predecessor_id, + pool); + svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool), + header_skel); + } + else + { + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel); + } + + /* CREATED-PATH */ + svn_skel__prepend(svn_skel__str_atom(noderev->created_path, pool), + header_skel); + + /* KIND */ + if (noderev->kind == svn_node_file) + svn_skel__prepend(svn_skel__str_atom("file", pool), header_skel); + else if (noderev->kind == svn_node_dir) + svn_skel__prepend(svn_skel__str_atom("dir", pool), header_skel); + else + SVN_ERR_MALFUNCTION(); + + /* ### do we really need to check *node->FOO_key ? if a key doesn't + ### exist, then the field should be NULL ... */ + + /* EDIT-DATA-KEY (optional) */ + if ((noderev->edit_key) && (*noderev->edit_key)) + svn_skel__prepend(svn_skel__str_atom(noderev->edit_key, pool), skel); + + /* DATA-KEY | (DATA-KEY DATA-KEY-UNIQID) */ + if ((noderev->data_key_uniquifier) && (*noderev->data_key_uniquifier)) + { + /* Build a 2-tuple with a rep key and uniquifier. */ + svn_skel_t *data_key_skel = svn_skel__make_empty_list(pool); + + /* DATA-KEY-UNIQID */ + svn_skel__prepend(svn_skel__str_atom(noderev->data_key_uniquifier, + pool), + data_key_skel); + + /* DATA-KEY */ + if ((noderev->data_key) && (*noderev->data_key)) + svn_skel__prepend(svn_skel__str_atom(noderev->data_key, pool), + data_key_skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), data_key_skel); + + /* Add our 2-tuple to the main skel. */ + svn_skel__prepend(data_key_skel, skel); + } + else + { + /* Just store the rep key (or empty placeholder) in the main skel. */ + if ((noderev->data_key) && (*noderev->data_key)) + svn_skel__prepend(svn_skel__str_atom(noderev->data_key, pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + } + + /* PROP-KEY */ + if ((noderev->prop_key) && (*noderev->prop_key)) + svn_skel__prepend(svn_skel__str_atom(noderev->prop_key, pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* HEADER */ + svn_skel__prepend(header_skel, skel); + + /* Validate and return the skel. */ + if (! is_valid_node_revision_skel(skel)) + return skel_err("node-revision"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_copy_skel(svn_skel_t **skel_p, + const copy_t *copy, + apr_pool_t *pool) +{ + svn_skel_t *skel; + svn_string_t *tmp_str; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + + /* DST-NODE-ID */ + tmp_str = svn_fs_base__id_unparse(copy->dst_noderev_id, pool); + svn_skel__prepend(svn_skel__mem_atom(tmp_str->data, tmp_str->len, pool), + skel); + + /* SRC-TXN-ID */ + if ((copy->src_txn_id) && (*copy->src_txn_id)) + svn_skel__prepend(svn_skel__str_atom(copy->src_txn_id, pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* SRC-PATH */ + if ((copy->src_path) && (*copy->src_path)) + svn_skel__prepend(svn_skel__str_atom(copy->src_path, pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* "copy" */ + if (copy->kind == copy_kind_real) + svn_skel__prepend(svn_skel__str_atom("copy", pool), skel); + else + svn_skel__prepend(svn_skel__str_atom("soft-copy", pool), skel); + + /* Validate and return the skel. */ + if (! is_valid_copy_skel(skel)) + return skel_err("copy"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_entries_skel(svn_skel_t **skel_p, + apr_hash_t *entries, + apr_pool_t *pool) +{ + svn_skel_t *skel = svn_skel__make_empty_list(pool); + apr_hash_index_t *hi; + + /* Create the skel. */ + if (entries) + { + /* Loop over hash entries */ + for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) + { + const void *key; + void *val; + apr_ssize_t klen; + svn_fs_id_t *value; + svn_string_t *id_str; + svn_skel_t *entry_skel = svn_skel__make_empty_list(pool); + + apr_hash_this(hi, &key, &klen, &val); + value = val; + + /* VALUE */ + id_str = svn_fs_base__id_unparse(value, pool); + svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, + pool), + entry_skel); + + /* NAME */ + svn_skel__prepend(svn_skel__mem_atom(key, klen, pool), entry_skel); + + /* Add entry to the entries skel. */ + svn_skel__prepend(entry_skel, skel); + } + } + + /* Return the skel. */ + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_change_skel(svn_skel_t **skel_p, + const change_t *change, + apr_pool_t *pool) +{ + svn_skel_t *skel; + svn_string_t *tmp_str; + svn_fs_path_change_kind_t kind; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + + /* PROP-MOD */ + if (change->prop_mod) + svn_skel__prepend(svn_skel__str_atom("1", pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* TEXT-MOD */ + if (change->text_mod) + svn_skel__prepend(svn_skel__str_atom("1", pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* KIND */ + switch (change->kind) + { + case svn_fs_path_change_reset: + svn_skel__prepend(svn_skel__str_atom("reset", pool), skel); + break; + case svn_fs_path_change_add: + svn_skel__prepend(svn_skel__str_atom("add", pool), skel); + break; + case svn_fs_path_change_delete: + svn_skel__prepend(svn_skel__str_atom("delete", pool), skel); + break; + case svn_fs_path_change_replace: + svn_skel__prepend(svn_skel__str_atom("replace", pool), skel); + break; + case svn_fs_path_change_modify: + default: + svn_skel__prepend(svn_skel__str_atom("modify", pool), skel); + break; + } + + /* NODE-REV-ID */ + if (change->noderev_id) + { + tmp_str = svn_fs_base__id_unparse(change->noderev_id, pool); + svn_skel__prepend(svn_skel__mem_atom(tmp_str->data, tmp_str->len, pool), + skel); + } + else + { + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + } + + /* PATH */ + svn_skel__prepend(svn_skel__str_atom(change->path, pool), skel); + + /* "change" */ + svn_skel__prepend(svn_skel__str_atom("change", pool), skel); + + /* Validate and return the skel. */ + if (! is_valid_change_skel(skel, &kind)) + return skel_err("change"); + if (kind != change->kind) + return skel_err("change"); + *skel_p = skel; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_base__unparse_lock_skel(svn_skel_t **skel_p, + const svn_lock_t *lock, + apr_pool_t *pool) +{ + svn_skel_t *skel; + + /* Create the skel. */ + skel = svn_skel__make_empty_list(pool); + + /* EXP-DATE is optional. If not present, just use an empty atom. */ + if (lock->expiration_date) + svn_skel__prepend(svn_skel__str_atom( + svn_time_to_cstring(lock->expiration_date, pool), + pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* CREATION-DATE */ + svn_skel__prepend(svn_skel__str_atom( + svn_time_to_cstring(lock->creation_date, pool), + pool), skel); + + /* XML_P */ + if (lock->is_dav_comment) + svn_skel__prepend(svn_skel__str_atom("1", pool), skel); + else + svn_skel__prepend(svn_skel__str_atom("0", pool), skel); + + /* COMMENT */ + if (lock->comment) + svn_skel__prepend(svn_skel__str_atom(lock->comment, pool), skel); + else + svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel); + + /* OWNER */ + svn_skel__prepend(svn_skel__str_atom(lock->owner, pool), skel); + + /* LOCK-TOKEN */ + svn_skel__prepend(svn_skel__str_atom(lock->token, pool), skel); + + /* PATH */ + svn_skel__prepend(svn_skel__str_atom(lock->path, pool), skel); + + /* "lock" */ + svn_skel__prepend(svn_skel__str_atom("lock", pool), skel); + + /* Validate and return the skel. */ + if (! is_valid_lock_skel(skel)) + return skel_err("lock"); + + *skel_p = skel; + return SVN_NO_ERROR; +} |