242 lines
6.5 KiB
C
242 lines
6.5 KiB
C
/* 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;
|
|
}
|