git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / libretro-common / lists / string_list.c
CommitLineData
3719602c
PC
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
32static 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 **/
67static 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 **/
89void 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
99bool 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 **/
118struct 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
141bool 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 **/
169bool 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 **/
206bool 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 **/
235void 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 **/
252void 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 */
288struct 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
317error:
318 string_list_free(list);
319 free(copy);
320 return NULL;
321}
322
323bool 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 **/
367struct 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
402bool 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 */
447int 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 */
476bool 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
494struct 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}