/src/neomutt/mutt/slist.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * A separated list of strings |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2018-2019 Richard Russon <rich@flatcap.org> |
7 | | * |
8 | | * @copyright |
9 | | * This program is free software: you can redistribute it and/or modify it under |
10 | | * the terms of the GNU General Public License as published by the Free Software |
11 | | * Foundation, either version 2 of the License, or (at your option) any later |
12 | | * version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
17 | | * details. |
18 | | * |
19 | | * You should have received a copy of the GNU General Public License along with |
20 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /** |
24 | | * @page mutt_slist A separated list of strings |
25 | | * |
26 | | * A separated list of strings |
27 | | */ |
28 | | |
29 | | #include "config.h" |
30 | | #include <stddef.h> |
31 | | #include "slist.h" |
32 | | #include "buffer.h" |
33 | | #include "list.h" |
34 | | #include "memory.h" |
35 | | #include "queue.h" |
36 | | #include "string2.h" |
37 | | |
38 | | /** |
39 | | * slist_new - Create a new string list |
40 | | * @param flags Flag to set, e.g. #SLIST_SEP_COMMA |
41 | | * @retval ptr New string list |
42 | | */ |
43 | | struct Slist *slist_new(uint32_t flags) |
44 | 0 | { |
45 | 0 | struct Slist *list = mutt_mem_calloc(1, sizeof(*list)); |
46 | 0 | list->flags = flags; |
47 | 0 | STAILQ_INIT(&list->head); |
48 | |
|
49 | 0 | return list; |
50 | 0 | } |
51 | | |
52 | | /** |
53 | | * slist_add_list - Add a list to another list |
54 | | * @param list String list to add to |
55 | | * @param add String list to add |
56 | | * @retval ptr Modified list |
57 | | */ |
58 | | struct Slist *slist_add_list(struct Slist *list, const struct Slist *add) |
59 | 0 | { |
60 | 0 | if (!add) |
61 | 0 | return list; |
62 | 0 | if (!list) |
63 | 0 | return slist_dup(add); |
64 | | |
65 | 0 | struct ListNode *np = NULL; |
66 | 0 | STAILQ_FOREACH(np, &add->head, entries) |
67 | 0 | { |
68 | 0 | mutt_list_insert_tail(&list->head, mutt_str_dup((char *) np->data)); |
69 | 0 | list->count++; |
70 | 0 | } |
71 | 0 | return list; |
72 | 0 | } |
73 | | |
74 | | /** |
75 | | * slist_add_string - Add a string to a list |
76 | | * @param list List to modify |
77 | | * @param str String to add |
78 | | * @retval ptr Modified list |
79 | | */ |
80 | | struct Slist *slist_add_string(struct Slist *list, const char *str) |
81 | 0 | { |
82 | 0 | if (!list) |
83 | 0 | return NULL; |
84 | | |
85 | 0 | if (str && (str[0] == '\0')) |
86 | 0 | str = NULL; |
87 | |
|
88 | 0 | if (!str && !(list->flags & SLIST_ALLOW_EMPTY)) |
89 | 0 | return list; |
90 | | |
91 | 0 | mutt_list_insert_tail(&list->head, mutt_str_dup(str)); |
92 | 0 | list->count++; |
93 | |
|
94 | 0 | return list; |
95 | 0 | } |
96 | | |
97 | | /** |
98 | | * slist_compare - Compare two string lists |
99 | | * @param a First list |
100 | | * @param b Second list |
101 | | * @retval true They are identical |
102 | | */ |
103 | | bool slist_compare(const struct Slist *a, const struct Slist *b) |
104 | 0 | { |
105 | 0 | if (!a && !b) /* both empty */ |
106 | 0 | return true; |
107 | 0 | if (!a ^ !b) /* one is empty, but not the other */ |
108 | 0 | return false; |
109 | 0 | if (a->count != b->count) |
110 | 0 | return false; |
111 | | |
112 | 0 | return mutt_list_compare(&a->head, &b->head); |
113 | 0 | } |
114 | | |
115 | | /** |
116 | | * slist_dup - Create a copy of an Slist object |
117 | | * @param list Slist to duplicate |
118 | | * @retval ptr New Slist object |
119 | | */ |
120 | | struct Slist *slist_dup(const struct Slist *list) |
121 | 0 | { |
122 | 0 | if (!list) |
123 | 0 | return NULL; |
124 | | |
125 | 0 | struct Slist *list_new = slist_new(list->flags); |
126 | |
|
127 | 0 | struct ListNode *np = NULL; |
128 | 0 | STAILQ_FOREACH(np, &list->head, entries) |
129 | 0 | { |
130 | 0 | mutt_list_insert_tail(&list_new->head, mutt_str_dup(np->data)); |
131 | 0 | } |
132 | 0 | list_new->count = list->count; |
133 | 0 | return list_new; |
134 | 0 | } |
135 | | |
136 | | /** |
137 | | * slist_empty - Empty out an Slist object |
138 | | * @param list Slist to empty |
139 | | * @retval ptr New Slist object |
140 | | */ |
141 | | struct Slist *slist_empty(struct Slist **list) |
142 | 0 | { |
143 | 0 | if (!list || !*list) |
144 | 0 | return NULL; |
145 | | |
146 | 0 | mutt_list_free(&(*list)->head); |
147 | |
|
148 | 0 | if ((*list)->flags & SLIST_ALLOW_EMPTY) |
149 | 0 | { |
150 | 0 | (*list)->count = 0; |
151 | 0 | return *list; |
152 | 0 | } |
153 | | |
154 | 0 | FREE(list); |
155 | 0 | return NULL; |
156 | 0 | } |
157 | | |
158 | | /** |
159 | | * slist_free - Free an Slist object |
160 | | * @param ptr Slist to free |
161 | | */ |
162 | | void slist_free(struct Slist **ptr) |
163 | 29.8k | { |
164 | 29.8k | if (!ptr || !*ptr) |
165 | 0 | return; |
166 | | |
167 | 29.8k | struct Slist *slist = *ptr; |
168 | 29.8k | mutt_list_free(&slist->head); |
169 | | |
170 | 29.8k | FREE(ptr); |
171 | 29.8k | } |
172 | | |
173 | | /** |
174 | | * slist_is_empty - Is the slist empty? |
175 | | * @param list List to check |
176 | | * @retval true List is empty |
177 | | */ |
178 | | bool slist_is_empty(const struct Slist *list) |
179 | 2.41M | { |
180 | 2.41M | if (!list) |
181 | 2.41M | return true; |
182 | | |
183 | 0 | return list->count == 0; |
184 | 2.41M | } |
185 | | |
186 | | /** |
187 | | * slist_is_member - Is a string a member of a list? |
188 | | * @param list List to modify |
189 | | * @param str String to find |
190 | | * @retval true String is in the list |
191 | | */ |
192 | | bool slist_is_member(const struct Slist *list, const char *str) |
193 | 169k | { |
194 | 169k | if (!list) |
195 | 0 | return false; |
196 | | |
197 | 169k | if (!str && !(list->flags & SLIST_ALLOW_EMPTY)) |
198 | 0 | return false; |
199 | | |
200 | 169k | struct ListNode *np = NULL; |
201 | 169k | STAILQ_FOREACH(np, &list->head, entries) |
202 | 437k | { |
203 | 437k | if (mutt_str_equal(np->data, str)) |
204 | 9.94k | return true; |
205 | 437k | } |
206 | 159k | return false; |
207 | 169k | } |
208 | | |
209 | | /** |
210 | | * slist_parse - Parse a list of strings into a list |
211 | | * @param str String of strings |
212 | | * @param flags Flags, e.g. #SLIST_ALLOW_EMPTY |
213 | | * @retval ptr New Slist object |
214 | | */ |
215 | | struct Slist *slist_parse(const char *str, uint32_t flags) |
216 | 29.8k | { |
217 | 29.8k | char *src = mutt_str_dup(str); |
218 | 29.8k | if (!src && !(flags & SLIST_ALLOW_EMPTY)) |
219 | 0 | return NULL; |
220 | | |
221 | 29.8k | char sep = ' '; |
222 | 29.8k | if ((flags & SLIST_SEP_MASK) == SLIST_SEP_COMMA) |
223 | 9.94k | sep = ','; |
224 | 19.8k | else if ((flags & SLIST_SEP_MASK) == SLIST_SEP_COLON) |
225 | 19.8k | sep = ':'; |
226 | | |
227 | 29.8k | struct Slist *list = mutt_mem_calloc(1, sizeof(struct Slist)); |
228 | 29.8k | list->flags = flags; |
229 | 29.8k | STAILQ_INIT(&list->head); |
230 | | |
231 | 29.8k | if (!src) |
232 | 0 | return list; |
233 | | |
234 | 29.8k | char *start = src; |
235 | 1.92M | for (char *p = start; *p; p++) |
236 | 1.89M | { |
237 | 1.89M | if ((p[0] == '\\') && (p[1] != '\0')) |
238 | 0 | { |
239 | 0 | p++; |
240 | 0 | continue; |
241 | 0 | } |
242 | | |
243 | 1.89M | if (p[0] == sep) |
244 | 139k | { |
245 | 139k | p[0] = '\0'; |
246 | 139k | if (slist_is_member(list, start)) |
247 | 9.94k | { |
248 | 9.94k | start = p + 1; |
249 | 9.94k | continue; |
250 | 9.94k | } |
251 | 129k | mutt_list_insert_tail(&list->head, mutt_str_dup(start)); |
252 | 129k | list->count++; |
253 | 129k | start = p + 1; |
254 | 129k | } |
255 | 1.89M | } |
256 | | |
257 | 29.8k | if (!slist_is_member(list, start)) |
258 | 29.8k | { |
259 | 29.8k | mutt_list_insert_tail(&list->head, mutt_str_dup(start)); |
260 | 29.8k | list->count++; |
261 | 29.8k | } |
262 | | |
263 | 29.8k | FREE(&src); |
264 | 29.8k | return list; |
265 | 29.8k | } |
266 | | |
267 | | /** |
268 | | * slist_remove_string - Remove a string from a list |
269 | | * @param list List to modify |
270 | | * @param str String to remove |
271 | | * @retval ptr Modified list |
272 | | */ |
273 | | struct Slist *slist_remove_string(struct Slist *list, const char *str) |
274 | 0 | { |
275 | 0 | if (!list) |
276 | 0 | return NULL; |
277 | 0 | if (!str && !(list->flags & SLIST_ALLOW_EMPTY)) |
278 | 0 | return list; |
279 | | |
280 | 0 | struct ListNode *prev = NULL; |
281 | 0 | struct ListNode *np = NULL; |
282 | 0 | struct ListNode *tmp = NULL; |
283 | 0 | STAILQ_FOREACH_SAFE(np, &list->head, entries, tmp) |
284 | 0 | { |
285 | 0 | if (mutt_str_equal(np->data, str)) |
286 | 0 | { |
287 | 0 | if (prev) |
288 | 0 | STAILQ_REMOVE_AFTER(&list->head, prev, entries); |
289 | 0 | else |
290 | 0 | STAILQ_REMOVE_HEAD(&list->head, entries); |
291 | 0 | FREE(&np->data); |
292 | 0 | FREE(&np); |
293 | 0 | list->count--; |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | prev = np; |
297 | 0 | } |
298 | 0 | return list; |
299 | 0 | } |
300 | | |
301 | | /** |
302 | | * slist_to_buffer - Export an Slist to a Buffer |
303 | | * @param list List to export |
304 | | * @param buf Buffer for the results |
305 | | * @retval num Number of strings written to Buffer |
306 | | */ |
307 | | int slist_to_buffer(const struct Slist *list, struct Buffer *buf) |
308 | 0 | { |
309 | 0 | if (!list || !buf || (list->count == 0)) |
310 | 0 | return 0; |
311 | | |
312 | 0 | struct ListNode *np = NULL; |
313 | 0 | STAILQ_FOREACH(np, &list->head, entries) |
314 | 0 | { |
315 | 0 | buf_addstr(buf, np->data); |
316 | 0 | if (STAILQ_NEXT(np, entries)) |
317 | 0 | { |
318 | 0 | const int sep = (list->flags & SLIST_SEP_MASK); |
319 | 0 | if (sep == SLIST_SEP_COMMA) |
320 | 0 | buf_addch(buf, ','); |
321 | 0 | else if (sep == SLIST_SEP_COLON) |
322 | 0 | buf_addch(buf, ':'); |
323 | 0 | else |
324 | 0 | buf_addch(buf, ' '); |
325 | 0 | } |
326 | 0 | } |
327 | |
|
328 | 0 | return list->count; |
329 | 0 | } |