/src/gnupg/common/strlist.c
Line | Count | Source |
1 | | /* strlist.c - string helpers |
2 | | * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. |
3 | | * Copyright (C) 2015, 2024 g10 Code GmbH |
4 | | * |
5 | | * This file is part of GnuPG. |
6 | | * |
7 | | * GnuPG is free software; you can redistribute and/or modify this |
8 | | * part of GnuPG under the terms of either |
9 | | * |
10 | | * - the GNU Lesser General Public License as published by the Free |
11 | | * Software Foundation; either version 3 of the License, or (at |
12 | | * your option) any later version. |
13 | | * |
14 | | * or |
15 | | * |
16 | | * - the GNU General Public License as published by the Free |
17 | | * Software Foundation; either version 2 of the License, or (at |
18 | | * your option) any later version. |
19 | | * |
20 | | * or both in parallel, as here. |
21 | | * |
22 | | * GnuPG is distributed in the hope that it will be useful, but |
23 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
24 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
25 | | * General Public License for more details. |
26 | | * |
27 | | * You should have received a copies of the GNU General Public License |
28 | | * and the GNU Lesser General Public License along with this program; |
29 | | * if not, see <https://www.gnu.org/licenses/>. |
30 | | * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) |
31 | | */ |
32 | | |
33 | | #include <config.h> |
34 | | #include <stdlib.h> |
35 | | #include <string.h> |
36 | | #include <stdarg.h> |
37 | | #include <ctype.h> |
38 | | |
39 | | #include "util.h" |
40 | | #include "common-defs.h" |
41 | | #include "strlist.h" |
42 | | #include "utf8conv.h" |
43 | | #include "mischelp.h" |
44 | | |
45 | | void |
46 | | free_strlist( strlist_t sl ) |
47 | 1 | { |
48 | 1 | strlist_t sl2; |
49 | | |
50 | 1 | for(; sl; sl = sl2 ) { |
51 | 0 | sl2 = sl->next; |
52 | 0 | xfree(sl); |
53 | 0 | } |
54 | 1 | } |
55 | | |
56 | | |
57 | | void |
58 | | free_strlist_wipe (strlist_t sl) |
59 | 0 | { |
60 | 0 | strlist_t sl2; |
61 | |
|
62 | 0 | for(; sl; sl = sl2 ) { |
63 | 0 | sl2 = sl->next; |
64 | 0 | wipememory (sl, sizeof *sl + strlen (sl->d)); |
65 | 0 | xfree(sl); |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | |
70 | | /* Add STRING to the LIST at the front. This function terminates the |
71 | | process on memory shortage. */ |
72 | | strlist_t |
73 | | add_to_strlist( strlist_t *list, const char *string ) |
74 | 0 | { |
75 | 0 | strlist_t sl; |
76 | |
|
77 | 0 | sl = xmalloc( sizeof *sl + strlen(string)); |
78 | 0 | sl->flags = 0; |
79 | 0 | strcpy(sl->d, string); |
80 | 0 | sl->next = *list; |
81 | 0 | *list = sl; |
82 | 0 | return sl; |
83 | 0 | } |
84 | | |
85 | | |
86 | | /* Add STRING to the LIST at the front. This function returns NULL |
87 | | and sets ERRNO on memory shortage. */ |
88 | | strlist_t |
89 | | add_to_strlist_try (strlist_t *list, const char *string) |
90 | 0 | { |
91 | 0 | strlist_t sl; |
92 | |
|
93 | 0 | sl = xtrymalloc (sizeof *sl + strlen (string)); |
94 | 0 | if (sl) |
95 | 0 | { |
96 | 0 | sl->flags = 0; |
97 | 0 | strcpy (sl->d, string); |
98 | 0 | sl->next = *list; |
99 | 0 | *list = sl; |
100 | 0 | } |
101 | 0 | return sl; |
102 | 0 | } |
103 | | |
104 | | |
105 | | /* Same as add_to_strlist() but if IS_UTF8 is *not* set, a conversion |
106 | | to UTF-8 is done. This function terminates the process on memory |
107 | | shortage. */ |
108 | | strlist_t |
109 | | add_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) |
110 | 0 | { |
111 | 0 | strlist_t sl; |
112 | |
|
113 | 0 | if (is_utf8) |
114 | 0 | sl = add_to_strlist( list, string ); |
115 | 0 | else |
116 | 0 | { |
117 | 0 | char *p = native_to_utf8( string ); |
118 | 0 | sl = add_to_strlist( list, p ); |
119 | 0 | xfree ( p ); |
120 | 0 | } |
121 | 0 | return sl; |
122 | 0 | } |
123 | | |
124 | | |
125 | | /* Add STRING to the LIST at the end. This function terminates the |
126 | | process on memory shortage. */ |
127 | | strlist_t |
128 | | append_to_strlist( strlist_t *list, const char *string ) |
129 | 0 | { |
130 | 0 | strlist_t sl; |
131 | 0 | sl = append_to_strlist_try (list, string); |
132 | 0 | if (!sl) |
133 | 0 | xoutofcore (); |
134 | 0 | return sl; |
135 | 0 | } |
136 | | |
137 | | |
138 | | /* Core of append_to_strlist_try which take the length of the string. |
139 | | * Return the item added to the end of the list. Or NULL in case of |
140 | | * an error. */ |
141 | | static strlist_t |
142 | | do_append_to_strlist (strlist_t *list, const char *string, size_t stringlen) |
143 | 0 | { |
144 | 0 | strlist_t r, sl; |
145 | |
|
146 | 0 | sl = xtrymalloc (sizeof *sl + stringlen); |
147 | 0 | if (!sl) |
148 | 0 | return NULL; |
149 | | |
150 | 0 | sl->flags = 0; |
151 | 0 | memcpy (sl->d, string, stringlen); |
152 | 0 | sl->d[stringlen] = 0; |
153 | 0 | sl->next = NULL; |
154 | 0 | if (!*list) |
155 | 0 | *list = sl; |
156 | 0 | else |
157 | 0 | { |
158 | 0 | for (r = *list; r->next; r = r->next) |
159 | 0 | ; |
160 | 0 | r->next = sl; |
161 | 0 | } |
162 | 0 | return sl; |
163 | 0 | } |
164 | | |
165 | | |
166 | | /* Add STRING to the LIST at the end. */ |
167 | | strlist_t |
168 | | append_to_strlist_try (strlist_t *list, const char *string) |
169 | 0 | { |
170 | 0 | return do_append_to_strlist (list, string, strlen (string)); |
171 | 0 | } |
172 | | |
173 | | |
174 | | strlist_t |
175 | | append_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) |
176 | 0 | { |
177 | 0 | strlist_t sl; |
178 | |
|
179 | 0 | if( is_utf8 ) |
180 | 0 | sl = append_to_strlist( list, string ); |
181 | 0 | else |
182 | 0 | { |
183 | 0 | char *p = native_to_utf8 (string); |
184 | 0 | sl = append_to_strlist( list, p ); |
185 | 0 | xfree( p ); |
186 | 0 | } |
187 | 0 | return sl; |
188 | 0 | } |
189 | | |
190 | | |
191 | | /* Tokenize STRING using the delimiters from DELIM and append each |
192 | | * token to the string list LIST. On success a pinter into LIST with |
193 | | * the first new token is returned. Returns NULL on error and sets |
194 | | * ERRNO. Take care, an error with ENOENT set mean that no tokens |
195 | | * were found in STRING. */ |
196 | | strlist_t |
197 | | tokenize_to_strlist (strlist_t *list, const char *string, const char *delim) |
198 | 0 | { |
199 | 0 | const char *s, *se; |
200 | 0 | size_t n; |
201 | 0 | strlist_t newlist = NULL; |
202 | 0 | strlist_t tail; |
203 | |
|
204 | 0 | s = string; |
205 | 0 | do |
206 | 0 | { |
207 | 0 | se = strpbrk (s, delim); |
208 | 0 | if (se) |
209 | 0 | n = se - s; |
210 | 0 | else |
211 | 0 | n = strlen (s); |
212 | 0 | if (!n) |
213 | 0 | continue; /* Skip empty string. */ |
214 | 0 | tail = do_append_to_strlist (&newlist, s, n); |
215 | 0 | if (!tail) |
216 | 0 | { |
217 | 0 | free_strlist (newlist); |
218 | 0 | return NULL; |
219 | 0 | } |
220 | 0 | trim_spaces (tail->d); |
221 | 0 | if (!*tail->d) /* Remove new but empty item from the list. */ |
222 | 0 | { |
223 | 0 | tail = strlist_prev (newlist, tail); |
224 | 0 | if (tail) |
225 | 0 | { |
226 | 0 | free_strlist (tail->next); |
227 | 0 | tail->next = NULL; |
228 | 0 | } |
229 | 0 | else if (newlist) |
230 | 0 | { |
231 | 0 | free_strlist (newlist); |
232 | 0 | newlist = NULL; |
233 | 0 | } |
234 | 0 | continue; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | while (se && (s = se + 1)); |
238 | | |
239 | 0 | if (!newlist) |
240 | 0 | { |
241 | | /* Not items found. Indicate this by returnning NULL with errno |
242 | | * set to ENOENT. */ |
243 | 0 | gpg_err_set_errno (ENOENT); |
244 | 0 | return NULL; |
245 | 0 | } |
246 | | |
247 | | /* Append NEWLIST to LIST. */ |
248 | 0 | if (!*list) |
249 | 0 | *list = newlist; |
250 | 0 | else |
251 | 0 | { |
252 | 0 | for (tail = *list; tail->next; tail = tail->next) |
253 | 0 | ; |
254 | 0 | tail->next = newlist; |
255 | 0 | } |
256 | 0 | return newlist; |
257 | 0 | } |
258 | | |
259 | | |
260 | | /* Return a copy of LIST. This function terminates the process on |
261 | | memory shortage.*/ |
262 | | strlist_t |
263 | | strlist_copy (strlist_t list) |
264 | 0 | { |
265 | 0 | strlist_t newlist = NULL, sl, *last; |
266 | |
|
267 | 0 | last = &newlist; |
268 | 0 | for (; list; list = list->next) |
269 | 0 | { |
270 | 0 | sl = xmalloc (sizeof *sl + strlen (list->d)); |
271 | 0 | sl->flags = list->flags; |
272 | 0 | strcpy(sl->d, list->d); |
273 | 0 | sl->next = NULL; |
274 | 0 | *last = sl; |
275 | 0 | last = &sl; |
276 | 0 | } |
277 | 0 | return newlist; |
278 | 0 | } |
279 | | |
280 | | |
281 | | |
282 | | strlist_t |
283 | | strlist_prev( strlist_t head, strlist_t node ) |
284 | 0 | { |
285 | 0 | strlist_t n; |
286 | |
|
287 | 0 | for(n=NULL; head && head != node; head = head->next ) |
288 | 0 | n = head; |
289 | 0 | return n; |
290 | 0 | } |
291 | | |
292 | | strlist_t |
293 | | strlist_last( strlist_t node ) |
294 | 0 | { |
295 | 0 | if( node ) |
296 | 0 | for( ; node->next ; node = node->next ) |
297 | 0 | ; |
298 | 0 | return node; |
299 | 0 | } |
300 | | |
301 | | |
302 | | /* Remove the first item from LIST and return its content in an |
303 | | allocated buffer. This function terminates the process on memory |
304 | | shortage. */ |
305 | | char * |
306 | | strlist_pop (strlist_t *list) |
307 | 0 | { |
308 | 0 | char *str=NULL; |
309 | 0 | strlist_t sl=*list; |
310 | |
|
311 | 0 | if(sl) |
312 | 0 | { |
313 | 0 | str = xmalloc(strlen(sl->d)+1); |
314 | 0 | strcpy(str,sl->d); |
315 | |
|
316 | 0 | *list=sl->next; |
317 | 0 | xfree(sl); |
318 | 0 | } |
319 | |
|
320 | 0 | return str; |
321 | 0 | } |
322 | | |
323 | | /* Return the first element of the string list HAYSTACK whose string |
324 | | matches NEEDLE. If no elements match, return NULL. */ |
325 | | strlist_t |
326 | | strlist_find (strlist_t haystack, const char *needle) |
327 | 0 | { |
328 | 0 | for (; |
329 | 0 | haystack; |
330 | 0 | haystack = haystack->next) |
331 | 0 | if (strcmp (haystack->d, needle) == 0) |
332 | 0 | return haystack; |
333 | 0 | return NULL; |
334 | 0 | } |
335 | | |
336 | | int |
337 | | strlist_length (strlist_t list) |
338 | 0 | { |
339 | 0 | int i; |
340 | 0 | for (i = 0; list; list = list->next) |
341 | 0 | i ++; |
342 | |
|
343 | 0 | return i; |
344 | 0 | } |
345 | | |
346 | | /* Reverse the list *LIST in place. */ |
347 | | strlist_t |
348 | | strlist_rev (strlist_t *list) |
349 | 0 | { |
350 | 0 | strlist_t l = *list; |
351 | 0 | strlist_t lrev = NULL; |
352 | |
|
353 | 0 | while (l) |
354 | 0 | { |
355 | 0 | strlist_t tail = l->next; |
356 | 0 | l->next = lrev; |
357 | 0 | lrev = l; |
358 | 0 | l = tail; |
359 | 0 | } |
360 | |
|
361 | 0 | *list = lrev; |
362 | 0 | return lrev; |
363 | 0 | } |