Home | History | Annotate | Line # | Download | only in libprop
prop_string.c revision 1.15
      1 /*	$NetBSD: prop_string.c,v 1.15 2020/06/20 00:16:50 christos 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 		 * acqured 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