xref: /netbsd/common/lib/libprop/prop_string.c (revision f7c0aabf)
1 /*	$NetBSD: prop_string.c,v 1.17 2022/08/03 21:13:46 riastradh 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 				_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
prop_string_create(void)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
prop_string_create_cstring(const char * str)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
prop_string_create_cstring_nocopy(const char * str)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)
prop_string_create_format(const char * fmt,...)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
prop_string_create_copy(const char * str)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
prop_string_create_nocopy(const char * str)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
prop_string_copy(prop_string_t ops)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
prop_string_copy_mutable(prop_string_t ops)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
prop_string_size(prop_string_t ps)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 *
prop_string_value(prop_string_t ps)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
prop_string_copy_value(prop_string_t ps,void * buf,size_t buflen)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
prop_string_mutable(prop_string_t ps)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 *
prop_string_cstring(prop_string_t ps)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 *
prop_string_cstring_nocopy(prop_string_t ps)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
prop_string_append(prop_string_t dst,prop_string_t src)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
prop_string_append_cstring(prop_string_t dst,const char * src)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
prop_string_equals(prop_string_t str1,prop_string_t str2)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
prop_string_equals_string(prop_string_t ps,const char * cp)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
prop_string_equals_cstring(prop_string_t ps,const char * cp)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
prop_string_compare(prop_string_t ps1,prop_string_t ps2)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
prop_string_compare_string(prop_string_t ps,const char * cp)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
_prop_string_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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