try a new github issue template
[pcsx_rearmed.git] / deps / libretro-common / lists / string_list.c
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
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  * @return true if successful, otherwise false.
66  **/
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  **/
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
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  * @return New string list if successful, otherwise NULL.
117  **/
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
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  * @return true if successful, otherwise false.
168  **/
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    if (!(data_dup = strdup(elem)))
186       return false;
187
188    list->elems[list->size].data = data_dup;
189    list->elems[list->size].attr = attr;
190
191    list->size++;
192    return true;
193 }
194
195 /**
196  * string_list_append_n:
197  * @list             : pointer to string list
198  * @elem             : element to add to the string list
199  * @length           : read at most this many bytes from elem
200  * @attr             : attributes of new element.
201  *
202  * Appends a new element to the string list.
203  *
204  * @return true if successful, otherwise false.
205  **/
206 bool string_list_append_n(struct string_list *list, const char *elem,
207       unsigned length, union string_list_elem_attr attr)
208 {
209    char *data_dup = NULL;
210
211    if (list->size >= list->cap &&
212          !string_list_capacity(list, list->cap * 2))
213       return false;
214
215    if (!(data_dup = (char*)malloc(length + 1)))
216       return false;
217
218    strlcpy(data_dup, elem, length + 1);
219
220    list->elems[list->size].data = data_dup;
221    list->elems[list->size].attr = attr;
222
223    list->size++;
224    return true;
225 }
226
227 /**
228  * string_list_set:
229  * @list             : pointer to string list
230  * @idx              : index of element in string list
231  * @str              : value for the element.
232  *
233  * Set value of element inside string list.
234  **/
235 void string_list_set(struct string_list *list,
236       unsigned idx, const char *str)
237 {
238    free(list->elems[idx].data);
239    list->elems[idx].data = strdup(str);
240 }
241
242 /**
243  * string_list_join_concat:
244  * @buffer           : buffer that @list will be joined to.
245  * @size             : length of @buffer.
246  * @list             : pointer to string list.
247  * @delim            : delimiter character for @list.
248  *
249  * A string list will be joined/concatenated as a
250  * string to @buffer, delimited by @delim.
251  **/
252 void string_list_join_concat(char *buffer, size_t size,
253       const struct string_list *list, const char *delim)
254 {
255    size_t i;
256    size_t len = strlen_size(buffer, size);
257
258    /* If buffer is already 'full', nothing
259     * further can be added
260     * > This condition will also be triggered
261     *   if buffer is not NULL-terminated,
262     *   in which case any attempt to increment
263     *   buffer or decrement size would lead to
264     *   undefined behaviour */
265    if (len >= size)
266       return;
267
268    buffer += len;
269    size   -= len;
270
271    for (i = 0; i < list->size; i++)
272    {
273       strlcat(buffer, list->elems[i].data, size);
274       if ((i + 1) < list->size)
275          strlcat(buffer, delim, size);
276    }
277 }
278
279 /**
280  * string_split:
281  * @str              : string to turn into a string list
282  * @delim            : delimiter character to use for splitting the string.
283  *
284  * Creates a new string list based on string @str, delimited by @delim.
285  *
286  * Returns: new string list if successful, otherwise NULL.
287  */
288 struct string_list *string_split(const char *str, const char *delim)
289 {
290    char *save      = NULL;
291    char *copy      = NULL;
292    const char *tmp = NULL;
293    struct string_list *list = string_list_new();
294
295    if (!list)
296       return NULL;
297
298    if (!(copy = strdup(str)))
299       goto error;
300
301    tmp = strtok_r(copy, delim, &save);
302    while (tmp)
303    {
304       union string_list_elem_attr attr;
305
306       attr.i = 0;
307
308       if (!string_list_append(list, tmp, attr))
309          goto error;
310
311       tmp = strtok_r(NULL, delim, &save);
312    }
313
314    free(copy);
315    return list;
316
317 error:
318    string_list_free(list);
319    free(copy);
320    return NULL;
321 }
322
323 bool string_split_noalloc(struct string_list *list,
324       const char *str, const char *delim)
325 {
326    char *save      = NULL;
327    char *copy      = NULL;
328    const char *tmp = NULL;
329
330    if (!list)
331       return false;
332
333    if (!(copy = strdup(str)))
334       return false;
335
336    tmp             = strtok_r(copy, delim, &save);
337    while (tmp)
338    {
339       union string_list_elem_attr attr;
340
341       attr.i = 0;
342
343       if (!string_list_append(list, tmp, attr))
344       {
345          free(copy);
346          return false;
347       }
348
349       tmp = strtok_r(NULL, delim, &save);
350    }
351
352    free(copy);
353    return true;
354 }
355
356 /**
357  * string_separate:
358  * @str              : string to turn into a string list
359  * @delim            : delimiter character to use for separating the string.
360  *
361  * Creates a new string list based on string @str, delimited by @delim.
362  * Includes empty strings - i.e. two adjacent delimiters will resolve
363  * to a string list element of "".
364  *
365  * @return New string list if successful, otherwise NULL.
366  **/
367 struct string_list *string_separate(char *str, const char *delim)
368 {
369    char *token              = NULL;
370    char **str_ptr           = NULL;
371    struct string_list *list = NULL;
372
373    /* Sanity check */
374    if (!str || string_is_empty(delim))
375       return NULL;
376    if (!(list = string_list_new()))
377            return NULL;
378
379    str_ptr = &str;
380    token   = string_tokenize(str_ptr, delim);
381
382    while (token)
383    {
384       union string_list_elem_attr attr;
385
386       attr.i = 0;
387
388       if (!string_list_append(list, token, attr))
389       {
390          free(token);
391          string_list_free(list);
392          return NULL;
393       }
394
395       free(token);
396       token = string_tokenize(str_ptr, delim);
397    }
398
399    return list;
400 }
401
402 bool string_separate_noalloc(
403       struct string_list *list,
404       char *str, const char *delim)
405 {
406    char *token              = NULL;
407    char **str_ptr           = NULL;
408
409    /* Sanity check */
410    if (!str || string_is_empty(delim) || !list)
411       return false;
412
413    str_ptr = &str;
414    token   = string_tokenize(str_ptr, delim);
415
416    while (token)
417    {
418       union string_list_elem_attr attr;
419
420       attr.i = 0;
421
422       if (!string_list_append(list, token, attr))
423       {
424          free(token);
425          return false;
426       }
427
428       free(token);
429       token = string_tokenize(str_ptr, delim);
430    }
431
432    return true;
433 }
434
435 /**
436  * string_list_find_elem:
437  *
438  * @param list
439  * Pointer to string list
440  * @param elem
441  * Element to find inside the string list.
442  *
443  * Searches for an element (@elem) inside the string list.
444  *
445  * @return Number of elements found, otherwise 0.
446  */
447 int string_list_find_elem(const struct string_list *list, const char *elem)
448 {
449    if (list)
450    {
451       size_t i;
452       for (i = 0; i < list->size; i++)
453       {
454          if (string_is_equal_noncase(list->elems[i].data, elem))
455             return (int)(i + 1);
456       }
457    }
458    return 0;
459 }
460
461 /**
462  * string_list_find_elem_prefix:
463  *
464  * @param list
465  * Pointer to string list
466  * @param prefix
467  * Prefix to append to @elem
468  * @param elem
469  * Element to find inside the string list.
470  *
471  * Searches for an element (@elem) inside the string list. Will
472  * also search for the same element prefixed by @prefix.
473  *
474  * @return true if element could be found, otherwise false.
475  */
476 bool string_list_find_elem_prefix(const struct string_list *list,
477       const char *prefix, const char *elem)
478 {
479    size_t i;
480    char prefixed[255];
481    if (!list)
482       return false;
483    strlcpy(prefixed, prefix, sizeof(prefixed));
484    strlcat(prefixed, elem,   sizeof(prefixed));
485    for (i = 0; i < list->size; i++)
486    {
487       if (     string_is_equal_noncase(list->elems[i].data, elem)
488             || string_is_equal_noncase(list->elems[i].data, prefixed))
489          return true;
490    }
491    return false;
492 }
493
494 struct string_list *string_list_clone(const struct string_list *src)
495 {
496    size_t i;
497    struct string_list_elem 
498       *elems              = NULL;
499    struct string_list 
500       *dest               = (struct string_list*)
501       malloc(sizeof(struct string_list));
502
503    if (!dest)
504       return NULL;
505
506    dest->elems            = NULL;
507    dest->size             = src->size;
508    if (src->cap < dest->size)
509       dest->cap           = dest->size;
510    else 
511       dest->cap           = src->cap;
512
513    if (!(elems = (struct string_list_elem*)
514       calloc(dest->cap, sizeof(struct string_list_elem))))
515    {
516       free(dest);
517       return NULL;
518    }
519
520    dest->elems            = elems;
521
522    for (i = 0; i < src->size; i++)
523    {
524       const char *_src       = src->elems[i].data;
525       size_t      len        = _src ? strlen(_src) : 0;
526
527       dest->elems[i].data    = NULL;
528       dest->elems[i].attr    = src->elems[i].attr;
529
530       if (len != 0)
531       {
532          char *result        = (char*)malloc(len + 1);
533          strcpy(result, _src);
534          dest->elems[i].data = result;
535       }
536    }
537
538    return dest;
539 }