chiark / gitweb /
udev-rules-parse: name_list -> udev_list
[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 static int list_is_empty(struct udev_list_node *list)
47 {
48         return list->next == list;
49 }
50
51 static void 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 static void list_node_remove(struct udev_list_node *entry)
62 {
63         struct udev_list_node *prev = entry->prev;
64         struct udev_list_node *next = entry->next;
65
66         next->prev = prev;
67         prev->next = next;
68
69         entry->prev = NULL;
70         entry->next = NULL;
71 }
72
73 /* return list entry which embeds this node */
74 static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
75 {
76         char *list;
77
78         list = (char *)node;
79         list -= offsetof(struct udev_list_entry, node);
80         return (struct udev_list_entry *)list;
81 }
82
83 /* insert entry into a list as the last element  */
84 static void list_entry_append(struct udev_list_entry *new, struct udev_list_node *list)
85 {
86         /* inserting before the list head make the node the last node in the list */
87         list_node_insert_between(&new->node, list->prev, list);
88         new->list = list;
89 }
90
91 /* insert entry into a list, before a given existing entry */
92 static void list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
93 {
94         list_node_insert_between(&new->node, entry->node.prev, &entry->node);
95         new->list = entry->list;
96 }
97
98 struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_node *list,
99                                             const char *name, const char *value,
100                                             int unique, int sort)
101 {
102         struct udev_list_entry *entry_loop = NULL;
103         struct udev_list_entry *entry_new;
104
105         if (unique)
106                 udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
107                         if (strcmp(entry_loop->name, name) == 0) {
108                                 info(udev, "'%s' is already in the list\n", name);
109                                 free(entry_loop->value);
110                                 if (value == NULL) {
111                                         entry_loop->value = NULL;
112                                         info(udev, "'%s' value unset\n", name);
113                                         return entry_loop;
114                                 }
115                                 entry_loop->value = strdup(value);
116                                 if (entry_loop->value == NULL)
117                                         return NULL;
118                                 info(udev, "'%s' value replaced with '%s'\n", name, value);
119                                 return entry_loop;
120                         }
121                 }
122
123         if (sort)
124                 udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
125                         if (strcmp(entry_loop->name, name) > 0)
126                                 break;
127                 }
128
129         entry_new = malloc(sizeof(struct udev_list_entry));
130         if (entry_new == NULL)
131                 return NULL;
132         memset(entry_new, 0x00, sizeof(struct udev_list_entry));
133         entry_new->udev = udev;
134         entry_new->name = strdup(name);
135         if (entry_new->name == NULL) {
136                 free(entry_new);
137                 return NULL;
138         }
139         if (value != NULL) {
140                 entry_new->value = strdup(value);
141                 if (entry_new->value == NULL) {
142                         free(entry_new->name);
143                         free(entry_new);
144                         return NULL;
145                 }
146         }
147         if (entry_loop != NULL)
148                 list_entry_insert_before(entry_new, entry_loop);
149         else
150                 list_entry_append(entry_new, list);
151         info(udev, "'%s=%s' added\n", entry_new->name, entry_new->value);
152         return entry_new;
153 }
154
155 void udev_list_entry_remove(struct udev_list_entry *entry)
156 {
157         list_node_remove(&entry->node);
158         free(entry->name);
159         free(entry->value);
160         free(entry);
161 }
162
163 void udev_list_cleanup(struct udev *udev, struct udev_list_node *list)
164 {
165         struct udev_list_entry *entry_loop;
166         struct udev_list_entry *entry_tmp;
167
168         udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
169                 udev_list_entry_remove(entry_loop);
170 }
171
172 void udev_list_entry_move_to_end(struct udev_list_entry *list_entry)
173 {
174         list_node_remove(&list_entry->node);
175         list_node_insert_between(&list_entry->node, list_entry->list->prev, list_entry->list);
176 }
177
178 void udev_list_entry_move_to_list(struct udev_list_entry *list_entry, struct udev_list_node *list)
179 {
180         list_node_remove(&list_entry->node);
181         list_node_insert_between(&list_entry->node, list->prev, list);
182         list_entry->list = list;
183 }
184
185 struct udev_list_entry *udev_list_get_entry(struct udev_list_node *list)
186 {
187         if (list_is_empty(list))
188                 return NULL;
189         return list_node_to_entry(list->next);
190 }
191
192 struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
193 {
194         struct udev_list_node *next;
195
196         if (list_entry == NULL)
197                 return NULL;
198         next = list_entry->node.next;
199         /* empty list or no more entries */
200         if (next == list_entry->list)
201                 return NULL;
202         return list_node_to_entry(next);
203 }
204
205 struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
206 {
207         struct udev_list_entry *entry;
208
209         udev_list_entry_foreach(entry, list_entry) {
210                 if (strcmp(udev_list_entry_get_name(entry), name) == 0) {
211                         dbg(entry->udev, "found '%s=%s'\n", entry->name, entry->value);
212                         return entry;
213                 }
214         }
215         return NULL;
216 }
217
218 const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
219 {
220         if (list_entry == NULL)
221                 return NULL;
222         return list_entry->name;
223 }
224
225 const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
226 {
227         if (list_entry == NULL)
228                 return NULL;
229         return list_entry->value;
230 }
231
232 extern int udev_list_entry_get_flag(struct udev_list_entry *list_entry)
233 {
234         if (list_entry == NULL)
235                 return -EINVAL;
236         return list_entry->flag;
237 }
238
239 extern void udev_list_entry_set_flag(struct udev_list_entry *list_entry, int flag)
240 {
241         if (list_entry == NULL)
242                 return;
243         list_entry->flag = flag;
244 }