]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libudev/libudev-list.c
shared/install: use _cleanup_free_
[thirdparty/systemd.git] / src / libudev / libudev-list.c
CommitLineData
88a6477e
KS
1/***
2 This file is part of systemd.
3
4 Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
e345e267 19
e345e267 20#include <errno.h>
b5efdb8a
LP
21#include <stddef.h>
22#include <stdlib.h>
e345e267
KS
23#include <string.h>
24
b5efdb8a 25#include "alloc-util.h"
e345e267
KS
26#include "libudev-private.h"
27
ce1d6d7f
KS
28/**
29 * SECTION:libudev-list
30 * @short_description: list operation
31 *
32 * Libudev list operations.
33 */
34
1e511322
KS
35/**
36 * udev_list_entry:
37 *
ce1d6d7f
KS
38 * Opaque object representing one entry in a list. An entry contains
39 * contains a name, and optionally a value.
1e511322 40 */
0de33a61 41struct udev_list_entry {
912541b0
KS
42 struct udev_list_node node;
43 struct udev_list *list;
44 char *name;
45 char *value;
46 int num;
e345e267
KS
47};
48
8958da13 49/* the list's head points to itself if empty */
869c9031 50void udev_list_node_init(struct udev_list_node *list)
e345e267 51{
912541b0
KS
52 list->next = list;
53 list->prev = list;
e345e267
KS
54}
55
869c9031 56int udev_list_node_is_empty(struct udev_list_node *list)
e345e267 57{
912541b0 58 return list->next == list;
e345e267
KS
59}
60
3feeb77c 61static void udev_list_node_insert_between(struct udev_list_node *new,
912541b0
KS
62 struct udev_list_node *prev,
63 struct udev_list_node *next)
e345e267 64{
912541b0
KS
65 next->prev = new;
66 new->next = next;
67 new->prev = prev;
68 prev->next = new;
e345e267
KS
69}
70
9dcf7ec8
KS
71void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list)
72{
912541b0 73 udev_list_node_insert_between(new, list->prev, list);
9dcf7ec8
KS
74}
75
76void udev_list_node_remove(struct udev_list_node *entry)
e345e267 77{
912541b0
KS
78 struct udev_list_node *prev = entry->prev;
79 struct udev_list_node *next = entry->next;
e345e267 80
912541b0
KS
81 next->prev = prev;
82 prev->next = next;
0de33a61 83
912541b0
KS
84 entry->prev = NULL;
85 entry->next = NULL;
e345e267
KS
86}
87
0de33a61 88/* return list entry which embeds this node */
b27ee00b 89static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
0de33a61 90{
b27ee00b 91 return container_of(node, struct udev_list_entry, node);
0de33a61
KS
92}
93
869c9031 94void udev_list_init(struct udev *udev, struct udev_list *list, bool unique)
0de33a61 95{
29804cc1 96 memzero(list, sizeof(struct udev_list));
912541b0
KS
97 list->udev = udev;
98 list->unique = unique;
99 udev_list_node_init(&list->node);
0de33a61
KS
100}
101
869c9031 102/* insert entry into a list as the last element */
e3dc56a2 103static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list)
1e78dcbe 104{
912541b0
KS
105 /* inserting before the list head make the node the last node in the list */
106 udev_list_node_insert_between(&new->node, list->node.prev, &list->node);
107 new->list = list;
1e78dcbe
KS
108}
109
0de33a61 110/* insert entry into a list, before a given existing entry */
e3dc56a2 111static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
0de33a61 112{
912541b0
KS
113 udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
114 new->list = entry->list;
0de33a61 115}
e345e267 116
869c9031
KS
117/* binary search in sorted array */
118static int list_search(struct udev_list *list, const char *name)
0de33a61 119{
912541b0
KS
120 unsigned int first, last;
121
122 first = 0;
123 last = list->entries_cur;
124 while (first < last) {
125 unsigned int i;
126 int cmp;
127
128 i = (first + last)/2;
129 cmp = strcmp(name, list->entries[i]->name);
130 if (cmp < 0)
131 last = i;
132 else if (cmp > 0)
133 first = i+1;
134 else
135 return i;
136 }
137
138 /* not found, return negative insertion-index+1 */
139 return -(first+1);
869c9031
KS
140}
141
142struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value)
143{
912541b0
KS
144 struct udev_list_entry *entry;
145 int i = 0;
146
147 if (list->unique) {
148 /* lookup existing name or insertion-index */
149 i = list_search(list, name);
150 if (i >= 0) {
151 entry = list->entries[i];
152
912541b0
KS
153 free(entry->value);
154 if (value == NULL) {
155 entry->value = NULL;
912541b0
KS
156 return entry;
157 }
158 entry->value = strdup(value);
159 if (entry->value == NULL)
160 return NULL;
912541b0
KS
161 return entry;
162 }
163 }
164
165 /* add new name */
955d98c9 166 entry = new0(struct udev_list_entry, 1);
912541b0
KS
167 if (entry == NULL)
168 return NULL;
169 entry->name = strdup(name);
170 if (entry->name == NULL) {
171 free(entry);
172 return NULL;
173 }
174 if (value != NULL) {
175 entry->value = strdup(value);
176 if (entry->value == NULL) {
177 free(entry->name);
178 free(entry);
179 return NULL;
180 }
181 }
182
183 if (list->unique) {
184 /* allocate or enlarge sorted array if needed */
185 if (list->entries_cur >= list->entries_max) {
cf2292f5 186 struct udev_list_entry **entries;
912541b0
KS
187 unsigned int add;
188
189 add = list->entries_max;
190 if (add < 1)
191 add = 64;
cf2292f5
MD
192 entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *));
193 if (entries == NULL) {
912541b0
KS
194 free(entry->name);
195 free(entry->value);
64825d3c 196 free(entry);
912541b0
KS
197 return NULL;
198 }
cf2292f5 199 list->entries = entries;
912541b0
KS
200 list->entries_max += add;
201 }
202
203 /* the negative i returned the insertion index */
204 i = (-i)-1;
205
206 /* insert into sorted list */
207 if ((unsigned int)i < list->entries_cur)
208 udev_list_entry_insert_before(entry, list->entries[i]);
209 else
210 udev_list_entry_append(entry, list);
211
212 /* insert into sorted array */
213 memmove(&list->entries[i+1], &list->entries[i],
214 (list->entries_cur - i) * sizeof(struct udev_list_entry *));
215 list->entries[i] = entry;
216 list->entries_cur++;
217 } else {
218 udev_list_entry_append(entry, list);
219 }
220
912541b0 221 return entry;
e345e267
KS
222}
223
1e78dcbe 224void udev_list_entry_delete(struct udev_list_entry *entry)
e345e267 225{
912541b0
KS
226 if (entry->list->entries != NULL) {
227 int i;
228 struct udev_list *list = entry->list;
229
230 /* remove entry from sorted array */
231 i = list_search(list, entry->name);
232 if (i >= 0) {
233 memmove(&list->entries[i], &list->entries[i+1],
234 ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *));
235 list->entries_cur--;
236 }
237 }
238
239 udev_list_node_remove(&entry->node);
240 free(entry->name);
241 free(entry->value);
242 free(entry);
e345e267
KS
243}
244
869c9031 245void udev_list_cleanup(struct udev_list *list)
e345e267 246{
912541b0
KS
247 struct udev_list_entry *entry_loop;
248 struct udev_list_entry *entry_tmp;
249
a1e58e8e 250 list->entries = mfree(list->entries);
912541b0
KS
251 list->entries_cur = 0;
252 list->entries_max = 0;
253 udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
254 udev_list_entry_delete(entry_loop);
be7f7f57
KS
255}
256
869c9031 257struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
e345e267 258{
912541b0
KS
259 if (udev_list_node_is_empty(&list->node))
260 return NULL;
261 return list_node_to_entry(list->node.next);
e345e267
KS
262}
263
1e511322
KS
264/**
265 * udev_list_entry_get_next:
266 * @list_entry: current entry
267 *
21dbe43a
KS
268 * Get the next entry from the list.
269 *
270 * Returns: udev_list_entry, #NULL if no more entries are available.
1e511322 271 */
54cf0b7f 272_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
e345e267 273{
912541b0
KS
274 struct udev_list_node *next;
275
276 if (list_entry == NULL)
277 return NULL;
278 next = list_entry->node.next;
279 /* empty list or no more entries */
280 if (next == &list_entry->list->node)
281 return NULL;
282 return list_node_to_entry(next);
e345e267
KS
283}
284
1e511322
KS
285/**
286 * udev_list_entry_get_by_name:
287 * @list_entry: current entry
288 * @name: name string to match
289 *
21dbe43a
KS
290 * Lookup an entry in the list with a certain name.
291 *
292 * Returns: udev_list_entry, #NULL if no matching entry is found.
1e511322 293 */
54cf0b7f 294_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
bc8184ed 295{
912541b0 296 int i;
bc8184ed 297
912541b0
KS
298 if (list_entry == NULL)
299 return NULL;
869c9031 300
912541b0
KS
301 if (!list_entry->list->unique)
302 return NULL;
869c9031 303
912541b0
KS
304 i = list_search(list_entry->list, name);
305 if (i < 0)
306 return NULL;
307 return list_entry->list->entries[i];
bc8184ed
KS
308}
309
1e511322
KS
310/**
311 * udev_list_entry_get_name:
312 * @list_entry: current entry
313 *
21dbe43a
KS
314 * Get the name of a list entry.
315 *
1e511322
KS
316 * Returns: the name string of this entry.
317 */
54cf0b7f 318_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
e345e267 319{
912541b0
KS
320 if (list_entry == NULL)
321 return NULL;
322 return list_entry->name;
e345e267
KS
323}
324
1e511322
KS
325/**
326 * udev_list_entry_get_value:
327 * @list_entry: current entry
328 *
21dbe43a
KS
329 * Get the value of list entry.
330 *
1e511322
KS
331 * Returns: the value string of this entry.
332 */
54cf0b7f 333_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
e345e267 334{
912541b0
KS
335 if (list_entry == NULL)
336 return NULL;
337 return list_entry->value;
e345e267 338}
df1dcc09 339
8958da13 340int udev_list_entry_get_num(struct udev_list_entry *list_entry)
df1dcc09 341{
912541b0
KS
342 if (list_entry == NULL)
343 return -EINVAL;
344 return list_entry->num;
df1dcc09
KS
345}
346
8958da13 347void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
df1dcc09 348{
912541b0
KS
349 if (list_entry == NULL)
350 return;
351 list_entry->num = num;
df1dcc09 352}