| /* 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; |
| } |