1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (string_list.c).
5 * ---------------------------------------------------------------------------------------
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:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
27 #include <lists/string_list.h>
28 #include <compat/strl.h>
29 #include <compat/posix_string.h>
30 #include <string/stdstring.h>
32 static bool string_list_deinitialize_internal(struct string_list *list)
40 for (i = 0; i < list->size; i++)
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;
59 * string_list_capacity:
60 * @list : pointer to string list
61 * @cap : new capacity for string list.
63 * Change maximum capacity of string list's size.
65 * @return true if successful, otherwise false.
67 static bool string_list_capacity(struct string_list *list, size_t cap)
69 struct string_list_elem *new_data = (struct string_list_elem*)
70 realloc(list->elems, cap * sizeof(*new_data));
76 memset(&new_data[list->cap], 0, sizeof(*new_data) * (cap - list->cap));
78 list->elems = new_data;
85 * @list : pointer to string list object
87 * Frees a string list.
89 void string_list_free(struct string_list *list)
94 string_list_deinitialize_internal(list);
99 bool string_list_deinitialize(struct string_list *list)
103 if (!string_list_deinitialize_internal(list))
114 * Creates a new string list. Has to be freed manually.
116 * @return New string list if successful, otherwise NULL.
118 struct string_list *string_list_new(void)
120 struct string_list_elem *
122 struct string_list *list = (struct string_list*)
123 malloc(sizeof(*list));
127 if (!(elems = (struct string_list_elem*)
128 calloc(32, sizeof(*elems))))
130 string_list_free(list);
141 bool string_list_initialize(struct string_list *list)
143 struct string_list_elem *
147 if (!(elems = (struct string_list_elem*)
148 calloc(32, sizeof(*elems))))
150 string_list_deinitialize(list);
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.
165 * Appends a new element to the string list.
167 * @return true if successful, otherwise false.
169 bool string_list_append(struct string_list *list, const char *elem,
170 union string_list_elem_attr attr)
172 char *data_dup = NULL;
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))
185 if (!(data_dup = strdup(elem)))
188 list->elems[list->size].data = data_dup;
189 list->elems[list->size].attr = attr;
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.
202 * Appends a new element to the string list.
204 * @return true if successful, otherwise false.
206 bool string_list_append_n(struct string_list *list, const char *elem,
207 unsigned length, union string_list_elem_attr attr)
209 char *data_dup = NULL;
211 if (list->size >= list->cap &&
212 !string_list_capacity(list, list->cap * 2))
215 if (!(data_dup = (char*)malloc(length + 1)))
218 strlcpy(data_dup, elem, length + 1);
220 list->elems[list->size].data = data_dup;
221 list->elems[list->size].attr = attr;
229 * @list : pointer to string list
230 * @idx : index of element in string list
231 * @str : value for the element.
233 * Set value of element inside string list.
235 void string_list_set(struct string_list *list,
236 unsigned idx, const char *str)
238 free(list->elems[idx].data);
239 list->elems[idx].data = strdup(str);
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.
249 * A string list will be joined/concatenated as a
250 * string to @buffer, delimited by @delim.
252 void string_list_join_concat(char *buffer, size_t size,
253 const struct string_list *list, const char *delim)
256 size_t len = strlen_size(buffer, size);
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 */
271 for (i = 0; i < list->size; i++)
273 strlcat(buffer, list->elems[i].data, size);
274 if ((i + 1) < list->size)
275 strlcat(buffer, delim, size);
281 * @str : string to turn into a string list
282 * @delim : delimiter character to use for splitting the string.
284 * Creates a new string list based on string @str, delimited by @delim.
286 * Returns: new string list if successful, otherwise NULL.
288 struct string_list *string_split(const char *str, const char *delim)
292 const char *tmp = NULL;
293 struct string_list *list = string_list_new();
298 if (!(copy = strdup(str)))
301 tmp = strtok_r(copy, delim, &save);
304 union string_list_elem_attr attr;
308 if (!string_list_append(list, tmp, attr))
311 tmp = strtok_r(NULL, delim, &save);
318 string_list_free(list);
323 bool string_split_noalloc(struct string_list *list,
324 const char *str, const char *delim)
328 const char *tmp = NULL;
333 if (!(copy = strdup(str)))
336 tmp = strtok_r(copy, delim, &save);
339 union string_list_elem_attr attr;
343 if (!string_list_append(list, tmp, attr))
349 tmp = strtok_r(NULL, delim, &save);
358 * @str : string to turn into a string list
359 * @delim : delimiter character to use for separating the string.
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 "".
365 * @return New string list if successful, otherwise NULL.
367 struct string_list *string_separate(char *str, const char *delim)
370 char **str_ptr = NULL;
371 struct string_list *list = NULL;
374 if (!str || string_is_empty(delim))
376 if (!(list = string_list_new()))
380 token = string_tokenize(str_ptr, delim);
384 union string_list_elem_attr attr;
388 if (!string_list_append(list, token, attr))
391 string_list_free(list);
396 token = string_tokenize(str_ptr, delim);
402 bool string_separate_noalloc(
403 struct string_list *list,
404 char *str, const char *delim)
407 char **str_ptr = NULL;
410 if (!str || string_is_empty(delim) || !list)
414 token = string_tokenize(str_ptr, delim);
418 union string_list_elem_attr attr;
422 if (!string_list_append(list, token, attr))
429 token = string_tokenize(str_ptr, delim);
436 * string_list_find_elem:
439 * Pointer to string list
441 * Element to find inside the string list.
443 * Searches for an element (@elem) inside the string list.
445 * @return Number of elements found, otherwise 0.
447 int string_list_find_elem(const struct string_list *list, const char *elem)
452 for (i = 0; i < list->size; i++)
454 if (string_is_equal_noncase(list->elems[i].data, elem))
462 * string_list_find_elem_prefix:
465 * Pointer to string list
467 * Prefix to append to @elem
469 * Element to find inside the string list.
471 * Searches for an element (@elem) inside the string list. Will
472 * also search for the same element prefixed by @prefix.
474 * @return true if element could be found, otherwise false.
476 bool string_list_find_elem_prefix(const struct string_list *list,
477 const char *prefix, const char *elem)
483 strlcpy(prefixed, prefix, sizeof(prefixed));
484 strlcat(prefixed, elem, sizeof(prefixed));
485 for (i = 0; i < list->size; i++)
487 if ( string_is_equal_noncase(list->elems[i].data, elem)
488 || string_is_equal_noncase(list->elems[i].data, prefixed))
494 struct string_list *string_list_clone(const struct string_list *src)
497 struct string_list_elem
500 *dest = (struct string_list*)
501 malloc(sizeof(struct string_list));
507 dest->size = src->size;
508 if (src->cap < dest->size)
509 dest->cap = dest->size;
511 dest->cap = src->cap;
513 if (!(elems = (struct string_list_elem*)
514 calloc(dest->cap, sizeof(struct string_list_elem))))
522 for (i = 0; i < src->size; i++)
524 const char *_src = src->elems[i].data;
525 size_t len = _src ? strlen(_src) : 0;
527 dest->elems[i].data = NULL;
528 dest->elems[i].attr = src->elems[i].attr;
532 char *result = (char*)malloc(len + 1);
533 strcpy(result, _src);
534 dest->elems[i].data = result;