1 /* skel.c --- parsing and unparsing 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 #include "svn_string.h"
25 #include "svn_error.h"
26 #include "svn_props.h"
27 #include "svn_pools.h"
28 #include "private/svn_skel.h"
29 #include "private/svn_string_private.h"
30
31
32 /* Parsing skeletons. */
33
34 enum char_type {
35 type_nothing = 0,
36 type_space = 1,
37 type_digit = 2,
38 type_paren = 3,
39 type_name = 4
40 };
41
42
43 /* We can't use the <ctype.h> macros here, because they are locale-
44 dependent. The syntax of a skel is specified directly in terms of
45 byte values, and is independent of locale. */
46
47 static const enum char_type skel_char_type[256] = {
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50 1, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0,
51 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
52
53 /* 64 */
54 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
55 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0, 3, 0, 0,
56 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
57 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0,
58
59 /* 128 */
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64
65 /* 192 */
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70 };
71
72
73
74 /* ### WTF? since when is number conversion LOCALE DEPENDENT? */
75 /* stsp: In C99, various numerical string properties such as decimal point,
76 * thousands separator, and the plus/minus sign are locale dependent. */
77
78 /* Converting text to numbers. */
79
80 /* Return the value of the string of digits at DATA as an ASCII
81 decimal number. The string is at most LEN bytes long. The value
82 of the number is at most MAX. Set *END to the address of the first
83 byte after the number, or zero if an error occurred while
84 converting the number (overflow, for example).
85
86 We would like to use strtoul, but that family of functions is
87 locale-dependent, whereas we're trying to parse data in a
88 locale-independent format. */
89 static apr_size_t
getsize(const char * data,apr_size_t len,const char ** endptr,apr_size_t max)90 getsize(const char *data, apr_size_t len,
91 const char **endptr, apr_size_t max)
92 {
93 /* We can't detect overflow by simply comparing value against max,
94 since multiplying value by ten can overflow in strange ways if
95 max is close to the limits of apr_size_t. For example, suppose
96 that max is 54, and apr_size_t is six bits long; its range is
97 0..63. If we're parsing the number "502", then value will be 50
98 after parsing the first two digits. 50 * 10 = 500. But 500
99 doesn't fit in an apr_size_t, so it'll be truncated to 500 mod 64
100 = 52, which is less than max, so we'd fail to recognize the
101 overflow. Furthermore, it *is* greater than 50, so you can't
102 detect overflow by checking whether value actually increased
103 after each multiplication --- sometimes it does increase, but
104 it's still wrong.
105
106 So we do the check for overflow before we multiply value and add
107 in the new digit. */
108 apr_size_t max_prefix = max / 10;
109 apr_size_t max_digit = max % 10;
110 apr_size_t i;
111 apr_size_t value = 0;
112
113 for (i = 0; i < len && '0' <= data[i] && data[i] <= '9'; i++)
114 {
115 apr_size_t digit = data[i] - '0';
116
117 /* Check for overflow. */
118 if (value > max_prefix
119 || (value == max_prefix && digit > max_digit))
120 {
121 *endptr = 0;
122 return 0;
123 }
124
125 value = (value * 10) + digit;
126 }
127
128 /* There must be at least one digit there. */
129 if (i == 0)
130 {
131 *endptr = 0;
132 return 0;
133 }
134 else
135 {
136 *endptr = data + i;
137 return value;
138 }
139 }
140
141
142 /* Checking validity of skels. */
143 static svn_error_t *
skel_err(const char * skel_type)144 skel_err(const char *skel_type)
145 {
146 return svn_error_createf(SVN_ERR_FS_MALFORMED_SKEL, NULL,
147 "Malformed%s%s skeleton",
148 skel_type ? " " : "",
149 skel_type ? skel_type : "");
150 }
151
152
153 static svn_boolean_t
is_valid_proplist_skel(const svn_skel_t * skel)154 is_valid_proplist_skel(const svn_skel_t *skel)
155 {
156 int len = svn_skel__list_length(skel);
157
158 if ((len >= 0) && (len & 1) == 0)
159 {
160 svn_skel_t *elt;
161
162 for (elt = skel->children; elt; elt = elt->next)
163 if (! elt->is_atom)
164 return FALSE;
165
166 return TRUE;
167 }
168
169 return FALSE;
170 }
171
172 static svn_boolean_t
is_valid_iproplist_skel(const svn_skel_t * skel)173 is_valid_iproplist_skel(const svn_skel_t *skel)
174 {
175 int len = svn_skel__list_length(skel);
176
177 if ((len >= 0) && (len & 1) == 0)
178 {
179 svn_skel_t *elt;
180
181 for (elt = skel->children; elt; elt = elt->next)
182 {
183 if (!elt->is_atom)
184 return FALSE;
185
186 if (elt->next == NULL)
187 return FALSE;
188
189 elt = elt->next;
190
191 if (! is_valid_proplist_skel(elt))
192 return FALSE;
193 }
194
195 return TRUE;
196 }
197
198 return FALSE;
199 }
200
201
202 static svn_skel_t *parse(const char *data, apr_size_t len,
203 apr_pool_t *pool);
204 static svn_skel_t *list(const char *data, apr_size_t len,
205 apr_pool_t *pool);
206 static svn_skel_t *implicit_atom(const char *data, apr_size_t len,
207 apr_pool_t *pool);
208 static svn_skel_t *explicit_atom(const char *data, apr_size_t len,
209 apr_pool_t *pool);
210
211
212 svn_skel_t *
svn_skel__parse(const char * data,apr_size_t len,apr_pool_t * pool)213 svn_skel__parse(const char *data,
214 apr_size_t len,
215 apr_pool_t *pool)
216 {
217 return parse(data, len, pool);
218 }
219
220
221 /* Parse any kind of skel object --- atom, or list. */
222 static svn_skel_t *
parse(const char * data,apr_size_t len,apr_pool_t * pool)223 parse(const char *data,
224 apr_size_t len,
225 apr_pool_t *pool)
226 {
227 char c;
228
229 /* The empty string isn't a valid skel. */
230 if (len <= 0)
231 return NULL;
232
233 c = *data;
234
235 /* Is it a list, or an atom? */
236 if (c == '(')
237 return list(data, len, pool);
238
239 /* Is it a string with an implicit length? */
240 if (skel_char_type[(unsigned char) c] == type_name)
241 return implicit_atom(data, len, pool);
242
243 /* Otherwise, we assume it's a string with an explicit length;
244 svn_skel__getsize will catch the error. */
245 else
246 return explicit_atom(data, len, pool);
247 }
248
249
250 static svn_skel_t *
list(const char * data,apr_size_t len,apr_pool_t * pool)251 list(const char *data,
252 apr_size_t len,
253 apr_pool_t *pool)
254 {
255 const char *end = data + len;
256 const char *list_start;
257
258 /* Verify that the list starts with an opening paren. At the
259 moment, all callers have checked this already, but it's more
260 robust this way. */
261 if (data >= end || *data != '(')
262 return NULL;
263
264 /* Mark where the list starts. */
265 list_start = data;
266
267 /* Skip the opening paren. */
268 data++;
269
270 /* Parse the children. */
271 {
272 svn_skel_t *children = NULL;
273 svn_skel_t **tail = &children;
274
275 for (;;)
276 {
277 svn_skel_t *element;
278
279 /* Skip any whitespace. */
280 while (data < end
281 && skel_char_type[(unsigned char) *data] == type_space)
282 data++;
283
284 /* End of data, but no closing paren? */
285 if (data >= end)
286 return NULL;
287
288 /* End of list? */
289 if (*data == ')')
290 {
291 data++;
292 break;
293 }
294
295 /* Parse the next element in the list. */
296 element = parse(data, end - data, pool);
297 if (! element)
298 return NULL;
299
300 /* Link that element into our list. */
301 element->next = NULL;
302 *tail = element;
303 tail = &element->next;
304
305 /* Advance past that element. */
306 data = element->data + element->len;
307 }
308
309 /* Construct the return value. */
310 {
311 svn_skel_t *s = apr_pcalloc(pool, sizeof(*s));
312
313 s->is_atom = FALSE;
314 s->data = list_start;
315 s->len = data - list_start;
316 s->children = children;
317
318 return s;
319 }
320 }
321 }
322
323
324 /* Parse an atom with implicit length --- one that starts with a name
325 character, terminated by whitespace, '(', ')', or end-of-data. */
326 static svn_skel_t *
implicit_atom(const char * data,apr_size_t len,apr_pool_t * pool)327 implicit_atom(const char *data,
328 apr_size_t len,
329 apr_pool_t *pool)
330 {
331 const char *start = data;
332 const char *end = data + len;
333 svn_skel_t *s;
334
335 /* Verify that the atom starts with a name character. At the
336 moment, all callers have checked this already, but it's more
337 robust this way. */
338 if (data >= end || skel_char_type[(unsigned char) *data] != type_name)
339 return NULL;
340
341 /* Find the end of the string. */
342 while (++data < end
343 && skel_char_type[(unsigned char) *data] != type_space
344 && skel_char_type[(unsigned char) *data] != type_paren)
345 ;
346
347 /* Allocate the skel representing this string. */
348 s = apr_pcalloc(pool, sizeof(*s));
349 s->is_atom = TRUE;
350 s->data = start;
351 s->len = data - start;
352
353 return s;
354 }
355
356
357 /* Parse an atom with explicit length --- one that starts with a byte
358 length, as a decimal ASCII number. */
359 static svn_skel_t *
explicit_atom(const char * data,apr_size_t len,apr_pool_t * pool)360 explicit_atom(const char *data,
361 apr_size_t len,
362 apr_pool_t *pool)
363 {
364 const char *end = data + len;
365 const char *next;
366 apr_size_t size;
367 svn_skel_t *s;
368
369 /* Parse the length. */
370 size = getsize(data, end - data, &next, end - data);
371 data = next;
372
373 /* Exit if we overflowed, or there wasn't a valid number there. */
374 if (! data)
375 return NULL;
376
377 /* Skip the whitespace character after the length. */
378 if (data >= end || skel_char_type[(unsigned char) *data] != type_space)
379 return NULL;
380 data++;
381
382 /* Check the length. */
383 if (end - data < size)
384 return NULL;
385
386 /* Allocate the skel representing this string. */
387 s = apr_pcalloc(pool, sizeof(*s));
388 s->is_atom = TRUE;
389 s->data = data;
390 s->len = size;
391
392 return s;
393 }
394
395
396
397 /* Unparsing skeletons. */
398
399 static apr_size_t estimate_unparsed_size(const svn_skel_t *skel);
400 static svn_stringbuf_t *unparse(const svn_skel_t *skel,
401 svn_stringbuf_t *str);
402
403
404 svn_stringbuf_t *
svn_skel__unparse(const svn_skel_t * skel,apr_pool_t * pool)405 svn_skel__unparse(const svn_skel_t *skel, apr_pool_t *pool)
406 {
407 svn_stringbuf_t *str
408 = svn_stringbuf_create_ensure(estimate_unparsed_size(skel) + 200, pool);
409
410 return unparse(skel, str);
411 }
412
413
414 /* Return an estimate of the number of bytes that the external
415 representation of SKEL will occupy. Since reallocing is expensive
416 in pools, it's worth trying to get the buffer size right the first
417 time. */
418 static apr_size_t
estimate_unparsed_size(const svn_skel_t * skel)419 estimate_unparsed_size(const svn_skel_t *skel)
420 {
421 if (skel->is_atom)
422 {
423 if (skel->len < 100)
424 /* If we have to use the explicit-length form, that'll be
425 two bytes for the length, one byte for the space, and
426 the contents. */
427 return skel->len + 3;
428 else
429 return skel->len + 30;
430 }
431 else
432 {
433 apr_size_t total_len;
434 svn_skel_t *child;
435
436 /* Allow space for opening and closing parens, and a space
437 between each pair of elements. */
438 total_len = 2;
439 for (child = skel->children; child; child = child->next)
440 total_len += estimate_unparsed_size(child) + 1;
441
442 return total_len;
443 }
444 }
445
446
447 /* Return non-zero iff we should use the implicit-length form for SKEL.
448 Assume that SKEL is an atom. */
449 static svn_boolean_t
use_implicit(const svn_skel_t * skel)450 use_implicit(const svn_skel_t *skel)
451 {
452 /* If it's null, or long, we should use explicit-length form. */
453 if (skel->len == 0
454 || skel->len >= 100)
455 return FALSE;
456
457 /* If it doesn't start with a name character, we must use
458 explicit-length form. */
459 if (skel_char_type[(unsigned char) skel->data[0]] != type_name)
460 return FALSE;
461
462 /* If it contains any whitespace or parens, then we must use
463 explicit-length form. */
464 {
465 apr_size_t i;
466
467 for (i = 1; i < skel->len; i++)
468 if (skel_char_type[(unsigned char) skel->data[i]] == type_space
469 || skel_char_type[(unsigned char) skel->data[i]] == type_paren)
470 return FALSE;
471 }
472
473 /* If we can't reject it for any of the above reasons, then we can
474 use implicit-length form. */
475 return TRUE;
476 }
477
478
479 /* Append the concrete representation of SKEL to the string STR. */
480 static svn_stringbuf_t *
unparse(const svn_skel_t * skel,svn_stringbuf_t * str)481 unparse(const svn_skel_t *skel, svn_stringbuf_t *str)
482 {
483 if (skel->is_atom)
484 {
485 /* Append an atom to STR. */
486 if (use_implicit(skel))
487 svn_stringbuf_appendbytes(str, skel->data, skel->len);
488 else
489 {
490 /* Append the length to STR. Ensure enough space for at least
491 * one 64 bit int. */
492 char buf[200 + SVN_INT64_BUFFER_SIZE];
493 apr_size_t length_len;
494
495 length_len = svn__ui64toa(buf, skel->len);
496
497 SVN_ERR_ASSERT_NO_RETURN(length_len > 0);
498
499 /* Make sure we have room for the length, the space, and the
500 atom's contents. */
501 svn_stringbuf_ensure(str, str->len + length_len + 1 + skel->len);
502 svn_stringbuf_appendbytes(str, buf, length_len);
503 svn_stringbuf_appendbyte(str, ' ');
504 svn_stringbuf_appendbytes(str, skel->data, skel->len);
505 }
506 }
507 else
508 {
509 /* Append a list to STR: an opening parenthesis, the list elements
510 * separated by a space, and a closing parenthesis. */
511 svn_skel_t *child;
512
513 svn_stringbuf_appendbyte(str, '(');
514
515 for (child = skel->children; child; child = child->next)
516 {
517 unparse(child, str);
518 if (child->next)
519 svn_stringbuf_appendbyte(str, ' ');
520 }
521
522 svn_stringbuf_appendbyte(str, ')');
523 }
524
525 return str;
526 }
527
528
529
530 /* Building skels. */
531
532
533 svn_skel_t *
svn_skel__str_atom(const char * str,apr_pool_t * pool)534 svn_skel__str_atom(const char *str, apr_pool_t *pool)
535 {
536 svn_skel_t *skel = apr_pcalloc(pool, sizeof(*skel));
537 skel->is_atom = TRUE;
538 skel->data = str;
539 skel->len = strlen(str);
540
541 return skel;
542 }
543
544
545 svn_skel_t *
svn_skel__mem_atom(const void * addr,apr_size_t len,apr_pool_t * pool)546 svn_skel__mem_atom(const void *addr,
547 apr_size_t len,
548 apr_pool_t *pool)
549 {
550 svn_skel_t *skel = apr_pcalloc(pool, sizeof(*skel));
551 skel->is_atom = TRUE;
552 skel->data = addr;
553 skel->len = len;
554
555 return skel;
556 }
557
558
559 svn_skel_t *
svn_skel__make_empty_list(apr_pool_t * pool)560 svn_skel__make_empty_list(apr_pool_t *pool)
561 {
562 svn_skel_t *skel = apr_pcalloc(pool, sizeof(*skel));
563 return skel;
564 }
565
svn_skel__dup(const svn_skel_t * src_skel,svn_boolean_t dup_data,apr_pool_t * result_pool)566 svn_skel_t *svn_skel__dup(const svn_skel_t *src_skel, svn_boolean_t dup_data,
567 apr_pool_t *result_pool)
568 {
569 svn_skel_t *skel = apr_pmemdup(result_pool, src_skel, sizeof(svn_skel_t));
570
571 if (dup_data && skel->data)
572 {
573 if (skel->is_atom)
574 skel->data = apr_pmemdup(result_pool, skel->data, skel->len);
575 else
576 {
577 /* When creating a skel this would be NULL, 0 for a list.
578 When parsing a string to a skel this might point to real data
579 delimiting the sublist. We don't copy that from here. */
580 skel->data = NULL;
581 skel->len = 0;
582 }
583 }
584
585 if (skel->children)
586 skel->children = svn_skel__dup(skel->children, dup_data, result_pool);
587
588 if (skel->next)
589 skel->next = svn_skel__dup(skel->next, dup_data, result_pool);
590
591 return skel;
592 }
593
594 void
svn_skel__prepend(svn_skel_t * skel,svn_skel_t * list_skel)595 svn_skel__prepend(svn_skel_t *skel, svn_skel_t *list_skel)
596 {
597 /* If list_skel isn't even a list, somebody's not using this
598 function properly. */
599 SVN_ERR_ASSERT_NO_RETURN(! list_skel->is_atom);
600
601 skel->next = list_skel->children;
602 list_skel->children = skel;
603 }
604
605
svn_skel__prepend_int(apr_int64_t value,svn_skel_t * skel,apr_pool_t * result_pool)606 void svn_skel__prepend_int(apr_int64_t value,
607 svn_skel_t *skel,
608 apr_pool_t *result_pool)
609 {
610 char *val_string = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE);
611 svn__i64toa(val_string, value);
612
613 svn_skel__prepend_str(val_string, skel, result_pool);
614 }
615
616
svn_skel__prepend_str(const char * value,svn_skel_t * skel,apr_pool_t * result_pool)617 void svn_skel__prepend_str(const char *value,
618 svn_skel_t *skel,
619 apr_pool_t *result_pool)
620 {
621 svn_skel_t *atom = svn_skel__str_atom(value, result_pool);
622
623 svn_skel__prepend(atom, skel);
624 }
625
626
svn_skel__append(svn_skel_t * list_skel,svn_skel_t * skel)627 void svn_skel__append(svn_skel_t *list_skel, svn_skel_t *skel)
628 {
629 SVN_ERR_ASSERT_NO_RETURN(list_skel != NULL && !list_skel->is_atom);
630
631 if (list_skel->children == NULL)
632 {
633 list_skel->children = skel;
634 }
635 else
636 {
637 list_skel = list_skel->children;
638 while (list_skel->next != NULL)
639 list_skel = list_skel->next;
640 list_skel->next = skel;
641 }
642 }
643
644
645 /* Examining skels. */
646
647
648 svn_boolean_t
svn_skel__matches_atom(const svn_skel_t * skel,const char * str)649 svn_skel__matches_atom(const svn_skel_t *skel, const char *str)
650 {
651 if (skel && skel->is_atom)
652 {
653 apr_size_t len = strlen(str);
654
655 return (skel->len == len
656 && ! memcmp(skel->data, str, len));
657 }
658 return FALSE;
659 }
660
661
662 int
svn_skel__list_length(const svn_skel_t * skel)663 svn_skel__list_length(const svn_skel_t *skel)
664 {
665 int len = 0;
666 const svn_skel_t *child;
667
668 if ((! skel) || skel->is_atom)
669 return -1;
670
671 for (child = skel->children; child; child = child->next)
672 len++;
673
674 return len;
675 }
676
677
678
679 /* Parsing and unparsing into high-level types. */
680
681 svn_error_t *
svn_skel__parse_int(apr_int64_t * n,const svn_skel_t * skel,apr_pool_t * scratch_pool)682 svn_skel__parse_int(apr_int64_t *n, const svn_skel_t *skel,
683 apr_pool_t *scratch_pool)
684 {
685 const char *str;
686
687 /* We need to duplicate the SKEL contents in order to get a NUL-terminated
688 version of it. The SKEL may not have valid memory at DATA[LEN]. */
689 str = apr_pstrmemdup(scratch_pool, skel->data, skel->len);
690 return svn_error_trace(svn_cstring_atoi64(n, str));
691 }
692
693
694 svn_error_t *
svn_skel__parse_proplist(apr_hash_t ** proplist_p,const svn_skel_t * skel,apr_pool_t * pool)695 svn_skel__parse_proplist(apr_hash_t **proplist_p,
696 const svn_skel_t *skel,
697 apr_pool_t *pool /* result_pool */)
698 {
699 apr_hash_t *proplist = NULL;
700 svn_skel_t *elt;
701
702 /* Validate the skel. */
703 if (! is_valid_proplist_skel(skel))
704 return skel_err("proplist");
705
706 /* Create the returned structure */
707 proplist = apr_hash_make(pool);
708 for (elt = skel->children; elt; elt = elt->next->next)
709 {
710 svn_string_t *value = svn_string_ncreate(elt->next->data,
711 elt->next->len, pool);
712 apr_hash_set(proplist,
713 apr_pstrmemdup(pool, elt->data, elt->len),
714 elt->len,
715 value);
716 }
717
718 /* Return the structure. */
719 *proplist_p = proplist;
720 return SVN_NO_ERROR;
721 }
722
723 svn_error_t *
svn_skel__parse_iprops(apr_array_header_t ** iprops,const svn_skel_t * skel,apr_pool_t * result_pool)724 svn_skel__parse_iprops(apr_array_header_t **iprops,
725 const svn_skel_t *skel,
726 apr_pool_t *result_pool)
727 {
728 svn_skel_t *elt;
729
730 /* Validate the skel. */
731 if (! is_valid_iproplist_skel(skel))
732 return skel_err("iprops");
733
734 /* Create the returned structure */
735 *iprops = apr_array_make(result_pool, 1,
736 sizeof(svn_prop_inherited_item_t *));
737
738 for (elt = skel->children; elt; elt = elt->next->next)
739 {
740 svn_prop_inherited_item_t *new_iprop = apr_palloc(result_pool,
741 sizeof(*new_iprop));
742 svn_string_t *repos_parent = svn_string_ncreate(elt->data, elt->len,
743 result_pool);
744 SVN_ERR(svn_skel__parse_proplist(&(new_iprop->prop_hash), elt->next,
745 result_pool));
746 new_iprop->path_or_url = repos_parent->data;
747 APR_ARRAY_PUSH(*iprops, svn_prop_inherited_item_t *) = new_iprop;
748 }
749 return SVN_NO_ERROR;
750 }
751
752 svn_error_t *
svn_skel__parse_prop(svn_string_t ** propval,const svn_skel_t * skel,const char * propname,apr_pool_t * pool)753 svn_skel__parse_prop(svn_string_t **propval,
754 const svn_skel_t *skel,
755 const char *propname,
756 apr_pool_t *pool /* result_pool */)
757 {
758 svn_skel_t *elt;
759
760 *propval = NULL;
761
762 /* Validate the skel. */
763 if (! is_valid_proplist_skel(skel))
764 return skel_err("proplist");
765
766 /* Look for PROPNAME in SKEL. */
767 for (elt = skel->children; elt; elt = elt->next->next)
768 {
769 if (elt->len == strlen(propname)
770 && strncmp(propname, elt->data, elt->len) == 0)
771 {
772 *propval = svn_string_ncreate(elt->next->data, elt->next->len,
773 pool);
774 break;
775 }
776 else
777 {
778 continue;
779 }
780 }
781 return SVN_NO_ERROR;
782 }
783
784
785 svn_error_t *
svn_skel__unparse_proplist(svn_skel_t ** skel_p,const apr_hash_t * proplist,apr_pool_t * pool)786 svn_skel__unparse_proplist(svn_skel_t **skel_p,
787 const apr_hash_t *proplist,
788 apr_pool_t *pool)
789 {
790 svn_skel_t *skel = svn_skel__make_empty_list(pool);
791 apr_hash_index_t *hi;
792
793 /* Create the skel. */
794 if (proplist)
795 {
796 /* Loop over hash entries */
797 for (hi = apr_hash_first(pool, (apr_hash_t *)proplist); hi;
798 hi = apr_hash_next(hi))
799 {
800 const void *key;
801 void *val;
802 apr_ssize_t klen;
803 svn_string_t *value;
804
805 apr_hash_this(hi, &key, &klen, &val);
806 value = val;
807
808 /* VALUE */
809 svn_skel__prepend(svn_skel__mem_atom(value->data, value->len, pool),
810 skel);
811
812 /* NAME */
813 svn_skel__prepend(svn_skel__mem_atom(key, klen, pool), skel);
814 }
815 }
816
817 /* Validate and return the skel. */
818 if (! is_valid_proplist_skel(skel))
819 return skel_err("proplist");
820 *skel_p = skel;
821 return SVN_NO_ERROR;
822 }
823
824 svn_error_t *
svn_skel__unparse_iproplist(svn_skel_t ** skel_p,const apr_array_header_t * inherited_props,apr_pool_t * result_pool,apr_pool_t * scratch_pool)825 svn_skel__unparse_iproplist(svn_skel_t **skel_p,
826 const apr_array_header_t *inherited_props,
827 apr_pool_t *result_pool,
828 apr_pool_t *scratch_pool)
829 {
830 svn_skel_t *skel = svn_skel__make_empty_list(result_pool);
831
832 /* Create the skel. */
833 if (inherited_props)
834 {
835 int i;
836 apr_hash_index_t *hi;
837
838 for (i = 0; i < inherited_props->nelts; i++)
839 {
840 svn_prop_inherited_item_t *iprop =
841 APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
842
843 svn_skel_t *skel_list = svn_skel__make_empty_list(result_pool);
844 svn_skel_t *skel_atom;
845
846 /* Loop over hash entries */
847 for (hi = apr_hash_first(scratch_pool, iprop->prop_hash);
848 hi;
849 hi = apr_hash_next(hi))
850 {
851 const void *key;
852 void *val;
853 apr_ssize_t klen;
854 svn_string_t *value;
855
856 apr_hash_this(hi, &key, &klen, &val);
857 value = val;
858
859 /* VALUE */
860 svn_skel__prepend(svn_skel__mem_atom(value->data, value->len,
861 result_pool), skel_list);
862
863 /* NAME */
864 svn_skel__prepend(svn_skel__mem_atom(key, klen, result_pool),
865 skel_list);
866 }
867
868 skel_atom = svn_skel__str_atom(
869 apr_pstrdup(result_pool, iprop->path_or_url), result_pool);
870 svn_skel__append(skel, skel_atom);
871 svn_skel__append(skel, skel_list);
872 }
873 }
874
875 /* Validate and return the skel. */
876 if (! is_valid_iproplist_skel(skel))
877 return skel_err("iproplist");
878
879 *skel_p = skel;
880 return SVN_NO_ERROR;
881 }
882