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