skel.c revision 362181
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 34enum 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 47static 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. */ 89static apr_size_t 90getsize(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. */ 143static svn_error_t * 144skel_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 153static svn_boolean_t 154is_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 172static svn_boolean_t 173is_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 202static svn_skel_t *parse(const char *data, apr_size_t len, 203 apr_pool_t *pool); 204static svn_skel_t *list(const char *data, apr_size_t len, 205 apr_pool_t *pool); 206static svn_skel_t *implicit_atom(const char *data, apr_size_t len, 207 apr_pool_t *pool); 208static svn_skel_t *explicit_atom(const char *data, apr_size_t len, 209 apr_pool_t *pool); 210 211 212svn_skel_t * 213svn_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. */ 222static svn_skel_t * 223parse(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 250static svn_skel_t * 251list(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. */ 326static svn_skel_t * 327implicit_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. */ 359static svn_skel_t * 360explicit_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 399static apr_size_t estimate_unparsed_size(const svn_skel_t *skel); 400static svn_stringbuf_t *unparse(const svn_skel_t *skel, 401 svn_stringbuf_t *str); 402 403 404svn_stringbuf_t * 405svn_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. */ 418static apr_size_t 419estimate_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. */ 449static svn_boolean_t 450use_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. */ 480static svn_stringbuf_t * 481unparse(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 533svn_skel_t * 534svn_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 545svn_skel_t * 546svn_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 559svn_skel_t * 560svn_skel__make_empty_list(apr_pool_t *pool) 561{ 562 svn_skel_t *skel = apr_pcalloc(pool, sizeof(*skel)); 563 return skel; 564} 565 566svn_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 594void 595svn_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 606void 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 617void 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 627void 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 648svn_boolean_t 649svn_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 662int 663svn_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 681svn_error_t * 682svn_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 694svn_error_t * 695svn_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 723svn_error_t * 724svn_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 752svn_error_t * 753svn_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 785svn_error_t * 786svn_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 824svn_error_t * 825svn_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