]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_fs_base/util/fs_skels.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_fs_base / util / fs_skels.c
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 \f
42 static svn_error_t *
43 skel_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 \f
53 /*** Validity Checking ***/
54
55 static svn_boolean_t
56 is_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
73 static svn_boolean_t
74 is_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
87 static svn_boolean_t
88 is_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
115 static svn_boolean_t
116 is_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
154 static svn_boolean_t
155 is_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
212 static svn_boolean_t
213 is_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
253 static svn_boolean_t
254 is_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
304 static svn_boolean_t
305 is_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
316 static svn_boolean_t
317 is_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
365 static svn_boolean_t
366 is_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 \f
384 /*** Parsing (conversion from skeleton to native FS type) ***/
385
386 svn_error_t *
387 svn_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
408 svn_error_t *
409 svn_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
484 svn_error_t *
485 svn_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
605 svn_error_t *
606 svn_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
712 svn_error_t *
713 svn_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
753 svn_error_t *
754 svn_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
797 svn_error_t *
798 svn_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
839 svn_error_t *
840 svn_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 \f
909 /*** Unparsing (conversion from native FS type to skeleton) ***/
910
911 svn_error_t *
912 svn_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
935 svn_error_t *
936 svn_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. */
1029 static svn_error_t *
1030 prepend_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
1061 svn_error_t *
1062 svn_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
1182 svn_error_t *
1183 svn_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
1303 svn_error_t *
1304 svn_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
1345 svn_error_t *
1346 svn_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
1389 svn_error_t *
1390 svn_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
1462 svn_error_t *
1463 svn_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 }