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