1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "hashmap.h"
5 #include "libudev-list-internal.h"
6 #include "list.h"
7 #include "sort-util.h"
8
9 /**
10 * SECTION:libudev-list
11 * @short_description: list operation
12 *
13 * Libudev list operations.
14 */
15
16 /**
17 * udev_list_entry:
18 *
19 * Opaque object representing one entry in a list. An entry contains
20 * contains a name, and optionally a value.
21 */
22 struct udev_list_entry {
23 struct udev_list *list;
24 char *name;
25 char *value;
26
27 LIST_FIELDS(struct udev_list_entry, entries);
28 };
29
30 struct udev_list {
31 Hashmap *unique_entries;
32 LIST_HEAD(struct udev_list_entry, entries);
33 bool unique:1;
34 bool uptodate:1;
35 };
36
udev_list_entry_free(struct udev_list_entry * entry)37 static struct udev_list_entry *udev_list_entry_free(struct udev_list_entry *entry) {
38 if (!entry)
39 return NULL;
40
41 if (entry->list) {
42 if (entry->list->unique && entry->name)
43 hashmap_remove(entry->list->unique_entries, entry->name);
44
45 if (!entry->list->unique || entry->list->uptodate)
46 LIST_REMOVE(entries, entry->list->entries, entry);
47 }
48
49 free(entry->name);
50 free(entry->value);
51
52 return mfree(entry);
53 }
54
55 DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_list_entry *, udev_list_entry_free);
56
udev_list_new(bool unique)57 struct udev_list *udev_list_new(bool unique) {
58 struct udev_list *list;
59
60 list = new(struct udev_list, 1);
61 if (!list)
62 return NULL;
63
64 *list = (struct udev_list) {
65 .unique = unique,
66 };
67
68 return list;
69 }
70
udev_list_entry_add(struct udev_list * list,const char * _name,const char * _value)71 struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *_name, const char *_value) {
72 _cleanup_(udev_list_entry_freep) struct udev_list_entry *entry = NULL;
73 _cleanup_free_ char *name = NULL, *value = NULL;
74
75 assert(list);
76 assert(_name);
77
78 name = strdup(_name);
79 if (!name)
80 return NULL;
81
82 if (_value) {
83 value = strdup(_value);
84 if (!value)
85 return NULL;
86 }
87
88 entry = new(struct udev_list_entry, 1);
89 if (!entry)
90 return NULL;
91
92 *entry = (struct udev_list_entry) {
93 .name = TAKE_PTR(name),
94 .value = TAKE_PTR(value),
95 };
96
97 if (list->unique) {
98 udev_list_entry_free(hashmap_get(list->unique_entries, entry->name));
99
100 if (hashmap_ensure_put(&list->unique_entries, &string_hash_ops, entry->name, entry) < 0)
101 return NULL;
102
103 list->uptodate = false;
104 } else
105 LIST_APPEND(entries, list->entries, entry);
106
107 entry->list = list;
108
109 return TAKE_PTR(entry);
110 }
111
udev_list_cleanup(struct udev_list * list)112 void udev_list_cleanup(struct udev_list *list) {
113 if (!list)
114 return;
115
116 if (list->unique) {
117 list->uptodate = false;
118 hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free);
119 } else
120 LIST_FOREACH(entries, i, list->entries)
121 udev_list_entry_free(i);
122 }
123
udev_list_free(struct udev_list * list)124 struct udev_list *udev_list_free(struct udev_list *list) {
125 if (!list)
126 return NULL;
127
128 udev_list_cleanup(list);
129 hashmap_free(list->unique_entries);
130
131 return mfree(list);
132 }
133
udev_list_entry_compare_func(struct udev_list_entry * const * a,struct udev_list_entry * const * b)134 static int udev_list_entry_compare_func(struct udev_list_entry * const *a, struct udev_list_entry * const *b) {
135 return strcmp((*a)->name, (*b)->name);
136 }
137
udev_list_get_entry(struct udev_list * list)138 struct udev_list_entry *udev_list_get_entry(struct udev_list *list) {
139 if (!list)
140 return NULL;
141
142 if (list->unique && !list->uptodate) {
143 size_t n;
144
145 LIST_HEAD_INIT(list->entries);
146
147 n = hashmap_size(list->unique_entries);
148 if (n == 0)
149 ;
150 else if (n == 1)
151 LIST_PREPEND(entries, list->entries, hashmap_first(list->unique_entries));
152 else {
153 _cleanup_free_ struct udev_list_entry **buf = NULL;
154 struct udev_list_entry *entry, **p;
155
156 buf = new(struct udev_list_entry *, n);
157 if (!buf)
158 return NULL;
159
160 p = buf;
161 HASHMAP_FOREACH(entry, list->unique_entries)
162 *p++ = entry;
163
164 typesafe_qsort(buf, n, udev_list_entry_compare_func);
165
166 for (size_t j = n; j > 0; j--)
167 LIST_PREPEND(entries, list->entries, buf[j-1]);
168 }
169
170 list->uptodate = true;
171 }
172
173 return list->entries;
174 }
175
176 /**
177 * udev_list_entry_get_next:
178 * @list_entry: current entry
179 *
180 * Get the next entry from the list.
181 *
182 * Returns: udev_list_entry, #NULL if no more entries are available.
183 */
udev_list_entry_get_next(struct udev_list_entry * list_entry)184 _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) {
185 if (!list_entry)
186 return NULL;
187 if (list_entry->list->unique && !list_entry->list->uptodate)
188 return NULL;
189 return list_entry->entries_next;
190 }
191
192 /**
193 * udev_list_entry_get_by_name:
194 * @list_entry: current entry
195 * @name: name string to match
196 *
197 * Lookup an entry in the list with a certain name.
198 *
199 * Returns: udev_list_entry, #NULL if no matching entry is found.
200 */
udev_list_entry_get_by_name(struct udev_list_entry * list_entry,const char * name)201 _public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) {
202 if (!list_entry)
203 return NULL;
204 if (!list_entry->list->unique || !list_entry->list->uptodate)
205 return NULL;
206 return hashmap_get(list_entry->list->unique_entries, name);
207 }
208
209 /**
210 * udev_list_entry_get_name:
211 * @list_entry: current entry
212 *
213 * Get the name of a list entry.
214 *
215 * Returns: the name string of this entry.
216 */
udev_list_entry_get_name(struct udev_list_entry * list_entry)217 _public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) {
218 if (!list_entry)
219 return NULL;
220 return list_entry->name;
221 }
222
223 /**
224 * udev_list_entry_get_value:
225 * @list_entry: current entry
226 *
227 * Get the value of list entry.
228 *
229 * Returns: the value string of this entry.
230 */
udev_list_entry_get_value(struct udev_list_entry * list_entry)231 _public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) {
232 if (!list_entry)
233 return NULL;
234 return list_entry->value;
235 }
236