xref: /netbsd-src/common/lib/libprop/prop_string.c (revision fc5ee90bebfa879be2d1609c5e7b08ecd08db7f5)
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*/
_prop_string_rb_compare_nodes(void * ctx _PROP_ARG_UNUSED,const void * n1,const void * n2)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*/
_prop_string_rb_compare_key(void * ctx _PROP_ARG_UNUSED,const void * n,const void * v)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)
_PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)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
_prop_string_free(prop_stack_t stack,prop_object_t * obj)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
_prop_string_externalize(struct _prop_object_externalize_context * ctx,void * v)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
_prop_string_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)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
_prop_string_alloc(int const flags)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
_prop_string_instantiate(int const flags,const char * const str,size_t const len)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
prop_string_create(void)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
prop_string_create_cstring(const char * str)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
prop_string_create_cstring_nocopy(const char * str)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)
prop_string_create_format(const char * fmt,...)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
prop_string_create_copy(const char * str)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
prop_string_create_nocopy(const char * str)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
prop_string_copy(prop_string_t ops)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
prop_string_copy_mutable(prop_string_t ops)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
prop_string_size(prop_string_t ps)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 *
prop_string_value(prop_string_t ps)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
prop_string_copy_value(prop_string_t ps,void * buf,size_t buflen)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
prop_string_mutable(prop_string_t ps)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 *
prop_string_cstring(prop_string_t ps)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 *
prop_string_cstring_nocopy(prop_string_t ps)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
prop_string_append(prop_string_t dst,prop_string_t src)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
prop_string_append_cstring(prop_string_t dst,const char * src)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
prop_string_equals(prop_string_t str1,prop_string_t str2)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
prop_string_equals_string(prop_string_t ps,const char * cp)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
prop_string_equals_cstring(prop_string_t ps,const char * cp)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
prop_string_compare(prop_string_t ps1,prop_string_t ps2)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
prop_string_compare_string(prop_string_t ps,const char * cp)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
_prop_string_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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