1 /* $NetBSD: prop_string.c,v 1.16 2021/09/07 13:24:45 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "prop_object_impl.h" 33 #include <prop/prop_string.h> 34 35 #include <sys/rbtree.h> 36 #if defined(_KERNEL) || defined(_STANDALONE) 37 #include <sys/stdarg.h> 38 #else 39 #include <stdarg.h> 40 #endif /* _KERNEL || _STANDALONE */ 41 42 struct _prop_string { 43 struct _prop_object ps_obj; 44 union { 45 char * psu_mutable; 46 const char * psu_immutable; 47 } ps_un; 48 #define ps_mutable ps_un.psu_mutable 49 #define ps_immutable ps_un.psu_immutable 50 size_t ps_size; /* not including \0 */ 51 struct rb_node ps_link; 52 int ps_flags; 53 }; 54 55 #define PS_F_NOCOPY 0x01 56 #define PS_F_MUTABLE 0x02 57 58 _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng") 59 60 _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string", 61 "property string container object") 62 63 static _prop_object_free_rv_t 64 _prop_string_free(prop_stack_t, prop_object_t *); 65 static bool _prop_string_externalize( 66 struct _prop_object_externalize_context *, 67 void *); 68 static _prop_object_equals_rv_t 69 _prop_string_equals(prop_object_t, prop_object_t, 70 void **, void **, 71 prop_object_t *, prop_object_t *); 72 73 static const struct _prop_object_type _prop_object_type_string = { 74 .pot_type = PROP_TYPE_STRING, 75 .pot_free = _prop_string_free, 76 .pot_extern = _prop_string_externalize, 77 .pot_equals = _prop_string_equals, 78 }; 79 80 #define prop_object_is_string(x) \ 81 ((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string) 82 #define prop_string_contents(x) ((x)->ps_immutable ? (x)->ps_immutable : "") 83 84 /* 85 * In order to reduce memory usage, all immutable string objects are 86 * de-duplicated. 87 */ 88 89 static int 90 /*ARGSUSED*/ 91 _prop_string_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED, 92 const void *n1, const void *n2) 93 { 94 const struct _prop_string * const ps1 = n1; 95 const struct _prop_string * const ps2 = n2; 96 97 _PROP_ASSERT(ps1->ps_immutable != NULL); 98 _PROP_ASSERT(ps2->ps_immutable != NULL); 99 100 return strcmp(ps1->ps_immutable, ps2->ps_immutable); 101 } 102 103 static int 104 /*ARGSUSED*/ 105 _prop_string_rb_compare_key(void *ctx _PROP_ARG_UNUSED, 106 const void *n, const void *v) 107 { 108 const struct _prop_string * const ps = n; 109 const char * const cp = v; 110 111 _PROP_ASSERT(ps->ps_immutable != NULL); 112 113 return strcmp(ps->ps_immutable, cp); 114 } 115 116 static const rb_tree_ops_t _prop_string_rb_tree_ops = { 117 .rbto_compare_nodes = _prop_string_rb_compare_nodes, 118 .rbto_compare_key = _prop_string_rb_compare_key, 119 .rbto_node_offset = offsetof(struct _prop_string, ps_link), 120 .rbto_context = NULL 121 }; 122 123 static struct rb_tree _prop_string_tree; 124 125 _PROP_ONCE_DECL(_prop_string_init_once) 126 _PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex) 127 128 static int 129 _prop_string_init(void) 130 { 131 132 _PROP_MUTEX_INIT(_prop_string_tree_mutex); 133 rb_tree_init(&_prop_string_tree, 134 &_prop_string_rb_tree_ops); 135 136 return 0; 137 } 138 139 /* ARGSUSED */ 140 static _prop_object_free_rv_t 141 _prop_string_free(prop_stack_t stack, prop_object_t *obj) 142 { 143 prop_string_t ps = *obj; 144 145 if ((ps->ps_flags & PS_F_MUTABLE) == 0) { 146 _PROP_MUTEX_LOCK(_prop_string_tree_mutex); 147 /* 148 * Double-check the retain count now that we've 149 * acquired the tree lock; holding this lock prevents 150 * new retains from coming in by finding it in the 151 * tree. 152 */ 153 if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0) 154 rb_tree_remove_node(&_prop_string_tree, ps); 155 else 156 ps = NULL; 157 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex); 158 159 if (ps == NULL) 160 return (_PROP_OBJECT_FREE_DONE); 161 } 162 163 if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL) 164 _PROP_FREE(ps->ps_mutable, M_PROP_STRING); 165 _PROP_POOL_PUT(_prop_string_pool, ps); 166 167 return (_PROP_OBJECT_FREE_DONE); 168 } 169 170 static bool 171 _prop_string_externalize(struct _prop_object_externalize_context *ctx, 172 void *v) 173 { 174 prop_string_t ps = v; 175 176 if (ps->ps_size == 0) 177 return (_prop_object_externalize_empty_tag(ctx, "string")); 178 179 if (_prop_object_externalize_start_tag(ctx, "string") == false || 180 _prop_object_externalize_append_encoded_cstring(ctx, 181 ps->ps_immutable) == false || 182 _prop_object_externalize_end_tag(ctx, "string") == false) 183 return (false); 184 185 return (true); 186 } 187 188 /* ARGSUSED */ 189 static _prop_object_equals_rv_t 190 _prop_string_equals(prop_object_t v1, prop_object_t v2, 191 void **stored_pointer1, void **stored_pointer2, 192 prop_object_t *next_obj1, prop_object_t *next_obj2) 193 { 194 prop_string_t str1 = v1; 195 prop_string_t str2 = v2; 196 197 if (str1 == str2) 198 return (_PROP_OBJECT_EQUALS_TRUE); 199 if (str1->ps_size != str2->ps_size) 200 return (_PROP_OBJECT_EQUALS_FALSE); 201 if (strcmp(prop_string_contents(str1), prop_string_contents(str2))) 202 return (_PROP_OBJECT_EQUALS_FALSE); 203 else 204 return (_PROP_OBJECT_EQUALS_TRUE); 205 } 206 207 static prop_string_t 208 _prop_string_alloc(int const flags) 209 { 210 prop_string_t ps; 211 212 ps = _PROP_POOL_GET(_prop_string_pool); 213 if (ps != NULL) { 214 _prop_object_init(&ps->ps_obj, &_prop_object_type_string); 215 216 ps->ps_mutable = NULL; 217 ps->ps_size = 0; 218 ps->ps_flags = flags; 219 } 220 221 return (ps); 222 } 223 224 static prop_string_t 225 _prop_string_instantiate(int const flags, const char * const str, 226 size_t const len) 227 { 228 prop_string_t ps; 229 230 _PROP_ONCE_RUN(_prop_string_init_once, _prop_string_init); 231 232 ps = _prop_string_alloc(flags); 233 if (ps != NULL) { 234 ps->ps_immutable = str; 235 ps->ps_size = len; 236 237 if ((flags & PS_F_MUTABLE) == 0) { 238 prop_string_t ops; 239 240 _PROP_MUTEX_LOCK(_prop_string_tree_mutex); 241 ops = rb_tree_insert_node(&_prop_string_tree, ps); 242 if (ops != ps) { 243 /* 244 * Equivalent string object already exist; 245 * free the new one and return a reference 246 * to the existing object. 247 */ 248 prop_object_retain(ops); 249 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex); 250 _PROP_POOL_PUT(_prop_string_pool, ps); 251 ps = ops; 252 } else { 253 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex); 254 } 255 } 256 } 257 258 return (ps); 259 } 260 261 _PROP_DEPRECATED(prop_string_create, 262 "this program uses prop_string_create(); all functions " 263 "supporting mutable prop_strings are deprecated.") 264 prop_string_t 265 prop_string_create(void) 266 { 267 268 return (_prop_string_alloc(PS_F_MUTABLE)); 269 } 270 271 _PROP_DEPRECATED(prop_string_create_cstring, 272 "this program uses prop_string_create_cstring(); all functions " 273 "supporting mutable prop_strings are deprecated.") 274 prop_string_t 275 prop_string_create_cstring(const char *str) 276 { 277 prop_string_t ps; 278 char *cp; 279 size_t len; 280 281 _PROP_ASSERT(str != NULL); 282 283 ps = _prop_string_alloc(PS_F_MUTABLE); 284 if (ps != NULL) { 285 len = strlen(str); 286 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 287 if (cp == NULL) { 288 prop_object_release(ps); 289 return (NULL); 290 } 291 strcpy(cp, str); 292 ps->ps_mutable = cp; 293 ps->ps_size = len; 294 } 295 return (ps); 296 } 297 298 _PROP_DEPRECATED(prop_string_create_cstring_nocopy, 299 "this program uses prop_string_create_cstring_nocopy(), " 300 "which is deprecated; use prop_string_create_nocopy() instead.") 301 prop_string_t 302 prop_string_create_cstring_nocopy(const char *str) 303 { 304 return prop_string_create_nocopy(str); 305 } 306 307 /* 308 * prop_string_create_format -- 309 * Create a string object using the provided format string. 310 */ 311 prop_string_t __printflike(1, 2) 312 prop_string_create_format(const char *fmt, ...) 313 { 314 prop_string_t ps; 315 char *str = NULL; 316 int len; 317 size_t nlen; 318 va_list ap; 319 320 _PROP_ASSERT(fmt != NULL); 321 322 va_start(ap, fmt); 323 len = vsnprintf(NULL, 0, fmt, ap); 324 va_end(ap); 325 326 if (len < 0) 327 return (NULL); 328 nlen = len + 1; 329 330 str = _PROP_MALLOC(nlen, M_PROP_STRING); 331 if (str == NULL) 332 return (NULL); 333 334 va_start(ap, fmt); 335 vsnprintf(str, nlen, fmt, ap); 336 va_end(ap); 337 338 ps = _prop_string_instantiate(0, str, (size_t)len); 339 if (ps == NULL) 340 _PROP_FREE(str, M_PROP_STRING); 341 342 return (ps); 343 } 344 345 /* 346 * prop_string_create_copy -- 347 * Create a string object by coping the provided constant string. 348 */ 349 prop_string_t 350 prop_string_create_copy(const char *str) 351 { 352 return prop_string_create_format("%s", str); 353 } 354 355 /* 356 * prop_string_create_nocopy -- 357 * Create a string object using the provided external constant 358 * string. 359 */ 360 prop_string_t 361 prop_string_create_nocopy(const char *str) 362 { 363 364 _PROP_ASSERT(str != NULL); 365 366 return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str)); 367 } 368 369 /* 370 * prop_string_copy -- 371 * Copy a string. This reduces to a retain in the common case. 372 * Deprecated mutable string objects must be copied. 373 */ 374 prop_string_t 375 prop_string_copy(prop_string_t ops) 376 { 377 prop_string_t ps; 378 char *cp; 379 380 if (! prop_object_is_string(ops)) 381 return (NULL); 382 383 if ((ops->ps_flags & PS_F_MUTABLE) == 0) { 384 prop_object_retain(ops); 385 return (ops); 386 } 387 388 cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING); 389 if (cp == NULL) 390 return NULL; 391 392 strcpy(cp, prop_string_contents(ops)); 393 394 ps = _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size); 395 if (ps == NULL) 396 _PROP_FREE(cp, M_PROP_STRING); 397 398 return (ps); 399 } 400 401 _PROP_DEPRECATED(prop_string_copy_mutable, 402 "this program uses prop_string_copy_mutable(); all functions " 403 "supporting mutable prop_strings are deprecated.") 404 prop_string_t 405 prop_string_copy_mutable(prop_string_t ops) 406 { 407 prop_string_t ps; 408 char *cp; 409 410 if (! prop_object_is_string(ops)) 411 return (NULL); 412 413 cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING); 414 if (cp == NULL) 415 return NULL; 416 417 strcpy(cp, prop_string_contents(ops)); 418 419 ps = _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size); 420 if (ps == NULL) 421 _PROP_FREE(cp, M_PROP_STRING); 422 423 return (ps); 424 } 425 426 /* 427 * prop_string_size -- 428 * Return the size of the string, not including the terminating NUL. 429 */ 430 size_t 431 prop_string_size(prop_string_t ps) 432 { 433 434 if (! prop_object_is_string(ps)) 435 return (0); 436 437 return (ps->ps_size); 438 } 439 440 /* 441 * prop_string_value -- 442 * Returns a pointer to the string object's value. This pointer 443 * remains valid only as long as the string object. 444 */ 445 const char * 446 prop_string_value(prop_string_t ps) 447 { 448 449 if (! prop_object_is_string(ps)) 450 return (NULL); 451 452 if ((ps->ps_flags & PS_F_MUTABLE) == 0) 453 return (ps->ps_immutable); 454 455 return (prop_string_contents(ps)); 456 } 457 458 /* 459 * prop_string_copy_value -- 460 * Copy the string object's value into the supplied buffer. 461 */ 462 bool 463 prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen) 464 { 465 466 if (! prop_object_is_string(ps)) 467 return (false); 468 469 if (buf == NULL || buflen < ps->ps_size + 1) 470 return (false); 471 472 strcpy(buf, prop_string_contents(ps)); 473 474 return (true); 475 } 476 477 _PROP_DEPRECATED(prop_string_mutable, 478 "this program uses prop_string_mutable(); all functions " 479 "supporting mutable prop_strings are deprecated.") 480 bool 481 prop_string_mutable(prop_string_t ps) 482 { 483 484 if (! prop_object_is_string(ps)) 485 return (false); 486 487 return ((ps->ps_flags & PS_F_MUTABLE) != 0); 488 } 489 490 _PROP_DEPRECATED(prop_string_cstring, 491 "this program uses prop_string_cstring(), " 492 "which is deprecated; use prop_string_copy_value() instead.") 493 char * 494 prop_string_cstring(prop_string_t ps) 495 { 496 char *cp; 497 498 if (! prop_object_is_string(ps)) 499 return (NULL); 500 501 cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP); 502 if (cp != NULL) 503 strcpy(cp, prop_string_contents(ps)); 504 505 return (cp); 506 } 507 508 _PROP_DEPRECATED(prop_string_cstring_nocopy, 509 "this program uses prop_string_cstring_nocopy(), " 510 "which is deprecated; use prop_string_value() instead.") 511 const char * 512 prop_string_cstring_nocopy(prop_string_t ps) 513 { 514 515 if (! prop_object_is_string(ps)) 516 return (NULL); 517 518 return (prop_string_contents(ps)); 519 } 520 521 _PROP_DEPRECATED(prop_string_append, 522 "this program uses prop_string_append(); all functions " 523 "supporting mutable prop_strings are deprecated.") 524 bool 525 prop_string_append(prop_string_t dst, prop_string_t src) 526 { 527 char *ocp, *cp; 528 size_t len; 529 530 if (! (prop_object_is_string(dst) && 531 prop_object_is_string(src))) 532 return (false); 533 534 if ((dst->ps_flags & PS_F_MUTABLE) == 0) 535 return (false); 536 537 len = dst->ps_size + src->ps_size; 538 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 539 if (cp == NULL) 540 return (false); 541 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), 542 prop_string_contents(src)); 543 ocp = dst->ps_mutable; 544 dst->ps_mutable = cp; 545 dst->ps_size = len; 546 if (ocp != NULL) 547 _PROP_FREE(ocp, M_PROP_STRING); 548 549 return (true); 550 } 551 552 _PROP_DEPRECATED(prop_string_append_cstring, 553 "this program uses prop_string_append_cstring(); all functions " 554 "supporting mutable prop_strings are deprecated.") 555 bool 556 prop_string_append_cstring(prop_string_t dst, const char *src) 557 { 558 char *ocp, *cp; 559 size_t len; 560 561 if (! prop_object_is_string(dst)) 562 return (false); 563 564 _PROP_ASSERT(src != NULL); 565 566 if ((dst->ps_flags & PS_F_MUTABLE) == 0) 567 return (false); 568 569 len = dst->ps_size + strlen(src); 570 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 571 if (cp == NULL) 572 return (false); 573 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src); 574 ocp = dst->ps_mutable; 575 dst->ps_mutable = cp; 576 dst->ps_size = len; 577 if (ocp != NULL) 578 _PROP_FREE(ocp, M_PROP_STRING); 579 580 return (true); 581 } 582 583 /* 584 * prop_string_equals -- 585 * Return true if two strings are equivalent. 586 */ 587 bool 588 prop_string_equals(prop_string_t str1, prop_string_t str2) 589 { 590 if (!prop_object_is_string(str1) || !prop_object_is_string(str2)) 591 return (false); 592 593 return prop_object_equals(str1, str2); 594 } 595 596 /* 597 * prop_string_equals_string -- 598 * Return true if the string object is equivalent to the specified 599 * C string. 600 */ 601 bool 602 prop_string_equals_string(prop_string_t ps, const char *cp) 603 { 604 605 if (! prop_object_is_string(ps)) 606 return (false); 607 608 return (strcmp(prop_string_contents(ps), cp) == 0); 609 } 610 611 _PROP_DEPRECATED(prop_string_equals_cstring, 612 "this program uses prop_string_equals_cstring(), " 613 "which is deprecated; prop_string_equals_string() instead.") 614 bool 615 prop_string_equals_cstring(prop_string_t ps, const char *cp) 616 { 617 return prop_string_equals_string(ps, cp); 618 } 619 620 /* 621 * prop_string_compare -- 622 * Compare two string objects, using strcmp() semantics. 623 */ 624 int 625 prop_string_compare(prop_string_t ps1, prop_string_t ps2) 626 { 627 if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2)) 628 return (-666); /* arbitrary */ 629 630 return (strcmp(prop_string_contents(ps1), 631 prop_string_contents(ps2))); 632 } 633 634 /* 635 * prop_string_compare_string -- 636 * Compare a string object to the specified C string, using 637 * strcmp() semantics. 638 */ 639 int 640 prop_string_compare_string(prop_string_t ps, const char *cp) 641 { 642 if (!prop_object_is_string(ps)) 643 return (-666); /* arbitrary */ 644 645 return (strcmp(prop_string_contents(ps), cp)); 646 } 647 648 /* 649 * _prop_string_internalize -- 650 * Parse a <string>...</string> and return the object created from the 651 * external representation. 652 */ 653 /* ARGSUSED */ 654 bool 655 _prop_string_internalize(prop_stack_t stack, prop_object_t *obj, 656 struct _prop_object_internalize_context *ctx) 657 { 658 prop_string_t string; 659 char *str; 660 size_t len, alen; 661 662 if (ctx->poic_is_empty_element) { 663 *obj = prop_string_create(); 664 return (true); 665 } 666 667 /* No attributes recognized here. */ 668 if (ctx->poic_tagattr != NULL) 669 return (true); 670 671 /* Compute the length of the result. */ 672 if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len, 673 NULL) == false) 674 return (true); 675 676 str = _PROP_MALLOC(len + 1, M_PROP_STRING); 677 if (str == NULL) 678 return (true); 679 680 if (_prop_object_internalize_decode_string(ctx, str, len, &alen, 681 &ctx->poic_cp) == false || 682 alen != len) { 683 _PROP_FREE(str, M_PROP_STRING); 684 return (true); 685 } 686 str[len] = '\0'; 687 688 if (_prop_object_internalize_find_tag(ctx, "string", 689 _PROP_TAG_TYPE_END) == false) { 690 _PROP_FREE(str, M_PROP_STRING); 691 return (true); 692 } 693 694 string = _prop_string_instantiate(0, str, len); 695 if (string == NULL) 696 _PROP_FREE(str, M_PROP_STRING); 697 698 *obj = string; 699 return (true); 700 } 701