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