/*-
* Copyright (c) 2006, 2020, 2025 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
if ((ps->ps_flags & PS_F_MUTABLE) == 0) {
_PROP_MUTEX_LOCK(_prop_string_tree_mutex);
/*
* Double-check the retain count now that we've
* acquired the tree lock; holding this lock prevents
* new retains from coming in by finding it in the
* tree.
*/
if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0)
rb_tree_remove_node(&_prop_string_tree, ps);
else
ps = NULL;
_PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
if (ps == NULL)
return (_PROP_OBJECT_FREE_DONE);
}
/*
* prop_string_copy --
* Copy a string. This reduces to a retain in the common case.
* Deprecated mutable string objects must be copied.
*/
_PROP_EXPORT prop_string_t
prop_string_copy(prop_string_t ops)
{
char *cp;
/*
* prop_string_size --
* Return the size of the string, not including the terminating NUL.
*/
_PROP_EXPORT size_t
prop_string_size(prop_string_t ps)
{
if (! prop_object_is_string(ps))
return (0);
return (ps->ps_size);
}
/*
* prop_string_value --
* Returns a pointer to the string object's value. This pointer
* remains valid only as long as the string object.
*/
_PROP_EXPORT const char *
prop_string_value(prop_string_t ps)
{
if (! prop_object_is_string(ps))
return (NULL);
if ((ps->ps_flags & PS_F_MUTABLE) == 0)
return (ps->ps_immutable);
return (prop_string_contents(ps));
}
/*
* prop_string_copy_value --
* Copy the string object's value into the supplied buffer.
*/
_PROP_EXPORT bool
prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen)
{
_PROP_DEPRECATED(prop_string_cstring_nocopy,
"this program uses prop_string_cstring_nocopy(), "
"which is deprecated; use prop_string_value() instead.")
_PROP_EXPORT const char *
prop_string_cstring_nocopy(prop_string_t ps)
{
if (! prop_object_is_string(ps))
return (NULL);
return (prop_string_contents(ps));
}
_PROP_DEPRECATED(prop_string_append,
"this program uses prop_string_append(); all functions "
"supporting mutable prop_strings are deprecated.")
_PROP_EXPORT bool
prop_string_append(prop_string_t dst, prop_string_t src)
{
char *ocp, *cp;
size_t len;
if (! (prop_object_is_string(dst) &&
prop_object_is_string(src)))
return (false);
if ((dst->ps_flags & PS_F_MUTABLE) == 0)
return (false);
len = dst->ps_size + src->ps_size;
cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
if (cp == NULL)
return (false);
snprintf(cp, len + 1, "%s%s", prop_string_contents(dst),
prop_string_contents(src));
ocp = dst->ps_mutable;
dst->ps_mutable = cp;
dst->ps_size = len;
if (ocp != NULL)
_PROP_FREE(ocp, M_PROP_STRING);
return (true);
}
_PROP_DEPRECATED(prop_string_append_cstring,
"this program uses prop_string_append_cstring(); all functions "
"supporting mutable prop_strings are deprecated.")
_PROP_EXPORT bool
prop_string_append_cstring(prop_string_t dst, const char *src)
{
char *ocp, *cp;
size_t len;
if (! prop_object_is_string(dst))
return (false);
_PROP_ASSERT(src != NULL);
if ((dst->ps_flags & PS_F_MUTABLE) == 0)
return (false);
len = dst->ps_size + strlen(src);
cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
if (cp == NULL)
return (false);
snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src);
ocp = dst->ps_mutable;
dst->ps_mutable = cp;
dst->ps_size = len;
if (ocp != NULL)
_PROP_FREE(ocp, M_PROP_STRING);
return (true);
}
/*
* prop_string_equals --
* Return true if two strings are equivalent.
*/
_PROP_EXPORT bool
prop_string_equals(prop_string_t str1, prop_string_t str2)
{
if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
return (false);
return prop_object_equals(str1, str2);
}
/*
* prop_string_equals_string --
* Return true if the string object is equivalent to the specified
* C string.
*/
_PROP_EXPORT bool
prop_string_equals_string(prop_string_t ps, const char *cp)
{
/*
* prop_string_compare_string --
* Compare a string object to the specified C string, using
* strcmp() semantics.
*/
_PROP_EXPORT int
prop_string_compare_string(prop_string_t ps, const char *cp)
{
if (!prop_object_is_string(ps))
return (-666); /* arbitrary */
return (strcmp(prop_string_contents(ps), cp));
}
/*
* _prop_string_internalize --
* Parse a <string>...</string> and return the object created from the
* external representation.
*/
/* ARGSUSED */
bool
_prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
char *str;
size_t len, alen;
/*
* N.B. for empty JSON strings, the layer above us has made it
* look like XML.
*/
if (ctx->poic_is_empty_element) {
*obj = prop_string_create();
return (true);
}
/* No attributes recognized here. */
if (ctx->poic_tagattr != NULL)
return (true);
/* Compute the length of the result. */
if (_prop_intern_decode_string(ctx, NULL, 0, &len, NULL) == false)
return (true);