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