chiark / gitweb /
udev: add some O_CLOEXEC
[elogind.git] / src / libudev / libudev-list.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  *
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.
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
22 /**
23  * SECTION:libudev-list
24  * @short_description: list operation
25  *
26  * Libudev list operations.
27  */
28
29 /**
30  * udev_list_entry:
31  *
32  * Opaque object representing one entry in a list. An entry contains
33  * contains a name, and optionally a value.
34  */
35 struct udev_list_entry {
36         struct udev_list_node node;
37         struct udev_list *list;
38         char *name;
39         char *value;
40         int num;
41 };
42
43 /* the list's head points to itself if empty */
44 void udev_list_node_init(struct udev_list_node *list)
45 {
46         list->next = list;
47         list->prev = list;
48 }
49
50 int udev_list_node_is_empty(struct udev_list_node *list)
51 {
52         return list->next == list;
53 }
54
55 static void udev_list_node_insert_between(struct udev_list_node *new,
56                                           struct udev_list_node *prev,
57                                           struct udev_list_node *next)
58 {
59         next->prev = new;
60         new->next = next;
61         new->prev = prev;
62         prev->next = new;
63 }
64
65 void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list)
66 {
67         udev_list_node_insert_between(new, list->prev, list);
68 }
69
70 void udev_list_node_remove(struct udev_list_node *entry)
71 {
72         struct udev_list_node *prev = entry->prev;
73         struct udev_list_node *next = entry->next;
74
75         next->prev = prev;
76         prev->next = next;
77
78         entry->prev = NULL;
79         entry->next = NULL;
80 }
81
82 /* return list entry which embeds this node */
83 static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
84 {
85         return container_of(node, struct udev_list_entry, node);
86 }
87
88 void udev_list_init(struct udev *udev, struct udev_list *list, bool unique)
89 {
90         memset(list, 0x00, sizeof(struct udev_list));
91         list->udev = udev;
92         list->unique = unique;
93         udev_list_node_init(&list->node);
94 }
95
96 /* insert entry into a list as the last element  */
97 void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list)
98 {
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;
102 }
103
104 /* insert entry into a list, before a given existing entry */
105 void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
106 {
107         udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
108         new->list = entry->list;
109 }
110
111 /* binary search in sorted array */
112 static int list_search(struct udev_list *list, const char *name)
113 {
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);
134 }
135
136 struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value)
137 {
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
147                         free(entry->value);
148                         if (value == NULL) {
149                                 entry->value = NULL;
150                                 return entry;
151                         }
152                         entry->value = strdup(value);
153                         if (entry->value == NULL)
154                                 return NULL;
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
212         return entry;
213 }
214
215 void udev_list_entry_delete(struct udev_list_entry *entry)
216 {
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);
234 }
235
236 void udev_list_cleanup(struct udev_list *list)
237 {
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);
247 }
248
249 struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
250 {
251         if (udev_list_node_is_empty(&list->node))
252                 return NULL;
253         return list_node_to_entry(list->node.next);
254 }
255
256 /**
257  * udev_list_entry_get_next:
258  * @list_entry: current entry
259  *
260  * Get the next entry from the list.
261  *
262  * Returns: udev_list_entry, #NULL if no more entries are available.
263  */
264 _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
265 {
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);
275 }
276
277 /**
278  * udev_list_entry_get_by_name:
279  * @list_entry: current entry
280  * @name: name string to match
281  *
282  * Lookup an entry in the list with a certain name.
283  *
284  * Returns: udev_list_entry, #NULL if no matching entry is found.
285  */
286 _public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
287 {
288         int i;
289
290         if (list_entry == NULL)
291                 return NULL;
292
293         if (!list_entry->list->unique)
294                 return NULL;
295
296         i = list_search(list_entry->list, name);
297         if (i < 0)
298                 return NULL;
299         return list_entry->list->entries[i];
300 }
301
302 /**
303  * udev_list_entry_get_name:
304  * @list_entry: current entry
305  *
306  * Get the name of a list entry.
307  *
308  * Returns: the name string of this entry.
309  */
310 _public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
311 {
312         if (list_entry == NULL)
313                 return NULL;
314         return list_entry->name;
315 }
316
317 /**
318  * udev_list_entry_get_value:
319  * @list_entry: current entry
320  *
321  * Get the value of list entry.
322  *
323  * Returns: the value string of this entry.
324  */
325 _public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
326 {
327         if (list_entry == NULL)
328                 return NULL;
329         return list_entry->value;
330 }
331
332 int udev_list_entry_get_num(struct udev_list_entry *list_entry)
333 {
334         if (list_entry == NULL)
335                 return -EINVAL;
336         return list_entry->num;
337 }
338
339 void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
340 {
341         if (list_entry == NULL)
342                 return;
343         list_entry->num = num;
344 }