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