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