1 /* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (string_list.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <string.h>
26
27 #include <lists/string_list.h>
28 #include <compat/strl.h>
29 #include <compat/posix_string.h>
30 #include <string/stdstring.h>
31
string_list_deinitialize_internal(struct string_list * list)32 static bool string_list_deinitialize_internal(struct string_list *list)
33 {
34 if (!list)
35 return false;
36
37 if (list->elems)
38 {
39 unsigned i;
40 for (i = 0; i < list->size; i++)
41 {
42 if (list->elems[i].data)
43 free(list->elems[i].data);
44 if (list->elems[i].userdata)
45 free(list->elems[i].userdata);
46 list->elems[i].data = NULL;
47 list->elems[i].userdata = NULL;
48 }
49
50 free(list->elems);
51 }
52
53 list->elems = NULL;
54
55 return true;
56 }
57
58 /**
59 * string_list_capacity:
60 * @list : pointer to string list
61 * @cap : new capacity for string list.
62 *
63 * Change maximum capacity of string list's size.
64 *
65 * Returns: true (1) if successful, otherwise false (0).
66 **/
string_list_capacity(struct string_list * list,size_t cap)67 static bool string_list_capacity(struct string_list *list, size_t cap)
68 {
69 struct string_list_elem *new_data = (struct string_list_elem*)
70 realloc(list->elems, cap * sizeof(*new_data));
71
72 if (!new_data)
73 return false;
74
75 if (cap > list->cap)
76 memset(&new_data[list->cap], 0, sizeof(*new_data) * (cap - list->cap));
77
78 list->elems = new_data;
79 list->cap = cap;
80 return true;
81 }
82
83 /**
84 * string_list_free
85 * @list : pointer to string list object
86 *
87 * Frees a string list.
88 */
string_list_free(struct string_list * list)89 void string_list_free(struct string_list *list)
90 {
91 if (!list)
92 return;
93
94 string_list_deinitialize_internal(list);
95
96 free(list);
97 }
98
string_list_deinitialize(struct string_list * list)99 bool string_list_deinitialize(struct string_list *list)
100 {
101 if (!list)
102 return false;
103 if (!string_list_deinitialize_internal(list))
104 return false;
105 list->elems = NULL;
106 list->size = 0;
107 list->cap = 0;
108 return true;
109 }
110
111 /**
112 * string_list_new:
113 *
114 * Creates a new string list. Has to be freed manually.
115 *
116 * Returns: new string list if successful, otherwise NULL.
117 */
string_list_new(void)118 struct string_list *string_list_new(void)
119 {
120 struct string_list_elem *
121 elems = NULL;
122 struct string_list *list = (struct string_list*)
123 malloc(sizeof(*list));
124 if (!list)
125 return NULL;
126
127 if (!(elems = (struct string_list_elem*)
128 calloc(32, sizeof(*elems))))
129 {
130 string_list_free(list);
131 return NULL;
132 }
133
134 list->elems = elems;
135 list->size = 0;
136 list->cap = 32;
137
138 return list;
139 }
140
string_list_initialize(struct string_list * list)141 bool string_list_initialize(struct string_list *list)
142 {
143 struct string_list_elem *
144 elems = NULL;
145 if (!list)
146 return false;
147 if (!(elems = (struct string_list_elem*)
148 calloc(32, sizeof(*elems))))
149 {
150 string_list_deinitialize(list);
151 return false;
152 }
153 list->elems = elems;
154 list->size = 0;
155 list->cap = 32;
156 return true;
157 }
158
159 /**
160 * string_list_append:
161 * @list : pointer to string list
162 * @elem : element to add to the string list
163 * @attr : attributes of new element.
164 *
165 * Appends a new element to the string list.
166 *
167 * Returns: true (1) if successful, otherwise false (0).
168 **/
string_list_append(struct string_list * list,const char * elem,union string_list_elem_attr attr)169 bool string_list_append(struct string_list *list, const char *elem,
170 union string_list_elem_attr attr)
171 {
172 char *data_dup = NULL;
173
174 /* Note: If 'list' is incorrectly initialised
175 * (i.e. if struct is zero initialised and
176 * string_list_initialize() is not called on
177 * it) capacity will be zero. This will cause
178 * a segfault. Handle this case by forcing the new
179 * capacity to a fixed size of 32 */
180 if (list->size >= list->cap &&
181 !string_list_capacity(list,
182 (list->cap > 0) ? (list->cap * 2) : 32))
183 return false;
184
185 data_dup = strdup(elem);
186 if (!data_dup)
187 return false;
188
189 list->elems[list->size].data = data_dup;
190 list->elems[list->size].attr = attr;
191
192 list->size++;
193 return true;
194 }
195
196 /**
197 * string_list_append_n:
198 * @list : pointer to string list
199 * @elem : element to add to the string list
200 * @length : read at most this many bytes from elem
201 * @attr : attributes of new element.
202 *
203 * Appends a new element to the string list.
204 *
205 * Returns: true (1) if successful, otherwise false (0).
206 **/
string_list_append_n(struct string_list * list,const char * elem,unsigned length,union string_list_elem_attr attr)207 bool string_list_append_n(struct string_list *list, const char *elem,
208 unsigned length, union string_list_elem_attr attr)
209 {
210 char *data_dup = NULL;
211
212 if (list->size >= list->cap &&
213 !string_list_capacity(list, list->cap * 2))
214 return false;
215
216 data_dup = (char*)malloc(length + 1);
217
218 if (!data_dup)
219 return false;
220
221 strlcpy(data_dup, elem, length + 1);
222
223 list->elems[list->size].data = data_dup;
224 list->elems[list->size].attr = attr;
225
226 list->size++;
227 return true;
228 }
229
230 /**
231 * string_list_set:
232 * @list : pointer to string list
233 * @idx : index of element in string list
234 * @str : value for the element.
235 *
236 * Set value of element inside string list.
237 **/
string_list_set(struct string_list * list,unsigned idx,const char * str)238 void string_list_set(struct string_list *list,
239 unsigned idx, const char *str)
240 {
241 free(list->elems[idx].data);
242 list->elems[idx].data = strdup(str);
243 }
244
245 /**
246 * string_list_join_concat:
247 * @buffer : buffer that @list will be joined to.
248 * @size : length of @buffer.
249 * @list : pointer to string list.
250 * @delim : delimiter character for @list.
251 *
252 * A string list will be joined/concatenated as a
253 * string to @buffer, delimited by @delim.
254 */
string_list_join_concat(char * buffer,size_t size,const struct string_list * list,const char * delim)255 void string_list_join_concat(char *buffer, size_t size,
256 const struct string_list *list, const char *delim)
257 {
258 size_t i;
259 size_t len = strlen_size(buffer, size);
260
261 /* If buffer is already 'full', nothing
262 * further can be added
263 * > This condition will also be triggered
264 * if buffer is not NUL-terminated,
265 * in which case any attempt to increment
266 * buffer or decrement size would lead to
267 * undefined behaviour */
268 if (len >= size)
269 return;
270
271 buffer += len;
272 size -= len;
273
274 for (i = 0; i < list->size; i++)
275 {
276 strlcat(buffer, list->elems[i].data, size);
277 if ((i + 1) < list->size)
278 strlcat(buffer, delim, size);
279 }
280 }
281
282 /**
283 * string_split:
284 * @str : string to turn into a string list
285 * @delim : delimiter character to use for splitting the string.
286 *
287 * Creates a new string list based on string @str, delimited by @delim.
288 *
289 * Returns: new string list if successful, otherwise NULL.
290 */
string_split(const char * str,const char * delim)291 struct string_list *string_split(const char *str, const char *delim)
292 {
293 char *save = NULL;
294 char *copy = NULL;
295 const char *tmp = NULL;
296 struct string_list *list = string_list_new();
297
298 if (!list)
299 return NULL;
300
301 copy = strdup(str);
302 if (!copy)
303 goto error;
304
305 tmp = strtok_r(copy, delim, &save);
306 while (tmp)
307 {
308 union string_list_elem_attr attr;
309
310 attr.i = 0;
311
312 if (!string_list_append(list, tmp, attr))
313 goto error;
314
315 tmp = strtok_r(NULL, delim, &save);
316 }
317
318 free(copy);
319 return list;
320
321 error:
322 string_list_free(list);
323 free(copy);
324 return NULL;
325 }
326
string_split_noalloc(struct string_list * list,const char * str,const char * delim)327 bool string_split_noalloc(struct string_list *list,
328 const char *str, const char *delim)
329 {
330 char *save = NULL;
331 char *copy = NULL;
332 const char *tmp = NULL;
333
334 if (!list)
335 return false;
336
337 copy = strdup(str);
338 if (!copy)
339 return false;
340
341 tmp = strtok_r(copy, delim, &save);
342 while (tmp)
343 {
344 union string_list_elem_attr attr;
345
346 attr.i = 0;
347
348 if (!string_list_append(list, tmp, attr))
349 {
350 free(copy);
351 return false;
352 }
353
354 tmp = strtok_r(NULL, delim, &save);
355 }
356
357 free(copy);
358 return true;
359 }
360
361 /**
362 * string_separate:
363 * @str : string to turn into a string list
364 * @delim : delimiter character to use for separating the string.
365 *
366 * Creates a new string list based on string @str, delimited by @delim.
367 * Includes empty strings - i.e. two adjacent delimiters will resolve
368 * to a string list element of "".
369 *
370 * Returns: new string list if successful, otherwise NULL.
371 */
string_separate(char * str,const char * delim)372 struct string_list *string_separate(char *str, const char *delim)
373 {
374 char *token = NULL;
375 char **str_ptr = NULL;
376 struct string_list *list = NULL;
377
378 /* Sanity check */
379 if (!str || string_is_empty(delim))
380 goto error;
381
382 str_ptr = &str;
383 list = string_list_new();
384
385 if (!list)
386 goto error;
387
388 token = string_tokenize(str_ptr, delim);
389 while (token)
390 {
391 union string_list_elem_attr attr;
392
393 attr.i = 0;
394
395 if (!string_list_append(list, token, attr))
396 goto error;
397
398 free(token);
399 token = NULL;
400
401 token = string_tokenize(str_ptr, delim);
402 }
403
404 return list;
405
406 error:
407 if (token)
408 free(token);
409 if (list)
410 string_list_free(list);
411 return NULL;
412 }
413
string_separate_noalloc(struct string_list * list,char * str,const char * delim)414 bool string_separate_noalloc(
415 struct string_list *list,
416 char *str, const char *delim)
417 {
418 char *token = NULL;
419 char **str_ptr = NULL;
420
421 /* Sanity check */
422 if (!str || string_is_empty(delim) || !list)
423 return false;
424
425 str_ptr = &str;
426 token = string_tokenize(str_ptr, delim);
427
428 while (token)
429 {
430 union string_list_elem_attr attr;
431
432 attr.i = 0;
433
434 if (!string_list_append(list, token, attr))
435 {
436 free(token);
437 return false;
438 }
439
440 free(token);
441 token = string_tokenize(str_ptr, delim);
442 }
443
444 return true;
445 }
446
447 /**
448 * string_list_find_elem:
449 * @list : pointer to string list
450 * @elem : element to find inside the string list.
451 *
452 * Searches for an element (@elem) inside the string list.
453 *
454 * Returns: true (1) if element could be found, otherwise false (0).
455 */
string_list_find_elem(const struct string_list * list,const char * elem)456 int string_list_find_elem(const struct string_list *list, const char *elem)
457 {
458 size_t i;
459
460 if (!list)
461 return false;
462
463 for (i = 0; i < list->size; i++)
464 {
465 if (string_is_equal_noncase(list->elems[i].data, elem))
466 return (int)(i + 1);
467 }
468
469 return false;
470 }
471
472 /**
473 * string_list_find_elem_prefix:
474 * @list : pointer to string list
475 * @prefix : prefix to append to @elem
476 * @elem : element to find inside the string list.
477 *
478 * Searches for an element (@elem) inside the string list. Will
479 * also search for the same element prefixed by @prefix.
480 *
481 * Returns: true (1) if element could be found, otherwise false (0).
482 */
string_list_find_elem_prefix(const struct string_list * list,const char * prefix,const char * elem)483 bool string_list_find_elem_prefix(const struct string_list *list,
484 const char *prefix, const char *elem)
485 {
486 size_t i;
487 char prefixed[255];
488
489 if (!list)
490 return false;
491
492 prefixed[0] = '\0';
493
494 strlcpy(prefixed, prefix, sizeof(prefixed));
495 strlcat(prefixed, elem, sizeof(prefixed));
496
497 for (i = 0; i < list->size; i++)
498 {
499 if (string_is_equal_noncase(list->elems[i].data, elem) ||
500 string_is_equal_noncase(list->elems[i].data, prefixed))
501 return true;
502 }
503
504 return false;
505 }
506
string_list_clone(const struct string_list * src)507 struct string_list *string_list_clone(
508 const struct string_list *src)
509 {
510 unsigned i;
511 struct string_list_elem
512 *elems = NULL;
513 struct string_list
514 *dest = (struct string_list*)
515 malloc(sizeof(struct string_list));
516
517 if (!dest)
518 return NULL;
519
520 dest->elems = NULL;
521 dest->size = src->size;
522 dest->cap = src->cap;
523 if (dest->cap < dest->size)
524 dest->cap = dest->size;
525
526 elems = (struct string_list_elem*)
527 calloc(dest->cap, sizeof(struct string_list_elem));
528
529 if (!elems)
530 {
531 free(dest);
532 return NULL;
533 }
534
535 dest->elems = elems;
536
537 for (i = 0; i < src->size; i++)
538 {
539 const char *_src = src->elems[i].data;
540 size_t len = _src ? strlen(_src) : 0;
541
542 dest->elems[i].data = NULL;
543 dest->elems[i].attr = src->elems[i].attr;
544
545 if (len != 0)
546 {
547 char *result = (char*)malloc(len + 1);
548 strcpy(result, _src);
549 dest->elems[i].data = result;
550 }
551 }
552
553 return dest;
554 }
555