chiark / gitweb /
e681e23e84c1506f62fcfcc070510c3683410e8d
[elogind.git] / udev / lib / 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 program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26
27 #include "libudev.h"
28 #include "libudev-private.h"
29
30 struct udev_list_entry {
31         struct udev *udev;
32         struct udev_list_node node;
33         struct udev_list_node *list;
34         char *name;
35         char *value;
36         int flag;
37 };
38
39 /* list head point to itself if empty */
40 void udev_list_init(struct udev_list_node *list)
41 {
42         list->next = list;
43         list->prev = list;
44 }
45
46 int udev_list_is_empty(struct udev_list_node *list)
47 {
48         return list->next == list;
49 }
50
51 static void udev_list_node_insert_between(struct udev_list_node *new,
52                                           struct udev_list_node *prev,
53                                           struct udev_list_node *next)
54 {
55         next->prev = new;
56         new->next = next;
57         new->prev = prev;
58         prev->next = new;
59 }
60
61 void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list)
62 {
63         udev_list_node_insert_between(new, list->prev, list);
64 }
65
66 void udev_list_node_remove(struct udev_list_node *entry)
67 {
68         struct udev_list_node *prev = entry->prev;
69         struct udev_list_node *next = entry->next;
70
71         next->prev = prev;
72         prev->next = next;
73
74         entry->prev = NULL;
75         entry->next = NULL;
76 }
77
78 /* return list entry which embeds this node */
79 static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
80 {
81         char *list;
82
83         list = (char *)node;
84         list -= offsetof(struct udev_list_entry, node);
85         return (struct udev_list_entry *)list;
86 }
87
88 /* insert entry into a list as the last element  */
89 static void list_entry_append(struct udev_list_entry *new, struct udev_list_node *list)
90 {
91         /* inserting before the list head make the node the last node in the list */
92         udev_list_node_insert_between(&new->node, list->prev, list);
93         new->list = list;
94 }
95
96 /* insert entry into a list, before a given existing entry */
97 static void list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
98 {
99         udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
100         new->list = entry->list;
101 }
102
103 struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_node *list,
104                                             const char *name, const char *value,
105                                             int unique, int sort)
106 {
107         struct udev_list_entry *entry_loop = NULL;
108         struct udev_list_entry *entry_new;
109
110         if (unique)
111                 udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
112                         if (strcmp(entry_loop->name, name) == 0) {
113                                 dbg(udev, "'%s' is already in the list\n", name);
114                                 free(entry_loop->value);
115                                 if (value == NULL) {
116                                         entry_loop->value = NULL;
117                                         dbg(udev, "'%s' value unset\n", name);
118                                         return entry_loop;
119                                 }
120                                 entry_loop->value = strdup(value);
121                                 if (entry_loop->value == NULL)
122                                         return NULL;
123                                 dbg(udev, "'%s' value replaced with '%s'\n", name, value);
124                                 return entry_loop;
125                         }
126                 }
127
128         if (sort)
129                 udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
130                         if (strcmp(entry_loop->name, name) > 0)
131                                 break;
132                 }
133
134         entry_new = malloc(sizeof(struct udev_list_entry));
135         if (entry_new == NULL)
136                 return NULL;
137         memset(entry_new, 0x00, sizeof(struct udev_list_entry));
138         entry_new->udev = udev;
139         entry_new->name = strdup(name);
140         if (entry_new->name == NULL) {
141                 free(entry_new);
142                 return NULL;
143         }
144         if (value != NULL) {
145                 entry_new->value = strdup(value);
146                 if (entry_new->value == NULL) {
147                         free(entry_new->name);
148                         free(entry_new);
149                         return NULL;
150                 }
151         }
152         if (entry_loop != NULL)
153                 list_entry_insert_before(entry_new, entry_loop);
154         else
155                 list_entry_append(entry_new, list);
156         dbg(udev, "'%s=%s' added\n", entry_new->name, entry_new->value);
157         return entry_new;
158 }
159
160 void udev_list_entry_remove(struct udev_list_entry *entry)
161 {
162         udev_list_node_remove(&entry->node);
163         free(entry->name);
164         free(entry->value);
165         free(entry);
166 }
167
168 void udev_list_cleanup_entries(struct udev *udev, struct udev_list_node *list)
169 {
170         struct udev_list_entry *entry_loop;
171         struct udev_list_entry *entry_tmp;
172
173         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
174                 udev_list_entry_remove(entry_loop);
175 }
176
177 void udev_list_entry_move_to_end(struct udev_list_entry *list_entry)
178 {
179         udev_list_node_remove(&list_entry->node);
180         udev_list_node_insert_between(&list_entry->node, list_entry->list->prev, list_entry->list);
181 }
182
183 void udev_list_entry_move_before(struct udev_list_entry *list_entry, struct udev_list_entry *entry)
184 {
185         udev_list_node_remove(&list_entry->node);
186         udev_list_node_insert_between(&list_entry->node, entry->node.prev, &entry->node);
187         list_entry->list = entry->list;
188 }
189
190 struct udev_list_entry *udev_list_get_entry(struct udev_list_node *list)
191 {
192         if (udev_list_is_empty(list))
193                 return NULL;
194         return list_node_to_entry(list->next);
195 }
196
197 struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
198 {
199         struct udev_list_node *next;
200
201         if (list_entry == NULL)
202                 return NULL;
203         next = list_entry->node.next;
204         /* empty list or no more entries */
205         if (next == list_entry->list)
206                 return NULL;
207         return list_node_to_entry(next);
208 }
209
210 struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
211 {
212         struct udev_list_entry *entry;
213
214         udev_list_entry_foreach(entry, list_entry) {
215                 if (strcmp(udev_list_entry_get_name(entry), name) == 0) {
216                         dbg(entry->udev, "found '%s=%s'\n", entry->name, entry->value);
217                         return entry;
218                 }
219         }
220         return NULL;
221 }
222
223 const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
224 {
225         if (list_entry == NULL)
226                 return NULL;
227         return list_entry->name;
228 }
229
230 const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
231 {
232         if (list_entry == NULL)
233                 return NULL;
234         return list_entry->value;
235 }
236
237 extern int udev_list_entry_get_flag(struct udev_list_entry *list_entry)
238 {
239         if (list_entry == NULL)
240                 return -EINVAL;
241         return list_entry->flag;
242 }
243
244 extern void udev_list_entry_set_flag(struct udev_list_entry *list_entry, int flag)
245 {
246         if (list_entry == NULL)
247                 return;
248         list_entry->flag = flag;
249 }