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