1 /* $NetBSD: prop_string.c,v 1.18 2023/11/17 21:29:33 thorpej 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 if ((flags & PS_F_NOCOPY) == 0) { 251 _PROP_FREE(ps->ps_mutable, 252 M_PROP_STRING); 253 } 254 _PROP_POOL_PUT(_prop_string_pool, ps); 255 ps = ops; 256 } else { 257 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex); 258 } 259 } 260 } else if ((flags & PS_F_NOCOPY) == 0) { 261 _PROP_FREE(__UNCONST(str), M_PROP_STRING); 262 } 263 264 return (ps); 265 } 266 267 _PROP_DEPRECATED(prop_string_create, 268 "this program uses prop_string_create(); all functions " 269 "supporting mutable prop_strings are deprecated.") 270 prop_string_t 271 prop_string_create(void) 272 { 273 274 return (_prop_string_alloc(PS_F_MUTABLE)); 275 } 276 277 _PROP_DEPRECATED(prop_string_create_cstring, 278 "this program uses prop_string_create_cstring(); all functions " 279 "supporting mutable prop_strings are deprecated.") 280 prop_string_t 281 prop_string_create_cstring(const char *str) 282 { 283 prop_string_t ps; 284 char *cp; 285 size_t len; 286 287 _PROP_ASSERT(str != NULL); 288 289 ps = _prop_string_alloc(PS_F_MUTABLE); 290 if (ps != NULL) { 291 len = strlen(str); 292 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 293 if (cp == NULL) { 294 prop_object_release(ps); 295 return (NULL); 296 } 297 strcpy(cp, str); 298 ps->ps_mutable = cp; 299 ps->ps_size = len; 300 } 301 return (ps); 302 } 303 304 _PROP_DEPRECATED(prop_string_create_cstring_nocopy, 305 "this program uses prop_string_create_cstring_nocopy(), " 306 "which is deprecated; use prop_string_create_nocopy() instead.") 307 prop_string_t 308 prop_string_create_cstring_nocopy(const char *str) 309 { 310 return prop_string_create_nocopy(str); 311 } 312 313 /* 314 * prop_string_create_format -- 315 * Create a string object using the provided format string. 316 */ 317 prop_string_t __printflike(1, 2) 318 prop_string_create_format(const char *fmt, ...) 319 { 320 char *str = NULL; 321 int len; 322 size_t nlen; 323 va_list ap; 324 325 _PROP_ASSERT(fmt != NULL); 326 327 va_start(ap, fmt); 328 len = vsnprintf(NULL, 0, fmt, ap); 329 va_end(ap); 330 331 if (len < 0) 332 return (NULL); 333 nlen = len + 1; 334 335 str = _PROP_MALLOC(nlen, M_PROP_STRING); 336 if (str == NULL) 337 return (NULL); 338 339 va_start(ap, fmt); 340 vsnprintf(str, nlen, fmt, ap); 341 va_end(ap); 342 343 return _prop_string_instantiate(0, str, (size_t)len); 344 } 345 346 /* 347 * prop_string_create_copy -- 348 * Create a string object by coping the provided constant string. 349 */ 350 prop_string_t 351 prop_string_create_copy(const char *str) 352 { 353 return prop_string_create_format("%s", str); 354 } 355 356 /* 357 * prop_string_create_nocopy -- 358 * Create a string object using the provided external constant 359 * string. 360 */ 361 prop_string_t 362 prop_string_create_nocopy(const char *str) 363 { 364 365 _PROP_ASSERT(str != NULL); 366 367 return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str)); 368 } 369 370 /* 371 * prop_string_copy -- 372 * Copy a string. This reduces to a retain in the common case. 373 * Deprecated mutable string objects must be copied. 374 */ 375 prop_string_t 376 prop_string_copy(prop_string_t ops) 377 { 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 return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size); 395 } 396 397 _PROP_DEPRECATED(prop_string_copy_mutable, 398 "this program uses prop_string_copy_mutable(); all functions " 399 "supporting mutable prop_strings are deprecated.") 400 prop_string_t 401 prop_string_copy_mutable(prop_string_t ops) 402 { 403 char *cp; 404 405 if (! prop_object_is_string(ops)) 406 return (NULL); 407 408 cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING); 409 if (cp == NULL) 410 return NULL; 411 412 strcpy(cp, prop_string_contents(ops)); 413 414 return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size); 415 } 416 417 /* 418 * prop_string_size -- 419 * Return the size of the string, not including the terminating NUL. 420 */ 421 size_t 422 prop_string_size(prop_string_t ps) 423 { 424 425 if (! prop_object_is_string(ps)) 426 return (0); 427 428 return (ps->ps_size); 429 } 430 431 /* 432 * prop_string_value -- 433 * Returns a pointer to the string object's value. This pointer 434 * remains valid only as long as the string object. 435 */ 436 const char * 437 prop_string_value(prop_string_t ps) 438 { 439 440 if (! prop_object_is_string(ps)) 441 return (NULL); 442 443 if ((ps->ps_flags & PS_F_MUTABLE) == 0) 444 return (ps->ps_immutable); 445 446 return (prop_string_contents(ps)); 447 } 448 449 /* 450 * prop_string_copy_value -- 451 * Copy the string object's value into the supplied buffer. 452 */ 453 bool 454 prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen) 455 { 456 457 if (! prop_object_is_string(ps)) 458 return (false); 459 460 if (buf == NULL || buflen < ps->ps_size + 1) 461 return (false); 462 463 strcpy(buf, prop_string_contents(ps)); 464 465 return (true); 466 } 467 468 _PROP_DEPRECATED(prop_string_mutable, 469 "this program uses prop_string_mutable(); all functions " 470 "supporting mutable prop_strings are deprecated.") 471 bool 472 prop_string_mutable(prop_string_t ps) 473 { 474 475 if (! prop_object_is_string(ps)) 476 return (false); 477 478 return ((ps->ps_flags & PS_F_MUTABLE) != 0); 479 } 480 481 _PROP_DEPRECATED(prop_string_cstring, 482 "this program uses prop_string_cstring(), " 483 "which is deprecated; use prop_string_copy_value() instead.") 484 char * 485 prop_string_cstring(prop_string_t ps) 486 { 487 char *cp; 488 489 if (! prop_object_is_string(ps)) 490 return (NULL); 491 492 cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP); 493 if (cp != NULL) 494 strcpy(cp, prop_string_contents(ps)); 495 496 return (cp); 497 } 498 499 _PROP_DEPRECATED(prop_string_cstring_nocopy, 500 "this program uses prop_string_cstring_nocopy(), " 501 "which is deprecated; use prop_string_value() instead.") 502 const char * 503 prop_string_cstring_nocopy(prop_string_t ps) 504 { 505 506 if (! prop_object_is_string(ps)) 507 return (NULL); 508 509 return (prop_string_contents(ps)); 510 } 511 512 _PROP_DEPRECATED(prop_string_append, 513 "this program uses prop_string_append(); all functions " 514 "supporting mutable prop_strings are deprecated.") 515 bool 516 prop_string_append(prop_string_t dst, prop_string_t src) 517 { 518 char *ocp, *cp; 519 size_t len; 520 521 if (! (prop_object_is_string(dst) && 522 prop_object_is_string(src))) 523 return (false); 524 525 if ((dst->ps_flags & PS_F_MUTABLE) == 0) 526 return (false); 527 528 len = dst->ps_size + src->ps_size; 529 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 530 if (cp == NULL) 531 return (false); 532 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), 533 prop_string_contents(src)); 534 ocp = dst->ps_mutable; 535 dst->ps_mutable = cp; 536 dst->ps_size = len; 537 if (ocp != NULL) 538 _PROP_FREE(ocp, M_PROP_STRING); 539 540 return (true); 541 } 542 543 _PROP_DEPRECATED(prop_string_append_cstring, 544 "this program uses prop_string_append_cstring(); all functions " 545 "supporting mutable prop_strings are deprecated.") 546 bool 547 prop_string_append_cstring(prop_string_t dst, const char *src) 548 { 549 char *ocp, *cp; 550 size_t len; 551 552 if (! prop_object_is_string(dst)) 553 return (false); 554 555 _PROP_ASSERT(src != NULL); 556 557 if ((dst->ps_flags & PS_F_MUTABLE) == 0) 558 return (false); 559 560 len = dst->ps_size + strlen(src); 561 cp = _PROP_MALLOC(len + 1, M_PROP_STRING); 562 if (cp == NULL) 563 return (false); 564 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src); 565 ocp = dst->ps_mutable; 566 dst->ps_mutable = cp; 567 dst->ps_size = len; 568 if (ocp != NULL) 569 _PROP_FREE(ocp, M_PROP_STRING); 570 571 return (true); 572 } 573 574 /* 575 * prop_string_equals -- 576 * Return true if two strings are equivalent. 577 */ 578 bool 579 prop_string_equals(prop_string_t str1, prop_string_t str2) 580 { 581 if (!prop_object_is_string(str1) || !prop_object_is_string(str2)) 582 return (false); 583 584 return prop_object_equals(str1, str2); 585 } 586 587 /* 588 * prop_string_equals_string -- 589 * Return true if the string object is equivalent to the specified 590 * C string. 591 */ 592 bool 593 prop_string_equals_string(prop_string_t ps, const char *cp) 594 { 595 596 if (! prop_object_is_string(ps)) 597 return (false); 598 599 return (strcmp(prop_string_contents(ps), cp) == 0); 600 } 601 602 _PROP_DEPRECATED(prop_string_equals_cstring, 603 "this program uses prop_string_equals_cstring(), " 604 "which is deprecated; prop_string_equals_string() instead.") 605 bool 606 prop_string_equals_cstring(prop_string_t ps, const char *cp) 607 { 608 return prop_string_equals_string(ps, cp); 609 } 610 611 /* 612 * prop_string_compare -- 613 * Compare two string objects, using strcmp() semantics. 614 */ 615 int 616 prop_string_compare(prop_string_t ps1, prop_string_t ps2) 617 { 618 if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2)) 619 return (-666); /* arbitrary */ 620 621 return (strcmp(prop_string_contents(ps1), 622 prop_string_contents(ps2))); 623 } 624 625 /* 626 * prop_string_compare_string -- 627 * Compare a string object to the specified C string, using 628 * strcmp() semantics. 629 */ 630 int 631 prop_string_compare_string(prop_string_t ps, const char *cp) 632 { 633 if (!prop_object_is_string(ps)) 634 return (-666); /* arbitrary */ 635 636 return (strcmp(prop_string_contents(ps), cp)); 637 } 638 639 /* 640 * _prop_string_internalize -- 641 * Parse a <string>...</string> and return the object created from the 642 * external representation. 643 */ 644 /* ARGSUSED */ 645 bool 646 _prop_string_internalize(prop_stack_t stack, prop_object_t *obj, 647 struct _prop_object_internalize_context *ctx) 648 { 649 char *str; 650 size_t len, alen; 651 652 if (ctx->poic_is_empty_element) { 653 *obj = prop_string_create(); 654 return (true); 655 } 656 657 /* No attributes recognized here. */ 658 if (ctx->poic_tagattr != NULL) 659 return (true); 660 661 /* Compute the length of the result. */ 662 if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len, 663 NULL) == false) 664 return (true); 665 666 str = _PROP_MALLOC(len + 1, M_PROP_STRING); 667 if (str == NULL) 668 return (true); 669 670 if (_prop_object_internalize_decode_string(ctx, str, len, &alen, 671 &ctx->poic_cp) == false || 672 alen != len) { 673 _PROP_FREE(str, M_PROP_STRING); 674 return (true); 675 } 676 str[len] = '\0'; 677 678 if (_prop_object_internalize_find_tag(ctx, "string", 679 _PROP_TAG_TYPE_END) == false) { 680 _PROP_FREE(str, M_PROP_STRING); 681 return (true); 682 } 683 684 *obj = _prop_string_instantiate(0, str, len); 685 return (true); 686 } 687