Blame src/proplist.c

Packit 3ae693
/*-*- Mode: C; c-basic-offset: 8 -*-*/
Packit 3ae693
Packit 3ae693
/***
Packit 3ae693
  This file is part of libcanberra.
Packit 3ae693
Packit 3ae693
  Copyright 2008 Lennart Poettering
Packit 3ae693
Packit 3ae693
  libcanberra is free software; you can redistribute it and/or modify
Packit 3ae693
  it under the terms of the GNU Lesser General Public License as
Packit 3ae693
  published by the Free Software Foundation, either version 2.1 of the
Packit 3ae693
  License, or (at your option) any later version.
Packit 3ae693
Packit 3ae693
  libcanberra is distributed in the hope that it will be useful, but
Packit 3ae693
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ae693
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit 3ae693
  Lesser General Public License for more details.
Packit 3ae693
Packit 3ae693
  You should have received a copy of the GNU Lesser General Public
Packit 3ae693
  License along with libcanberra. If not, see
Packit 3ae693
  <http://www.gnu.org/licenses/>.
Packit 3ae693
***/
Packit 3ae693
Packit 3ae693
#ifdef HAVE_CONFIG_H
Packit 3ae693
#include <config.h>
Packit 3ae693
#endif
Packit 3ae693
Packit 3ae693
#include <stdarg.h>
Packit 3ae693
Packit 3ae693
#include "canberra.h"
Packit 3ae693
#include "proplist.h"
Packit 3ae693
#include "macro.h"
Packit 3ae693
#include "malloc.h"
Packit 3ae693
Packit 3ae693
static unsigned calc_hash(const char *c) {
Packit 3ae693
        unsigned hash = 0;
Packit 3ae693
Packit 3ae693
        for (; *c; c++)
Packit 3ae693
                hash = 31 * hash + (unsigned) *c;
Packit 3ae693
Packit 3ae693
        return hash;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_proplist_create:
Packit 3ae693
 * @p: A pointer where to fill in a pointer for the new property list.
Packit 3ae693
 *
Packit 3ae693
 * Allocate a new empty property list.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_proplist_create(ca_proplist **_p) {
Packit 3ae693
        ca_proplist *p;
Packit 3ae693
        ca_return_val_if_fail(_p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(p = ca_new0(ca_proplist, 1)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        if (!(p->mutex = ca_mutex_new())) {
Packit 3ae693
                ca_free(p);
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        *_p = p;
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int _unset(ca_proplist *p, const char *key) {
Packit 3ae693
        ca_prop *prop, *nprop;
Packit 3ae693
        unsigned i;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        i = calc_hash(key) % N_HASHTABLE;
Packit 3ae693
Packit 3ae693
        nprop = NULL;
Packit 3ae693
        for (prop = p->prop_hashtable[i]; prop; nprop = prop, prop = prop->next_in_slot)
Packit 3ae693
                if (strcmp(prop->key, key) == 0)
Packit 3ae693
                        break;
Packit 3ae693
Packit 3ae693
        if (prop) {
Packit 3ae693
                if (nprop)
Packit 3ae693
                        nprop->next_in_slot = prop->next_in_slot;
Packit 3ae693
                else
Packit 3ae693
                        p->prop_hashtable[i] = prop->next_in_slot;
Packit 3ae693
Packit 3ae693
                if (prop->prev_item)
Packit 3ae693
                        prop->prev_item->next_item = prop->next_item;
Packit 3ae693
                else
Packit 3ae693
                        p->first_item = prop->next_item;
Packit 3ae693
Packit 3ae693
                if (prop->next_item)
Packit 3ae693
                        prop->next_item->prev_item = prop->prev_item;
Packit 3ae693
Packit 3ae693
                ca_free(prop->key);
Packit 3ae693
                ca_free(prop);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_proplist_sets:
Packit 3ae693
 * @p: The property list to add this key/value pair to
Packit 3ae693
 * @key: The key for this key/value pair
Packit 3ae693
 * @value: The value for this key/value pair
Packit 3ae693
 *
Packit 3ae693
 * Add a new string key/value pair to the property list.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_proplist_sets(ca_proplist *p, const char *key, const char *value) {
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(value, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        return ca_proplist_set(p, key, value, strlen(value)+1);
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_proplist_setf:
Packit 3ae693
 * @p: The property list to add this key/value pair to
Packit 3ae693
 * @key: The key for this key/value pair
Packit 3ae693
 * @format: The format string for the value for this key/value pair
Packit 3ae693
 * @...: The parameters for the format string
Packit 3ae693
 *
Packit 3ae693
 * Much like ca_proplist_sets(): add a new string key/value pair to
Packit 3ae693
 * the property list. Takes a standard C format string plus arguments
Packit 3ae693
 * and formats a string of it.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) {
Packit 3ae693
        int ret;
Packit 3ae693
        char *k;
Packit 3ae693
        ca_prop *prop;
Packit 3ae693
        size_t size = 100;
Packit 3ae693
        unsigned h;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(format, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(k = ca_strdup(key)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        for (;;) {
Packit 3ae693
                va_list ap;
Packit 3ae693
                int r;
Packit 3ae693
Packit 3ae693
                if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + size))) {
Packit 3ae693
                        ca_free(k);
Packit 3ae693
                        return CA_ERROR_OOM;
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
Packit 3ae693
                va_start(ap, format);
Packit 3ae693
                r = vsnprintf(CA_PROP_DATA(prop), size, format, ap);
Packit 3ae693
                va_end(ap);
Packit 3ae693
Packit 3ae693
                ((char*) CA_PROP_DATA(prop))[size-1] = 0;
Packit 3ae693
Packit 3ae693
                if (r > -1 && (size_t) r < size) {
Packit 3ae693
                        prop->nbytes = (size_t) r+1;
Packit 3ae693
                        break;
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
                if (r > -1)    /* glibc 2.1 */
Packit 3ae693
                        size = (size_t) r+1;
Packit 3ae693
                else           /* glibc 2.0 */
Packit 3ae693
                        size *= 2;
Packit 3ae693
Packit 3ae693
                ca_free(prop);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        prop->key = k;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(p->mutex);
Packit 3ae693
Packit 3ae693
        if ((ret = _unset(p, key)) < 0) {
Packit 3ae693
                ca_free(prop);
Packit 3ae693
                ca_free(k);
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        h = calc_hash(key) % N_HASHTABLE;
Packit 3ae693
Packit 3ae693
        prop->next_in_slot = p->prop_hashtable[h];
Packit 3ae693
        p->prop_hashtable[h] = prop;
Packit 3ae693
Packit 3ae693
        prop->prev_item = NULL;
Packit 3ae693
        if ((prop->next_item = p->first_item))
Packit 3ae693
                prop->next_item->prev_item = prop;
Packit 3ae693
        p->first_item = prop;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(p->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_proplist_set:
Packit 3ae693
 * @p: The property list to add this key/value pair to
Packit 3ae693
 * @key: The key for this key/value pair
Packit 3ae693
 * @data: The binary value for this key value pair
Packit 3ae693
 * @nbytes: The size of thebinary value for this key value pair.
Packit 3ae693
 *
Packit 3ae693
 * Add a new binary key/value pair to the property list.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes) {
Packit 3ae693
        int ret;
Packit 3ae693
        char *k;
Packit 3ae693
        ca_prop *prop;
Packit 3ae693
        unsigned h;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(!nbytes || data, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(k = ca_strdup(key)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + nbytes))) {
Packit 3ae693
                ca_free(k);
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        prop->key = k;
Packit 3ae693
        prop->nbytes = nbytes;
Packit 3ae693
        memcpy(CA_PROP_DATA(prop), data, nbytes);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(p->mutex);
Packit 3ae693
Packit 3ae693
        if ((ret = _unset(p, key)) < 0) {
Packit 3ae693
                ca_free(prop);
Packit 3ae693
                ca_free(k);
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        h = calc_hash(key) % N_HASHTABLE;
Packit 3ae693
Packit 3ae693
        prop->next_in_slot = p->prop_hashtable[h];
Packit 3ae693
        p->prop_hashtable[h] = prop;
Packit 3ae693
Packit 3ae693
        prop->prev_item = NULL;
Packit 3ae693
        if ((prop->next_item = p->first_item))
Packit 3ae693
                prop->next_item->prev_item = prop;
Packit 3ae693
        p->first_item = prop;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(p->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/* Not exported, not self-locking */
Packit 3ae693
ca_prop* ca_proplist_get_unlocked(ca_proplist *p, const char *key) {
Packit 3ae693
        ca_prop *prop;
Packit 3ae693
        unsigned i;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, NULL);
Packit 3ae693
        ca_return_val_if_fail(key, NULL);
Packit 3ae693
Packit 3ae693
        i = calc_hash(key) % N_HASHTABLE;
Packit 3ae693
Packit 3ae693
        for (prop = p->prop_hashtable[i]; prop; prop = prop->next_in_slot)
Packit 3ae693
                if (strcmp(prop->key, key) == 0)
Packit 3ae693
                        return prop;
Packit 3ae693
Packit 3ae693
        return NULL;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/* Not exported, not self-locking */
Packit 3ae693
const char* ca_proplist_gets_unlocked(ca_proplist *p, const char *key) {
Packit 3ae693
        ca_prop *prop;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, NULL);
Packit 3ae693
        ca_return_val_if_fail(key, NULL);
Packit 3ae693
Packit 3ae693
        if (!(prop = ca_proplist_get_unlocked(p, key)))
Packit 3ae693
                return NULL;
Packit 3ae693
Packit 3ae693
        if (!memchr(CA_PROP_DATA(prop), 0, prop->nbytes))
Packit 3ae693
                return NULL;
Packit 3ae693
Packit 3ae693
        return CA_PROP_DATA(prop);
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_proplist_destroy:
Packit 3ae693
 * @p: The property list to destroy
Packit 3ae693
 *
Packit 3ae693
 * Destroys a property list that was created with ca_proplist_create() earlier.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_proplist_destroy(ca_proplist *p) {
Packit 3ae693
        ca_prop *prop, *nprop;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        for (prop = p->first_item; prop; prop = nprop) {
Packit 3ae693
                nprop = prop->next_item;
Packit 3ae693
                ca_free(prop->key);
Packit 3ae693
                ca_free(prop);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ca_mutex_free(p->mutex);
Packit 3ae693
Packit 3ae693
        ca_free(p);
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int merge_into(ca_proplist *a, ca_proplist *b) {
Packit 3ae693
        int ret = CA_SUCCESS;
Packit 3ae693
        ca_prop *prop;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(a, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(b, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(b->mutex);
Packit 3ae693
Packit 3ae693
        for (prop = b->first_item; prop; prop = prop->next_item)
Packit 3ae693
                if ((ret = ca_proplist_set(a, prop->key, CA_PROP_DATA(prop), prop->nbytes)) < 0)
Packit 3ae693
                        break;
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(b->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_proplist_merge(ca_proplist **_a, ca_proplist *b, ca_proplist *c) {
Packit 3ae693
        ca_proplist *a;
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(_a, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(b, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = ca_proplist_create(&a)) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        if ((ret = merge_into(a, b)) < 0 ||
Packit 3ae693
            (ret = merge_into(a, c)) < 0) {
Packit 3ae693
                ca_proplist_destroy(a);
Packit 3ae693
                return ret;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        *_a = a;
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
ca_bool_t ca_proplist_contains(ca_proplist *p, const char *key) {
Packit 3ae693
        ca_bool_t b;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, FALSE);
Packit 3ae693
        ca_return_val_if_fail(key, FALSE);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(p->mutex);
Packit 3ae693
        b = !!ca_proplist_get_unlocked(p, key);
Packit 3ae693
        ca_mutex_unlock(p->mutex);
Packit 3ae693
Packit 3ae693
        return b;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_proplist_merge_ap(ca_proplist *p, va_list ap) {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        for (;;) {
Packit 3ae693
                const char *key, *value;
Packit 3ae693
Packit 3ae693
                if (!(key = va_arg(ap, const char*)))
Packit 3ae693
                        break;
Packit 3ae693
Packit 3ae693
                if (!(value = va_arg(ap, const char*)))
Packit 3ae693
                        return CA_ERROR_INVALID;
Packit 3ae693
Packit 3ae693
                if ((ret = ca_proplist_sets(p, key, value)) < 0)
Packit 3ae693
                        return ret;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_proplist_from_ap(ca_proplist **_p, va_list ap) {
Packit 3ae693
        int ret;
Packit 3ae693
        ca_proplist *p;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(_p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = ca_proplist_create(&p)) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        if ((ret = ca_proplist_merge_ap(p, ap)) < 0)
Packit 3ae693
                goto fail;
Packit 3ae693
Packit 3ae693
        *_p = p;
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
Packit 3ae693
fail:
Packit 3ae693
        ca_assert_se(ca_proplist_destroy(p) == CA_SUCCESS);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}