1 /* Copyright  (C) 2010-2018 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 /**
33  * string_list_free
34  * @list             : pointer to string list object
35  *
36  * Frees a string list.
37  */
string_list_free(struct string_list * list)38 void string_list_free(struct string_list *list)
39 {
40    size_t i;
41    if (!list)
42       return;
43 
44    if (list->elems)
45    {
46       for (i = 0; i < list->size; i++)
47       {
48          if (list->elems[i].data)
49             free(list->elems[i].data);
50          list->elems[i].data = NULL;
51       }
52 
53       free(list->elems);
54    }
55 
56    list->elems = NULL;
57    free(list);
58 }
59 
60 /**
61  * string_list_capacity:
62  * @list             : pointer to string list
63  * @cap              : new capacity for string list.
64  *
65  * Change maximum capacity of string list's size.
66  *
67  * Returns: true (1) if successful, otherwise false (0).
68  **/
string_list_capacity(struct string_list * list,size_t cap)69 static bool string_list_capacity(struct string_list *list, size_t cap)
70 {
71    struct string_list_elem *new_data = (struct string_list_elem*)
72       realloc(list->elems, cap * sizeof(*new_data));
73 
74    if (!new_data)
75       return false;
76 
77    if (cap > list->cap)
78       memset(&new_data[list->cap], 0, sizeof(*new_data) * (cap - list->cap));
79 
80    list->elems = new_data;
81    list->cap   = cap;
82    return true;
83 }
84 
85 /**
86  * string_list_new:
87  *
88  * Creates a new string list. Has to be freed manually.
89  *
90  * Returns: new string list if successful, otherwise NULL.
91  */
string_list_new(void)92 struct string_list *string_list_new(void)
93 {
94    struct string_list *list = (struct string_list*)
95       calloc(1, sizeof(*list));
96 
97    if (!list)
98       return NULL;
99 
100    if (!string_list_capacity(list, 32))
101    {
102       string_list_free(list);
103       return NULL;
104    }
105 
106    return list;
107 }
108 
109 /**
110  * string_list_append:
111  * @list             : pointer to string list
112  * @elem             : element to add to the string list
113  * @attr             : attributes of new element.
114  *
115  * Appends a new element to the string list.
116  *
117  * Returns: true (1) if successful, otherwise false (0).
118  **/
string_list_append(struct string_list * list,const char * elem,union string_list_elem_attr attr)119 bool string_list_append(struct string_list *list, const char *elem,
120       union string_list_elem_attr attr)
121 {
122    char *data_dup = NULL;
123 
124    if (list->size >= list->cap &&
125          !string_list_capacity(list, list->cap * 2))
126       return false;
127 
128    data_dup = strdup(elem);
129    if (!data_dup)
130       return false;
131 
132    list->elems[list->size].data = data_dup;
133    list->elems[list->size].attr = attr;
134 
135    list->size++;
136    return true;
137 }
138 
139 /**
140  * string_list_append_n:
141  * @list             : pointer to string list
142  * @elem             : element to add to the string list
143  * @length           : read at most this many bytes from elem
144  * @attr             : attributes of new element.
145  *
146  * Appends a new element to the string list.
147  *
148  * Returns: true (1) if successful, otherwise false (0).
149  **/
string_list_append_n(struct string_list * list,const char * elem,unsigned length,union string_list_elem_attr attr)150 bool string_list_append_n(struct string_list *list, const char *elem,
151       unsigned length, union string_list_elem_attr attr)
152 {
153    char *data_dup = NULL;
154 
155    if (list->size >= list->cap &&
156          !string_list_capacity(list, list->cap * 2))
157       return false;
158 
159    data_dup = (char*)malloc(length + 1);
160 
161    if (!data_dup)
162       return false;
163 
164    strlcpy(data_dup, elem, length + 1);
165 
166    list->elems[list->size].data = data_dup;
167    list->elems[list->size].attr = attr;
168 
169    list->size++;
170    return true;
171 }
172 
173 /**
174  * string_list_set:
175  * @list             : pointer to string list
176  * @idx              : index of element in string list
177  * @str              : value for the element.
178  *
179  * Set value of element inside string list.
180  **/
string_list_set(struct string_list * list,unsigned idx,const char * str)181 void string_list_set(struct string_list *list,
182       unsigned idx, const char *str)
183 {
184    free(list->elems[idx].data);
185    list->elems[idx].data = strdup(str);
186 }
187 
188 /**
189  * string_list_join_concat:
190  * @buffer           : buffer that @list will be joined to.
191  * @size             : length of @buffer.
192  * @list             : pointer to string list.
193  * @delim            : delimiter character for @list.
194  *
195  * A string list will be joined/concatenated as a
196  * string to @buffer, delimited by @delim.
197  */
string_list_join_concat(char * buffer,size_t size,const struct string_list * list,const char * delim)198 void string_list_join_concat(char *buffer, size_t size,
199       const struct string_list *list, const char *delim)
200 {
201    size_t i, len = strlen(buffer);
202 
203    buffer += len;
204    size   -= len;
205 
206    for (i = 0; i < list->size; i++)
207    {
208       strlcat(buffer, list->elems[i].data, size);
209       if ((i + 1) < list->size)
210          strlcat(buffer, delim, size);
211    }
212 }
213 
214 /**
215  * string_split:
216  * @str              : string to turn into a string list
217  * @delim            : delimiter character to use for splitting the string.
218  *
219  * Creates a new string list based on string @str, delimited by @delim.
220  *
221  * Returns: new string list if successful, otherwise NULL.
222  */
string_split(const char * str,const char * delim)223 struct string_list *string_split(const char *str, const char *delim)
224 {
225    char *save      = NULL;
226    char *copy      = NULL;
227    const char *tmp = NULL;
228    struct string_list *list = string_list_new();
229 
230    if (!list)
231       goto error;
232 
233    copy = strdup(str);
234    if (!copy)
235       goto error;
236 
237    tmp = strtok_r(copy, delim, &save);
238    while (tmp)
239    {
240       union string_list_elem_attr attr;
241 
242       attr.i = 0;
243 
244       if (!string_list_append(list, tmp, attr))
245          goto error;
246 
247       tmp = strtok_r(NULL, delim, &save);
248    }
249 
250    free(copy);
251    return list;
252 
253 error:
254    string_list_free(list);
255    free(copy);
256    return NULL;
257 }
258 
259 /**
260  * string_list_find_elem:
261  * @list             : pointer to string list
262  * @elem             : element to find inside the string list.
263  *
264  * Searches for an element (@elem) inside the string list.
265  *
266  * Returns: true (1) if element could be found, otherwise false (0).
267  */
string_list_find_elem(const struct string_list * list,const char * elem)268 int string_list_find_elem(const struct string_list *list, const char *elem)
269 {
270    size_t i;
271 
272    if (!list)
273       return false;
274 
275    for (i = 0; i < list->size; i++)
276    {
277       if (string_is_equal_noncase(list->elems[i].data, elem))
278          return (int)(i + 1);
279    }
280 
281    return false;
282 }
283 
284 /**
285  * string_list_find_elem_prefix:
286  * @list             : pointer to string list
287  * @prefix           : prefix to append to @elem
288  * @elem             : element to find inside the string list.
289  *
290  * Searches for an element (@elem) inside the string list. Will
291  * also search for the same element prefixed by @prefix.
292  *
293  * Returns: true (1) if element could be found, otherwise false (0).
294  */
string_list_find_elem_prefix(const struct string_list * list,const char * prefix,const char * elem)295 bool string_list_find_elem_prefix(const struct string_list *list,
296       const char *prefix, const char *elem)
297 {
298    size_t i;
299    char prefixed[255];
300 
301    if (!list)
302       return false;
303 
304    prefixed[0] = '\0';
305 
306    strlcpy(prefixed, prefix, sizeof(prefixed));
307    strlcat(prefixed, elem,   sizeof(prefixed));
308 
309    for (i = 0; i < list->size; i++)
310    {
311       if (string_is_equal_noncase(list->elems[i].data, elem) ||
312             string_is_equal_noncase(list->elems[i].data, prefixed))
313          return true;
314    }
315 
316    return false;
317 }
318 
string_list_clone(const struct string_list * src)319 struct string_list *string_list_clone(
320       const struct string_list *src)
321 {
322    unsigned i;
323    struct string_list_elem *elems = NULL;
324    struct string_list *dest       = (struct string_list*)
325       calloc(1, sizeof(struct string_list));
326 
327    if (!dest)
328       return NULL;
329 
330    dest->size      = src->size;
331    dest->cap       = src->cap;
332    if (dest->cap < dest->size)
333       dest->cap    = dest->size;
334 
335    elems           = (struct string_list_elem*)
336       calloc(dest->cap, sizeof(struct string_list_elem));
337 
338    if (!elems)
339    {
340       free(dest);
341       return NULL;
342    }
343 
344    dest->elems            = elems;
345 
346    for (i = 0; i < src->size; i++)
347    {
348       const char *_src    = src->elems[i].data;
349       size_t      len     = _src ? strlen(_src) : 0;
350 
351       dest->elems[i].data = NULL;
352       dest->elems[i].attr = src->elems[i].attr;
353 
354       if (len != 0)
355       {
356          char *result        = (char*)malloc(len + 1);
357          strcpy(result, _src);
358          dest->elems[i].data = result;
359       }
360    }
361 
362    return dest;
363 }
364