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