xref: /netbsd-src/common/lib/libprop/prop_string.c (revision fc5ee90bebfa879be2d1609c5e7b08ecd08db7f5)
1*fc5ee90bSthorpej /*	$NetBSD: prop_string.c,v 1.18 2023/11/17 21:29:33 thorpej Exp $	*/
2774eb1a3Sthorpej 
3774eb1a3Sthorpej /*-
4a792b843Sthorpej  * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc.
5774eb1a3Sthorpej  * All rights reserved.
6774eb1a3Sthorpej  *
7774eb1a3Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8774eb1a3Sthorpej  * by Jason R. Thorpe.
9774eb1a3Sthorpej  *
10774eb1a3Sthorpej  * Redistribution and use in source and binary forms, with or without
11774eb1a3Sthorpej  * modification, are permitted provided that the following conditions
12774eb1a3Sthorpej  * are met:
13774eb1a3Sthorpej  * 1. Redistributions of source code must retain the above copyright
14774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer.
15774eb1a3Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer in the
17774eb1a3Sthorpej  *    documentation and/or other materials provided with the distribution.
18774eb1a3Sthorpej  *
19774eb1a3Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20774eb1a3Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21774eb1a3Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22774eb1a3Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23774eb1a3Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24774eb1a3Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25774eb1a3Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26774eb1a3Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27774eb1a3Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28774eb1a3Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29774eb1a3Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
30774eb1a3Sthorpej  */
31774eb1a3Sthorpej 
32774eb1a3Sthorpej #include "prop_object_impl.h"
338319f966Sthorpej #include <prop/prop_string.h>
34774eb1a3Sthorpej 
35a792b843Sthorpej #include <sys/rbtree.h>
36a792b843Sthorpej #if defined(_KERNEL) || defined(_STANDALONE)
37a792b843Sthorpej #include <sys/stdarg.h>
38a792b843Sthorpej #else
39a792b843Sthorpej #include <stdarg.h>
40a792b843Sthorpej #endif /* _KERNEL || _STANDALONE */
41a792b843Sthorpej 
42774eb1a3Sthorpej struct _prop_string {
43774eb1a3Sthorpej 	struct _prop_object	ps_obj;
44774eb1a3Sthorpej 	union {
45774eb1a3Sthorpej 		char *		psu_mutable;
46774eb1a3Sthorpej 		const char *	psu_immutable;
47774eb1a3Sthorpej 	} ps_un;
48774eb1a3Sthorpej #define	ps_mutable		ps_un.psu_mutable
49774eb1a3Sthorpej #define	ps_immutable		ps_un.psu_immutable
50774eb1a3Sthorpej 	size_t			ps_size;	/* not including \0 */
51a792b843Sthorpej 	struct rb_node		ps_link;
52774eb1a3Sthorpej 	int			ps_flags;
53774eb1a3Sthorpej };
54774eb1a3Sthorpej 
55774eb1a3Sthorpej #define	PS_F_NOCOPY		0x01
56a792b843Sthorpej #define	PS_F_MUTABLE		0x02
57774eb1a3Sthorpej 
58774eb1a3Sthorpej _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng")
59774eb1a3Sthorpej 
60774eb1a3Sthorpej _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
61774eb1a3Sthorpej 		    "property string container object")
62774eb1a3Sthorpej 
634ce0dc3aSthorpej static _prop_object_free_rv_t
644ce0dc3aSthorpej 		_prop_string_free(prop_stack_t, prop_object_t *);
6504377267Sthorpej static bool	_prop_string_externalize(
663e69f1b2Sthorpej 				struct _prop_object_externalize_context *,
673e69f1b2Sthorpej 				void *);
684ce0dc3aSthorpej static _prop_object_equals_rv_t
694ce0dc3aSthorpej 		_prop_string_equals(prop_object_t, prop_object_t,
704deb5931Sjoerg 				    void **, void **,
714deb5931Sjoerg 				    prop_object_t *, prop_object_t *);
723e69f1b2Sthorpej 
733e69f1b2Sthorpej static const struct _prop_object_type _prop_object_type_string = {
743e69f1b2Sthorpej 	.pot_type	=	PROP_TYPE_STRING,
753e69f1b2Sthorpej 	.pot_free	=	_prop_string_free,
763e69f1b2Sthorpej 	.pot_extern	=	_prop_string_externalize,
773e69f1b2Sthorpej 	.pot_equals	=	_prop_string_equals,
783e69f1b2Sthorpej };
793e69f1b2Sthorpej 
803e69f1b2Sthorpej #define	prop_object_is_string(x)	\
81beabdd9bSthorpej 	((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
82774eb1a3Sthorpej #define	prop_string_contents(x)  ((x)->ps_immutable ? (x)->ps_immutable : "")
83774eb1a3Sthorpej 
84a792b843Sthorpej /*
85a792b843Sthorpej  * In order to reduce memory usage, all immutable string objects are
86a792b843Sthorpej  * de-duplicated.
87a792b843Sthorpej  */
88a792b843Sthorpej 
89a792b843Sthorpej static int
90a792b843Sthorpej /*ARGSUSED*/
_prop_string_rb_compare_nodes(void * ctx _PROP_ARG_UNUSED,const void * n1,const void * n2)91a792b843Sthorpej _prop_string_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
92a792b843Sthorpej 			      const void *n1, const void *n2)
93a792b843Sthorpej {
94a792b843Sthorpej 	const struct _prop_string * const ps1 = n1;
95a792b843Sthorpej 	const struct _prop_string * const ps2 = n2;
96a792b843Sthorpej 
97a792b843Sthorpej 	_PROP_ASSERT(ps1->ps_immutable != NULL);
98a792b843Sthorpej 	_PROP_ASSERT(ps2->ps_immutable != NULL);
99a792b843Sthorpej 
100a792b843Sthorpej 	return strcmp(ps1->ps_immutable, ps2->ps_immutable);
101a792b843Sthorpej }
102a792b843Sthorpej 
103a792b843Sthorpej static int
104a792b843Sthorpej /*ARGSUSED*/
_prop_string_rb_compare_key(void * ctx _PROP_ARG_UNUSED,const void * n,const void * v)105a792b843Sthorpej _prop_string_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
106a792b843Sthorpej 			    const void *n, const void *v)
107a792b843Sthorpej {
108a792b843Sthorpej 	const struct _prop_string * const ps = n;
109a792b843Sthorpej 	const char * const cp = v;
110a792b843Sthorpej 
111a792b843Sthorpej 	_PROP_ASSERT(ps->ps_immutable != NULL);
112a792b843Sthorpej 
113a792b843Sthorpej 	return strcmp(ps->ps_immutable, cp);
114a792b843Sthorpej }
115a792b843Sthorpej 
116a792b843Sthorpej static const rb_tree_ops_t _prop_string_rb_tree_ops = {
117a792b843Sthorpej 	.rbto_compare_nodes = _prop_string_rb_compare_nodes,
118a792b843Sthorpej 	.rbto_compare_key = _prop_string_rb_compare_key,
119a792b843Sthorpej 	.rbto_node_offset = offsetof(struct _prop_string, ps_link),
120a792b843Sthorpej 	.rbto_context = NULL
121a792b843Sthorpej };
122a792b843Sthorpej 
123a792b843Sthorpej static struct rb_tree _prop_string_tree;
124a792b843Sthorpej 
125a792b843Sthorpej _PROP_ONCE_DECL(_prop_string_init_once)
_PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)126a792b843Sthorpej _PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)
127a792b843Sthorpej 
128a792b843Sthorpej static int
129a792b843Sthorpej _prop_string_init(void)
130a792b843Sthorpej {
131a792b843Sthorpej 
132a792b843Sthorpej 	_PROP_MUTEX_INIT(_prop_string_tree_mutex);
133a792b843Sthorpej 	rb_tree_init(&_prop_string_tree,
134a792b843Sthorpej 		     &_prop_string_rb_tree_ops);
135a792b843Sthorpej 
136a792b843Sthorpej 	return 0;
137a792b843Sthorpej }
138a792b843Sthorpej 
139e835604cSjoerg /* ARGSUSED */
1404ce0dc3aSthorpej static _prop_object_free_rv_t
_prop_string_free(prop_stack_t stack,prop_object_t * obj)141e835604cSjoerg _prop_string_free(prop_stack_t stack, prop_object_t *obj)
142774eb1a3Sthorpej {
143e835604cSjoerg 	prop_string_t ps = *obj;
144774eb1a3Sthorpej 
145a792b843Sthorpej 	if ((ps->ps_flags & PS_F_MUTABLE) == 0) {
146a792b843Sthorpej 		_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
147a792b843Sthorpej 		/*
148a792b843Sthorpej 		 * Double-check the retain count now that we've
1494ddb8793Sandvar 		 * acquired the tree lock; holding this lock prevents
150a792b843Sthorpej 		 * new retains from coming in by finding it in the
151a792b843Sthorpej 		 * tree.
152a792b843Sthorpej 		 */
153a792b843Sthorpej 		if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0)
154a792b843Sthorpej 			rb_tree_remove_node(&_prop_string_tree, ps);
155a792b843Sthorpej 		else
156a792b843Sthorpej 			ps = NULL;
157a792b843Sthorpej 		_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
158a792b843Sthorpej 
159a792b843Sthorpej 		if (ps == NULL)
160a792b843Sthorpej 			return (_PROP_OBJECT_FREE_DONE);
161a792b843Sthorpej 	}
162a792b843Sthorpej 
163774eb1a3Sthorpej 	if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
164774eb1a3Sthorpej 	    	_PROP_FREE(ps->ps_mutable, M_PROP_STRING);
165e835604cSjoerg 	_PROP_POOL_PUT(_prop_string_pool, ps);
166e835604cSjoerg 
167e835604cSjoerg 	return (_PROP_OBJECT_FREE_DONE);
168774eb1a3Sthorpej }
169774eb1a3Sthorpej 
17004377267Sthorpej static bool
_prop_string_externalize(struct _prop_object_externalize_context * ctx,void * v)171774eb1a3Sthorpej _prop_string_externalize(struct _prop_object_externalize_context *ctx,
172774eb1a3Sthorpej 			 void *v)
173774eb1a3Sthorpej {
174774eb1a3Sthorpej 	prop_string_t ps = v;
175774eb1a3Sthorpej 
176774eb1a3Sthorpej 	if (ps->ps_size == 0)
177774eb1a3Sthorpej 		return (_prop_object_externalize_empty_tag(ctx, "string"));
178774eb1a3Sthorpej 
17904377267Sthorpej 	if (_prop_object_externalize_start_tag(ctx, "string") == false ||
180774eb1a3Sthorpej 	    _prop_object_externalize_append_encoded_cstring(ctx,
18104377267Sthorpej 	    					ps->ps_immutable) == false ||
18204377267Sthorpej 	    _prop_object_externalize_end_tag(ctx, "string") == false)
18304377267Sthorpej 		return (false);
184774eb1a3Sthorpej 
18504377267Sthorpej 	return (true);
186774eb1a3Sthorpej }
187774eb1a3Sthorpej 
1884deb5931Sjoerg /* ARGSUSED */
1894ce0dc3aSthorpej 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)1904deb5931Sjoerg _prop_string_equals(prop_object_t v1, prop_object_t v2,
1914deb5931Sjoerg     void **stored_pointer1, void **stored_pointer2,
1924deb5931Sjoerg     prop_object_t *next_obj1, prop_object_t *next_obj2)
1933e69f1b2Sthorpej {
1943e69f1b2Sthorpej 	prop_string_t str1 = v1;
1953e69f1b2Sthorpej 	prop_string_t str2 = v2;
1963e69f1b2Sthorpej 
1973e69f1b2Sthorpej 	if (str1 == str2)
1984deb5931Sjoerg 		return (_PROP_OBJECT_EQUALS_TRUE);
1993e69f1b2Sthorpej 	if (str1->ps_size != str2->ps_size)
2004deb5931Sjoerg 		return (_PROP_OBJECT_EQUALS_FALSE);
2014deb5931Sjoerg 	if (strcmp(prop_string_contents(str1), prop_string_contents(str2)))
2024deb5931Sjoerg 		return (_PROP_OBJECT_EQUALS_FALSE);
2034deb5931Sjoerg 	else
2044deb5931Sjoerg 		return (_PROP_OBJECT_EQUALS_TRUE);
2053e69f1b2Sthorpej }
2063e69f1b2Sthorpej 
207774eb1a3Sthorpej static prop_string_t
_prop_string_alloc(int const flags)208a792b843Sthorpej _prop_string_alloc(int const flags)
209774eb1a3Sthorpej {
210774eb1a3Sthorpej 	prop_string_t ps;
211774eb1a3Sthorpej 
212774eb1a3Sthorpej 	ps = _PROP_POOL_GET(_prop_string_pool);
213774eb1a3Sthorpej 	if (ps != NULL) {
2143e69f1b2Sthorpej 		_prop_object_init(&ps->ps_obj, &_prop_object_type_string);
215774eb1a3Sthorpej 
216774eb1a3Sthorpej 		ps->ps_mutable = NULL;
217774eb1a3Sthorpej 		ps->ps_size = 0;
218a792b843Sthorpej 		ps->ps_flags = flags;
219774eb1a3Sthorpej 	}
220774eb1a3Sthorpej 
221774eb1a3Sthorpej 	return (ps);
222774eb1a3Sthorpej }
223774eb1a3Sthorpej 
224a792b843Sthorpej static prop_string_t
_prop_string_instantiate(int const flags,const char * const str,size_t const len)225a792b843Sthorpej _prop_string_instantiate(int const flags, const char * const str,
226a792b843Sthorpej     size_t const len)
227a792b843Sthorpej {
228a792b843Sthorpej 	prop_string_t ps;
229a792b843Sthorpej 
230a792b843Sthorpej 	_PROP_ONCE_RUN(_prop_string_init_once, _prop_string_init);
231a792b843Sthorpej 
232a792b843Sthorpej 	ps = _prop_string_alloc(flags);
233a792b843Sthorpej 	if (ps != NULL) {
234a792b843Sthorpej 		ps->ps_immutable = str;
235a792b843Sthorpej 		ps->ps_size = len;
236a792b843Sthorpej 
237a792b843Sthorpej 		if ((flags & PS_F_MUTABLE) == 0) {
238a792b843Sthorpej 			prop_string_t ops;
239a792b843Sthorpej 
240a792b843Sthorpej 			_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
241a792b843Sthorpej 			ops = rb_tree_insert_node(&_prop_string_tree, ps);
242a792b843Sthorpej 			if (ops != ps) {
243774eb1a3Sthorpej 				/*
244a792b843Sthorpej 				 * Equivalent string object already exist;
245a792b843Sthorpej 				 * free the new one and return a reference
246a792b843Sthorpej 				 * to the existing object.
247774eb1a3Sthorpej 				 */
248a792b843Sthorpej 				prop_object_retain(ops);
249a792b843Sthorpej 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
250*fc5ee90bSthorpej 				if ((flags & PS_F_NOCOPY) == 0) {
251*fc5ee90bSthorpej 					_PROP_FREE(ps->ps_mutable,
252*fc5ee90bSthorpej 					    M_PROP_STRING);
253*fc5ee90bSthorpej 				}
254a792b843Sthorpej 				_PROP_POOL_PUT(_prop_string_pool, ps);
255a792b843Sthorpej 				ps = ops;
256a792b843Sthorpej 			} else {
257a792b843Sthorpej 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
258a792b843Sthorpej 			}
259a792b843Sthorpej 		}
260*fc5ee90bSthorpej 	} else if ((flags & PS_F_NOCOPY) == 0) {
261*fc5ee90bSthorpej 		_PROP_FREE(__UNCONST(str), M_PROP_STRING);
262a792b843Sthorpej 	}
263a792b843Sthorpej 
264a792b843Sthorpej 	return (ps);
265a792b843Sthorpej }
266a792b843Sthorpej 
267a792b843Sthorpej _PROP_DEPRECATED(prop_string_create,
268a792b843Sthorpej     "this program uses prop_string_create(); all functions "
269a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
270774eb1a3Sthorpej prop_string_t
prop_string_create(void)271774eb1a3Sthorpej prop_string_create(void)
272774eb1a3Sthorpej {
273774eb1a3Sthorpej 
274a792b843Sthorpej 	return (_prop_string_alloc(PS_F_MUTABLE));
275774eb1a3Sthorpej }
276774eb1a3Sthorpej 
277a792b843Sthorpej _PROP_DEPRECATED(prop_string_create_cstring,
278a792b843Sthorpej     "this program uses prop_string_create_cstring(); all functions "
279a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
280774eb1a3Sthorpej prop_string_t
prop_string_create_cstring(const char * str)281774eb1a3Sthorpej prop_string_create_cstring(const char *str)
282774eb1a3Sthorpej {
283774eb1a3Sthorpej 	prop_string_t ps;
284774eb1a3Sthorpej 	char *cp;
285774eb1a3Sthorpej 	size_t len;
286774eb1a3Sthorpej 
287a792b843Sthorpej 	_PROP_ASSERT(str != NULL);
288a792b843Sthorpej 
289a792b843Sthorpej 	ps = _prop_string_alloc(PS_F_MUTABLE);
290774eb1a3Sthorpej 	if (ps != NULL) {
291774eb1a3Sthorpej 		len = strlen(str);
292774eb1a3Sthorpej 		cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
293774eb1a3Sthorpej 		if (cp == NULL) {
294e835604cSjoerg 			prop_object_release(ps);
295774eb1a3Sthorpej 			return (NULL);
296774eb1a3Sthorpej 		}
297774eb1a3Sthorpej 		strcpy(cp, str);
298774eb1a3Sthorpej 		ps->ps_mutable = cp;
299774eb1a3Sthorpej 		ps->ps_size = len;
300774eb1a3Sthorpej 	}
301774eb1a3Sthorpej 	return (ps);
302774eb1a3Sthorpej }
303774eb1a3Sthorpej 
304a792b843Sthorpej _PROP_DEPRECATED(prop_string_create_cstring_nocopy,
305a792b843Sthorpej     "this program uses prop_string_create_cstring_nocopy(), "
306a792b843Sthorpej     "which is deprecated; use prop_string_create_nocopy() instead.")
307774eb1a3Sthorpej prop_string_t
prop_string_create_cstring_nocopy(const char * str)308774eb1a3Sthorpej prop_string_create_cstring_nocopy(const char *str)
309774eb1a3Sthorpej {
310a792b843Sthorpej 	return prop_string_create_nocopy(str);
311774eb1a3Sthorpej }
312a792b843Sthorpej 
313a792b843Sthorpej /*
314a792b843Sthorpej  * prop_string_create_format --
315a792b843Sthorpej  *	Create a string object using the provided format string.
316a792b843Sthorpej  */
317a792b843Sthorpej prop_string_t __printflike(1, 2)
prop_string_create_format(const char * fmt,...)318a792b843Sthorpej prop_string_create_format(const char *fmt, ...)
319a792b843Sthorpej {
320a792b843Sthorpej 	char *str = NULL;
321a792b843Sthorpej 	int len;
322d3ce6b60Schristos 	size_t nlen;
323a792b843Sthorpej 	va_list ap;
324a792b843Sthorpej 
325a792b843Sthorpej 	_PROP_ASSERT(fmt != NULL);
326a792b843Sthorpej 
327a792b843Sthorpej 	va_start(ap, fmt);
328a792b843Sthorpej 	len = vsnprintf(NULL, 0, fmt, ap);
329a792b843Sthorpej 	va_end(ap);
330a792b843Sthorpej 
331a792b843Sthorpej 	if (len < 0)
332a792b843Sthorpej 		return (NULL);
333d3ce6b60Schristos 	nlen = len + 1;
334a792b843Sthorpej 
335d3ce6b60Schristos 	str = _PROP_MALLOC(nlen, M_PROP_STRING);
336a792b843Sthorpej 	if (str == NULL)
337a792b843Sthorpej 		return (NULL);
338a792b843Sthorpej 
339a792b843Sthorpej 	va_start(ap, fmt);
340d3ce6b60Schristos 	vsnprintf(str, nlen, fmt, ap);
341a792b843Sthorpej 	va_end(ap);
342a792b843Sthorpej 
343*fc5ee90bSthorpej 	return _prop_string_instantiate(0, str, (size_t)len);
344774eb1a3Sthorpej }
345774eb1a3Sthorpej 
346774eb1a3Sthorpej /*
347a792b843Sthorpej  * prop_string_create_copy --
348a792b843Sthorpej  *	Create a string object by coping the provided constant string.
349a792b843Sthorpej  */
350a792b843Sthorpej prop_string_t
prop_string_create_copy(const char * str)351a792b843Sthorpej prop_string_create_copy(const char *str)
352a792b843Sthorpej {
353a792b843Sthorpej 	return prop_string_create_format("%s", str);
354a792b843Sthorpej }
355a792b843Sthorpej 
356a792b843Sthorpej /*
357a792b843Sthorpej  * prop_string_create_nocopy --
358a792b843Sthorpej  *	Create a string object using the provided external constant
359a792b843Sthorpej  *	string.
360a792b843Sthorpej  */
361a792b843Sthorpej prop_string_t
prop_string_create_nocopy(const char * str)362a792b843Sthorpej prop_string_create_nocopy(const char *str)
363a792b843Sthorpej {
364a792b843Sthorpej 
365a792b843Sthorpej 	_PROP_ASSERT(str != NULL);
366a792b843Sthorpej 
367a792b843Sthorpej 	return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str));
368a792b843Sthorpej }
369a792b843Sthorpej 
370a792b843Sthorpej /*
371774eb1a3Sthorpej  * prop_string_copy --
372a792b843Sthorpej  *	Copy a string.  This reduces to a retain in the common case.
373a792b843Sthorpej  *	Deprecated mutable string objects must be copied.
374774eb1a3Sthorpej  */
375774eb1a3Sthorpej prop_string_t
prop_string_copy(prop_string_t ops)376774eb1a3Sthorpej prop_string_copy(prop_string_t ops)
377774eb1a3Sthorpej {
378a792b843Sthorpej 	char *cp;
379774eb1a3Sthorpej 
380d21620b2Sthorpej 	if (! prop_object_is_string(ops))
381d21620b2Sthorpej 		return (NULL);
382774eb1a3Sthorpej 
383a792b843Sthorpej 	if ((ops->ps_flags & PS_F_MUTABLE) == 0) {
384a792b843Sthorpej 		prop_object_retain(ops);
385a792b843Sthorpej 		return (ops);
386774eb1a3Sthorpej 	}
387a792b843Sthorpej 
388a792b843Sthorpej 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
389a792b843Sthorpej 	if (cp == NULL)
390a792b843Sthorpej 		return NULL;
391a792b843Sthorpej 
392774eb1a3Sthorpej 	strcpy(cp, prop_string_contents(ops));
393a792b843Sthorpej 
394*fc5ee90bSthorpej 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
395774eb1a3Sthorpej }
396774eb1a3Sthorpej 
397a792b843Sthorpej _PROP_DEPRECATED(prop_string_copy_mutable,
398a792b843Sthorpej     "this program uses prop_string_copy_mutable(); all functions "
399a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
400774eb1a3Sthorpej prop_string_t
prop_string_copy_mutable(prop_string_t ops)401774eb1a3Sthorpej prop_string_copy_mutable(prop_string_t ops)
402774eb1a3Sthorpej {
403774eb1a3Sthorpej 	char *cp;
404774eb1a3Sthorpej 
405d21620b2Sthorpej 	if (! prop_object_is_string(ops))
406d21620b2Sthorpej 		return (NULL);
407774eb1a3Sthorpej 
408a792b843Sthorpej 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
409a792b843Sthorpej 	if (cp == NULL)
410a792b843Sthorpej 		return NULL;
411a792b843Sthorpej 
412774eb1a3Sthorpej 	strcpy(cp, prop_string_contents(ops));
413a792b843Sthorpej 
414*fc5ee90bSthorpej 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
415774eb1a3Sthorpej }
416774eb1a3Sthorpej 
417774eb1a3Sthorpej /*
418774eb1a3Sthorpej  * prop_string_size --
419d21620b2Sthorpej  *	Return the size of the string, not including the terminating NUL.
420774eb1a3Sthorpej  */
421774eb1a3Sthorpej size_t
prop_string_size(prop_string_t ps)422774eb1a3Sthorpej prop_string_size(prop_string_t ps)
423774eb1a3Sthorpej {
424774eb1a3Sthorpej 
425d21620b2Sthorpej 	if (! prop_object_is_string(ps))
426d21620b2Sthorpej 		return (0);
427d21620b2Sthorpej 
428774eb1a3Sthorpej 	return (ps->ps_size);
429774eb1a3Sthorpej }
430774eb1a3Sthorpej 
431774eb1a3Sthorpej /*
432a792b843Sthorpej  * prop_string_value --
433a792b843Sthorpej  *	Returns a pointer to the string object's value.  This pointer
434a792b843Sthorpej  *	remains valid only as long as the string object.
435774eb1a3Sthorpej  */
436a792b843Sthorpej const char *
prop_string_value(prop_string_t ps)437a792b843Sthorpej prop_string_value(prop_string_t ps)
438a792b843Sthorpej {
439a792b843Sthorpej 
440a792b843Sthorpej 	if (! prop_object_is_string(ps))
441a792b843Sthorpej 		return (NULL);
442a792b843Sthorpej 
443a792b843Sthorpej 	if ((ps->ps_flags & PS_F_MUTABLE) == 0)
444a792b843Sthorpej 		return (ps->ps_immutable);
445a792b843Sthorpej 
446a792b843Sthorpej 	return (prop_string_contents(ps));
447a792b843Sthorpej }
448a792b843Sthorpej 
449a792b843Sthorpej /*
450a792b843Sthorpej  * prop_string_copy_value --
451a792b843Sthorpej  *	Copy the string object's value into the supplied buffer.
452a792b843Sthorpej  */
453a792b843Sthorpej bool
prop_string_copy_value(prop_string_t ps,void * buf,size_t buflen)454a792b843Sthorpej prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen)
455a792b843Sthorpej {
456a792b843Sthorpej 
457a792b843Sthorpej 	if (! prop_object_is_string(ps))
458a792b843Sthorpej 		return (false);
459a792b843Sthorpej 
460a792b843Sthorpej 	if (buf == NULL || buflen < ps->ps_size + 1)
461a792b843Sthorpej 		return (false);
462a792b843Sthorpej 
463a792b843Sthorpej 	strcpy(buf, prop_string_contents(ps));
464a792b843Sthorpej 
465a792b843Sthorpej 	return (true);
466a792b843Sthorpej }
467a792b843Sthorpej 
468a792b843Sthorpej _PROP_DEPRECATED(prop_string_mutable,
469a792b843Sthorpej     "this program uses prop_string_mutable(); all functions "
470a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
47104377267Sthorpej bool
prop_string_mutable(prop_string_t ps)472774eb1a3Sthorpej prop_string_mutable(prop_string_t ps)
473774eb1a3Sthorpej {
474774eb1a3Sthorpej 
475d21620b2Sthorpej 	if (! prop_object_is_string(ps))
47604377267Sthorpej 		return (false);
477d21620b2Sthorpej 
478a792b843Sthorpej 	return ((ps->ps_flags & PS_F_MUTABLE) != 0);
479774eb1a3Sthorpej }
480774eb1a3Sthorpej 
481a792b843Sthorpej _PROP_DEPRECATED(prop_string_cstring,
482a792b843Sthorpej     "this program uses prop_string_cstring(), "
483a792b843Sthorpej     "which is deprecated; use prop_string_copy_value() instead.")
484774eb1a3Sthorpej char *
prop_string_cstring(prop_string_t ps)485774eb1a3Sthorpej prop_string_cstring(prop_string_t ps)
486774eb1a3Sthorpej {
487774eb1a3Sthorpej 	char *cp;
488774eb1a3Sthorpej 
489d21620b2Sthorpej 	if (! prop_object_is_string(ps))
490d21620b2Sthorpej 		return (NULL);
491d21620b2Sthorpej 
492774eb1a3Sthorpej 	cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP);
493774eb1a3Sthorpej 	if (cp != NULL)
494774eb1a3Sthorpej 		strcpy(cp, prop_string_contents(ps));
495774eb1a3Sthorpej 
496774eb1a3Sthorpej 	return (cp);
497774eb1a3Sthorpej }
498774eb1a3Sthorpej 
499a792b843Sthorpej _PROP_DEPRECATED(prop_string_cstring_nocopy,
500a792b843Sthorpej     "this program uses prop_string_cstring_nocopy(), "
501a792b843Sthorpej     "which is deprecated; use prop_string_value() instead.")
502774eb1a3Sthorpej const char *
prop_string_cstring_nocopy(prop_string_t ps)503774eb1a3Sthorpej prop_string_cstring_nocopy(prop_string_t ps)
504774eb1a3Sthorpej {
505774eb1a3Sthorpej 
506d21620b2Sthorpej 	if (! prop_object_is_string(ps))
507d21620b2Sthorpej 		return (NULL);
508d21620b2Sthorpej 
509774eb1a3Sthorpej 	return (prop_string_contents(ps));
510774eb1a3Sthorpej }
511774eb1a3Sthorpej 
512a792b843Sthorpej _PROP_DEPRECATED(prop_string_append,
513a792b843Sthorpej     "this program uses prop_string_append(); all functions "
514a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
51504377267Sthorpej bool
prop_string_append(prop_string_t dst,prop_string_t src)516774eb1a3Sthorpej prop_string_append(prop_string_t dst, prop_string_t src)
517774eb1a3Sthorpej {
518774eb1a3Sthorpej 	char *ocp, *cp;
519774eb1a3Sthorpej 	size_t len;
520774eb1a3Sthorpej 
521d21620b2Sthorpej 	if (! (prop_object_is_string(dst) &&
522d21620b2Sthorpej 	       prop_object_is_string(src)))
52304377267Sthorpej 		return (false);
524774eb1a3Sthorpej 
525a792b843Sthorpej 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
52604377267Sthorpej 		return (false);
527774eb1a3Sthorpej 
528774eb1a3Sthorpej 	len = dst->ps_size + src->ps_size;
529774eb1a3Sthorpej 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
530774eb1a3Sthorpej 	if (cp == NULL)
53104377267Sthorpej 		return (false);
532a6e338b0Schristos 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst),
533774eb1a3Sthorpej 		prop_string_contents(src));
534774eb1a3Sthorpej 	ocp = dst->ps_mutable;
535774eb1a3Sthorpej 	dst->ps_mutable = cp;
536774eb1a3Sthorpej 	dst->ps_size = len;
537774eb1a3Sthorpej 	if (ocp != NULL)
538774eb1a3Sthorpej 		_PROP_FREE(ocp, M_PROP_STRING);
539774eb1a3Sthorpej 
54004377267Sthorpej 	return (true);
541774eb1a3Sthorpej }
542774eb1a3Sthorpej 
543a792b843Sthorpej _PROP_DEPRECATED(prop_string_append_cstring,
544a792b843Sthorpej     "this program uses prop_string_append_cstring(); all functions "
545a792b843Sthorpej     "supporting mutable prop_strings are deprecated.")
54604377267Sthorpej bool
prop_string_append_cstring(prop_string_t dst,const char * src)547774eb1a3Sthorpej prop_string_append_cstring(prop_string_t dst, const char *src)
548774eb1a3Sthorpej {
549774eb1a3Sthorpej 	char *ocp, *cp;
550774eb1a3Sthorpej 	size_t len;
551774eb1a3Sthorpej 
552d21620b2Sthorpej 	if (! prop_object_is_string(dst))
55304377267Sthorpej 		return (false);
554d21620b2Sthorpej 
555774eb1a3Sthorpej 	_PROP_ASSERT(src != NULL);
556774eb1a3Sthorpej 
557a792b843Sthorpej 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
55804377267Sthorpej 		return (false);
559774eb1a3Sthorpej 
560774eb1a3Sthorpej 	len = dst->ps_size + strlen(src);
561774eb1a3Sthorpej 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
562774eb1a3Sthorpej 	if (cp == NULL)
56304377267Sthorpej 		return (false);
564a6e338b0Schristos 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src);
565774eb1a3Sthorpej 	ocp = dst->ps_mutable;
566774eb1a3Sthorpej 	dst->ps_mutable = cp;
567774eb1a3Sthorpej 	dst->ps_size = len;
568774eb1a3Sthorpej 	if (ocp != NULL)
569774eb1a3Sthorpej 		_PROP_FREE(ocp, M_PROP_STRING);
570774eb1a3Sthorpej 
57104377267Sthorpej 	return (true);
572774eb1a3Sthorpej }
573774eb1a3Sthorpej 
574774eb1a3Sthorpej /*
575774eb1a3Sthorpej  * prop_string_equals --
57604377267Sthorpej  *	Return true if two strings are equivalent.
577774eb1a3Sthorpej  */
57804377267Sthorpej bool
prop_string_equals(prop_string_t str1,prop_string_t str2)579774eb1a3Sthorpej prop_string_equals(prop_string_t str1, prop_string_t str2)
580774eb1a3Sthorpej {
5814deb5931Sjoerg 	if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
5824deb5931Sjoerg 		return (false);
583774eb1a3Sthorpej 
5844deb5931Sjoerg 	return prop_object_equals(str1, str2);
585774eb1a3Sthorpej }
586774eb1a3Sthorpej 
587774eb1a3Sthorpej /*
588a792b843Sthorpej  * prop_string_equals_string --
589a792b843Sthorpej  *	Return true if the string object is equivalent to the specified
590774eb1a3Sthorpej  *	C string.
591774eb1a3Sthorpej  */
59204377267Sthorpej bool
prop_string_equals_string(prop_string_t ps,const char * cp)593a792b843Sthorpej prop_string_equals_string(prop_string_t ps, const char *cp)
594774eb1a3Sthorpej {
595774eb1a3Sthorpej 
596d21620b2Sthorpej 	if (! prop_object_is_string(ps))
59704377267Sthorpej 		return (false);
598d21620b2Sthorpej 
599774eb1a3Sthorpej 	return (strcmp(prop_string_contents(ps), cp) == 0);
600774eb1a3Sthorpej }
601774eb1a3Sthorpej 
602a792b843Sthorpej _PROP_DEPRECATED(prop_string_equals_cstring,
603a792b843Sthorpej     "this program uses prop_string_equals_cstring(), "
604a792b843Sthorpej     "which is deprecated; prop_string_equals_string() instead.")
605a792b843Sthorpej bool
prop_string_equals_cstring(prop_string_t ps,const char * cp)606a792b843Sthorpej prop_string_equals_cstring(prop_string_t ps, const char *cp)
607a792b843Sthorpej {
608a792b843Sthorpej 	return prop_string_equals_string(ps, cp);
609a792b843Sthorpej }
610a792b843Sthorpej 
611a792b843Sthorpej /*
612a792b843Sthorpej  * prop_string_compare --
613a792b843Sthorpej  *	Compare two string objects, using strcmp() semantics.
614a792b843Sthorpej  */
615a792b843Sthorpej int
prop_string_compare(prop_string_t ps1,prop_string_t ps2)616a792b843Sthorpej prop_string_compare(prop_string_t ps1, prop_string_t ps2)
617a792b843Sthorpej {
618a792b843Sthorpej 	if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2))
619a792b843Sthorpej 		return (-666);	/* arbitrary */
620a792b843Sthorpej 
621a792b843Sthorpej 	return (strcmp(prop_string_contents(ps1),
622a792b843Sthorpej 		       prop_string_contents(ps2)));
623a792b843Sthorpej }
624a792b843Sthorpej 
625a792b843Sthorpej /*
626a792b843Sthorpej  * prop_string_compare_string --
627a792b843Sthorpej  *	Compare a string object to the specified C string, using
628a792b843Sthorpej  *	strcmp() semantics.
629a792b843Sthorpej  */
630a792b843Sthorpej int
prop_string_compare_string(prop_string_t ps,const char * cp)631a792b843Sthorpej prop_string_compare_string(prop_string_t ps, const char *cp)
632a792b843Sthorpej {
633a792b843Sthorpej 	if (!prop_object_is_string(ps))
634a792b843Sthorpej 		return (-666);	/* arbitrary */
635a792b843Sthorpej 
636a792b843Sthorpej 	return (strcmp(prop_string_contents(ps), cp));
637a792b843Sthorpej }
638a792b843Sthorpej 
639774eb1a3Sthorpej /*
640774eb1a3Sthorpej  * _prop_string_internalize --
641774eb1a3Sthorpej  *	Parse a <string>...</string> and return the object created from the
642774eb1a3Sthorpej  *	external representation.
643774eb1a3Sthorpej  */
644e835604cSjoerg /* ARGSUSED */
645e835604cSjoerg bool
_prop_string_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)646e835604cSjoerg _prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
647e835604cSjoerg     struct _prop_object_internalize_context *ctx)
648774eb1a3Sthorpej {
649774eb1a3Sthorpej 	char *str;
650774eb1a3Sthorpej 	size_t len, alen;
651774eb1a3Sthorpej 
652e835604cSjoerg 	if (ctx->poic_is_empty_element) {
653e835604cSjoerg 		*obj = prop_string_create();
654e835604cSjoerg 		return (true);
655e835604cSjoerg 	}
656774eb1a3Sthorpej 
657774eb1a3Sthorpej 	/* No attributes recognized here. */
658774eb1a3Sthorpej 	if (ctx->poic_tagattr != NULL)
659e835604cSjoerg 		return (true);
660774eb1a3Sthorpej 
661774eb1a3Sthorpej 	/* Compute the length of the result. */
662ab821170Smartin 	if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
66304377267Sthorpej 						   NULL) == false)
664e835604cSjoerg 		return (true);
665774eb1a3Sthorpej 
666774eb1a3Sthorpej 	str = _PROP_MALLOC(len + 1, M_PROP_STRING);
667774eb1a3Sthorpej 	if (str == NULL)
668e835604cSjoerg 		return (true);
669774eb1a3Sthorpej 
670774eb1a3Sthorpej 	if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
67104377267Sthorpej 						   &ctx->poic_cp) == false ||
672774eb1a3Sthorpej 	    alen != len) {
673774eb1a3Sthorpej 		_PROP_FREE(str, M_PROP_STRING);
674e835604cSjoerg 		return (true);
675774eb1a3Sthorpej 	}
676774eb1a3Sthorpej 	str[len] = '\0';
677774eb1a3Sthorpej 
678774eb1a3Sthorpej 	if (_prop_object_internalize_find_tag(ctx, "string",
67904377267Sthorpej 					      _PROP_TAG_TYPE_END) == false) {
680774eb1a3Sthorpej 		_PROP_FREE(str, M_PROP_STRING);
681e835604cSjoerg 		return (true);
682774eb1a3Sthorpej 	}
683774eb1a3Sthorpej 
684*fc5ee90bSthorpej 	*obj = _prop_string_instantiate(0, str, len);
685e835604cSjoerg 	return (true);
686774eb1a3Sthorpej }
687