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