NetBSD/common/lib/libppath/ppath.c

940 lines
18 KiB
C

/* $NetBSD: ppath.c,v 1.5 2020/06/06 22:28:07 thorpej Exp $ */
/*-
* Copyright (c) 2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by David Young <dyoung@NetBSD.org>.
*
* 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.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: ppath.c,v 1.5 2020/06/06 22:28:07 thorpej Exp $");
#ifdef _KERNEL
#include <sys/systm.h>
#endif
#include <ppath/ppath.h>
#include <ppath/ppath_impl.h>
enum _ppath_type {
PPATH_T_IDX = 0
, PPATH_T_KEY = 1
};
typedef enum _ppath_type ppath_type_t;
struct _ppath_component {
unsigned int pc_refcnt;
ppath_type_t pc_type;
union {
char *u_key;
unsigned int u_idx;
} pc_u;
#define pc_key pc_u.u_key
#define pc_idx pc_u.u_idx
};
struct _ppath {
unsigned int p_refcnt;
unsigned int p_len;
ppath_component_t *p_cmpt[PPATH_MAX_COMPONENTS];
};
static int ppath_copydel_object_of_type(prop_object_t, prop_object_t *,
const ppath_t *, prop_type_t);
static int ppath_copyset_object_helper(prop_object_t, prop_object_t *,
const ppath_t *, const prop_object_t);
static void
ppath_strfree(char *s)
{
size_t size = strlen(s) + 1;
ppath_free(s, size);
}
static char *
ppath_strdup(const char *s)
{
size_t size = strlen(s) + 1;
char *p;
if ((p = ppath_alloc(size)) == NULL)
return NULL;
return strcpy(p, s);
}
int
ppath_component_idx(const ppath_component_t *pc)
{
if (pc->pc_type != PPATH_T_IDX)
return -1;
return pc->pc_idx;
}
const char *
ppath_component_key(const ppath_component_t *pc)
{
if (pc->pc_type != PPATH_T_KEY)
return NULL;
return pc->pc_key;
}
ppath_component_t *
ppath_idx(unsigned int idx)
{
ppath_component_t *pc;
if ((pc = ppath_alloc(sizeof(*pc))) == NULL)
return NULL;
pc->pc_idx = idx;
pc->pc_type = PPATH_T_IDX;
pc->pc_refcnt = 1;
ppath_component_extant_inc();
return pc;
}
ppath_component_t *
ppath_key(const char *key)
{
ppath_component_t *pc;
if ((pc = ppath_alloc(sizeof(*pc))) == NULL)
return NULL;
if ((pc->pc_key = ppath_strdup(key)) == NULL) {
ppath_free(pc, sizeof(*pc));
return NULL;
}
pc->pc_type = PPATH_T_KEY;
pc->pc_refcnt = 1;
ppath_component_extant_inc();
return pc;
}
ppath_component_t *
ppath_component_retain(ppath_component_t *pc)
{
ppath_assert(pc->pc_refcnt != 0);
pc->pc_refcnt++;
return pc;
}
void
ppath_component_release(ppath_component_t *pc)
{
ppath_assert(pc->pc_refcnt != 0);
if (--pc->pc_refcnt != 0)
return;
if (pc->pc_type == PPATH_T_KEY)
ppath_strfree(pc->pc_key);
ppath_component_extant_dec();
ppath_free(pc, sizeof(*pc));
}
ppath_t *
ppath_create(void)
{
ppath_t *p;
if ((p = ppath_alloc(sizeof(*p))) == NULL)
return NULL;
p->p_refcnt = 1;
ppath_extant_inc();
return p;
}
ppath_t *
ppath_pop(ppath_t *p, ppath_component_t **pcp)
{
ppath_component_t *pc;
if (p == NULL || p->p_len == 0)
return NULL;
pc = p->p_cmpt[--p->p_len];
if (pcp != NULL)
*pcp = pc;
else
ppath_component_release(pc);
return p;
}
ppath_t *
ppath_push(ppath_t *p, ppath_component_t *pc)
{
if (p == NULL || p->p_len == __arraycount(p->p_cmpt))
return NULL;
p->p_cmpt[p->p_len++] = ppath_component_retain(pc);
return p;
}
ppath_component_t *
ppath_component_at(const ppath_t *p, unsigned int i)
{
ppath_component_t *pc;
if (p == NULL || i >= p->p_len)
return NULL;
pc = p->p_cmpt[i];
return ppath_component_retain(pc);
}
unsigned int
ppath_length(const ppath_t *p)
{
return p->p_len;
}
ppath_t *
ppath_subpath(const ppath_t *p, unsigned int first, unsigned int exclast)
{
unsigned int i;
ppath_t *np;
ppath_component_t *pc;
if (p == NULL || (np = ppath_create()) == NULL)
return NULL;
for (i = first; i < exclast; i++) {
if ((pc = ppath_component_at(p, i)) == NULL)
break;
ppath_push(np, pc);
ppath_component_release(pc);
}
return np;
}
ppath_t *
ppath_push_idx(ppath_t *p0, unsigned int idx)
{
ppath_component_t *pc;
ppath_t *p;
if ((pc = ppath_idx(idx)) == NULL)
return NULL;
p = ppath_push(p0, pc);
ppath_component_release(pc);
return p;
}
ppath_t *
ppath_push_key(ppath_t *p0, const char *key)
{
ppath_component_t *pc;
ppath_t *p;
if ((pc = ppath_key(key)) == NULL)
return NULL;
p = ppath_push(p0, pc);
ppath_component_release(pc);
return p;
}
ppath_t *
ppath_replace_idx(ppath_t *p, unsigned int idx)
{
ppath_component_t *pc0, *pc;
if (p == NULL || p->p_len == 0)
return NULL;
pc0 = p->p_cmpt[p->p_len - 1];
if (pc0->pc_type != PPATH_T_IDX)
return NULL;
if ((pc = ppath_idx(idx)) == NULL)
return NULL;
p->p_cmpt[p->p_len - 1] = pc;
ppath_component_release(pc0);
return p;
}
ppath_t *
ppath_replace_key(ppath_t *p, const char *key)
{
ppath_component_t *pc0, *pc;
if (p == NULL || p->p_len == 0)
return NULL;
pc0 = p->p_cmpt[p->p_len - 1];
if (pc0->pc_type != PPATH_T_KEY)
return NULL;
if ((pc = ppath_key(key)) == NULL)
return NULL;
p->p_cmpt[p->p_len - 1] = pc;
ppath_component_release(pc0);
return p;
}
ppath_t *
ppath_copy(const ppath_t *p0)
{
ppath_t *p;
unsigned int i;
if ((p = ppath_create()) == NULL)
return NULL;
for (i = 0; i < p0->p_len; i++)
p->p_cmpt[i] = ppath_component_retain(p0->p_cmpt[i]);
p->p_len = p0->p_len;
return p;
}
ppath_t *
ppath_retain(ppath_t *p)
{
assert(p->p_refcnt != 0);
p->p_refcnt++;
return p;
}
void
ppath_release(ppath_t *p)
{
unsigned int i;
assert(p->p_refcnt != 0);
if (--p->p_refcnt != 0)
return;
for (i = 0; i < p->p_len; i++)
ppath_component_release(p->p_cmpt[i]);
ppath_extant_dec();
ppath_free(p, sizeof(*p));
}
static prop_object_t
ppath_lookup_helper(prop_object_t o0, const ppath_t *p, prop_object_t *pop,
ppath_component_t **pcp, unsigned int *ip)
{
unsigned int i;
prop_object_t o, po;
ppath_type_t t;
ppath_component_t *pc = NULL;
for (po = NULL, o = o0, i = 0; i < p->p_len && o != NULL; i++) {
pc = p->p_cmpt[i];
t = pc->pc_type;
switch (prop_object_type(o)) {
case PROP_TYPE_ARRAY:
po = o;
o = (t == PPATH_T_IDX)
? prop_array_get(o, pc->pc_idx)
: NULL;
break;
case PROP_TYPE_DICTIONARY:
po = o;
o = (t == PPATH_T_KEY)
? prop_dictionary_get(o, pc->pc_key)
: NULL;
break;
default:
o = NULL;
break;
}
}
if (pop != NULL)
*pop = po;
if (pcp != NULL)
*pcp = pc;
if (ip != NULL)
*ip = i;
return o;
}
prop_object_t
ppath_lookup(prop_object_t o, const ppath_t *p)
{
return ppath_lookup_helper(o, p, NULL, NULL, NULL);
}
static int
ppath_create_object_and_release(prop_object_t o, const ppath_t *p,
prop_object_t v)
{
int rc;
rc = ppath_create_object(o, p, v);
prop_object_release(v);
return rc;
}
int
ppath_create_string(prop_object_t o, const ppath_t *p, const char *s)
{
return ppath_create_object_and_release(o, p,
prop_string_create_copy(s));
}
int
ppath_create_data(prop_object_t o, const ppath_t *p,
const void *data, size_t size)
{
return ppath_create_object_and_release(o, p,
prop_data_create_copy(data, size));
}
int
ppath_create_uint64(prop_object_t o, const ppath_t *p, uint64_t u)
{
return ppath_create_object_and_release(o, p,
prop_number_create_unsigned(u));
}
int
ppath_create_int64(prop_object_t o, const ppath_t *p, int64_t i)
{
return ppath_create_object_and_release(o, p,
prop_number_create_signed(i));
}
int
ppath_create_bool(prop_object_t o, const ppath_t *p, bool b)
{
return ppath_create_object_and_release(o, p, prop_bool_create(b));
}
int
ppath_create_object(prop_object_t o, const ppath_t *p, prop_object_t v)
{
unsigned int i;
ppath_component_t *pc;
prop_object_t po;
if (ppath_lookup_helper(o, p, &po, &pc, &i) != NULL)
return EEXIST;
if (i != ppath_length(p))
return ENOENT;
switch (pc->pc_type) {
case PPATH_T_IDX:
return prop_array_set(po, pc->pc_idx, v) ? 0 : ENOMEM;
case PPATH_T_KEY:
return prop_dictionary_set(po, pc->pc_key, v) ? 0 : ENOMEM;
default:
return ENOENT;
}
}
int
ppath_set_object(prop_object_t o, const ppath_t *p, prop_object_t v)
{
ppath_component_t *pc;
prop_object_t po;
if (ppath_lookup_helper(o, p, &po, &pc, NULL) == NULL)
return ENOENT;
switch (pc->pc_type) {
case PPATH_T_IDX:
return prop_array_set(po, pc->pc_idx, v) ? 0 : ENOMEM;
case PPATH_T_KEY:
return prop_dictionary_set(po, pc->pc_key, v) ? 0 : ENOMEM;
default:
return ENOENT;
}
}
static int
ppath_set_object_and_release(prop_object_t o, const ppath_t *p, prop_object_t v)
{
prop_object_t ov;
int rc;
if (v == NULL)
return ENOMEM;
if ((ov = ppath_lookup_helper(o, p, NULL, NULL, NULL)) == NULL)
return ENOENT;
if (prop_object_type(ov) != prop_object_type(v))
return EFTYPE;
rc = ppath_set_object(o, p, v);
prop_object_release(v);
return rc;
}
int
ppath_get_object(prop_object_t o, const ppath_t *p, prop_object_t *vp)
{
prop_object_t v;
if ((v = ppath_lookup_helper(o, p, NULL, NULL, NULL)) == NULL)
return ENOENT;
if (vp != NULL)
*vp = v;
return 0;
}
static int
ppath_get_object_of_type(prop_object_t o, const ppath_t *p, prop_object_t *vp,
prop_type_t t)
{
int rc;
if ((rc = ppath_get_object(o, p, vp)) != 0)
return rc;
return (prop_object_type(*vp) == t) ? 0 : EFTYPE;
}
int
ppath_delete_object(prop_object_t o, const ppath_t *p)
{
ppath_component_t *pc;
prop_object_t po;
if (ppath_lookup_helper(o, p, &po, &pc, NULL) == NULL)
return ENOENT;
switch (pc->pc_type) {
case PPATH_T_IDX:
prop_array_remove(po, pc->pc_idx);
return 0;
case PPATH_T_KEY:
prop_dictionary_remove(po, pc->pc_key);
return 0;
default:
return ENOENT;
}
}
static int
ppath_delete_object_of_type(prop_object_t o, const ppath_t *p, prop_type_t t)
{
prop_object_t v;
if ((v = ppath_lookup_helper(o, p, NULL, NULL, NULL)) == NULL)
return ENOENT;
if (prop_object_type(v) != t)
return EFTYPE;
return ppath_delete_object(o, p);
}
int
ppath_copydel_string(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copydel_object_of_type(o, op, p, PROP_TYPE_STRING);
}
int
ppath_copydel_data(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copydel_object_of_type(o, op, p, PROP_TYPE_DATA);
}
int
ppath_copydel_uint64(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copydel_object_of_type(o, op, p, PROP_TYPE_NUMBER);
}
int
ppath_copydel_int64(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copydel_object_of_type(o, op, p, PROP_TYPE_NUMBER);
}
int
ppath_copydel_bool(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copydel_object_of_type(o, op, p, PROP_TYPE_BOOL);
}
static int
ppath_copydel_object_of_type(prop_object_t o, prop_object_t *op,
const ppath_t *p, prop_type_t t)
{
prop_object_t v;
if ((v = ppath_lookup_helper(o, p, NULL, NULL, NULL)) == NULL)
return ENOENT;
if (prop_object_type(v) != t)
return EFTYPE;
return ppath_copydel_object(o, op, p);
}
int
ppath_copydel_object(prop_object_t o, prop_object_t *op, const ppath_t *p)
{
return ppath_copyset_object_helper(o, op, p, NULL);
}
int
ppath_copyset_object(prop_object_t o, prop_object_t *op, const ppath_t *p,
const prop_object_t v)
{
ppath_assert(v != NULL);
return ppath_copyset_object_helper(o, op, p, v);
}
static int
ppath_copyset_object_helper(prop_object_t o, prop_object_t *op,
const ppath_t *p0, const prop_object_t v0)
{
bool copy, success;
ppath_component_t *npc, *pc;
ppath_t *cp, *p;
prop_object_t npo = NULL, po, v;
for (cp = p = ppath_copy(p0), v = v0;
p != NULL;
p = ppath_pop(p, NULL), v = npo) {
if (ppath_lookup_helper(o, p, &po, &pc, NULL) == NULL)
return ENOENT;
if (pc == NULL)
break;
if (ppath_lookup_helper(*op, p, &npo, &npc, NULL) == NULL)
npo = po;
copy = (npo == po);
switch (pc->pc_type) {
case PPATH_T_IDX:
if (copy && (npo = prop_array_copy_mutable(po)) == NULL)
return ENOMEM;
success = (v == NULL)
? (prop_array_remove(npo, pc->pc_idx), true)
: prop_array_set(npo, pc->pc_idx, v);
break;
case PPATH_T_KEY:
if (copy &&
(npo = prop_dictionary_copy_mutable(po)) == NULL)
return ENOMEM;
success = (v == NULL)
? (prop_dictionary_remove(npo, pc->pc_key), true)
: prop_dictionary_set(npo, pc->pc_key, v);
break;
default:
return ENOENT;
}
if (!success) {
if (copy)
prop_object_release(npo);
return ENOMEM;
}
}
if (cp == NULL)
return ENOMEM;
ppath_release(cp);
if (op != NULL && npo != NULL)
*op = npo;
else if (npo != NULL)
prop_object_release(npo);
return 0;
}
static int
ppath_copyset_object_and_release(prop_object_t o, prop_object_t *op,
const ppath_t *p, prop_object_t v)
{
prop_object_t ov;
int rc;
if (v == NULL)
return ENOMEM;
if ((ov = ppath_lookup_helper(o, p, NULL, NULL, NULL)) == NULL)
return ENOENT;
if (prop_object_type(ov) != prop_object_type(v))
return EFTYPE;
rc = ppath_copyset_object(o, op, p, v);
prop_object_release(v);
return rc;
}
int
ppath_copyset_bool(prop_object_t o, prop_object_t *op, const ppath_t *p, bool b)
{
return ppath_copyset_object_and_release(o, op, p, prop_bool_create(b));
}
int
ppath_set_bool(prop_object_t o, const ppath_t *p, bool b)
{
return ppath_set_object_and_release(o, p, prop_bool_create(b));
}
int
ppath_get_bool(prop_object_t o, const ppath_t *p, bool *bp)
{
prop_object_t v;
int rc;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_BOOL)) != 0)
return rc;
if (bp != NULL)
*bp = prop_bool_true(v);
return 0;
}
int
ppath_delete_bool(prop_object_t o, const ppath_t *p)
{
return ppath_delete_object_of_type(o, p, PROP_TYPE_BOOL);
}
int
ppath_copyset_data(prop_object_t o, prop_object_t *op, const ppath_t *p,
const void *data, size_t size)
{
return ppath_copyset_object_and_release(o, op, p,
prop_data_create_copy(data, size));
}
int
ppath_set_data(prop_object_t o, const ppath_t *p, const void *data, size_t size)
{
return ppath_set_object_and_release(o, p,
prop_data_create_copy(data, size));
}
int
ppath_get_data(prop_object_t o, const ppath_t *p, const void **datap,
size_t *sizep)
{
prop_object_t v;
int rc;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_DATA)) != 0)
return rc;
if (datap != NULL)
*datap = prop_data_value(v);
if (sizep != NULL)
*sizep = prop_data_size(v);
return 0;
}
int
ppath_dup_data(prop_object_t o, const ppath_t *p, void **datap, size_t *sizep)
{
prop_object_t v;
int rc;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_DATA)) != 0)
return rc;
const size_t data_size = prop_data_size(v);
if (datap != NULL) {
void *buf = ppath_alloc(data_size);
if (buf != NULL)
(void) prop_data_copy_value(v, buf, data_size);
*datap = buf;
}
if (sizep != NULL)
*sizep = data_size;
return 0;
}
int
ppath_delete_data(prop_object_t o, const ppath_t *p)
{
return ppath_delete_object_of_type(o, p, PROP_TYPE_DATA);
}
int
ppath_copyset_int64(prop_object_t o, prop_object_t *op, const ppath_t *p,
int64_t i)
{
return ppath_copyset_object_and_release(o, op, p,
prop_number_create_signed(i));
}
int
ppath_set_int64(prop_object_t o, const ppath_t *p, int64_t i)
{
return ppath_set_object_and_release(o, p,
prop_number_create_signed(i));
}
int
ppath_get_int64(prop_object_t o, const ppath_t *p, int64_t *ip)
{
prop_object_t v;
int rc;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_DATA)) != 0)
return rc;
if (prop_number_unsigned(v))
return EFTYPE;
if (ip != NULL)
*ip = prop_number_signed_value(v);
return 0;
}
int
ppath_delete_int64(prop_object_t o, const ppath_t *p)
{
return ppath_delete_object_of_type(o, p, PROP_TYPE_NUMBER);
}
int
ppath_copyset_string(prop_object_t o, prop_object_t *op, const ppath_t *p,
const char *s)
{
return ppath_copyset_object_and_release(o, op, p,
prop_string_create_copy(s));
}
int
ppath_set_string(prop_object_t o, const ppath_t *p, const char *s)
{
return ppath_set_object_and_release(o, p,
prop_string_create_copy(s));
}
int
ppath_get_string(prop_object_t o, const ppath_t *p, const char **sp)
{
int rc;
prop_object_t v;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_STRING)) != 0)
return rc;
if (sp != NULL)
*sp = prop_string_value(v);
return 0;
}
int
ppath_dup_string(prop_object_t o, const ppath_t *p, char **sp)
{
int rc;
prop_object_t v;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_STRING)) != 0)
return rc;
const size_t string_size = prop_string_size(v);
if (sp != NULL) {
char *cp = ppath_alloc(string_size + 1);
if (cp != NULL)
(void)prop_string_copy_value(v, cp, string_size + 1);
*sp = cp;
}
return 0;
}
int
ppath_delete_string(prop_object_t o, const ppath_t *p)
{
return ppath_delete_object_of_type(o, p, PROP_TYPE_STRING);
}
int
ppath_copyset_uint64(prop_object_t o, prop_object_t *op, const ppath_t *p,
uint64_t u)
{
return ppath_copyset_object_and_release(o, op, p,
prop_number_create_unsigned(u));
}
int
ppath_set_uint64(prop_object_t o, const ppath_t *p, uint64_t u)
{
return ppath_set_object_and_release(o, p,
prop_number_create_unsigned(u));
}
int
ppath_get_uint64(prop_object_t o, const ppath_t *p, uint64_t *up)
{
prop_object_t v;
int rc;
if ((rc = ppath_get_object_of_type(o, p, &v, PROP_TYPE_DATA)) != 0)
return rc;
if (!prop_number_unsigned(v))
return EFTYPE;
if (up != NULL)
*up = prop_number_unsigned_value(v);
return 0;
}
int
ppath_delete_uint64(prop_object_t o, const ppath_t *p)
{
return ppath_delete_object_of_type(o, p, PROP_TYPE_NUMBER);
}