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