Home | History | Annotate | Line # | Download | only in libprop
prop_string.c revision 1.18
      1  1.18   thorpej /*	$NetBSD: prop_string.c,v 1.18 2023/11/17 21:29:33 thorpej Exp $	*/
      2   1.1   thorpej 
      3   1.1   thorpej /*-
      4  1.14   thorpej  * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc.
      5   1.1   thorpej  * All rights reserved.
      6   1.1   thorpej  *
      7   1.1   thorpej  * This code is derived from software contributed to The NetBSD Foundation
      8   1.1   thorpej  * by Jason R. Thorpe.
      9   1.1   thorpej  *
     10   1.1   thorpej  * Redistribution and use in source and binary forms, with or without
     11   1.1   thorpej  * modification, are permitted provided that the following conditions
     12   1.1   thorpej  * are met:
     13   1.1   thorpej  * 1. Redistributions of source code must retain the above copyright
     14   1.1   thorpej  *    notice, this list of conditions and the following disclaimer.
     15   1.1   thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1   thorpej  *    notice, this list of conditions and the following disclaimer in the
     17   1.1   thorpej  *    documentation and/or other materials provided with the distribution.
     18   1.1   thorpej  *
     19   1.1   thorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.1   thorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1   thorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1   thorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.1   thorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.1   thorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.1   thorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.1   thorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1   thorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1   thorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1   thorpej  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1   thorpej  */
     31   1.1   thorpej 
     32  1.13   thorpej #include "prop_object_impl.h"
     33   1.1   thorpej #include <prop/prop_string.h>
     34   1.1   thorpej 
     35  1.14   thorpej #include <sys/rbtree.h>
     36  1.14   thorpej #if defined(_KERNEL) || defined(_STANDALONE)
     37  1.14   thorpej #include <sys/stdarg.h>
     38  1.14   thorpej #else
     39  1.14   thorpej #include <stdarg.h>
     40  1.14   thorpej #endif /* _KERNEL || _STANDALONE */
     41  1.14   thorpej 
     42   1.1   thorpej struct _prop_string {
     43   1.1   thorpej 	struct _prop_object	ps_obj;
     44   1.1   thorpej 	union {
     45   1.1   thorpej 		char *		psu_mutable;
     46   1.1   thorpej 		const char *	psu_immutable;
     47   1.1   thorpej 	} ps_un;
     48   1.1   thorpej #define	ps_mutable		ps_un.psu_mutable
     49   1.1   thorpej #define	ps_immutable		ps_un.psu_immutable
     50   1.1   thorpej 	size_t			ps_size;	/* not including \0 */
     51  1.14   thorpej 	struct rb_node		ps_link;
     52   1.1   thorpej 	int			ps_flags;
     53   1.1   thorpej };
     54   1.1   thorpej 
     55   1.1   thorpej #define	PS_F_NOCOPY		0x01
     56  1.14   thorpej #define	PS_F_MUTABLE		0x02
     57   1.1   thorpej 
     58   1.1   thorpej _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng")
     59   1.1   thorpej 
     60   1.1   thorpej _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
     61   1.1   thorpej 		    "property string container object")
     62   1.1   thorpej 
     63  1.11   thorpej static _prop_object_free_rv_t
     64  1.11   thorpej 		_prop_string_free(prop_stack_t, prop_object_t *);
     65   1.7   thorpej static bool	_prop_string_externalize(
     66   1.2   thorpej 				struct _prop_object_externalize_context *,
     67   1.2   thorpej 				void *);
     68  1.11   thorpej static _prop_object_equals_rv_t
     69  1.11   thorpej 		_prop_string_equals(prop_object_t, prop_object_t,
     70  1.11   thorpej 				    void **, void **,
     71  1.11   thorpej 				    prop_object_t *, prop_object_t *);
     72   1.2   thorpej 
     73   1.2   thorpej static const struct _prop_object_type _prop_object_type_string = {
     74   1.2   thorpej 	.pot_type	=	PROP_TYPE_STRING,
     75   1.2   thorpej 	.pot_free	=	_prop_string_free,
     76   1.2   thorpej 	.pot_extern	=	_prop_string_externalize,
     77   1.2   thorpej 	.pot_equals	=	_prop_string_equals,
     78   1.2   thorpej };
     79   1.2   thorpej 
     80   1.2   thorpej #define	prop_object_is_string(x)	\
     81   1.4   thorpej 	((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
     82   1.1   thorpej #define	prop_string_contents(x)  ((x)->ps_immutable ? (x)->ps_immutable : "")
     83   1.1   thorpej 
     84  1.14   thorpej /*
     85  1.14   thorpej  * In order to reduce memory usage, all immutable string objects are
     86  1.14   thorpej  * de-duplicated.
     87  1.14   thorpej  */
     88  1.14   thorpej 
     89  1.14   thorpej static int
     90  1.14   thorpej /*ARGSUSED*/
     91  1.14   thorpej _prop_string_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
     92  1.14   thorpej 			      const void *n1, const void *n2)
     93  1.14   thorpej {
     94  1.14   thorpej 	const struct _prop_string * const ps1 = n1;
     95  1.14   thorpej 	const struct _prop_string * const ps2 = n2;
     96  1.14   thorpej 
     97  1.14   thorpej 	_PROP_ASSERT(ps1->ps_immutable != NULL);
     98  1.14   thorpej 	_PROP_ASSERT(ps2->ps_immutable != NULL);
     99  1.14   thorpej 
    100  1.14   thorpej 	return strcmp(ps1->ps_immutable, ps2->ps_immutable);
    101  1.14   thorpej }
    102  1.14   thorpej 
    103  1.14   thorpej static int
    104  1.14   thorpej /*ARGSUSED*/
    105  1.14   thorpej _prop_string_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
    106  1.14   thorpej 			    const void *n, const void *v)
    107  1.14   thorpej {
    108  1.14   thorpej 	const struct _prop_string * const ps = n;
    109  1.14   thorpej 	const char * const cp = v;
    110  1.14   thorpej 
    111  1.14   thorpej 	_PROP_ASSERT(ps->ps_immutable != NULL);
    112  1.14   thorpej 
    113  1.14   thorpej 	return strcmp(ps->ps_immutable, cp);
    114  1.14   thorpej }
    115  1.14   thorpej 
    116  1.14   thorpej static const rb_tree_ops_t _prop_string_rb_tree_ops = {
    117  1.14   thorpej 	.rbto_compare_nodes = _prop_string_rb_compare_nodes,
    118  1.14   thorpej 	.rbto_compare_key = _prop_string_rb_compare_key,
    119  1.14   thorpej 	.rbto_node_offset = offsetof(struct _prop_string, ps_link),
    120  1.14   thorpej 	.rbto_context = NULL
    121  1.14   thorpej };
    122  1.14   thorpej 
    123  1.14   thorpej static struct rb_tree _prop_string_tree;
    124  1.14   thorpej 
    125  1.14   thorpej _PROP_ONCE_DECL(_prop_string_init_once)
    126  1.14   thorpej _PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)
    127  1.14   thorpej 
    128  1.14   thorpej static int
    129  1.14   thorpej _prop_string_init(void)
    130  1.14   thorpej {
    131  1.14   thorpej 
    132  1.14   thorpej 	_PROP_MUTEX_INIT(_prop_string_tree_mutex);
    133  1.14   thorpej 	rb_tree_init(&_prop_string_tree,
    134  1.14   thorpej 		     &_prop_string_rb_tree_ops);
    135  1.17  riastrad 
    136  1.14   thorpej 	return 0;
    137  1.14   thorpej }
    138  1.14   thorpej 
    139   1.8     joerg /* ARGSUSED */
    140  1.11   thorpej static _prop_object_free_rv_t
    141   1.8     joerg _prop_string_free(prop_stack_t stack, prop_object_t *obj)
    142   1.1   thorpej {
    143   1.8     joerg 	prop_string_t ps = *obj;
    144   1.1   thorpej 
    145  1.14   thorpej 	if ((ps->ps_flags & PS_F_MUTABLE) == 0) {
    146  1.14   thorpej 		_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
    147  1.14   thorpej 		/*
    148  1.14   thorpej 		 * Double-check the retain count now that we've
    149  1.16    andvar 		 * acquired the tree lock; holding this lock prevents
    150  1.14   thorpej 		 * new retains from coming in by finding it in the
    151  1.14   thorpej 		 * tree.
    152  1.14   thorpej 		 */
    153  1.14   thorpej 		if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0)
    154  1.14   thorpej 			rb_tree_remove_node(&_prop_string_tree, ps);
    155  1.14   thorpej 		else
    156  1.14   thorpej 			ps = NULL;
    157  1.14   thorpej 		_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    158  1.14   thorpej 
    159  1.14   thorpej 		if (ps == NULL)
    160  1.14   thorpej 			return (_PROP_OBJECT_FREE_DONE);
    161  1.14   thorpej 	}
    162  1.14   thorpej 
    163   1.1   thorpej 	if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
    164   1.1   thorpej 	    	_PROP_FREE(ps->ps_mutable, M_PROP_STRING);
    165   1.8     joerg 	_PROP_POOL_PUT(_prop_string_pool, ps);
    166   1.8     joerg 
    167   1.8     joerg 	return (_PROP_OBJECT_FREE_DONE);
    168   1.1   thorpej }
    169   1.1   thorpej 
    170   1.7   thorpej static bool
    171   1.1   thorpej _prop_string_externalize(struct _prop_object_externalize_context *ctx,
    172   1.1   thorpej 			 void *v)
    173   1.1   thorpej {
    174   1.1   thorpej 	prop_string_t ps = v;
    175   1.1   thorpej 
    176   1.1   thorpej 	if (ps->ps_size == 0)
    177   1.1   thorpej 		return (_prop_object_externalize_empty_tag(ctx, "string"));
    178   1.1   thorpej 
    179   1.7   thorpej 	if (_prop_object_externalize_start_tag(ctx, "string") == false ||
    180   1.1   thorpej 	    _prop_object_externalize_append_encoded_cstring(ctx,
    181   1.7   thorpej 	    					ps->ps_immutable) == false ||
    182   1.7   thorpej 	    _prop_object_externalize_end_tag(ctx, "string") == false)
    183   1.7   thorpej 		return (false);
    184  1.17  riastrad 
    185   1.7   thorpej 	return (true);
    186   1.1   thorpej }
    187   1.1   thorpej 
    188   1.9     joerg /* ARGSUSED */
    189  1.11   thorpej static _prop_object_equals_rv_t
    190   1.9     joerg _prop_string_equals(prop_object_t v1, prop_object_t v2,
    191   1.9     joerg     void **stored_pointer1, void **stored_pointer2,
    192   1.9     joerg     prop_object_t *next_obj1, prop_object_t *next_obj2)
    193   1.2   thorpej {
    194   1.2   thorpej 	prop_string_t str1 = v1;
    195   1.2   thorpej 	prop_string_t str2 = v2;
    196   1.2   thorpej 
    197   1.2   thorpej 	if (str1 == str2)
    198   1.9     joerg 		return (_PROP_OBJECT_EQUALS_TRUE);
    199   1.2   thorpej 	if (str1->ps_size != str2->ps_size)
    200   1.9     joerg 		return (_PROP_OBJECT_EQUALS_FALSE);
    201   1.9     joerg 	if (strcmp(prop_string_contents(str1), prop_string_contents(str2)))
    202   1.9     joerg 		return (_PROP_OBJECT_EQUALS_FALSE);
    203   1.9     joerg 	else
    204   1.9     joerg 		return (_PROP_OBJECT_EQUALS_TRUE);
    205   1.2   thorpej }
    206   1.2   thorpej 
    207   1.1   thorpej static prop_string_t
    208  1.14   thorpej _prop_string_alloc(int const flags)
    209   1.1   thorpej {
    210   1.1   thorpej 	prop_string_t ps;
    211   1.1   thorpej 
    212   1.1   thorpej 	ps = _PROP_POOL_GET(_prop_string_pool);
    213   1.1   thorpej 	if (ps != NULL) {
    214   1.2   thorpej 		_prop_object_init(&ps->ps_obj, &_prop_object_type_string);
    215   1.1   thorpej 
    216   1.1   thorpej 		ps->ps_mutable = NULL;
    217   1.1   thorpej 		ps->ps_size = 0;
    218  1.14   thorpej 		ps->ps_flags = flags;
    219  1.14   thorpej 	}
    220  1.14   thorpej 
    221  1.14   thorpej 	return (ps);
    222  1.14   thorpej }
    223  1.14   thorpej 
    224  1.14   thorpej static prop_string_t
    225  1.14   thorpej _prop_string_instantiate(int const flags, const char * const str,
    226  1.14   thorpej     size_t const len)
    227  1.14   thorpej {
    228  1.14   thorpej 	prop_string_t ps;
    229  1.14   thorpej 
    230  1.14   thorpej 	_PROP_ONCE_RUN(_prop_string_init_once, _prop_string_init);
    231  1.14   thorpej 
    232  1.14   thorpej 	ps = _prop_string_alloc(flags);
    233  1.14   thorpej 	if (ps != NULL) {
    234  1.14   thorpej 		ps->ps_immutable = str;
    235  1.14   thorpej 		ps->ps_size = len;
    236  1.14   thorpej 
    237  1.14   thorpej 		if ((flags & PS_F_MUTABLE) == 0) {
    238  1.14   thorpej 			prop_string_t ops;
    239  1.14   thorpej 
    240  1.14   thorpej 			_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
    241  1.14   thorpej 			ops = rb_tree_insert_node(&_prop_string_tree, ps);
    242  1.14   thorpej 			if (ops != ps) {
    243  1.14   thorpej 				/*
    244  1.14   thorpej 				 * Equivalent string object already exist;
    245  1.14   thorpej 				 * free the new one and return a reference
    246  1.14   thorpej 				 * to the existing object.
    247  1.14   thorpej 				 */
    248  1.14   thorpej 				prop_object_retain(ops);
    249  1.14   thorpej 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    250  1.18   thorpej 				if ((flags & PS_F_NOCOPY) == 0) {
    251  1.18   thorpej 					_PROP_FREE(ps->ps_mutable,
    252  1.18   thorpej 					    M_PROP_STRING);
    253  1.18   thorpej 				}
    254  1.14   thorpej 				_PROP_POOL_PUT(_prop_string_pool, ps);
    255  1.14   thorpej 				ps = ops;
    256  1.14   thorpej 			} else {
    257  1.14   thorpej 				_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
    258  1.14   thorpej 			}
    259  1.14   thorpej 		}
    260  1.18   thorpej 	} else if ((flags & PS_F_NOCOPY) == 0) {
    261  1.18   thorpej 		_PROP_FREE(__UNCONST(str), M_PROP_STRING);
    262   1.1   thorpej 	}
    263   1.1   thorpej 
    264   1.1   thorpej 	return (ps);
    265   1.1   thorpej }
    266   1.1   thorpej 
    267  1.14   thorpej _PROP_DEPRECATED(prop_string_create,
    268  1.14   thorpej     "this program uses prop_string_create(); all functions "
    269  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    270   1.1   thorpej prop_string_t
    271   1.1   thorpej prop_string_create(void)
    272   1.1   thorpej {
    273   1.1   thorpej 
    274  1.14   thorpej 	return (_prop_string_alloc(PS_F_MUTABLE));
    275   1.1   thorpej }
    276   1.1   thorpej 
    277  1.14   thorpej _PROP_DEPRECATED(prop_string_create_cstring,
    278  1.14   thorpej     "this program uses prop_string_create_cstring(); all functions "
    279  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    280   1.1   thorpej prop_string_t
    281   1.1   thorpej prop_string_create_cstring(const char *str)
    282   1.1   thorpej {
    283   1.1   thorpej 	prop_string_t ps;
    284   1.1   thorpej 	char *cp;
    285   1.1   thorpej 	size_t len;
    286   1.1   thorpej 
    287  1.14   thorpej 	_PROP_ASSERT(str != NULL);
    288  1.14   thorpej 
    289  1.14   thorpej 	ps = _prop_string_alloc(PS_F_MUTABLE);
    290   1.1   thorpej 	if (ps != NULL) {
    291   1.1   thorpej 		len = strlen(str);
    292   1.1   thorpej 		cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    293   1.1   thorpej 		if (cp == NULL) {
    294   1.8     joerg 			prop_object_release(ps);
    295   1.1   thorpej 			return (NULL);
    296   1.1   thorpej 		}
    297   1.1   thorpej 		strcpy(cp, str);
    298   1.1   thorpej 		ps->ps_mutable = cp;
    299   1.1   thorpej 		ps->ps_size = len;
    300   1.1   thorpej 	}
    301   1.1   thorpej 	return (ps);
    302   1.1   thorpej }
    303   1.1   thorpej 
    304  1.14   thorpej _PROP_DEPRECATED(prop_string_create_cstring_nocopy,
    305  1.14   thorpej     "this program uses prop_string_create_cstring_nocopy(), "
    306  1.14   thorpej     "which is deprecated; use prop_string_create_nocopy() instead.")
    307  1.14   thorpej prop_string_t
    308  1.14   thorpej prop_string_create_cstring_nocopy(const char *str)
    309  1.14   thorpej {
    310  1.14   thorpej 	return prop_string_create_nocopy(str);
    311  1.14   thorpej }
    312  1.14   thorpej 
    313   1.1   thorpej /*
    314  1.14   thorpej  * prop_string_create_format --
    315  1.14   thorpej  *	Create a string object using the provided format string.
    316   1.1   thorpej  */
    317  1.14   thorpej prop_string_t __printflike(1, 2)
    318  1.14   thorpej prop_string_create_format(const char *fmt, ...)
    319   1.1   thorpej {
    320  1.14   thorpej 	char *str = NULL;
    321  1.14   thorpej 	int len;
    322  1.15  christos 	size_t nlen;
    323  1.14   thorpej 	va_list ap;
    324  1.14   thorpej 
    325  1.14   thorpej 	_PROP_ASSERT(fmt != NULL);
    326  1.14   thorpej 
    327  1.14   thorpej 	va_start(ap, fmt);
    328  1.14   thorpej 	len = vsnprintf(NULL, 0, fmt, ap);
    329  1.14   thorpej 	va_end(ap);
    330  1.14   thorpej 
    331  1.14   thorpej 	if (len < 0)
    332  1.14   thorpej 		return (NULL);
    333  1.15  christos 	nlen = len + 1;
    334  1.14   thorpej 
    335  1.15  christos 	str = _PROP_MALLOC(nlen, M_PROP_STRING);
    336  1.14   thorpej 	if (str == NULL)
    337  1.14   thorpej 		return (NULL);
    338  1.14   thorpej 
    339  1.14   thorpej 	va_start(ap, fmt);
    340  1.15  christos 	vsnprintf(str, nlen, fmt, ap);
    341  1.14   thorpej 	va_end(ap);
    342  1.14   thorpej 
    343  1.18   thorpej 	return _prop_string_instantiate(0, str, (size_t)len);
    344   1.1   thorpej }
    345   1.1   thorpej 
    346   1.1   thorpej /*
    347  1.14   thorpej  * prop_string_create_copy --
    348  1.14   thorpej  *	Create a string object by coping the provided constant string.
    349  1.14   thorpej  */
    350  1.14   thorpej prop_string_t
    351  1.14   thorpej prop_string_create_copy(const char *str)
    352  1.14   thorpej {
    353  1.14   thorpej 	return prop_string_create_format("%s", str);
    354  1.14   thorpej }
    355  1.14   thorpej 
    356  1.14   thorpej /*
    357  1.14   thorpej  * prop_string_create_nocopy --
    358  1.14   thorpej  *	Create a string object using the provided external constant
    359  1.14   thorpej  *	string.
    360  1.14   thorpej  */
    361  1.14   thorpej prop_string_t
    362  1.14   thorpej prop_string_create_nocopy(const char *str)
    363  1.14   thorpej {
    364  1.14   thorpej 
    365  1.14   thorpej 	_PROP_ASSERT(str != NULL);
    366  1.14   thorpej 
    367  1.14   thorpej 	return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str));
    368  1.14   thorpej }
    369  1.14   thorpej 
    370  1.14   thorpej /*
    371   1.1   thorpej  * prop_string_copy --
    372  1.14   thorpej  *	Copy a string.  This reduces to a retain in the common case.
    373  1.14   thorpej  *	Deprecated mutable string objects must be copied.
    374   1.1   thorpej  */
    375   1.1   thorpej prop_string_t
    376   1.1   thorpej prop_string_copy(prop_string_t ops)
    377   1.1   thorpej {
    378  1.14   thorpej 	char *cp;
    379   1.1   thorpej 
    380   1.3   thorpej 	if (! prop_object_is_string(ops))
    381   1.3   thorpej 		return (NULL);
    382   1.1   thorpej 
    383  1.14   thorpej 	if ((ops->ps_flags & PS_F_MUTABLE) == 0) {
    384  1.14   thorpej 		prop_object_retain(ops);
    385  1.14   thorpej 		return (ops);
    386   1.1   thorpej 	}
    387  1.14   thorpej 
    388  1.14   thorpej 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
    389  1.14   thorpej 	if (cp == NULL)
    390  1.14   thorpej 		return NULL;
    391  1.17  riastrad 
    392  1.14   thorpej 	strcpy(cp, prop_string_contents(ops));
    393  1.14   thorpej 
    394  1.18   thorpej 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
    395   1.1   thorpej }
    396   1.1   thorpej 
    397  1.14   thorpej _PROP_DEPRECATED(prop_string_copy_mutable,
    398  1.14   thorpej     "this program uses prop_string_copy_mutable(); all functions "
    399  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    400   1.1   thorpej prop_string_t
    401   1.1   thorpej prop_string_copy_mutable(prop_string_t ops)
    402   1.1   thorpej {
    403   1.1   thorpej 	char *cp;
    404   1.1   thorpej 
    405   1.3   thorpej 	if (! prop_object_is_string(ops))
    406   1.3   thorpej 		return (NULL);
    407   1.1   thorpej 
    408  1.14   thorpej 	cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
    409  1.14   thorpej 	if (cp == NULL)
    410  1.14   thorpej 		return NULL;
    411  1.17  riastrad 
    412  1.14   thorpej 	strcpy(cp, prop_string_contents(ops));
    413  1.14   thorpej 
    414  1.18   thorpej 	return _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
    415   1.1   thorpej }
    416   1.1   thorpej 
    417   1.1   thorpej /*
    418   1.1   thorpej  * prop_string_size --
    419   1.3   thorpej  *	Return the size of the string, not including the terminating NUL.
    420   1.1   thorpej  */
    421   1.1   thorpej size_t
    422   1.1   thorpej prop_string_size(prop_string_t ps)
    423   1.1   thorpej {
    424   1.1   thorpej 
    425   1.3   thorpej 	if (! prop_object_is_string(ps))
    426   1.3   thorpej 		return (0);
    427   1.3   thorpej 
    428   1.1   thorpej 	return (ps->ps_size);
    429   1.1   thorpej }
    430   1.1   thorpej 
    431   1.1   thorpej /*
    432  1.14   thorpej  * prop_string_value --
    433  1.14   thorpej  *	Returns a pointer to the string object's value.  This pointer
    434  1.14   thorpej  *	remains valid only as long as the string object.
    435  1.14   thorpej  */
    436  1.14   thorpej const char *
    437  1.14   thorpej prop_string_value(prop_string_t ps)
    438  1.14   thorpej {
    439  1.14   thorpej 
    440  1.14   thorpej 	if (! prop_object_is_string(ps))
    441  1.14   thorpej 		return (NULL);
    442  1.14   thorpej 
    443  1.14   thorpej 	if ((ps->ps_flags & PS_F_MUTABLE) == 0)
    444  1.14   thorpej 		return (ps->ps_immutable);
    445  1.17  riastrad 
    446  1.14   thorpej 	return (prop_string_contents(ps));
    447  1.14   thorpej }
    448  1.14   thorpej 
    449  1.14   thorpej /*
    450  1.14   thorpej  * prop_string_copy_value --
    451  1.14   thorpej  *	Copy the string object's value into the supplied buffer.
    452   1.1   thorpej  */
    453   1.7   thorpej bool
    454  1.14   thorpej prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen)
    455  1.14   thorpej {
    456  1.14   thorpej 
    457  1.14   thorpej 	if (! prop_object_is_string(ps))
    458  1.14   thorpej 		return (false);
    459  1.14   thorpej 
    460  1.14   thorpej 	if (buf == NULL || buflen < ps->ps_size + 1)
    461  1.14   thorpej 		return (false);
    462  1.17  riastrad 
    463  1.14   thorpej 	strcpy(buf, prop_string_contents(ps));
    464  1.14   thorpej 
    465  1.14   thorpej 	return (true);
    466  1.14   thorpej }
    467  1.14   thorpej 
    468  1.14   thorpej _PROP_DEPRECATED(prop_string_mutable,
    469  1.14   thorpej     "this program uses prop_string_mutable(); all functions "
    470  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    471  1.14   thorpej bool
    472   1.1   thorpej prop_string_mutable(prop_string_t ps)
    473   1.1   thorpej {
    474   1.1   thorpej 
    475   1.3   thorpej 	if (! prop_object_is_string(ps))
    476   1.7   thorpej 		return (false);
    477   1.3   thorpej 
    478  1.14   thorpej 	return ((ps->ps_flags & PS_F_MUTABLE) != 0);
    479   1.1   thorpej }
    480   1.1   thorpej 
    481  1.14   thorpej _PROP_DEPRECATED(prop_string_cstring,
    482  1.14   thorpej     "this program uses prop_string_cstring(), "
    483  1.14   thorpej     "which is deprecated; use prop_string_copy_value() instead.")
    484   1.1   thorpej char *
    485   1.1   thorpej prop_string_cstring(prop_string_t ps)
    486   1.1   thorpej {
    487   1.1   thorpej 	char *cp;
    488   1.1   thorpej 
    489   1.3   thorpej 	if (! prop_object_is_string(ps))
    490   1.3   thorpej 		return (NULL);
    491   1.3   thorpej 
    492   1.1   thorpej 	cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP);
    493   1.1   thorpej 	if (cp != NULL)
    494   1.1   thorpej 		strcpy(cp, prop_string_contents(ps));
    495  1.17  riastrad 
    496   1.1   thorpej 	return (cp);
    497   1.1   thorpej }
    498   1.1   thorpej 
    499  1.14   thorpej _PROP_DEPRECATED(prop_string_cstring_nocopy,
    500  1.14   thorpej     "this program uses prop_string_cstring_nocopy(), "
    501  1.14   thorpej     "which is deprecated; use prop_string_value() instead.")
    502   1.1   thorpej const char *
    503   1.1   thorpej prop_string_cstring_nocopy(prop_string_t ps)
    504   1.1   thorpej {
    505   1.1   thorpej 
    506   1.3   thorpej 	if (! prop_object_is_string(ps))
    507   1.3   thorpej 		return (NULL);
    508   1.3   thorpej 
    509   1.1   thorpej 	return (prop_string_contents(ps));
    510   1.1   thorpej }
    511   1.1   thorpej 
    512  1.14   thorpej _PROP_DEPRECATED(prop_string_append,
    513  1.14   thorpej     "this program uses prop_string_append(); all functions "
    514  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    515   1.7   thorpej bool
    516   1.1   thorpej prop_string_append(prop_string_t dst, prop_string_t src)
    517   1.1   thorpej {
    518   1.1   thorpej 	char *ocp, *cp;
    519   1.1   thorpej 	size_t len;
    520   1.1   thorpej 
    521   1.3   thorpej 	if (! (prop_object_is_string(dst) &&
    522   1.3   thorpej 	       prop_object_is_string(src)))
    523   1.7   thorpej 		return (false);
    524   1.1   thorpej 
    525  1.14   thorpej 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
    526   1.7   thorpej 		return (false);
    527   1.1   thorpej 
    528   1.1   thorpej 	len = dst->ps_size + src->ps_size;
    529   1.1   thorpej 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    530   1.1   thorpej 	if (cp == NULL)
    531   1.7   thorpej 		return (false);
    532  1.12  christos 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst),
    533   1.1   thorpej 		prop_string_contents(src));
    534   1.1   thorpej 	ocp = dst->ps_mutable;
    535   1.1   thorpej 	dst->ps_mutable = cp;
    536   1.1   thorpej 	dst->ps_size = len;
    537   1.1   thorpej 	if (ocp != NULL)
    538   1.1   thorpej 		_PROP_FREE(ocp, M_PROP_STRING);
    539  1.17  riastrad 
    540   1.7   thorpej 	return (true);
    541   1.1   thorpej }
    542   1.1   thorpej 
    543  1.14   thorpej _PROP_DEPRECATED(prop_string_append_cstring,
    544  1.14   thorpej     "this program uses prop_string_append_cstring(); all functions "
    545  1.14   thorpej     "supporting mutable prop_strings are deprecated.")
    546   1.7   thorpej bool
    547   1.1   thorpej prop_string_append_cstring(prop_string_t dst, const char *src)
    548   1.1   thorpej {
    549   1.1   thorpej 	char *ocp, *cp;
    550   1.1   thorpej 	size_t len;
    551   1.1   thorpej 
    552   1.3   thorpej 	if (! prop_object_is_string(dst))
    553   1.7   thorpej 		return (false);
    554   1.3   thorpej 
    555   1.1   thorpej 	_PROP_ASSERT(src != NULL);
    556   1.1   thorpej 
    557  1.14   thorpej 	if ((dst->ps_flags & PS_F_MUTABLE) == 0)
    558   1.7   thorpej 		return (false);
    559  1.14   thorpej 
    560   1.1   thorpej 	len = dst->ps_size + strlen(src);
    561   1.1   thorpej 	cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
    562   1.1   thorpej 	if (cp == NULL)
    563   1.7   thorpej 		return (false);
    564  1.12  christos 	snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src);
    565   1.1   thorpej 	ocp = dst->ps_mutable;
    566   1.1   thorpej 	dst->ps_mutable = cp;
    567   1.1   thorpej 	dst->ps_size = len;
    568   1.1   thorpej 	if (ocp != NULL)
    569   1.1   thorpej 		_PROP_FREE(ocp, M_PROP_STRING);
    570  1.17  riastrad 
    571   1.7   thorpej 	return (true);
    572   1.1   thorpej }
    573   1.1   thorpej 
    574   1.1   thorpej /*
    575   1.1   thorpej  * prop_string_equals --
    576   1.7   thorpej  *	Return true if two strings are equivalent.
    577   1.1   thorpej  */
    578   1.7   thorpej bool
    579   1.1   thorpej prop_string_equals(prop_string_t str1, prop_string_t str2)
    580   1.1   thorpej {
    581   1.9     joerg 	if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
    582   1.9     joerg 		return (false);
    583   1.1   thorpej 
    584   1.9     joerg 	return prop_object_equals(str1, str2);
    585   1.1   thorpej }
    586   1.1   thorpej 
    587   1.1   thorpej /*
    588  1.14   thorpej  * prop_string_equals_string --
    589  1.14   thorpej  *	Return true if the string object is equivalent to the specified
    590   1.1   thorpej  *	C string.
    591   1.1   thorpej  */
    592   1.7   thorpej bool
    593  1.14   thorpej prop_string_equals_string(prop_string_t ps, const char *cp)
    594   1.1   thorpej {
    595   1.1   thorpej 
    596   1.3   thorpej 	if (! prop_object_is_string(ps))
    597   1.7   thorpej 		return (false);
    598   1.3   thorpej 
    599   1.1   thorpej 	return (strcmp(prop_string_contents(ps), cp) == 0);
    600   1.1   thorpej }
    601   1.1   thorpej 
    602  1.14   thorpej _PROP_DEPRECATED(prop_string_equals_cstring,
    603  1.14   thorpej     "this program uses prop_string_equals_cstring(), "
    604  1.14   thorpej     "which is deprecated; prop_string_equals_string() instead.")
    605  1.14   thorpej bool
    606  1.14   thorpej prop_string_equals_cstring(prop_string_t ps, const char *cp)
    607  1.14   thorpej {
    608  1.14   thorpej 	return prop_string_equals_string(ps, cp);
    609  1.14   thorpej }
    610  1.14   thorpej 
    611  1.14   thorpej /*
    612  1.14   thorpej  * prop_string_compare --
    613  1.14   thorpej  *	Compare two string objects, using strcmp() semantics.
    614  1.14   thorpej  */
    615  1.14   thorpej int
    616  1.14   thorpej prop_string_compare(prop_string_t ps1, prop_string_t ps2)
    617  1.14   thorpej {
    618  1.14   thorpej 	if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2))
    619  1.14   thorpej 		return (-666);	/* arbitrary */
    620  1.14   thorpej 
    621  1.14   thorpej 	return (strcmp(prop_string_contents(ps1),
    622  1.14   thorpej 		       prop_string_contents(ps2)));
    623  1.14   thorpej }
    624  1.14   thorpej 
    625  1.14   thorpej /*
    626  1.14   thorpej  * prop_string_compare_string --
    627  1.14   thorpej  *	Compare a string object to the specified C string, using
    628  1.14   thorpej  *	strcmp() semantics.
    629  1.14   thorpej  */
    630  1.14   thorpej int
    631  1.14   thorpej prop_string_compare_string(prop_string_t ps, const char *cp)
    632  1.14   thorpej {
    633  1.14   thorpej 	if (!prop_object_is_string(ps))
    634  1.14   thorpej 		return (-666);	/* arbitrary */
    635  1.14   thorpej 
    636  1.14   thorpej 	return (strcmp(prop_string_contents(ps), cp));
    637  1.14   thorpej }
    638  1.14   thorpej 
    639   1.1   thorpej /*
    640   1.1   thorpej  * _prop_string_internalize --
    641   1.1   thorpej  *	Parse a <string>...</string> and return the object created from the
    642   1.1   thorpej  *	external representation.
    643   1.1   thorpej  */
    644   1.8     joerg /* ARGSUSED */
    645   1.8     joerg bool
    646   1.8     joerg _prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
    647   1.8     joerg     struct _prop_object_internalize_context *ctx)
    648   1.1   thorpej {
    649   1.1   thorpej 	char *str;
    650   1.1   thorpej 	size_t len, alen;
    651   1.1   thorpej 
    652   1.8     joerg 	if (ctx->poic_is_empty_element) {
    653   1.8     joerg 		*obj = prop_string_create();
    654   1.8     joerg 		return (true);
    655   1.8     joerg 	}
    656  1.17  riastrad 
    657   1.1   thorpej 	/* No attributes recognized here. */
    658   1.1   thorpej 	if (ctx->poic_tagattr != NULL)
    659   1.8     joerg 		return (true);
    660   1.1   thorpej 
    661   1.1   thorpej 	/* Compute the length of the result. */
    662   1.6    martin 	if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
    663   1.7   thorpej 						   NULL) == false)
    664   1.8     joerg 		return (true);
    665  1.17  riastrad 
    666   1.1   thorpej 	str = _PROP_MALLOC(len + 1, M_PROP_STRING);
    667   1.1   thorpej 	if (str == NULL)
    668   1.8     joerg 		return (true);
    669  1.17  riastrad 
    670   1.1   thorpej 	if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
    671   1.7   thorpej 						   &ctx->poic_cp) == false ||
    672   1.1   thorpej 	    alen != len) {
    673   1.1   thorpej 		_PROP_FREE(str, M_PROP_STRING);
    674   1.8     joerg 		return (true);
    675   1.1   thorpej 	}
    676   1.1   thorpej 	str[len] = '\0';
    677   1.1   thorpej 
    678   1.1   thorpej 	if (_prop_object_internalize_find_tag(ctx, "string",
    679   1.7   thorpej 					      _PROP_TAG_TYPE_END) == false) {
    680   1.1   thorpej 		_PROP_FREE(str, M_PROP_STRING);
    681   1.8     joerg 		return (true);
    682   1.1   thorpej 	}
    683   1.1   thorpej 
    684  1.18   thorpej 	*obj = _prop_string_instantiate(0, str, len);
    685   1.8     joerg 	return (true);
    686   1.1   thorpej }
    687