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