chiark / gitweb /
libudev: more list rework
[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 list_node node;
33         struct list_node *list;
34         char *name;
35         char *value;
36 };
37
38 /* list head point to itself if empty */
39 void list_init(struct list_node *list)
40 {
41         list->next = list;
42         list->prev = list;
43 }
44
45 static int list_is_empty(struct list_node *list)
46 {
47         return list->next == list;
48 }
49
50 static void list_node_insert_between(struct list_node *new,
51                                      struct list_node *prev,
52                                      struct list_node *next)
53 {
54         next->prev = new;
55         new->next = next;
56         new->prev = prev;
57         prev->next = new;
58 }
59
60 static void list_node_remove(struct list_node *entry)
61 {
62         struct list_node *prev = entry->prev;
63         struct list_node *next = entry->next;
64
65         next->prev = prev;
66         prev->next = next;
67
68         entry->prev = NULL;
69         entry->next = NULL;
70 }
71
72 /* return list entry which embeds this node */
73 static struct udev_list_entry *list_node_to_entry(struct list_node *node)
74 {
75         char *list;
76
77         list = (char *)node;
78         list -= offsetof(struct udev_list_entry, node);
79         return (struct udev_list_entry *)list;
80 }
81
82 /* insert entry into a list as the last element  */
83 static void list_entry_append(struct udev_list_entry *new, struct list_node *list)
84 {
85         /* inserting before the list head make the node the last node in the list */
86         list_node_insert_between(&new->node, list->prev, list);
87         new->list = list;
88 }
89
90 /* insert entry into a list, before a given existing entry */
91 static void list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
92 {
93         list_node_insert_between(&new->node, entry->node.prev, &entry->node);
94         new->list = entry->list;
95 }
96
97 void list_entry_remove(struct udev_list_entry *entry)
98 {
99         list_node_remove(&entry->node);
100         entry->list = NULL;
101 }
102
103 struct udev_list_entry *list_entry_add(struct udev *udev, struct 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, list_get_entry(list)) {
112                         if (strcmp(entry_loop->name, name) == 0) {
113                                 info(udev, "'%s' is already in the list\n", name);
114                                 if (value != NULL) {
115                                         free(entry_loop->value);
116                                         entry_loop->value = strdup(value);
117                                         if (entry_loop->value == NULL)
118                                                 return NULL;
119                                         info(udev, "'%s' value replaced with '%s'\n", name, value);
120                                 }
121                                 return entry_loop;
122                         }
123                 }
124
125         if (sort)
126                 udev_list_entry_foreach(entry_loop, list_get_entry(list)) {
127                         if (strcmp(entry_loop->name, name) > 0)
128                                 break;
129                 }
130
131         entry_new = malloc(sizeof(struct udev_list_entry));
132         if (entry_new == NULL)
133                 return NULL;
134         memset(entry_new, 0x00, sizeof(struct udev_list_entry));
135         entry_new->udev = udev;
136         entry_new->name = strdup(name);
137         if (entry_new->name == NULL) {
138                 free(entry_new);
139                 return NULL;
140         }
141         if (value != NULL) {
142                 entry_new->value = strdup(value);
143                 if (entry_new->value == NULL) {
144                         free(entry_new->name);
145                         free(entry_new);
146                         return NULL;
147                 }
148         }
149         if (entry_loop != NULL)
150                 list_entry_insert_before(entry_new, entry_loop);
151         else
152                 list_entry_append(entry_new, list);
153         return entry_new;
154 }
155
156 void list_entry_move_to_end(struct udev_list_entry *list_entry)
157 {
158         list_node_remove(&list_entry->node);
159         list_node_insert_between(&list_entry->node, list_entry->list->prev, list_entry->list);
160 }
161
162 void list_cleanup(struct udev *udev, struct list_node *list)
163 {
164         struct udev_list_entry *entry_loop;
165         struct udev_list_entry *entry_tmp;
166
167         list_entry_foreach_safe(entry_loop, entry_tmp, list_get_entry(list)) {
168                 list_entry_remove(entry_loop);
169                 free(entry_loop->name);
170                 free(entry_loop->value);
171                 free(entry_loop);
172         }
173 }
174
175 struct udev_list_entry *list_get_entry(struct list_node *list)
176 {
177         if (list_is_empty(list))
178                 return NULL;
179         return list_node_to_entry(list->next);
180 }
181
182 struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
183 {
184         struct list_node *next;
185
186         if (list_entry == NULL)
187                 return NULL;
188         next = list_entry->node.next;
189         /* empty list or no more entries */
190         if (next == list_entry->list)
191                 return NULL;
192         return list_node_to_entry(next);
193 }
194
195 const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
196 {
197         if (list_entry == NULL)
198                 return NULL;
199         return list_entry->name;
200 }
201
202 const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
203 {
204         if (list_entry == NULL)
205                 return NULL;
206         return list_entry->value;
207 }