1/* fs_skels.c --- conversion between fs native types and skeletons
2 *
3 * ====================================================================
4 *    Licensed to the Apache Software Foundation (ASF) under one
5 *    or more contributor license agreements.  See the NOTICE file
6 *    distributed with this work for additional information
7 *    regarding copyright ownership.  The ASF licenses this file
8 *    to you under the Apache License, Version 2.0 (the
9 *    "License"); you may not use this file except in compliance
10 *    with the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 *    Unless required by applicable law or agreed to in writing,
15 *    software distributed under the License is distributed on an
16 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 *    KIND, either express or implied.  See the License for the
18 *    specific language governing permissions and limitations
19 *    under the License.
20 * ====================================================================
21 */
22
23#include <string.h>
24
25#include <apr_md5.h>
26#include <apr_sha1.h>
27
28#include "svn_error.h"
29#include "svn_string.h"
30#include "svn_types.h"
31#include "svn_time.h"
32
33#include "private/svn_skel.h"
34#include "private/svn_dep_compat.h"
35#include "private/svn_subr_private.h"
36
37#include "svn_checksum.h"
38#include "fs_skels.h"
39#include "../id.h"
40
41
42static svn_error_t *
43skel_err(const char *skel_type)
44{
45  return svn_error_createf(SVN_ERR_FS_MALFORMED_SKEL, NULL,
46                           "Malformed%s%s skeleton",
47                           skel_type ? " " : "",
48                           skel_type ? skel_type : "");
49}
50
51
52
53/*** Validity Checking ***/
54
55static svn_boolean_t
56is_valid_checksum_skel(svn_skel_t *skel)
57{
58  if (svn_skel__list_length(skel) != 2)
59    return FALSE;
60
61  if (svn_skel__matches_atom(skel->children, "md5")
62      && skel->children->next->is_atom)
63    return TRUE;
64
65  if (svn_skel__matches_atom(skel->children, "sha1")
66      && skel->children->next->is_atom)
67    return TRUE;
68
69  return FALSE;
70}
71
72
73static svn_boolean_t
74is_valid_revision_skel(svn_skel_t *skel)
75{
76  int len = svn_skel__list_length(skel);
77
78  if ((len == 2)
79      && svn_skel__matches_atom(skel->children, "revision")
80      && skel->children->next->is_atom)
81    return TRUE;
82
83  return FALSE;
84}
85
86
87static svn_boolean_t
88is_valid_transaction_skel(svn_skel_t *skel, transaction_kind_t *kind)
89{
90  int len = svn_skel__list_length(skel);
91
92  if (len != 5)
93    return FALSE;
94
95  /* Determine (and verify) the kind. */
96  if (svn_skel__matches_atom(skel->children, "transaction"))
97    *kind = transaction_kind_normal;
98  else if (svn_skel__matches_atom(skel->children, "committed"))
99    *kind = transaction_kind_committed;
100  else if (svn_skel__matches_atom(skel->children, "dead"))
101    *kind = transaction_kind_dead;
102  else
103    return FALSE;
104
105  if (skel->children->next->is_atom
106      && skel->children->next->next->is_atom
107      && (! skel->children->next->next->next->is_atom)
108      && (! skel->children->next->next->next->next->is_atom))
109    return TRUE;
110
111  return FALSE;
112}
113
114
115static svn_boolean_t
116is_valid_rep_delta_chunk_skel(svn_skel_t *skel)
117{
118  int len;
119  svn_skel_t *window;
120  svn_skel_t *diff;
121
122  /* check the delta skel. */
123  if ((svn_skel__list_length(skel) != 2)
124      || (! skel->children->is_atom))
125    return FALSE;
126
127  /* check the window. */
128  window = skel->children->next;
129  len = svn_skel__list_length(window);
130  if ((len < 3) || (len > 4))
131    return FALSE;
132  if (! ((! window->children->is_atom)
133         && (window->children->next->is_atom)
134         && (window->children->next->next->is_atom)))
135    return FALSE;
136  if ((len == 4)
137      && (! window->children->next->next->next->is_atom))
138    return FALSE;
139
140  /* check the diff. ### currently we support only svndiff version
141     0 delta data. */
142  diff = window->children;
143  if ((svn_skel__list_length(diff) == 3)
144      && (svn_skel__matches_atom(diff->children, "svndiff"))
145      && ((svn_skel__matches_atom(diff->children->next, "0"))
146          || (svn_skel__matches_atom(diff->children->next, "1")))
147      && (diff->children->next->next->is_atom))
148    return TRUE;
149
150  return FALSE;
151}
152
153
154static svn_boolean_t
155is_valid_representation_skel(svn_skel_t *skel)
156{
157  int len = svn_skel__list_length(skel);
158  svn_skel_t *header;
159  int header_len;
160
161  /* the rep has at least two items in it, a HEADER list, and at least
162     one piece of kind-specific data. */
163  if (len < 2)
164    return FALSE;
165
166  /* check the header.  it must have KIND and TXN atoms, and
167     optionally 1 or 2 checksums (which is a list form). */
168  header = skel->children;
169  header_len = svn_skel__list_length(header);
170  if (! (((header_len == 2)     /* 2 means old repository, checksum absent */
171          && (header->children->is_atom)
172          && (header->children->next->is_atom))
173         || ((header_len == 3)  /* 3 means md5 checksum present */
174             && (header->children->is_atom)
175             && (header->children->next->is_atom)
176             && (is_valid_checksum_skel(header->children->next->next)))
177         || ((header_len == 4)  /* 3 means md5 and sha1 checksums present */
178             && (header->children->is_atom)
179             && (header->children->next->is_atom)
180             && (is_valid_checksum_skel(header->children->next->next))
181             && (is_valid_checksum_skel(header->children->next->next->next)))))
182    return FALSE;
183
184  /* check for fulltext rep. */
185  if ((len == 2)
186      && (svn_skel__matches_atom(header->children, "fulltext")))
187    return TRUE;
188
189  /* check for delta rep. */
190  if ((len >= 2)
191      && (svn_skel__matches_atom(header->children, "delta")))
192    {
193      /* it's a delta rep.  check the validity.  */
194      svn_skel_t *chunk = skel->children->next;
195
196      /* loop over chunks, checking each one. */
197      while (chunk)
198        {
199          if (! is_valid_rep_delta_chunk_skel(chunk))
200            return FALSE;
201          chunk = chunk->next;
202        }
203
204      /* all good on this delta rep. */
205      return TRUE;
206    }
207
208  return FALSE;
209}
210
211
212static svn_boolean_t
213is_valid_node_revision_header_skel(svn_skel_t *skel, svn_skel_t **kind_p)
214{
215  int len = svn_skel__list_length(skel);
216
217  if (len < 2)
218    return FALSE;
219
220  /* set the *KIND_P pointer. */
221  *kind_p = skel->children;
222
223  /* check for valid lengths. */
224  if (! ((len == 2) || (len == 3) || (len == 4) || (len == 6)))
225    return FALSE;
226
227  /* got mergeinfo stuff? */
228  if ((len > 4)
229      && (! (skel->children->next->next->next->next->is_atom
230             && skel->children->next->next->next->next->next->is_atom)))
231    return FALSE;
232
233  /* got predecessor count? */
234  if ((len > 3)
235      && (! skel->children->next->next->next->is_atom))
236    return FALSE;
237
238  /* got predecessor? */
239  if ((len > 2)
240      && (! skel->children->next->next->is_atom))
241    return FALSE;
242
243  /* got the basics? */
244  if (! (skel->children->is_atom
245         && skel->children->next->is_atom
246         && (skel->children->next->data[0] == '/')))
247    return FALSE;
248
249  return TRUE;
250}
251
252
253static svn_boolean_t
254is_valid_node_revision_skel(svn_skel_t *skel)
255{
256  int len = svn_skel__list_length(skel);
257  svn_skel_t *header = skel->children;
258  svn_skel_t *kind;
259
260  if (len < 1)
261    return FALSE;
262
263  if (! is_valid_node_revision_header_skel(header, &kind))
264    return FALSE;
265
266  if (svn_skel__matches_atom(kind, "dir"))
267    {
268      if (! ((len == 3)
269             && header->next->is_atom
270             && header->next->next->is_atom))
271        return FALSE;
272    }
273  else if (svn_skel__matches_atom(kind, "file"))
274    {
275      if (len < 3)
276        return FALSE;
277
278      if (! header->next->is_atom)
279        return FALSE;
280
281      /* As of SVN_FS_BASE__MIN_REP_SHARING_FORMAT version, the
282         DATA-KEY slot can be a 2-tuple. */
283      if (! header->next->next->is_atom)
284        {
285          if (! ((svn_skel__list_length(header->next->next) == 2)
286                 && header->next->next->children->is_atom
287                 && header->next->next->children->len
288                 && header->next->next->children->next->is_atom
289                 && header->next->next->children->next->len))
290            return FALSE;
291        }
292
293      if ((len > 3) && (! header->next->next->next->is_atom))
294        return FALSE;
295
296      if (len > 4)
297        return FALSE;
298    }
299
300  return TRUE;
301}
302
303
304static svn_boolean_t
305is_valid_copy_skel(svn_skel_t *skel)
306{
307  return ((svn_skel__list_length(skel) == 4)
308          && (svn_skel__matches_atom(skel->children, "copy")
309              || svn_skel__matches_atom(skel->children, "soft-copy"))
310          && skel->children->next->is_atom
311          && skel->children->next->next->is_atom
312          && skel->children->next->next->next->is_atom);
313}
314
315
316static svn_boolean_t
317is_valid_change_skel(svn_skel_t *skel, svn_fs_path_change_kind_t *kind)
318{
319  if ((svn_skel__list_length(skel) == 6)
320      && svn_skel__matches_atom(skel->children, "change")
321      && skel->children->next->is_atom
322      && skel->children->next->next->is_atom
323      && skel->children->next->next->next->is_atom
324      && skel->children->next->next->next->next->is_atom
325      && skel->children->next->next->next->next->next->is_atom)
326    {
327      svn_skel_t *kind_skel = skel->children->next->next->next;
328
329      /* check the kind (and return it) */
330      if (svn_skel__matches_atom(kind_skel, "reset"))
331        {
332          if (kind)
333            *kind = svn_fs_path_change_reset;
334          return TRUE;
335        }
336      if (svn_skel__matches_atom(kind_skel, "add"))
337        {
338          if (kind)
339            *kind = svn_fs_path_change_add;
340          return TRUE;
341        }
342      if (svn_skel__matches_atom(kind_skel, "delete"))
343        {
344          if (kind)
345            *kind = svn_fs_path_change_delete;
346          return TRUE;
347        }
348      if (svn_skel__matches_atom(kind_skel, "replace"))
349        {
350          if (kind)
351            *kind = svn_fs_path_change_replace;
352          return TRUE;
353        }
354      if (svn_skel__matches_atom(kind_skel, "modify"))
355        {
356          if (kind)
357            *kind = svn_fs_path_change_modify;
358          return TRUE;
359        }
360    }
361  return FALSE;
362}
363
364
365static svn_boolean_t
366is_valid_lock_skel(svn_skel_t *skel)
367{
368  if ((svn_skel__list_length(skel) == 8)
369      && svn_skel__matches_atom(skel->children, "lock")
370      && skel->children->next->is_atom
371      && skel->children->next->next->is_atom
372      && skel->children->next->next->next->is_atom
373      && skel->children->next->next->next->next->is_atom
374      && skel->children->next->next->next->next->next->is_atom
375      && skel->children->next->next->next->next->next->next->is_atom
376      && skel->children->next->next->next->next->next->next->next->is_atom)
377    return TRUE;
378
379  return FALSE;
380}
381
382
383
384/*** Parsing (conversion from skeleton to native FS type) ***/
385
386svn_error_t *
387svn_fs_base__parse_revision_skel(revision_t **revision_p,
388                                 svn_skel_t *skel,
389                                 apr_pool_t *pool)
390{
391  revision_t *revision;
392
393  /* Validate the skel. */
394  if (! is_valid_revision_skel(skel))
395    return skel_err("revision");
396
397  /* Create the returned structure */
398  revision = apr_pcalloc(pool, sizeof(*revision));
399  revision->txn_id = apr_pstrmemdup(pool, skel->children->next->data,
400                                    skel->children->next->len);
401
402  /* Return the structure. */
403  *revision_p = revision;
404  return SVN_NO_ERROR;
405}
406
407
408svn_error_t *
409svn_fs_base__parse_transaction_skel(transaction_t **transaction_p,
410                                    svn_skel_t *skel,
411                                    apr_pool_t *pool)
412{
413  transaction_t *transaction;
414  transaction_kind_t kind;
415  svn_skel_t *root_id, *base_id_or_rev, *proplist, *copies;
416  int len;
417
418  /* Validate the skel. */
419  if (! is_valid_transaction_skel(skel, &kind))
420    return skel_err("transaction");
421
422  root_id = skel->children->next;
423  base_id_or_rev = skel->children->next->next;
424  proplist = skel->children->next->next->next;
425  copies = skel->children->next->next->next->next;
426
427  /* Create the returned structure */
428  transaction = apr_pcalloc(pool, sizeof(*transaction));
429
430  /* KIND */
431  transaction->kind = kind;
432
433  /* REVISION or BASE-ID */
434  if (kind == transaction_kind_committed)
435    {
436      /* Committed transactions have a revision number... */
437      transaction->base_id = NULL;
438      transaction->revision =
439        SVN_STR_TO_REV(apr_pstrmemdup(pool, base_id_or_rev->data,
440                                      base_id_or_rev->len));
441      if (! SVN_IS_VALID_REVNUM(transaction->revision))
442        return skel_err("transaction");
443
444    }
445  else
446    {
447      /* ...where unfinished transactions have a base node-revision-id. */
448      transaction->revision = SVN_INVALID_REVNUM;
449      transaction->base_id = svn_fs_base__id_parse(base_id_or_rev->data,
450                                                   base_id_or_rev->len, pool);
451    }
452
453  /* ROOT-ID */
454  transaction->root_id = svn_fs_base__id_parse(root_id->data,
455                                               root_id->len, pool);
456
457  /* PROPLIST */
458  SVN_ERR(svn_skel__parse_proplist(&(transaction->proplist),
459                                   proplist, pool));
460
461  /* COPIES */
462  if ((len = svn_skel__list_length(copies)))
463    {
464      const char *copy_id;
465      apr_array_header_t *txncopies;
466      svn_skel_t *cpy = copies->children;
467
468      txncopies = apr_array_make(pool, len, sizeof(copy_id));
469      while (cpy)
470        {
471          copy_id = apr_pstrmemdup(pool, cpy->data, cpy->len);
472          APR_ARRAY_PUSH(txncopies, const char *) = copy_id;
473          cpy = cpy->next;
474        }
475      transaction->copies = txncopies;
476    }
477
478  /* Return the structure. */
479  *transaction_p = transaction;
480  return SVN_NO_ERROR;
481}
482
483
484svn_error_t *
485svn_fs_base__parse_representation_skel(representation_t **rep_p,
486                                       svn_skel_t *skel,
487                                       apr_pool_t *pool)
488{
489  representation_t *rep;
490  svn_skel_t *header_skel;
491
492  /* Validate the skel. */
493  if (! is_valid_representation_skel(skel))
494    return skel_err("representation");
495  header_skel = skel->children;
496
497  /* Create the returned structure */
498  rep = apr_pcalloc(pool, sizeof(*rep));
499
500  /* KIND */
501  if (svn_skel__matches_atom(header_skel->children, "fulltext"))
502    rep->kind = rep_kind_fulltext;
503  else
504    rep->kind = rep_kind_delta;
505
506  /* TXN */
507  rep->txn_id = apr_pstrmemdup(pool, header_skel->children->next->data,
508                               header_skel->children->next->len);
509
510  /* MD5 */
511  if (header_skel->children->next->next)
512    {
513      svn_skel_t *checksum_skel = header_skel->children->next->next;
514      rep->md5_checksum =
515        svn_checksum__from_digest_md5((const unsigned char *)
516                                      (checksum_skel->children->next->data),
517                                      pool);
518
519      /* SHA1 */
520      if (header_skel->children->next->next->next)
521        {
522          checksum_skel = header_skel->children->next->next->next;
523          rep->sha1_checksum =
524            svn_checksum__from_digest_sha1(
525              (const unsigned char *)(checksum_skel->children->next->data),
526              pool);
527        }
528    }
529
530  /* KIND-SPECIFIC stuff */
531  if (rep->kind == rep_kind_fulltext)
532    {
533      /* "fulltext"-specific. */
534      rep->contents.fulltext.string_key
535        = apr_pstrmemdup(pool,
536                         skel->children->next->data,
537                         skel->children->next->len);
538    }
539  else
540    {
541      /* "delta"-specific. */
542      svn_skel_t *chunk_skel = skel->children->next;
543      rep_delta_chunk_t *chunk;
544      apr_array_header_t *chunks;
545
546      /* Alloc the chunk array. */
547      chunks = apr_array_make(pool, svn_skel__list_length(skel) - 1,
548                              sizeof(chunk));
549
550      /* Process the chunks. */
551      while (chunk_skel)
552        {
553          svn_skel_t *window_skel = chunk_skel->children->next;
554          svn_skel_t *diff_skel = window_skel->children;
555          apr_int64_t val;
556          apr_uint64_t uval;
557          const char *str;
558
559          /* Allocate a chunk and its window */
560          chunk = apr_palloc(pool, sizeof(*chunk));
561
562          /* Populate the window */
563          str = apr_pstrmemdup(pool, diff_skel->children->next->data,
564                               diff_skel->children->next->len);
565          SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, 255, 10));
566          chunk->version = (apr_byte_t)uval;
567
568          chunk->string_key
569            = apr_pstrmemdup(pool,
570                             diff_skel->children->next->next->data,
571                             diff_skel->children->next->next->len);
572
573          str = apr_pstrmemdup(pool, window_skel->children->next->data,
574                               window_skel->children->next->len);
575          SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, APR_SIZE_MAX, 10));
576          chunk->size = (apr_size_t)uval;
577
578          chunk->rep_key
579            = apr_pstrmemdup(pool,
580                             window_skel->children->next->next->data,
581                             window_skel->children->next->next->len);
582
583          str = apr_pstrmemdup(pool, chunk_skel->children->data,
584                               chunk_skel->children->len);
585          SVN_ERR(svn_cstring_strtoi64(&val, str, 0, APR_INT64_MAX, 10));
586          chunk->offset = (svn_filesize_t)val;
587
588          /* Add this chunk to the array. */
589          APR_ARRAY_PUSH(chunks, rep_delta_chunk_t *) = chunk;
590
591          /* Next... */
592          chunk_skel = chunk_skel->next;
593        }
594
595      /* Add the chunks array to the representation. */
596      rep->contents.delta.chunks = chunks;
597    }
598
599  /* Return the structure. */
600  *rep_p = rep;
601  return SVN_NO_ERROR;
602}
603
604
605svn_error_t *
606svn_fs_base__parse_node_revision_skel(node_revision_t **noderev_p,
607                                      svn_skel_t *skel,
608                                      apr_pool_t *pool)
609{
610  node_revision_t *noderev;
611  svn_skel_t *header_skel, *cur_skel;
612
613  /* Validate the skel. */
614  if (! is_valid_node_revision_skel(skel))
615    return skel_err("node-revision");
616  header_skel = skel->children;
617
618  /* Create the returned structure */
619  noderev = apr_pcalloc(pool, sizeof(*noderev));
620
621  /* KIND */
622  if (svn_skel__matches_atom(header_skel->children, "dir"))
623    noderev->kind = svn_node_dir;
624  else
625    noderev->kind = svn_node_file;
626
627  /* CREATED-PATH */
628  noderev->created_path = apr_pstrmemdup(pool,
629                                         header_skel->children->next->data,
630                                         header_skel->children->next->len);
631
632  /* PREDECESSOR-ID */
633  if (header_skel->children->next->next)
634    {
635      cur_skel = header_skel->children->next->next;
636      if (cur_skel->len)
637        noderev->predecessor_id = svn_fs_base__id_parse(cur_skel->data,
638                                                        cur_skel->len, pool);
639
640      /* PREDECESSOR-COUNT */
641      noderev->predecessor_count = -1;
642      if (cur_skel->next)
643        {
644          const char *str;
645
646          cur_skel = cur_skel->next;
647          if (cur_skel->len)
648            {
649              str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len);
650              SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, str));
651            }
652
653          /* HAS-MERGEINFO and MERGEINFO-COUNT */
654          if (cur_skel->next)
655            {
656              int val;
657
658              cur_skel = cur_skel->next;
659              str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len);
660              SVN_ERR(svn_cstring_atoi(&val, str));
661              noderev->has_mergeinfo = (val != 0);
662
663              str = apr_pstrmemdup(pool, cur_skel->next->data,
664                                   cur_skel->next->len);
665              SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, str));
666            }
667        }
668    }
669
670  /* PROP-KEY */
671  if (skel->children->next->len)
672    noderev->prop_key = apr_pstrmemdup(pool, skel->children->next->data,
673                                       skel->children->next->len);
674
675  /* DATA-KEY */
676  if (skel->children->next->next->is_atom)
677    {
678      /* This is a real data rep key. */
679      if (skel->children->next->next->len)
680        noderev->data_key = apr_pstrmemdup(pool,
681                                           skel->children->next->next->data,
682                                           skel->children->next->next->len);
683      noderev->data_key_uniquifier = NULL;
684    }
685  else
686    {
687      /* This is a 2-tuple with a data rep key and a uniquifier. */
688      noderev->data_key =
689        apr_pstrmemdup(pool,
690                       skel->children->next->next->children->data,
691                       skel->children->next->next->children->len);
692      noderev->data_key_uniquifier =
693        apr_pstrmemdup(pool,
694                       skel->children->next->next->children->next->data,
695                       skel->children->next->next->children->next->len);
696    }
697
698  /* EDIT-DATA-KEY (optional, files only) */
699  if ((noderev->kind == svn_node_file)
700      && skel->children->next->next->next
701      && skel->children->next->next->next->len)
702    noderev->edit_key
703      = apr_pstrmemdup(pool, skel->children->next->next->next->data,
704                       skel->children->next->next->next->len);
705
706  /* Return the structure. */
707  *noderev_p = noderev;
708  return SVN_NO_ERROR;
709}
710
711
712svn_error_t *
713svn_fs_base__parse_copy_skel(copy_t **copy_p,
714                             svn_skel_t *skel,
715                             apr_pool_t *pool)
716{
717  copy_t *copy;
718
719  /* Validate the skel. */
720  if (! is_valid_copy_skel(skel))
721    return skel_err("copy");
722
723  /* Create the returned structure */
724  copy = apr_pcalloc(pool, sizeof(*copy));
725
726  /* KIND */
727  if (svn_skel__matches_atom(skel->children, "soft-copy"))
728    copy->kind = copy_kind_soft;
729  else
730    copy->kind = copy_kind_real;
731
732  /* SRC-PATH */
733  copy->src_path = apr_pstrmemdup(pool,
734                                  skel->children->next->data,
735                                  skel->children->next->len);
736
737  /* SRC-TXN-ID */
738  copy->src_txn_id = apr_pstrmemdup(pool,
739                                    skel->children->next->next->data,
740                                    skel->children->next->next->len);
741
742  /* DST-NODE-ID */
743  copy->dst_noderev_id
744    = svn_fs_base__id_parse(skel->children->next->next->next->data,
745                            skel->children->next->next->next->len, pool);
746
747  /* Return the structure. */
748  *copy_p = copy;
749  return SVN_NO_ERROR;
750}
751
752
753svn_error_t *
754svn_fs_base__parse_entries_skel(apr_hash_t **entries_p,
755                                svn_skel_t *skel,
756                                apr_pool_t *pool)
757{
758  apr_hash_t *entries = NULL;
759  int len = svn_skel__list_length(skel);
760  svn_skel_t *elt;
761
762  if (! (len >= 0))
763    return skel_err("entries");
764
765  if (len > 0)
766    {
767      /* Else, allocate a hash and populate it. */
768      entries = apr_hash_make(pool);
769
770      /* Check entries are well-formed as we go along. */
771      for (elt = skel->children; elt; elt = elt->next)
772        {
773          const char *name;
774          svn_fs_id_t *id;
775
776          /* ENTRY must be a list of two elements. */
777          if (svn_skel__list_length(elt) != 2)
778            return skel_err("entries");
779
780          /* Get the entry's name and ID. */
781          name = apr_pstrmemdup(pool, elt->children->data,
782                                elt->children->len);
783          id = svn_fs_base__id_parse(elt->children->next->data,
784                                     elt->children->next->len, pool);
785
786          /* Add the entry to the hash. */
787          apr_hash_set(entries, name, elt->children->len, id);
788        }
789    }
790
791  /* Return the structure. */
792  *entries_p = entries;
793  return SVN_NO_ERROR;
794}
795
796
797svn_error_t *
798svn_fs_base__parse_change_skel(change_t **change_p,
799                               svn_skel_t *skel,
800                               apr_pool_t *pool)
801{
802  change_t *change;
803  svn_fs_path_change_kind_t kind;
804
805  /* Validate the skel. */
806  if (! is_valid_change_skel(skel, &kind))
807    return skel_err("change");
808
809  /* Create the returned structure */
810  change = apr_pcalloc(pool, sizeof(*change));
811
812  /* PATH */
813  change->path = apr_pstrmemdup(pool, skel->children->next->data,
814                                skel->children->next->len);
815
816  /* NODE-REV-ID */
817  if (skel->children->next->next->len)
818    change->noderev_id = svn_fs_base__id_parse
819      (skel->children->next->next->data, skel->children->next->next->len,
820       pool);
821
822  /* KIND */
823  change->kind = kind;
824
825  /* TEXT-MOD */
826  if (skel->children->next->next->next->next->len)
827    change->text_mod = TRUE;
828
829  /* PROP-MOD */
830  if (skel->children->next->next->next->next->next->len)
831    change->prop_mod = TRUE;
832
833  /* Return the structure. */
834  *change_p = change;
835  return SVN_NO_ERROR;
836}
837
838
839svn_error_t *
840svn_fs_base__parse_lock_skel(svn_lock_t **lock_p,
841                             svn_skel_t *skel,
842                             apr_pool_t *pool)
843{
844  svn_lock_t *lock;
845  const char *timestr;
846
847  /* Validate the skel. */
848  if (! is_valid_lock_skel(skel))
849    return skel_err("lock");
850
851  /* Create the returned structure */
852  lock = apr_pcalloc(pool, sizeof(*lock));
853
854  /* PATH */
855  lock->path = apr_pstrmemdup(pool, skel->children->next->data,
856                              skel->children->next->len);
857
858  /* LOCK-TOKEN */
859  lock->token = apr_pstrmemdup(pool,
860                               skel->children->next->next->data,
861                               skel->children->next->next->len);
862
863  /* OWNER */
864  lock->owner = apr_pstrmemdup(pool,
865                               skel->children->next->next->next->data,
866                               skel->children->next->next->next->len);
867
868  /* COMMENT  (could be just an empty atom) */
869  if (skel->children->next->next->next->next->len)
870    lock->comment =
871      apr_pstrmemdup(pool,
872                     skel->children->next->next->next->next->data,
873                     skel->children->next->next->next->next->len);
874
875  /* XML_P */
876  if (svn_skel__matches_atom
877      (skel->children->next->next->next->next->next, "1"))
878    lock->is_dav_comment = TRUE;
879  else
880    lock->is_dav_comment = FALSE;
881
882  /* CREATION-DATE */
883  timestr = apr_pstrmemdup
884    (pool,
885     skel->children->next->next->next->next->next->next->data,
886     skel->children->next->next->next->next->next->next->len);
887  SVN_ERR(svn_time_from_cstring(&(lock->creation_date),
888                                timestr, pool));
889
890  /* EXPIRATION-DATE  (could be just an empty atom) */
891  if (skel->children->next->next->next->next->next->next->next->len)
892    {
893      timestr =
894        apr_pstrmemdup
895        (pool,
896         skel->children->next->next->next->next->next->next->next->data,
897         skel->children->next->next->next->next->next->next->next->len);
898      SVN_ERR(svn_time_from_cstring(&(lock->expiration_date),
899                                    timestr, pool));
900    }
901
902  /* Return the structure. */
903  *lock_p = lock;
904  return SVN_NO_ERROR;
905}
906
907
908
909/*** Unparsing (conversion from native FS type to skeleton) ***/
910
911svn_error_t *
912svn_fs_base__unparse_revision_skel(svn_skel_t **skel_p,
913                                   const revision_t *revision,
914                                   apr_pool_t *pool)
915{
916  svn_skel_t *skel;
917
918  /* Create the skel. */
919  skel = svn_skel__make_empty_list(pool);
920
921  /* TXN_ID */
922  svn_skel__prepend(svn_skel__str_atom(revision->txn_id, pool), skel);
923
924  /* "revision" */
925  svn_skel__prepend(svn_skel__str_atom("revision", pool), skel);
926
927  /* Validate and return the skel. */
928  if (! is_valid_revision_skel(skel))
929    return skel_err("revision");
930  *skel_p = skel;
931  return SVN_NO_ERROR;
932}
933
934
935svn_error_t *
936svn_fs_base__unparse_transaction_skel(svn_skel_t **skel_p,
937                                      const transaction_t *transaction,
938                                      apr_pool_t *pool)
939{
940  svn_skel_t *skel;
941  svn_skel_t *proplist_skel, *copies_skel, *header_skel;
942  svn_string_t *id_str;
943  transaction_kind_t kind;
944
945  /* Create the skel. */
946  skel = svn_skel__make_empty_list(pool);
947
948  switch (transaction->kind)
949    {
950    case transaction_kind_committed:
951      header_skel = svn_skel__str_atom("committed", pool);
952      if ((transaction->base_id)
953          || (! SVN_IS_VALID_REVNUM(transaction->revision)))
954        return skel_err("transaction");
955      break;
956    case transaction_kind_dead:
957      header_skel = svn_skel__str_atom("dead", pool);
958      if ((! transaction->base_id)
959          || (SVN_IS_VALID_REVNUM(transaction->revision)))
960        return skel_err("transaction");
961      break;
962    case transaction_kind_normal:
963      header_skel = svn_skel__str_atom("transaction", pool);
964      if ((! transaction->base_id)
965          || (SVN_IS_VALID_REVNUM(transaction->revision)))
966        return skel_err("transaction");
967      break;
968    default:
969      return skel_err("transaction");
970    }
971
972
973  /* COPIES */
974  copies_skel = svn_skel__make_empty_list(pool);
975  if (transaction->copies && transaction->copies->nelts)
976    {
977      int i;
978      for (i = transaction->copies->nelts - 1; i >= 0; i--)
979        {
980          svn_skel__prepend(svn_skel__str_atom(
981                                APR_ARRAY_IDX(transaction->copies, i,
982                                              const char *),
983                                pool),
984                            copies_skel);
985        }
986    }
987  svn_skel__prepend(copies_skel, skel);
988
989  /* PROPLIST */
990  SVN_ERR(svn_skel__unparse_proplist(&proplist_skel,
991                                     transaction->proplist, pool));
992  svn_skel__prepend(proplist_skel, skel);
993
994  /* REVISION or BASE-ID */
995  if (transaction->kind == transaction_kind_committed)
996    {
997      /* Committed transactions have a revision number... */
998      svn_skel__prepend(svn_skel__str_atom(apr_psprintf(pool, "%ld",
999                                                        transaction->revision),
1000                                           pool), skel);
1001    }
1002  else
1003    {
1004      /* ...where other transactions have a base node revision ID. */
1005      id_str = svn_fs_base__id_unparse(transaction->base_id, pool);
1006      svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool),
1007                        skel);
1008    }
1009
1010  /* ROOT-ID */
1011  id_str = svn_fs_base__id_unparse(transaction->root_id, pool);
1012  svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool), skel);
1013
1014  /* KIND (see above) */
1015  svn_skel__prepend(header_skel, skel);
1016
1017  /* Validate and return the skel. */
1018  if (! is_valid_transaction_skel(skel, &kind))
1019    return skel_err("transaction");
1020  if (kind != transaction->kind)
1021    return skel_err("transaction");
1022  *skel_p = skel;
1023  return SVN_NO_ERROR;
1024}
1025
1026
1027/* Construct a skel representing CHECKSUM, allocated in POOL, and prepend
1028 * it onto the existing skel SKEL. */
1029static svn_error_t *
1030prepend_checksum(svn_skel_t *skel,
1031                 svn_checksum_t *checksum,
1032                 apr_pool_t *pool)
1033{
1034  svn_skel_t *checksum_skel = svn_skel__make_empty_list(pool);
1035
1036  switch (checksum->kind)
1037    {
1038    case svn_checksum_md5:
1039      svn_skel__prepend(svn_skel__mem_atom(checksum->digest,
1040                                           APR_MD5_DIGESTSIZE, pool),
1041                        checksum_skel);
1042      svn_skel__prepend(svn_skel__str_atom("md5", pool), checksum_skel);
1043      break;
1044
1045    case svn_checksum_sha1:
1046      svn_skel__prepend(svn_skel__mem_atom(checksum->digest,
1047                                           APR_SHA1_DIGESTSIZE, pool),
1048                        checksum_skel);
1049      svn_skel__prepend(svn_skel__str_atom("sha1", pool), checksum_skel);
1050      break;
1051
1052    default:
1053      return skel_err("checksum");
1054    }
1055  svn_skel__prepend(checksum_skel, skel);
1056
1057  return SVN_NO_ERROR;
1058}
1059
1060
1061svn_error_t *
1062svn_fs_base__unparse_representation_skel(svn_skel_t **skel_p,
1063                                         const representation_t *rep,
1064                                         int format,
1065                                         apr_pool_t *pool)
1066{
1067  svn_skel_t *skel = svn_skel__make_empty_list(pool);
1068  svn_skel_t *header_skel = svn_skel__make_empty_list(pool);
1069
1070  /** Some parts of the header are common to all representations; do
1071      those parts first. **/
1072
1073  /* SHA1 */
1074  if ((format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) && rep->sha1_checksum)
1075    SVN_ERR(prepend_checksum(header_skel, rep->sha1_checksum, pool));
1076
1077  /* MD5 */
1078  {
1079    svn_checksum_t *md5_checksum = rep->md5_checksum;
1080    if (! md5_checksum)
1081      md5_checksum = svn_checksum_create(svn_checksum_md5, pool);
1082    SVN_ERR(prepend_checksum(header_skel, md5_checksum, pool));
1083  }
1084
1085  /* TXN */
1086  if (rep->txn_id)
1087    svn_skel__prepend(svn_skel__str_atom(rep->txn_id, pool), header_skel);
1088  else
1089    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel);
1090
1091  /** Do the kind-specific stuff. **/
1092
1093  if (rep->kind == rep_kind_fulltext)
1094    {
1095      /*** Fulltext Representation. ***/
1096
1097      /* STRING-KEY */
1098      if ((! rep->contents.fulltext.string_key)
1099          || (! *rep->contents.fulltext.string_key))
1100        svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1101      else
1102        svn_skel__prepend(svn_skel__str_atom(rep->contents.fulltext.string_key,
1103                                             pool), skel);
1104
1105      /* "fulltext" */
1106      svn_skel__prepend(svn_skel__str_atom("fulltext", pool), header_skel);
1107
1108      /* header */
1109      svn_skel__prepend(header_skel, skel);
1110    }
1111  else if (rep->kind == rep_kind_delta)
1112    {
1113      /*** Delta Representation. ***/
1114      int i;
1115      apr_array_header_t *chunks = rep->contents.delta.chunks;
1116
1117      /* Loop backwards through the windows, creating and prepending skels. */
1118      for (i = chunks->nelts; i > 0; i--)
1119        {
1120          svn_skel_t *window_skel = svn_skel__make_empty_list(pool);
1121          svn_skel_t *chunk_skel = svn_skel__make_empty_list(pool);
1122          svn_skel_t *diff_skel = svn_skel__make_empty_list(pool);
1123          const char *size_str, *offset_str, *version_str;
1124          rep_delta_chunk_t *chunk = APR_ARRAY_IDX(chunks, i - 1,
1125                                                   rep_delta_chunk_t *);
1126
1127          /* OFFSET */
1128          offset_str = apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT,
1129                                    chunk->offset);
1130
1131          /* SIZE */
1132          size_str = apr_psprintf(pool, "%" APR_SIZE_T_FMT, chunk->size);
1133
1134          /* VERSION */
1135          version_str = apr_psprintf(pool, "%d", chunk->version);
1136
1137          /* DIFF */
1138          if ((! chunk->string_key) || (! *chunk->string_key))
1139            svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), diff_skel);
1140          else
1141            svn_skel__prepend(svn_skel__str_atom(chunk->string_key, pool),
1142                              diff_skel);
1143          svn_skel__prepend(svn_skel__str_atom(version_str, pool), diff_skel);
1144          svn_skel__prepend(svn_skel__str_atom("svndiff", pool), diff_skel);
1145
1146          /* REP-KEY */
1147          if ((! chunk->rep_key) || (! *(chunk->rep_key)))
1148            svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool),
1149                              window_skel);
1150          else
1151            svn_skel__prepend(svn_skel__str_atom(chunk->rep_key, pool),
1152                              window_skel);
1153          svn_skel__prepend(svn_skel__str_atom(size_str, pool), window_skel);
1154          svn_skel__prepend(diff_skel, window_skel);
1155
1156          /* window header. */
1157          svn_skel__prepend(window_skel, chunk_skel);
1158          svn_skel__prepend(svn_skel__str_atom(offset_str, pool),
1159                               chunk_skel);
1160
1161          /* Add this window item to the main skel. */
1162          svn_skel__prepend(chunk_skel, skel);
1163        }
1164
1165      /* "delta" */
1166      svn_skel__prepend(svn_skel__str_atom("delta", pool), header_skel);
1167
1168      /* header */
1169      svn_skel__prepend(header_skel, skel);
1170    }
1171  else /* unknown kind */
1172    SVN_ERR_MALFUNCTION();
1173
1174  /* Validate and return the skel. */
1175  if (! is_valid_representation_skel(skel))
1176    return skel_err("representation");
1177  *skel_p = skel;
1178  return SVN_NO_ERROR;
1179}
1180
1181
1182svn_error_t *
1183svn_fs_base__unparse_node_revision_skel(svn_skel_t **skel_p,
1184                                        const node_revision_t *noderev,
1185                                        int format,
1186                                        apr_pool_t *pool)
1187{
1188  svn_skel_t *skel;
1189  svn_skel_t *header_skel;
1190  const char *num_str;
1191
1192  /* Create the skel. */
1193  skel = svn_skel__make_empty_list(pool);
1194  header_skel = svn_skel__make_empty_list(pool);
1195
1196  /* Store mergeinfo stuffs only if the schema level supports it. */
1197  if (format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1198    {
1199      /* MERGEINFO-COUNT */
1200      num_str = apr_psprintf(pool, "%" APR_INT64_T_FMT,
1201                             noderev->mergeinfo_count);
1202      svn_skel__prepend(svn_skel__str_atom(num_str, pool), header_skel);
1203
1204      /* HAS-MERGEINFO */
1205      svn_skel__prepend(svn_skel__mem_atom(noderev->has_mergeinfo ? "1" : "0",
1206                                           1, pool), header_skel);
1207
1208      /* PREDECESSOR-COUNT padding (only if we *don't* have a valid
1209         value; if we do, we'll pick that up below) */
1210      if (noderev->predecessor_count == -1)
1211        {
1212          svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel);
1213        }
1214    }
1215
1216  /* PREDECESSOR-COUNT */
1217  if (noderev->predecessor_count != -1)
1218    {
1219      const char *count_str = apr_psprintf(pool, "%d",
1220                                           noderev->predecessor_count);
1221      svn_skel__prepend(svn_skel__str_atom(count_str, pool), header_skel);
1222    }
1223
1224  /* PREDECESSOR-ID */
1225  if (noderev->predecessor_id)
1226    {
1227      svn_string_t *id_str = svn_fs_base__id_unparse(noderev->predecessor_id,
1228                                                     pool);
1229      svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len, pool),
1230                        header_skel);
1231    }
1232  else
1233    {
1234      svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), header_skel);
1235    }
1236
1237  /* CREATED-PATH */
1238  svn_skel__prepend(svn_skel__str_atom(noderev->created_path, pool),
1239                    header_skel);
1240
1241  /* KIND */
1242  if (noderev->kind == svn_node_file)
1243    svn_skel__prepend(svn_skel__str_atom("file", pool), header_skel);
1244  else if (noderev->kind == svn_node_dir)
1245    svn_skel__prepend(svn_skel__str_atom("dir", pool), header_skel);
1246  else
1247    SVN_ERR_MALFUNCTION();
1248
1249  /* ### do we really need to check *node->FOO_key ? if a key doesn't
1250     ### exist, then the field should be NULL ...  */
1251
1252  /* EDIT-DATA-KEY (optional) */
1253  if ((noderev->edit_key) && (*noderev->edit_key))
1254    svn_skel__prepend(svn_skel__str_atom(noderev->edit_key, pool), skel);
1255
1256  /* DATA-KEY | (DATA-KEY DATA-KEY-UNIQID) */
1257  if ((noderev->data_key_uniquifier) && (*noderev->data_key_uniquifier))
1258    {
1259      /* Build a 2-tuple with a rep key and uniquifier. */
1260      svn_skel_t *data_key_skel = svn_skel__make_empty_list(pool);
1261
1262      /* DATA-KEY-UNIQID */
1263      svn_skel__prepend(svn_skel__str_atom(noderev->data_key_uniquifier,
1264                                           pool),
1265                        data_key_skel);
1266
1267      /* DATA-KEY */
1268      if ((noderev->data_key) && (*noderev->data_key))
1269        svn_skel__prepend(svn_skel__str_atom(noderev->data_key, pool),
1270                          data_key_skel);
1271      else
1272        svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), data_key_skel);
1273
1274      /* Add our 2-tuple to the main skel. */
1275      svn_skel__prepend(data_key_skel, skel);
1276    }
1277  else
1278    {
1279      /* Just store the rep key (or empty placeholder) in the main skel. */
1280      if ((noderev->data_key) && (*noderev->data_key))
1281        svn_skel__prepend(svn_skel__str_atom(noderev->data_key, pool), skel);
1282      else
1283        svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1284    }
1285
1286  /* PROP-KEY */
1287  if ((noderev->prop_key) && (*noderev->prop_key))
1288    svn_skel__prepend(svn_skel__str_atom(noderev->prop_key, pool), skel);
1289  else
1290    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1291
1292  /* HEADER */
1293  svn_skel__prepend(header_skel, skel);
1294
1295  /* Validate and return the skel. */
1296  if (! is_valid_node_revision_skel(skel))
1297    return skel_err("node-revision");
1298  *skel_p = skel;
1299  return SVN_NO_ERROR;
1300}
1301
1302
1303svn_error_t *
1304svn_fs_base__unparse_copy_skel(svn_skel_t **skel_p,
1305                               const copy_t *copy,
1306                               apr_pool_t *pool)
1307{
1308  svn_skel_t *skel;
1309  svn_string_t *tmp_str;
1310
1311  /* Create the skel. */
1312  skel = svn_skel__make_empty_list(pool);
1313
1314  /* DST-NODE-ID */
1315  tmp_str = svn_fs_base__id_unparse(copy->dst_noderev_id, pool);
1316  svn_skel__prepend(svn_skel__mem_atom(tmp_str->data, tmp_str->len, pool),
1317                    skel);
1318
1319  /* SRC-TXN-ID */
1320  if ((copy->src_txn_id) && (*copy->src_txn_id))
1321    svn_skel__prepend(svn_skel__str_atom(copy->src_txn_id, pool), skel);
1322  else
1323    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1324
1325  /* SRC-PATH */
1326  if ((copy->src_path) && (*copy->src_path))
1327    svn_skel__prepend(svn_skel__str_atom(copy->src_path, pool), skel);
1328  else
1329    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1330
1331  /* "copy" */
1332  if (copy->kind == copy_kind_real)
1333    svn_skel__prepend(svn_skel__str_atom("copy", pool), skel);
1334  else
1335    svn_skel__prepend(svn_skel__str_atom("soft-copy", pool), skel);
1336
1337  /* Validate and return the skel. */
1338  if (! is_valid_copy_skel(skel))
1339    return skel_err("copy");
1340  *skel_p = skel;
1341  return SVN_NO_ERROR;
1342}
1343
1344
1345svn_error_t *
1346svn_fs_base__unparse_entries_skel(svn_skel_t **skel_p,
1347                                  apr_hash_t *entries,
1348                                  apr_pool_t *pool)
1349{
1350  svn_skel_t *skel = svn_skel__make_empty_list(pool);
1351  apr_hash_index_t *hi;
1352
1353  /* Create the skel. */
1354  if (entries)
1355    {
1356      /* Loop over hash entries */
1357      for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1358        {
1359          const void *key;
1360          void *val;
1361          apr_ssize_t klen;
1362          svn_fs_id_t *value;
1363          svn_string_t *id_str;
1364          svn_skel_t *entry_skel = svn_skel__make_empty_list(pool);
1365
1366          apr_hash_this(hi, &key, &klen, &val);
1367          value = val;
1368
1369          /* VALUE */
1370          id_str = svn_fs_base__id_unparse(value, pool);
1371          svn_skel__prepend(svn_skel__mem_atom(id_str->data, id_str->len,
1372                                               pool),
1373                            entry_skel);
1374
1375          /* NAME */
1376          svn_skel__prepend(svn_skel__mem_atom(key, klen, pool), entry_skel);
1377
1378          /* Add entry to the entries skel. */
1379          svn_skel__prepend(entry_skel, skel);
1380        }
1381    }
1382
1383  /* Return the skel. */
1384  *skel_p = skel;
1385  return SVN_NO_ERROR;
1386}
1387
1388
1389svn_error_t *
1390svn_fs_base__unparse_change_skel(svn_skel_t **skel_p,
1391                                 const change_t *change,
1392                                 apr_pool_t *pool)
1393{
1394  svn_skel_t *skel;
1395  svn_string_t *tmp_str;
1396  svn_fs_path_change_kind_t kind;
1397
1398  /* Create the skel. */
1399  skel = svn_skel__make_empty_list(pool);
1400
1401  /* PROP-MOD */
1402  if (change->prop_mod)
1403    svn_skel__prepend(svn_skel__str_atom("1", pool), skel);
1404  else
1405    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1406
1407  /* TEXT-MOD */
1408  if (change->text_mod)
1409    svn_skel__prepend(svn_skel__str_atom("1", pool), skel);
1410  else
1411    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1412
1413  /* KIND */
1414  switch (change->kind)
1415    {
1416    case svn_fs_path_change_reset:
1417      svn_skel__prepend(svn_skel__str_atom("reset", pool), skel);
1418      break;
1419    case svn_fs_path_change_add:
1420      svn_skel__prepend(svn_skel__str_atom("add", pool), skel);
1421      break;
1422    case svn_fs_path_change_delete:
1423      svn_skel__prepend(svn_skel__str_atom("delete", pool), skel);
1424      break;
1425    case svn_fs_path_change_replace:
1426      svn_skel__prepend(svn_skel__str_atom("replace", pool), skel);
1427      break;
1428    case svn_fs_path_change_modify:
1429    default:
1430      svn_skel__prepend(svn_skel__str_atom("modify", pool), skel);
1431      break;
1432    }
1433
1434  /* NODE-REV-ID */
1435  if (change->noderev_id)
1436    {
1437      tmp_str = svn_fs_base__id_unparse(change->noderev_id, pool);
1438      svn_skel__prepend(svn_skel__mem_atom(tmp_str->data, tmp_str->len, pool),
1439                        skel);
1440    }
1441  else
1442    {
1443      svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1444    }
1445
1446  /* PATH */
1447  svn_skel__prepend(svn_skel__str_atom(change->path, pool), skel);
1448
1449  /* "change" */
1450  svn_skel__prepend(svn_skel__str_atom("change", pool), skel);
1451
1452  /* Validate and return the skel. */
1453  if (! is_valid_change_skel(skel, &kind))
1454    return skel_err("change");
1455  if (kind != change->kind)
1456    return skel_err("change");
1457  *skel_p = skel;
1458  return SVN_NO_ERROR;
1459}
1460
1461
1462svn_error_t *
1463svn_fs_base__unparse_lock_skel(svn_skel_t **skel_p,
1464                               const svn_lock_t *lock,
1465                               apr_pool_t *pool)
1466{
1467  svn_skel_t *skel;
1468
1469  /* Create the skel. */
1470  skel = svn_skel__make_empty_list(pool);
1471
1472  /* EXP-DATE is optional.  If not present, just use an empty atom. */
1473  if (lock->expiration_date)
1474    svn_skel__prepend(svn_skel__str_atom(
1475                          svn_time_to_cstring(lock->expiration_date, pool),
1476                          pool), skel);
1477  else
1478    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1479
1480  /* CREATION-DATE */
1481  svn_skel__prepend(svn_skel__str_atom(
1482                        svn_time_to_cstring(lock->creation_date, pool),
1483                        pool), skel);
1484
1485  /* XML_P */
1486  if (lock->is_dav_comment)
1487    svn_skel__prepend(svn_skel__str_atom("1", pool), skel);
1488  else
1489    svn_skel__prepend(svn_skel__str_atom("0", pool), skel);
1490
1491  /* COMMENT */
1492  if (lock->comment)
1493    svn_skel__prepend(svn_skel__str_atom(lock->comment, pool), skel);
1494  else
1495    svn_skel__prepend(svn_skel__mem_atom(NULL, 0, pool), skel);
1496
1497  /* OWNER */
1498  svn_skel__prepend(svn_skel__str_atom(lock->owner, pool), skel);
1499
1500  /* LOCK-TOKEN */
1501  svn_skel__prepend(svn_skel__str_atom(lock->token, pool), skel);
1502
1503  /* PATH */
1504  svn_skel__prepend(svn_skel__str_atom(lock->path, pool), skel);
1505
1506  /* "lock" */
1507  svn_skel__prepend(svn_skel__str_atom("lock", pool), skel);
1508
1509  /* Validate and return the skel. */
1510  if (! is_valid_lock_skel(skel))
1511    return skel_err("lock");
1512
1513  *skel_p = skel;
1514  return SVN_NO_ERROR;
1515}
1516