diff options
Diffstat (limited to '')
-rw-r--r-- | src/libudev/libudev-list.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c new file mode 100644 index 0000000..3b2a2cd --- /dev/null +++ b/src/libudev/libudev-list.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "hashmap.h" +#include "libudev-list-internal.h" +#include "list.h" +#include "sort-util.h" + +/** + * SECTION:libudev-list + * @short_description: list operation + * + * Libudev list operations. + */ + +/** + * udev_list_entry: + * + * Opaque object representing one entry in a list. An entry contains + * contains a name, and optionally a value. + */ +struct udev_list_entry { + struct udev_list *list; + char *name; + char *value; + + LIST_FIELDS(struct udev_list_entry, entries); +}; + +struct udev_list { + Hashmap *unique_entries; + LIST_HEAD(struct udev_list_entry, entries); + bool unique:1; + bool uptodate:1; +}; + +static struct udev_list_entry *udev_list_entry_free(struct udev_list_entry *entry) { + if (!entry) + return NULL; + + if (entry->list) { + if (entry->list->unique) + hashmap_remove(entry->list->unique_entries, entry->name); + else + LIST_REMOVE(entries, entry->list->entries, entry); + } + + free(entry->name); + free(entry->value); + + return mfree(entry); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_list_entry *, udev_list_entry_free); + +struct udev_list *udev_list_new(bool unique) { + struct udev_list *list; + + list = new(struct udev_list, 1); + if (!list) + return NULL; + + *list = (struct udev_list) { + .unique = unique, + }; + + return list; +} + +struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *_name, const char *_value) { + _cleanup_(udev_list_entry_freep) struct udev_list_entry *entry = NULL; + _cleanup_free_ char *name = NULL, *value = NULL; + int r; + + assert(list); + + name = strdup(_name); + if (!name) + return NULL; + + if (_value) { + value = strdup(_value); + if (!value) + return NULL; + } + + entry = new(struct udev_list_entry, 1); + if (!entry) + return NULL; + + *entry = (struct udev_list_entry) { + .list = list, + .name = TAKE_PTR(name), + .value = TAKE_PTR(value), + }; + + if (list->unique) { + r = hashmap_ensure_allocated(&list->unique_entries, &string_hash_ops); + if (r < 0) + return NULL; + + udev_list_entry_free(hashmap_get(list->unique_entries, entry->name)); + + r = hashmap_put(list->unique_entries, entry->name, entry); + if (r < 0) + return NULL; + + list->uptodate = false; + } else + LIST_APPEND(entries, list->entries, entry); + + return TAKE_PTR(entry); +} + +void udev_list_cleanup(struct udev_list *list) { + struct udev_list_entry *i, *n; + + if (!list) + return; + + if (list->unique) { + hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free); + list->uptodate = false; + } else + LIST_FOREACH_SAFE(entries, i, n, list->entries) + udev_list_entry_free(i); +} + +struct udev_list *udev_list_free(struct udev_list *list) { + if (!list) + return NULL; + + udev_list_cleanup(list); + hashmap_free(list->unique_entries); + + return mfree(list); +} + +static int udev_list_entry_compare_func(struct udev_list_entry * const *a, struct udev_list_entry * const *b) { + return strcmp((*a)->name, (*b)->name); +} + +struct udev_list_entry *udev_list_get_entry(struct udev_list *list) { + if (!list) + return NULL; + + if (list->unique && !list->uptodate) { + size_t n; + + LIST_HEAD_INIT(list->entries); + + n = hashmap_size(list->unique_entries); + if (n == 0) + ; + else if (n == 1) + LIST_PREPEND(entries, list->entries, hashmap_first(list->unique_entries)); + else { + _cleanup_free_ struct udev_list_entry **buf = NULL; + struct udev_list_entry *entry, **p; + size_t j; + + buf = new(struct udev_list_entry *, n); + if (!buf) + return NULL; + + p = buf; + HASHMAP_FOREACH(entry, list->unique_entries) + *p++ = entry; + + typesafe_qsort(buf, n, udev_list_entry_compare_func); + + for (j = n; j > 0; j--) + LIST_PREPEND(entries, list->entries, buf[j-1]); + } + + list->uptodate = true; + } + + return list->entries; +} + +/** + * udev_list_entry_get_next: + * @list_entry: current entry + * + * Get the next entry from the list. + * + * Returns: udev_list_entry, #NULL if no more entries are available. + */ +_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) { + if (!list_entry) + return NULL; + if (list_entry->list->unique && !list_entry->list->uptodate) + return NULL; + return list_entry->entries_next; +} + +/** + * udev_list_entry_get_by_name: + * @list_entry: current entry + * @name: name string to match + * + * Lookup an entry in the list with a certain name. + * + * Returns: udev_list_entry, #NULL if no matching entry is found. + */ +_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) { + if (!list_entry) + return NULL; + if (!list_entry->list->unique || !list_entry->list->uptodate) + return NULL; + return hashmap_get(list_entry->list->unique_entries, name); +} + +/** + * udev_list_entry_get_name: + * @list_entry: current entry + * + * Get the name of a list entry. + * + * Returns: the name string of this entry. + */ +_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) { + if (!list_entry) + return NULL; + return list_entry->name; +} + +/** + * udev_list_entry_get_value: + * @list_entry: current entry + * + * Get the value of list entry. + * + * Returns: the value string of this entry. + */ +_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) { + if (!list_entry) + return NULL; + return list_entry->value; +} |