]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libudev/libudev-list.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / libudev / libudev-list.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "hashmap.h"
5 #include "libudev-list-internal.h"
6 #include "list.h"
7 #include "sort-util.h"
8
9 /**
10 * SECTION:libudev-list
11 * @short_description: list operation
12 *
13 * Libudev list operations.
14 */
15
16 /**
17 * udev_list_entry:
18 *
19 * Opaque object representing one entry in a list. An entry contains
20 * contains a name, and optionally a value.
21 */
22 struct udev_list_entry {
23 struct udev_list *list;
24 char *name;
25 char *value;
26
27 LIST_FIELDS(struct udev_list_entry, entries);
28 };
29
30 struct udev_list {
31 Hashmap *unique_entries;
32 LIST_HEAD(struct udev_list_entry, entries);
33 bool unique:1;
34 bool uptodate:1;
35 };
36
37 static struct udev_list_entry *udev_list_entry_free(struct udev_list_entry *entry) {
38 if (!entry)
39 return NULL;
40
41 if (entry->list) {
42 if (entry->list->unique)
43 hashmap_remove(entry->list->unique_entries, entry->name);
44 else
45 LIST_REMOVE(entries, entry->list->entries, entry);
46 }
47
48 free(entry->name);
49 free(entry->value);
50
51 return mfree(entry);
52 }
53
54 DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_list_entry *, udev_list_entry_free);
55
56 struct udev_list *udev_list_new(bool unique) {
57 struct udev_list *list;
58
59 list = new(struct udev_list, 1);
60 if (!list)
61 return NULL;
62
63 *list = (struct udev_list) {
64 .unique = unique,
65 };
66
67 return list;
68 }
69
70 struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *_name, const char *_value) {
71 _cleanup_(udev_list_entry_freep) struct udev_list_entry *entry = NULL;
72 _cleanup_free_ char *name = NULL, *value = NULL;
73 int r;
74
75 assert(list);
76
77 name = strdup(_name);
78 if (!name)
79 return NULL;
80
81 if (_value) {
82 value = strdup(_value);
83 if (!value)
84 return NULL;
85 }
86
87 entry = new(struct udev_list_entry, 1);
88 if (!entry)
89 return NULL;
90
91 *entry = (struct udev_list_entry) {
92 .list = list,
93 .name = TAKE_PTR(name),
94 .value = TAKE_PTR(value),
95 };
96
97 if (list->unique) {
98 r = hashmap_ensure_allocated(&list->unique_entries, &string_hash_ops);
99 if (r < 0)
100 return NULL;
101
102 udev_list_entry_free(hashmap_get(list->unique_entries, entry->name));
103
104 r = hashmap_put(list->unique_entries, entry->name, entry);
105 if (r < 0)
106 return NULL;
107
108 list->uptodate = false;
109 } else
110 LIST_APPEND(entries, list->entries, entry);
111
112 return TAKE_PTR(entry);
113 }
114
115 void udev_list_cleanup(struct udev_list *list) {
116 struct udev_list_entry *i, *n;
117
118 if (!list)
119 return;
120
121 if (list->unique) {
122 hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free);
123 list->uptodate = false;
124 } else
125 LIST_FOREACH_SAFE(entries, i, n, list->entries)
126 udev_list_entry_free(i);
127 }
128
129 struct udev_list *udev_list_free(struct udev_list *list) {
130 if (!list)
131 return NULL;
132
133 udev_list_cleanup(list);
134 hashmap_free(list->unique_entries);
135
136 return mfree(list);
137 }
138
139 static int udev_list_entry_compare_func(struct udev_list_entry * const *a, struct udev_list_entry * const *b) {
140 return strcmp((*a)->name, (*b)->name);
141 }
142
143 struct udev_list_entry *udev_list_get_entry(struct udev_list *list) {
144 if (!list)
145 return NULL;
146
147 if (list->unique && !list->uptodate) {
148 size_t n;
149
150 LIST_HEAD_INIT(list->entries);
151
152 n = hashmap_size(list->unique_entries);
153 if (n == 0)
154 ;
155 else if (n == 1)
156 LIST_PREPEND(entries, list->entries, hashmap_first(list->unique_entries));
157 else {
158 _cleanup_free_ struct udev_list_entry **buf = NULL;
159 struct udev_list_entry *entry, **p;
160 size_t j;
161
162 buf = new(struct udev_list_entry *, n);
163 if (!buf)
164 return NULL;
165
166 p = buf;
167 HASHMAP_FOREACH(entry, list->unique_entries)
168 *p++ = entry;
169
170 typesafe_qsort(buf, n, udev_list_entry_compare_func);
171
172 for (j = n; j > 0; j--)
173 LIST_PREPEND(entries, list->entries, buf[j-1]);
174 }
175
176 list->uptodate = true;
177 }
178
179 return list->entries;
180 }
181
182 /**
183 * udev_list_entry_get_next:
184 * @list_entry: current entry
185 *
186 * Get the next entry from the list.
187 *
188 * Returns: udev_list_entry, #NULL if no more entries are available.
189 */
190 _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) {
191 if (!list_entry)
192 return NULL;
193 if (list_entry->list->unique && !list_entry->list->uptodate)
194 return NULL;
195 return list_entry->entries_next;
196 }
197
198 /**
199 * udev_list_entry_get_by_name:
200 * @list_entry: current entry
201 * @name: name string to match
202 *
203 * Lookup an entry in the list with a certain name.
204 *
205 * Returns: udev_list_entry, #NULL if no matching entry is found.
206 */
207 _public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) {
208 if (!list_entry)
209 return NULL;
210 if (!list_entry->list->unique || !list_entry->list->uptodate)
211 return NULL;
212 return hashmap_get(list_entry->list->unique_entries, name);
213 }
214
215 /**
216 * udev_list_entry_get_name:
217 * @list_entry: current entry
218 *
219 * Get the name of a list entry.
220 *
221 * Returns: the name string of this entry.
222 */
223 _public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) {
224 if (!list_entry)
225 return NULL;
226 return list_entry->name;
227 }
228
229 /**
230 * udev_list_entry_get_value:
231 * @list_entry: current entry
232 *
233 * Get the value of list entry.
234 *
235 * Returns: the value string of this entry.
236 */
237 _public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) {
238 if (!list_entry)
239 return NULL;
240 return list_entry->value;
241 }