Home | History | Annotate | Line # | Download | only in libprop
prop_string.c revision 1.23
      1 /*	$NetBSD: prop_string.c,v 1.23 2025/05/13 15:00:16 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006, 2020, 2025 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 _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
     60 		    "property string container object")
     61 
     62 static const struct _prop_object_type_tags _prop_string_type_tags = {
     63 	.xml_tag		=	"string",
     64 	.json_open_tag		=	"\"",
     65 	.json_close_tag		=	"\"",
     66 };
     67 
     68 static _prop_object_free_rv_t
     69 		_prop_string_free(prop_stack_t, prop_object_t *);
     70 static bool	_prop_string_externalize(
     71 				struct _prop_object_externalize_context *,
     72 				void *);
     73 static _prop_object_equals_rv_t
     74 		_prop_string_equals(prop_object_t, prop_object_t,
     75 				    void **, void **,
     76 				    prop_object_t *, prop_object_t *);
     77 
     78 static const struct _prop_object_type _prop_object_type_string = {
     79 	.pot_type	=	PROP_TYPE_STRING,
     80 	.pot_free	=	_prop_string_free,
     81 	.pot_extern	=	_prop_string_externalize,
     82 	.pot_equals	=	_prop_string_equals,
     83 };
     84 
     85 #define	prop_object_is_string(x)	\
     86 	((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
     87 #define	prop_string_contents(x)  ((x)->ps_immutable ? (x)->ps_immutable : "")
     88 
     89 /*
     90  * In order to reduce memory usage, all immutable string objects are
     91  * de-duplicated.
     92  */
     93 
     94 static int
     95 /*ARGSUSED*/
     96 _prop_string_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
     97 			      const void *n1, const void *n2)
     98 {
     99 	const struct _prop_string * const ps1 = n1;
    100 	const struct _prop_string * const ps2 = n2;
    101 
    102 	_PROP_ASSERT(ps1->ps_immutable != NULL);
    103 	_PROP_ASSERT(ps2->ps_immutable != NULL);
    104 
    105 	return strcmp(ps1->ps_immutable, ps2->ps_immutable);
    106 }
    107 
    108 static int
    109 /*ARGSUSED*/
    110 _prop_string_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
    111 			    const void *n, const void *v)
    112 {
    113 	const struct _prop_string * const ps = n;
    114 	const char * const cp = v;
    115 
    116 	_PROP_ASSERT(ps->ps_immutable != NULL);
    117 
    118 	return strcmp(ps->ps_immutable, cp);
    119 }
    120 
    121 static const rb_tree_ops_t _prop_string_rb_tree_ops = {
    122 	.rbto_compare_nodes = _prop_string_rb_compare_nodes,
    123 	.rbto_compare_key = _prop_string_rb_compare_key,
    124 	.rbto_node_offset = offsetof(struct _prop_string, ps_link),
    125 	.rbto_context = NULL
    126 };
    127 
    128 static struct rb_tree _prop_string_tree;
    129 
    130 _PROP_ONCE_DECL(_prop_string_init_once)
    131 _PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)
    132 
    133 static int
    134 _prop_string_init(void)
    135 {
    136 
    137 	_PROP_MUTEX_INIT(_prop_string_tree_mutex);
    138 	rb_tree_init(&_prop_string_tree,
    139 		     &_prop_string_rb_tree_ops);
    140 
    141 	return 0;
    142 }
    143 
    144 /* ARGSUSED */
    145 static _prop_object_free_rv_t
    146 _prop_string_free(prop_stack_t stack, prop_object_t *obj)
    147 {
    148 	prop_string_t ps = *obj;
    149 
    150 	if ((ps->ps_flags & PS_F_MUTABLE) == 0) {
    151 		_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
    152 		/*
    153 		 * Double-check the retain count now that we've
    154 		 * acquired the tree lock; holding this lock prevents
    155 		 * new retains from coming in by finding it in the
    156 		 * tree.
    157 		 */
    158 		if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0)
    159 			rb_tree_remove_node(&_prop_string_tree, ps);
    160 		else
    161 			ps = NULL;
    162 		_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    163 
    164 		if (ps == NULL)
    165 			return (_PROP_OBJECT_FREE_DONE);
    166 	}
    167 
    168 	if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
    169 	    	_PROP_FREE(ps->ps_mutable, M_PROP_STRING);
    170 	_PROP_POOL_PUT(_prop_string_pool, ps);
    171 
    172 	return (_PROP_OBJECT_FREE_DONE);
    173 }
    174 
    175 bool
    176 _prop_string_externalize_internal(struct _prop_object_externalize_context *ctx,
    177 				  const struct _prop_object_type_tags *tags,
    178 				  const char *str)
    179 {
    180 	if (_prop_object_externalize_start_tag(ctx, tags, NULL) == false ||
    181 	    _prop_object_externalize_append_encoded_cstring(ctx,
    182 						str) == false ||
    183 	    _prop_object_externalize_end_tag(ctx, tags) == false) {
    184 		return false;
    185 	}
    186 
    187 	return true;
    188 }
    189 
    190 static bool
    191 _prop_string_externalize(struct _prop_object_externalize_context *ctx,
    192 			 void *v)
    193 {
    194 	prop_string_t ps = v;
    195 
    196 	if (ps->ps_size == 0) {
    197 		return _prop_object_externalize_empty_tag(ctx,
    198 		    &_prop_string_type_tags);
    199 	}
    200 
    201 	return _prop_string_externalize_internal(ctx, &_prop_string_type_tags,
    202 	    ps->ps_immutable);
    203 }
    204 
    205 /* ARGSUSED */
    206 static _prop_object_equals_rv_t
    207 _prop_string_equals(prop_object_t v1, prop_object_t v2,
    208     void **stored_pointer1, void **stored_pointer2,
    209     prop_object_t *next_obj1, prop_object_t *next_obj2)
    210 {
    211 	prop_string_t str1 = v1;
    212 	prop_string_t str2 = v2;
    213 
    214 	if (str1 == str2)
    215 		return (_PROP_OBJECT_EQUALS_TRUE);
    216 	if (str1->ps_size != str2->ps_size)
    217 		return (_PROP_OBJECT_EQUALS_FALSE);
    218 	if (strcmp(prop_string_contents(str1), prop_string_contents(str2)))
    219 		return (_PROP_OBJECT_EQUALS_FALSE);
    220 	else
    221 		return (_PROP_OBJECT_EQUALS_TRUE);
    222 }
    223 
    224 static prop_string_t
    225 _prop_string_alloc(int const flags)
    226 {
    227 	prop_string_t ps;
    228 
    229 	ps = _PROP_POOL_GET(_prop_string_pool);
    230 	if (ps != NULL) {
    231 		_prop_object_init(&ps->ps_obj, &_prop_object_type_string);
    232 
    233 		ps->ps_mutable = NULL;
    234 		ps->ps_size = 0;
    235 		ps->ps_flags = flags;
    236 	}
    237 
    238 	return (ps);
    239 }
    240 
    241 static prop_string_t
    242 _prop_string_instantiate(int const flags, const char * const str,
    243     size_t const len)
    244 {
    245 	prop_string_t ps;
    246 
    247 	_PROP_ONCE_RUN(_prop_string_init_once, _prop_string_init);
    248 
    249 	ps = _prop_string_alloc(flags);
    250 	if (ps != NULL) {
    251 		ps->ps_immutable = str;
    252 		ps->ps_size = len;
    253 
    254 		if ((flags & PS_F_MUTABLE) == 0) {
    255 			prop_string_t ops;
    256 
    257 			_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
    258 			ops = rb_tree_insert_node(&_prop_string_tree, ps);
    259 			if (ops != ps) {
    260 				/*
    261 				 * Equivalent string object already exist;
    262 				 * free the new one and return a reference
    263 				 * to the existing object.
    264 				 */
    265 				prop_object_retain(ops);
    266 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    267 				if ((flags & PS_F_NOCOPY) == 0) {
    268 					_PROP_FREE(ps->ps_mutable,
    269 					    M_PROP_STRING);
    270 				}
    271 				_PROP_POOL_PUT(_prop_string_pool, ps);
    272 				ps = ops;
    273 			} else {
    274 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    275 			}
    276 		}
    277 	} else if ((flags & PS_F_NOCOPY) == 0) {
    278 		_PROP_FREE(_PROP_UNCONST(str), M_PROP_STRING);
    279 	}
    280 
    281 	return (ps);
    282 }
    283 
    284 _PROP_DEPRECATED(prop_string_create,
    285     "this program uses prop_string_create(); all functions "
    286     "supporting mutable prop_strings are deprecated.")
    287 _PROP_EXPORT prop_string_t
    288 prop_string_create(void)
    289 {
    290 
    291 	return (_prop_string_alloc(PS_F_MUTABLE));
    292 }
    293 
    294 _PROP_DEPRECATED(prop_string_create_cstring,
    295     "this program uses prop_string_create_cstring(); all functions "
    296     "supporting mutable prop_strings are deprecated.")
    297 _PROP_EXPORT prop_string_t
    298 prop_string_create_cstring(const char *str)
    299 {
    300 	prop_string_t ps;
    301 	char *cp;
    302 	size_t len;
    303 
    304 	_PROP_ASSERT(str != NULL);
    305 
    306 	ps = _prop_string_alloc(PS_F_MUTABLE);
    307 	if (ps != NULL) {
    308 		len = strlen(str);
    309 		cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    310 		if (cp == NULL) {
    311 			prop_object_release(ps);
    312 			return (NULL);
    313 		}
    314 		strcpy(cp, str);
    315 		ps->ps_mutable = cp;
    316 		ps->ps_size = len;
    317 	}
    318 	return (ps);
    319 }
    320 
    321 _PROP_DEPRECATED(prop_string_create_cstring_nocopy,
    322     "this program uses prop_string_create_cstring_nocopy(), "
    323     "which is deprecated; use prop_string_create_nocopy() instead.")
    324 _PROP_EXPORT prop_string_t
    325 prop_string_create_cstring_nocopy(const char *str)
    326 {
    327 	return prop_string_create_nocopy(str);
    328 }
    329 
    330 /*
    331  * prop_string_create_format --
    332  *	Create a string object using the provided format string.
    333  */
    334 _PROP_EXPORT prop_string_t __printflike(1, 2)
    335 prop_string_create_format(const char *fmt, ...)
    336 {
    337 	char *str = NULL;
    338 	int len;
    339 	size_t nlen;
    340 	va_list ap;
    341 
    342 	_PROP_ASSERT(fmt != NULL);
    343 
    344 	va_start(ap, fmt);
    345 	len = vsnprintf(NULL, 0, fmt, ap);
    346 	va_end(ap);
    347 
    348 	if (len < 0)
    349 		return (NULL);
    350 	nlen = len + 1;
    351 
    352 	str = _PROP_MALLOC(nlen, M_PROP_STRING);
    353 	if (str == NULL)
    354 		return (NULL);
    355 
    356 	va_start(ap, fmt);
    357 	vsnprintf(str, nlen, fmt, ap);
    358 	va_end(ap);
    359 
    360 	return _prop_string_instantiate(0, str, (size_t)len);
    361 }
    362 
    363 /*
    364  * prop_string_create_copy --
    365  *	Create a string object by coping the provided constant string.
    366  */
    367 _PROP_EXPORT prop_string_t
    368 prop_string_create_copy(const char *str)
    369 {
    370 	return prop_string_create_format("%s", str);
    371 }
    372 
    373 /*
    374  * prop_string_create_nocopy --
    375  *	Create a string object using the provided external constant
    376  *	string.
    377  */
    378 _PROP_EXPORT prop_string_t
    379 prop_string_create_nocopy(const char *str)
    380 {
    381 
    382 	_PROP_ASSERT(str != NULL);
    383 
    384 	return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str));
    385 }
    386 
    387 /*
    388  * prop_string_copy --
    389  *	Copy a string.  This reduces to a retain in the common case.
    390  *	Deprecated mutable string objects must be copied.
    391  */
    392 _PROP_EXPORT prop_string_t
    393 prop_string_copy(prop_string_t ops)
    394 {
    395 	char *cp;
    396 
    397 	if (! prop_object_is_string(ops))
    398 		return (NULL);
    399 
    400 	if ((ops->ps_flags & PS_F_MUTABLE) == 0) {
    401 		prop_object_retain(ops);
    402 		return (ops);
    403 	}
    404 
    405 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
    406 	if (cp == NULL)
    407 		return NULL;
    408 
    409 	strcpy(cp, prop_string_contents(ops));
    410 
    411 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
    412 }
    413 
    414 _PROP_DEPRECATED(prop_string_copy_mutable,
    415     "this program uses prop_string_copy_mutable(); all functions "
    416     "supporting mutable prop_strings are deprecated.")
    417 _PROP_EXPORT prop_string_t
    418 prop_string_copy_mutable(prop_string_t ops)
    419 {
    420 	char *cp;
    421 
    422 	if (! prop_object_is_string(ops))
    423 		return (NULL);
    424 
    425 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
    426 	if (cp == NULL)
    427 		return NULL;
    428 
    429 	strcpy(cp, prop_string_contents(ops));
    430 
    431 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
    432 }
    433 
    434 /*
    435  * prop_string_size --
    436  *	Return the size of the string, not including the terminating NUL.
    437  */
    438 _PROP_EXPORT size_t
    439 prop_string_size(prop_string_t ps)
    440 {
    441 
    442 	if (! prop_object_is_string(ps))
    443 		return (0);
    444 
    445 	return (ps->ps_size);
    446 }
    447 
    448 /*
    449  * prop_string_value --
    450  *	Returns a pointer to the string object's value.  This pointer
    451  *	remains valid only as long as the string object.
    452  */
    453 _PROP_EXPORT const char *
    454 prop_string_value(prop_string_t ps)
    455 {
    456 
    457 	if (! prop_object_is_string(ps))
    458 		return (NULL);
    459 
    460 	if ((ps->ps_flags & PS_F_MUTABLE) == 0)
    461 		return (ps->ps_immutable);
    462 
    463 	return (prop_string_contents(ps));
    464 }
    465 
    466 /*
    467  * prop_string_copy_value --
    468  *	Copy the string object's value into the supplied buffer.
    469  */
    470 _PROP_EXPORT bool
    471 prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen)
    472 {
    473 
    474 	if (! prop_object_is_string(ps))
    475 		return (false);
    476 
    477 	if (buf == NULL || buflen < ps->ps_size + 1)
    478 		return (false);
    479 
    480 	strcpy(buf, prop_string_contents(ps));
    481 
    482 	return (true);
    483 }
    484 
    485 _PROP_DEPRECATED(prop_string_mutable,
    486     "this program uses prop_string_mutable(); all functions "
    487     "supporting mutable prop_strings are deprecated.")
    488 _PROP_EXPORT bool
    489 prop_string_mutable(prop_string_t ps)
    490 {
    491 
    492 	if (! prop_object_is_string(ps))
    493 		return (false);
    494 
    495 	return ((ps->ps_flags & PS_F_MUTABLE) != 0);
    496 }
    497 
    498 _PROP_DEPRECATED(prop_string_cstring,
    499     "this program uses prop_string_cstring(), "
    500     "which is deprecated; use prop_string_copy_value() instead.")
    501 _PROP_EXPORT char *
    502 prop_string_cstring(prop_string_t ps)
    503 {
    504 	char *cp;
    505 
    506 	if (! prop_object_is_string(ps))
    507 		return (NULL);
    508 
    509 	cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP);
    510 	if (cp != NULL)
    511 		strcpy(cp, prop_string_contents(ps));
    512 
    513 	return (cp);
    514 }
    515 
    516 _PROP_DEPRECATED(prop_string_cstring_nocopy,
    517     "this program uses prop_string_cstring_nocopy(), "
    518     "which is deprecated; use prop_string_value() instead.")
    519 _PROP_EXPORT const char *
    520 prop_string_cstring_nocopy(prop_string_t ps)
    521 {
    522 
    523 	if (! prop_object_is_string(ps))
    524 		return (NULL);
    525 
    526 	return (prop_string_contents(ps));
    527 }
    528 
    529 _PROP_DEPRECATED(prop_string_append,
    530     "this program uses prop_string_append(); all functions "
    531     "supporting mutable prop_strings are deprecated.")
    532 _PROP_EXPORT bool
    533 prop_string_append(prop_string_t dst, prop_string_t src)
    534 {
    535 	char *ocp, *cp;
    536 	size_t len;
    537 
    538 	if (! (prop_object_is_string(dst) &&
    539 	       prop_object_is_string(src)))
    540 		return (false);
    541 
    542 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
    543 		return (false);
    544 
    545 	len = dst->ps_size + src->ps_size;
    546 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    547 	if (cp == NULL)
    548 		return (false);
    549 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst),
    550 		prop_string_contents(src));
    551 	ocp = dst->ps_mutable;
    552 	dst->ps_mutable = cp;
    553 	dst->ps_size = len;
    554 	if (ocp != NULL)
    555 		_PROP_FREE(ocp, M_PROP_STRING);
    556 
    557 	return (true);
    558 }
    559 
    560 _PROP_DEPRECATED(prop_string_append_cstring,
    561     "this program uses prop_string_append_cstring(); all functions "
    562     "supporting mutable prop_strings are deprecated.")
    563 _PROP_EXPORT bool
    564 prop_string_append_cstring(prop_string_t dst, const char *src)
    565 {
    566 	char *ocp, *cp;
    567 	size_t len;
    568 
    569 	if (! prop_object_is_string(dst))
    570 		return (false);
    571 
    572 	_PROP_ASSERT(src != NULL);
    573 
    574 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
    575 		return (false);
    576 
    577 	len = dst->ps_size + strlen(src);
    578 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    579 	if (cp == NULL)
    580 		return (false);
    581 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src);
    582 	ocp = dst->ps_mutable;
    583 	dst->ps_mutable = cp;
    584 	dst->ps_size = len;
    585 	if (ocp != NULL)
    586 		_PROP_FREE(ocp, M_PROP_STRING);
    587 
    588 	return (true);
    589 }
    590 
    591 /*
    592  * prop_string_equals --
    593  *	Return true if two strings are equivalent.
    594  */
    595 _PROP_EXPORT bool
    596 prop_string_equals(prop_string_t str1, prop_string_t str2)
    597 {
    598 	if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
    599 		return (false);
    600 
    601 	return prop_object_equals(str1, str2);
    602 }
    603 
    604 /*
    605  * prop_string_equals_string --
    606  *	Return true if the string object is equivalent to the specified
    607  *	C string.
    608  */
    609 _PROP_EXPORT bool
    610 prop_string_equals_string(prop_string_t ps, const char *cp)
    611 {
    612 
    613 	if (! prop_object_is_string(ps))
    614 		return (false);
    615 
    616 	return (strcmp(prop_string_contents(ps), cp) == 0);
    617 }
    618 
    619 _PROP_DEPRECATED(prop_string_equals_cstring,
    620     "this program uses prop_string_equals_cstring(), "
    621     "which is deprecated; prop_string_equals_string() instead.")
    622 _PROP_EXPORT bool
    623 prop_string_equals_cstring(prop_string_t ps, const char *cp)
    624 {
    625 	return prop_string_equals_string(ps, cp);
    626 }
    627 
    628 /*
    629  * prop_string_compare --
    630  *	Compare two string objects, using strcmp() semantics.
    631  */
    632 _PROP_EXPORT int
    633 prop_string_compare(prop_string_t ps1, prop_string_t ps2)
    634 {
    635 	if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2))
    636 		return (-666);	/* arbitrary */
    637 
    638 	return (strcmp(prop_string_contents(ps1),
    639 		       prop_string_contents(ps2)));
    640 }
    641 
    642 /*
    643  * prop_string_compare_string --
    644  *	Compare a string object to the specified C string, using
    645  *	strcmp() semantics.
    646  */
    647 _PROP_EXPORT int
    648 prop_string_compare_string(prop_string_t ps, const char *cp)
    649 {
    650 	if (!prop_object_is_string(ps))
    651 		return (-666);	/* arbitrary */
    652 
    653 	return (strcmp(prop_string_contents(ps), cp));
    654 }
    655 
    656 /*
    657  * _prop_string_internalize --
    658  *	Parse a <string>...</string> and return the object created from the
    659  *	external representation.
    660  */
    661 /* ARGSUSED */
    662 bool
    663 _prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
    664     struct _prop_object_internalize_context *ctx)
    665 {
    666 	char *str;
    667 	size_t len, alen;
    668 
    669 	/*
    670 	 * N.B. for empty JSON strings, the layer above us has made it
    671 	 * look like XML.
    672 	 */
    673 	if (ctx->poic_is_empty_element) {
    674 		*obj = prop_string_create();
    675 		return (true);
    676 	}
    677 
    678 	/* No attributes recognized here. */
    679 	if (ctx->poic_tagattr != NULL)
    680 		return (true);
    681 
    682 	/* Compute the length of the result. */
    683 	if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
    684 						   NULL) == false)
    685 		return (true);
    686 
    687 	str = _PROP_MALLOC(len + 1, M_PROP_STRING);
    688 	if (str == NULL)
    689 		return (true);
    690 
    691 	if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
    692 						   &ctx->poic_cp) == false ||
    693 	    alen != len) {
    694 		_PROP_FREE(str, M_PROP_STRING);
    695 		return (true);
    696 	}
    697 	str[len] = '\0';
    698 
    699 	if (ctx->poic_format == PROP_FORMAT_JSON) {
    700 		if (*ctx->poic_cp != '"') {
    701 			_PROP_FREE(str, M_PROP_STRING);
    702 			return (true);
    703 		}
    704 		ctx->poic_cp++;
    705 	} else {
    706 		if (_prop_object_internalize_find_tag(ctx, "string",
    707 					      _PROP_TAG_TYPE_END) == false) {
    708 			_PROP_FREE(str, M_PROP_STRING);
    709 			return (true);
    710 		}
    711 	}
    712 
    713 	*obj = _prop_string_instantiate(0, str, len);
    714 	return (true);
    715 }
    716