chiark / gitweb /
udev: get rid of SYSCONFDIR
[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                                 free(entry);
190                                 return NULL;
191                         }
192                         list->entries_max += add;
193                 }
194
195                 /* the negative i returned the insertion index */
196                 i = (-i)-1;
197
198                 /* insert into sorted list */
199                 if ((unsigned int)i < list->entries_cur)
200                         udev_list_entry_insert_before(entry, list->entries[i]);
201                 else
202                         udev_list_entry_append(entry, list);
203
204                 /* insert into sorted array */
205                 memmove(&list->entries[i+1], &list->entries[i],
206                         (list->entries_cur - i) * sizeof(struct udev_list_entry *));
207                 list->entries[i] = entry;
208                 list->entries_cur++;
209         } else {
210                 udev_list_entry_append(entry, list);
211         }
212
213         return entry;
214 }
215
216 void udev_list_entry_delete(struct udev_list_entry *entry)
217 {
218         if (entry->list->entries != NULL) {
219                 int i;
220                 struct udev_list *list = entry->list;
221
222                 /* remove entry from sorted array */
223                 i = list_search(list, entry->name);
224                 if (i >= 0) {
225                         memmove(&list->entries[i], &list->entries[i+1],
226                                 ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *));
227                         list->entries_cur--;
228                 }
229         }
230
231         udev_list_node_remove(&entry->node);
232         free(entry->name);
233         free(entry->value);
234         free(entry);
235 }
236
237 void udev_list_cleanup(struct udev_list *list)
238 {
239         struct udev_list_entry *entry_loop;
240         struct udev_list_entry *entry_tmp;
241
242         free(list->entries);
243         list->entries = NULL;
244         list->entries_cur = 0;
245         list->entries_max = 0;
246         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
247                 udev_list_entry_delete(entry_loop);
248 }
249
250 struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
251 {
252         if (udev_list_node_is_empty(&list->node))
253                 return NULL;
254         return list_node_to_entry(list->node.next);
255 }
256
257 /**
258  * udev_list_entry_get_next:
259  * @list_entry: current entry
260  *
261  * Get the next entry from the list.
262  *
263  * Returns: udev_list_entry, #NULL if no more entries are available.
264  */
265 _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
266 {
267         struct udev_list_node *next;
268
269         if (list_entry == NULL)
270                 return NULL;
271         next = list_entry->node.next;
272         /* empty list or no more entries */
273         if (next == &list_entry->list->node)
274                 return NULL;
275         return list_node_to_entry(next);
276 }
277
278 /**
279  * udev_list_entry_get_by_name:
280  * @list_entry: current entry
281  * @name: name string to match
282  *
283  * Lookup an entry in the list with a certain name.
284  *
285  * Returns: udev_list_entry, #NULL if no matching entry is found.
286  */
287 _public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
288 {
289         int i;
290
291         if (list_entry == NULL)
292                 return NULL;
293
294         if (!list_entry->list->unique)
295                 return NULL;
296
297         i = list_search(list_entry->list, name);
298         if (i < 0)
299                 return NULL;
300         return list_entry->list->entries[i];
301 }
302
303 /**
304  * udev_list_entry_get_name:
305  * @list_entry: current entry
306  *
307  * Get the name of a list entry.
308  *
309  * Returns: the name string of this entry.
310  */
311 _public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
312 {
313         if (list_entry == NULL)
314                 return NULL;
315         return list_entry->name;
316 }
317
318 /**
319  * udev_list_entry_get_value:
320  * @list_entry: current entry
321  *
322  * Get the value of list entry.
323  *
324  * Returns: the value string of this entry.
325  */
326 _public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
327 {
328         if (list_entry == NULL)
329                 return NULL;
330         return list_entry->value;
331 }
332
333 int udev_list_entry_get_num(struct udev_list_entry *list_entry)
334 {
335         if (list_entry == NULL)
336                 return -EINVAL;
337         return list_entry->num;
338 }
339
340 void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
341 {
342         if (list_entry == NULL)
343                 return;
344         list_entry->num = num;
345 }