1 /* Copyright (C) 2010-2018 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (file_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 <stdlib.h>
25 #include <string.h>
26
27 #include <retro_common.h>
28 #include <lists/file_list.h>
29 #include <string/stdstring.h>
30 #include <compat/strcasestr.h>
31
file_list_reserve(file_list_t * list,size_t nitems)32 bool file_list_reserve(file_list_t *list, size_t nitems)
33 {
34 const size_t item_size = sizeof(struct item_file);
35 struct item_file *new_data;
36
37 if (nitems < list->capacity || nitems > (size_t)-1/item_size)
38 return false;
39
40 new_data = (struct item_file*)realloc(list->list, nitems * item_size);
41
42 if (!new_data)
43 return false;
44
45 memset(&new_data[list->capacity], 0, item_size * (nitems - list->capacity));
46
47 list->list = new_data;
48 list->capacity = nitems;
49
50 return true;
51 }
52
file_list_prepend(file_list_t * list,const char * path,const char * label,unsigned type,size_t directory_ptr,size_t entry_idx)53 bool file_list_prepend(file_list_t *list,
54 const char *path, const char *label,
55 unsigned type, size_t directory_ptr,
56 size_t entry_idx)
57 {
58 return file_list_insert(list, path,
59 label, type,
60 directory_ptr, entry_idx,
61 0
62 );
63 }
64
file_list_insert(file_list_t * list,const char * path,const char * label,unsigned type,size_t directory_ptr,size_t entry_idx,size_t idx)65 bool file_list_insert(file_list_t *list,
66 const char *path, const char *label,
67 unsigned type, size_t directory_ptr,
68 size_t entry_idx,
69 size_t idx)
70 {
71 int i;
72
73 /* Expand file list if needed */
74 if (list->size >= list->capacity)
75 if (!file_list_reserve(list, list->capacity * 2 + 1))
76 return false;
77
78 for (i = (unsigned)list->size; i > (int)idx; i--)
79 {
80 struct item_file *copy = (struct item_file*)
81 calloc(1, sizeof(struct item_file));
82
83 memcpy(copy, &list->list[i-1], sizeof(struct item_file));
84
85 memcpy(&list->list[i-1], &list->list[i], sizeof(struct item_file));
86 memcpy(&list->list[i], copy, sizeof(struct item_file));
87
88 free(copy);
89 }
90
91 list->list[idx].path = NULL;
92 list->list[idx].label = NULL;
93 list->list[idx].alt = NULL;
94 list->list[idx].type = type;
95 list->list[idx].directory_ptr = directory_ptr;
96 list->list[idx].entry_idx = entry_idx;
97 list->list[idx].userdata = NULL;
98 list->list[idx].actiondata = NULL;
99
100 if (label)
101 list->list[idx].label = strdup(label);
102 if (path)
103 list->list[idx].path = strdup(path);
104
105 list->size++;
106
107 return true;
108 }
109
file_list_append(file_list_t * list,const char * path,const char * label,unsigned type,size_t directory_ptr,size_t entry_idx)110 bool file_list_append(file_list_t *list,
111 const char *path, const char *label,
112 unsigned type, size_t directory_ptr,
113 size_t entry_idx)
114 {
115 unsigned idx = (unsigned)list->size;
116 /* Expand file list if needed */
117 if (idx >= list->capacity)
118 if (!file_list_reserve(list, list->capacity * 2 + 1))
119 return false;
120
121 list->list[idx].path = NULL;
122 list->list[idx].label = NULL;
123 list->list[idx].alt = NULL;
124 list->list[idx].type = type;
125 list->list[idx].directory_ptr = directory_ptr;
126 list->list[idx].entry_idx = entry_idx;
127 list->list[idx].userdata = NULL;
128 list->list[idx].actiondata = NULL;
129
130 if (label)
131 list->list[idx].label = strdup(label);
132 if (path)
133 list->list[idx].path = strdup(path);
134
135 list->size++;
136
137 return true;
138 }
139
file_list_get_size(const file_list_t * list)140 size_t file_list_get_size(const file_list_t *list)
141 {
142 if (!list)
143 return 0;
144 return list->size;
145 }
146
file_list_get_directory_ptr(const file_list_t * list)147 size_t file_list_get_directory_ptr(const file_list_t *list)
148 {
149 size_t size = file_list_get_size(list);
150 return list->list[size].directory_ptr;
151 }
152
file_list_pop(file_list_t * list,size_t * directory_ptr)153 void file_list_pop(file_list_t *list, size_t *directory_ptr)
154 {
155 if (!list)
156 return;
157
158 if (list->size != 0)
159 {
160 --list->size;
161 if (list->list[list->size].path)
162 free(list->list[list->size].path);
163 list->list[list->size].path = NULL;
164
165 if (list->list[list->size].label)
166 free(list->list[list->size].label);
167 list->list[list->size].label = NULL;
168 }
169
170 if (directory_ptr)
171 *directory_ptr = list->list[list->size].directory_ptr;
172 }
173
file_list_free(file_list_t * list)174 void file_list_free(file_list_t *list)
175 {
176 size_t i;
177
178 if (!list)
179 return;
180
181 for (i = 0; i < list->size; i++)
182 {
183 file_list_free_userdata(list, i);
184 file_list_free_actiondata(list, i);
185
186 if (list->list[i].path)
187 free(list->list[i].path);
188 list->list[i].path = NULL;
189
190 if (list->list[i].label)
191 free(list->list[i].label);
192 list->list[i].label = NULL;
193
194 if (list->list[i].alt)
195 free(list->list[i].alt);
196 list->list[i].alt = NULL;
197 }
198 if (list->list)
199 free(list->list);
200 list->list = NULL;
201 free(list);
202 }
203
file_list_clear(file_list_t * list)204 void file_list_clear(file_list_t *list)
205 {
206 size_t i;
207
208 if (!list)
209 return;
210
211 for (i = 0; i < list->size; i++)
212 {
213 if (list->list[i].path)
214 free(list->list[i].path);
215 list->list[i].path = NULL;
216
217 if (list->list[i].label)
218 free(list->list[i].label);
219 list->list[i].label = NULL;
220
221 if (list->list[i].alt)
222 free(list->list[i].alt);
223 list->list[i].alt = NULL;
224 }
225
226 list->size = 0;
227 }
228
file_list_set_label_at_offset(file_list_t * list,size_t idx,const char * label)229 void file_list_set_label_at_offset(file_list_t *list, size_t idx,
230 const char *label)
231 {
232 if (!list)
233 return;
234
235 if (list->list[idx].label)
236 free(list->list[idx].label);
237 list->list[idx].alt = NULL;
238
239 if (label)
240 list->list[idx].label = strdup(label);
241 }
242
file_list_get_label_at_offset(const file_list_t * list,size_t idx,const char ** label)243 void file_list_get_label_at_offset(const file_list_t *list, size_t idx,
244 const char **label)
245 {
246 if (!label || !list)
247 return;
248
249 *label = list->list[idx].path;
250 if (list->list[idx].label)
251 *label = list->list[idx].label;
252 }
253
file_list_set_alt_at_offset(file_list_t * list,size_t idx,const char * alt)254 void file_list_set_alt_at_offset(file_list_t *list, size_t idx,
255 const char *alt)
256 {
257 if (!list || !alt)
258 return;
259
260 if (list->list[idx].alt)
261 free(list->list[idx].alt);
262 list->list[idx].alt = NULL;
263
264 if (alt)
265 list->list[idx].alt = strdup(alt);
266 }
267
file_list_get_alt_at_offset(const file_list_t * list,size_t idx,const char ** alt)268 void file_list_get_alt_at_offset(const file_list_t *list, size_t idx,
269 const char **alt)
270 {
271 if (list && alt)
272 *alt = list->list[idx].alt
273 ? list->list[idx].alt
274 : list->list[idx].path;
275 }
276
file_list_alt_cmp(const void * a_,const void * b_)277 static int file_list_alt_cmp(const void *a_, const void *b_)
278 {
279 const struct item_file *a = (const struct item_file*)a_;
280 const struct item_file *b = (const struct item_file*)b_;
281 const char *cmp_a = a->alt ? a->alt : a->path;
282 const char *cmp_b = b->alt ? b->alt : b->path;
283 return strcasecmp(cmp_a, cmp_b);
284 }
285
file_list_type_cmp(const void * a_,const void * b_)286 static int file_list_type_cmp(const void *a_, const void *b_)
287 {
288 const struct item_file *a = (const struct item_file*)a_;
289 const struct item_file *b = (const struct item_file*)b_;
290 if (a->type < b->type)
291 return -1;
292 if (a->type == b->type)
293 return 0;
294
295 return 1;
296 }
297
file_list_sort_on_alt(file_list_t * list)298 void file_list_sort_on_alt(file_list_t *list)
299 {
300 qsort(list->list, list->size, sizeof(list->list[0]), file_list_alt_cmp);
301 }
302
file_list_sort_on_type(file_list_t * list)303 void file_list_sort_on_type(file_list_t *list)
304 {
305 qsort(list->list, list->size, sizeof(list->list[0]), file_list_type_cmp);
306 }
307
file_list_get_userdata_at_offset(const file_list_t * list,size_t idx)308 void *file_list_get_userdata_at_offset(const file_list_t *list, size_t idx)
309 {
310 if (!list)
311 return NULL;
312 return list->list[idx].userdata;
313 }
314
file_list_set_userdata(const file_list_t * list,size_t idx,void * ptr)315 void file_list_set_userdata(const file_list_t *list, size_t idx, void *ptr)
316 {
317 if (list && ptr)
318 list->list[idx].userdata = ptr;
319 }
320
file_list_set_actiondata(const file_list_t * list,size_t idx,void * ptr)321 void file_list_set_actiondata(const file_list_t *list, size_t idx, void *ptr)
322 {
323 if (list && ptr)
324 list->list[idx].actiondata = ptr;
325 }
326
file_list_get_actiondata_at_offset(const file_list_t * list,size_t idx)327 void *file_list_get_actiondata_at_offset(const file_list_t *list, size_t idx)
328 {
329 if (!list)
330 return NULL;
331 return list->list[idx].actiondata;
332 }
333
file_list_free_actiondata(const file_list_t * list,size_t idx)334 void file_list_free_actiondata(const file_list_t *list, size_t idx)
335 {
336 if (!list)
337 return;
338 if (list->list[idx].actiondata)
339 free(list->list[idx].actiondata);
340 list->list[idx].actiondata = NULL;
341 }
342
file_list_free_userdata(const file_list_t * list,size_t idx)343 void file_list_free_userdata(const file_list_t *list, size_t idx)
344 {
345 if (!list)
346 return;
347 if (list->list[idx].userdata)
348 free(list->list[idx].userdata);
349 list->list[idx].userdata = NULL;
350 }
351
file_list_get_last_actiondata(const file_list_t * list)352 void *file_list_get_last_actiondata(const file_list_t *list)
353 {
354 if (!list)
355 return NULL;
356 return list->list[list->size - 1].actiondata;
357 }
358
file_list_get_at_offset(const file_list_t * list,size_t idx,const char ** path,const char ** label,unsigned * file_type,size_t * entry_idx)359 void file_list_get_at_offset(const file_list_t *list, size_t idx,
360 const char **path, const char **label, unsigned *file_type,
361 size_t *entry_idx)
362 {
363 if (!list)
364 return;
365
366 if (path)
367 *path = list->list[idx].path;
368 if (label)
369 *label = list->list[idx].label;
370 if (file_type)
371 *file_type = list->list[idx].type;
372 if (entry_idx)
373 *entry_idx = list->list[idx].entry_idx;
374 }
375
file_list_get_last(const file_list_t * list,const char ** path,const char ** label,unsigned * file_type,size_t * entry_idx)376 void file_list_get_last(const file_list_t *list,
377 const char **path, const char **label,
378 unsigned *file_type, size_t *entry_idx)
379 {
380 if (list && list->size)
381 file_list_get_at_offset(list, list->size - 1, path, label, file_type, entry_idx);
382 }
383
file_list_search(const file_list_t * list,const char * needle,size_t * idx)384 bool file_list_search(const file_list_t *list, const char *needle, size_t *idx)
385 {
386 size_t i;
387 const char *alt = NULL;
388 bool ret = false;
389
390 if (!list)
391 return false;
392
393 for (i = 0; i < list->size; i++)
394 {
395 const char *str = NULL;
396
397 file_list_get_alt_at_offset(list, i, &alt);
398 if (!alt)
399 {
400 file_list_get_label_at_offset(list, i, &alt);
401 if (!alt)
402 continue;
403 }
404
405 str = (const char *)strcasestr(alt, needle);
406 if (str == alt)
407 {
408 /* Found match with first chars, best possible match. */
409 *idx = i;
410 ret = true;
411 break;
412 }
413 else if (str && !ret)
414 {
415 /* Found mid-string match, but try to find a match with
416 * first characters before we settle. */
417 *idx = i;
418 ret = true;
419 }
420 }
421
422 return ret;
423 }
424