summaryrefslogtreecommitdiffstats
path: root/src/libudev/libudev-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libudev/libudev-list.c')
-rw-r--r--src/libudev/libudev-list.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c
new file mode 100644
index 0000000..0adc1d5
--- /dev/null
+++ b/src/libudev/libudev-list.c
@@ -0,0 +1,235 @@
+/* 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 && entry->name)
+ hashmap_remove(entry->list->unique_entries, entry->name);
+
+ if (!entry->list->unique || entry->list->uptodate)
+ 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;
+
+ assert(list);
+ assert(_name);
+
+ 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) {
+ .name = TAKE_PTR(name),
+ .value = TAKE_PTR(value),
+ };
+
+ if (list->unique) {
+ udev_list_entry_free(hashmap_get(list->unique_entries, entry->name));
+
+ if (hashmap_ensure_put(&list->unique_entries, &string_hash_ops, entry->name, entry) < 0)
+ return NULL;
+
+ list->uptodate = false;
+ } else
+ LIST_APPEND(entries, list->entries, entry);
+
+ entry->list = list;
+
+ return TAKE_PTR(entry);
+}
+
+void udev_list_cleanup(struct udev_list *list) {
+ if (!list)
+ return;
+
+ if (list->unique) {
+ list->uptodate = false;
+ hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free);
+ } else
+ LIST_FOREACH(entries, i, 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;
+
+ 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 (size_t 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;
+}