libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / file / config_file.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (config_file.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 <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <errno.h>
28
29 #include <retro_miscellaneous.h>
30 #include <compat/strl.h>
31 #include <compat/posix_string.h>
32 #include <compat/fopen_utf8.h>
33 #include <compat/msvc.h>
34 #include <file/config_file.h>
35 #include <file/file_path.h>
36 #include <string/stdstring.h>
37 #include <streams/file_stream.h>
38 #include <array/rhmap.h>
39
40 #define MAX_INCLUDE_DEPTH 16
41
42 struct config_include_list
43 {
44    char *path;
45    struct config_include_list *next;
46 };
47
48 /* Forward declaration */
49 static bool config_file_parse_line(config_file_t *conf,
50       struct config_entry_list *list, char *line, config_file_cb_t *cb);
51
52 static int config_file_sort_compare_func(struct config_entry_list *a,
53       struct config_entry_list *b)
54 {
55    if (a && b)
56    {
57       if (a->key)
58       {
59          if (b->key)
60             return strcasecmp(a->key, b->key);
61          return 1;
62       }
63       else if (b->key)
64          return -1;
65    }
66
67    return 0;
68 }
69
70 /* https://stackoverflow.com/questions/7685/merge-sort-a-linked-list */
71 static struct config_entry_list* config_file_merge_sort_linked_list(
72          struct config_entry_list *list, int (*compare)(
73          struct config_entry_list *one,struct config_entry_list *two))
74 {
75    struct config_entry_list
76          *right  = list,
77          *temp   = list,
78          *last   = list,
79          *result = 0,
80          *next   = 0,
81          *tail   = 0;
82
83    /* Trivial case. */
84    if (!list || !list->next)
85       return list;
86
87    /* Find halfway through the list (by running two pointers,
88     * one at twice the speed of the other). */
89    while (temp && temp->next)
90    {
91       last     = right;
92       right    = right->next;
93       temp     = temp->next->next;
94    }
95
96    /* Break the list in two. (prev pointers are broken here,
97     * but we fix later) */
98    last->next  = 0;
99
100    /* Recurse on the two smaller lists: */
101    list        = config_file_merge_sort_linked_list(list, compare);
102    right       = config_file_merge_sort_linked_list(right, compare);
103
104    /* Merge: */
105    while (list || right)
106    {
107       /* Take from empty lists, or compare: */
108       if (!right)
109       {
110          next  = list;
111          list  = list->next;
112       }
113       else if (!list)
114       {
115          next  = right;
116          right = right->next;
117       }
118       else if (compare(list, right) < 0)
119       {
120          next  = list;
121          list  = list->next;
122       }
123       else
124       {
125          next  = right;
126          right = right->next;
127       }
128
129       if (!result)
130          result     = next;
131       else
132          tail->next = next;
133
134       tail          = next;
135    }
136
137    return result;
138 }
139
140 /**
141  * config_file_strip_comment:
142  *
143  * Searches input string for a comment ('#') entry
144  * > If first character is '#', then entire line is
145  *   a comment and may correspond to a directive
146  *   (command action - e.g. include sub-config file).
147  *   In this case, 'str' is set to NUL and the comment
148  *   itself (everything after the '#' character) is
149  *   returned
150  * > If a '#' character is found inside a string literal
151  *   value, then it does not correspond to a comment and
152  *   is ignored. In this case, 'str' is left untouched
153  *   and NULL is returned
154  * > If a '#' character is found anywhere else, then the
155  *   comment text is a suffix of the input string and
156  *   has no programmatic value. In this case, the comment
157  *   is removed from the end of 'str' and NULL is returned
158  **/
159 static char *config_file_strip_comment(char *str)
160 {
161    /* Search for a comment (#) character */
162    char *comment = strchr(str, '#');
163
164    if (comment)
165    {
166       char *literal_start = NULL;
167
168       /* Check whether entire line is a comment
169        * > First character == '#' */
170       if (str == comment)
171       {
172          /* Set 'str' to NUL and return comment
173           * for processing at a higher level */
174          *str = '\0';
175          return ++comment;
176       }
177
178       /* Comment character occurs at an offset:
179        * Search for the start of a string literal value */
180       literal_start = strchr(str, '\"');
181
182       /* Check whether string literal start occurs
183        * *before* the comment character */
184       if (literal_start && (literal_start < comment))
185       {
186          /* Search for the end of the string literal
187           * value */
188          char *literal_end = strchr(literal_start + 1, '\"');
189
190          /* Check whether string literal end occurs
191           * *after* the comment character
192           * > If this is the case, ignore the comment
193           * > Leave 'str' untouched and return NULL */
194          if (literal_end && (literal_end > comment))
195             return NULL;
196       }
197
198       /* If we reach this point, then a comment
199        * exists outside of a string literal
200        * > Trim the entire comment from the end
201        *   of 'str' */
202       *comment = '\0';
203    }
204
205    return NULL;
206 }
207
208 static char *config_file_extract_value(char *line)
209 {
210    char *dst = NULL;
211    while (ISSPACE((int)*line))
212       line++;
213
214    /* Note: From this point on, an empty value
215     * string is valid - and in this case, strldup("", sizeof(""))
216     * will be returned (see Note 2)
217     * > If we instead return NULL, the the entry
218     *   is ignored completely - which means we cannot
219     *   track *changes* in entry value */
220
221    /* If first character is ("), we have a full string
222     * literal */
223    if (*line == '"')
224    {
225       size_t idx  = 0;
226       char *value = NULL;
227       /* Skip to next character */
228       line++;
229
230       /* If this a ("), then value string is empty */
231       if (*line != '"')
232       {
233          /* Find the next (") character */
234          while (line[idx] && (line[idx] != '\"'))
235             idx++;
236
237          line[idx] = '\0';
238          if ((value = line) && *value)
239             return strdup(value);
240       }
241    }
242    /* This is not a string literal - just read
243     * until the next space is found
244     * > Note: Skip this if line is empty */
245    else if (*line != '\0')
246    {
247       size_t idx  = 0;
248       char *value = NULL;
249       /* Find next space character */
250       while (line[idx] && isgraph((int)line[idx]))
251          idx++;
252
253       line[idx] = '\0';
254       if ((value = line) && *value)
255          return strdup(value);
256    }
257
258    /* Note 2: This is an unrolled strldup call 
259     * to avoid an unnecessary dependency -
260     * call is strldup("", sizeof(""))
261     **/
262    dst = (char*)malloc(sizeof(char) * 2);
263    strlcpy(dst, "", 1);
264    return dst;
265 }
266
267 /* Move semantics? */
268 static void config_file_add_child_list(config_file_t *parent,
269       config_file_t *child)
270 {
271    struct config_entry_list *list = child->entries;
272    bool merge_hash_map            = false;
273
274    if (parent->entries)
275    {
276       struct config_entry_list *head = parent->entries;
277       while (head->next)
278          head = head->next;
279
280       /* set list readonly */
281       while (list)
282       {
283          list->readonly = true;
284          list           = list->next;
285       }
286       head->next        = child->entries;
287
288       merge_hash_map    = true;
289    }
290    else
291    {
292       /* set list readonly */
293       while (list)
294       {
295          list->readonly = true;
296          list           = list->next;
297       }
298       parent->entries   = child->entries;
299    }
300
301    /* Rebase tail. */
302    if (parent->entries)
303    {
304       struct config_entry_list *head =
305          (struct config_entry_list*)parent->entries;
306
307       while (head->next)
308          head = head->next;
309       parent->tail = head;
310    }
311    else
312       parent->tail = NULL;
313
314    /* Update hash map */
315    if (merge_hash_map)
316    {
317       size_t i;
318       size_t cap;
319
320       /* We are merging two lists - if any child entry
321        * (key) is not present in the parent list, add it
322        * to the parent hash map */
323       for (i = 0, cap = RHMAP_CAP(child->entries_map); i != cap; i++)
324       {
325          uint32_t child_hash   = RHMAP_KEY(child->entries_map, i);
326          const char *child_key = RHMAP_KEY_STR(child->entries_map, i);
327
328          if (child_hash &&
329              child_key &&
330              !RHMAP_HAS_FULL(parent->entries_map, child_hash, child_key))
331          {
332             struct config_entry_list *entry = child->entries_map[i];
333
334             if (entry)
335                RHMAP_SET_FULL(parent->entries_map, child_hash, child_key, entry);
336          }
337       }
338
339       /* Child entries map is no longer required,
340        * so free it now */
341       RHMAP_FREE(child->entries_map);
342    }
343    else
344    {
345       /* If parent list was originally empty,
346        * take map from child list */
347       RHMAP_FREE(parent->entries_map);
348       parent->entries_map = child->entries_map;
349       child->entries_map  = NULL;
350    }
351
352    child->entries = NULL;
353 }
354
355 static void config_file_get_realpath(char *s, size_t len,
356       char *path, const char *config_path)
357 {
358 #ifdef _WIN32
359    if (!string_is_empty(config_path))
360       fill_pathname_resolve_relative(s, config_path,
361             path, len);
362 #else
363 #if !defined(__PSL1GHT__) && !defined(__PS3__)
364    if (*path == '~')
365    {
366       const char *home = getenv("HOME");
367       if (home)
368       {
369          strlcpy(s, home,     len);
370          strlcat(s, path + 1, len);
371       }
372       else
373          strlcpy(s, path + 1, len);
374    }
375    else
376 #endif
377       if (!string_is_empty(config_path))
378          fill_pathname_resolve_relative(s, config_path, path, len);
379 #endif
380 }
381
382 static void config_file_add_sub_conf(config_file_t *conf, char *path,
383       char *real_path, size_t len, config_file_cb_t *cb)
384 {
385    struct config_include_list *head = conf->includes;
386    struct config_include_list *node = (struct config_include_list*)
387       malloc(sizeof(*node));
388
389    if (node)
390    {
391       node->next        = NULL;
392       /* Add include list */
393       node->path        = strdup(path);
394
395       if (head)
396       {
397          while (head->next)
398             head        = head->next;
399
400          head->next     = node;
401       }
402       else
403          conf->includes = node;
404    }
405
406    config_file_get_realpath(real_path, len, path,
407          conf->path);
408 }
409
410 void config_file_add_reference(config_file_t *conf, char *path)
411 {
412    /* It is expected that the conf has it's path already set */
413    char short_path[PATH_MAX_LENGTH];
414    if (!conf->references)
415       conf->references = path_linked_list_new();
416    fill_pathname_abbreviated_or_relative(short_path, conf->path, path, sizeof(short_path));
417    path_linked_list_add_path(conf->references, short_path);
418 }
419
420 static int config_file_load_internal(
421       struct config_file *conf,
422       const char *path, unsigned depth, config_file_cb_t *cb)
423 {
424    RFILE         *file = NULL;
425    char      *new_path = strdup(path);
426    if (!new_path)
427       return 1;
428
429    conf->path          = new_path;
430    conf->include_depth = depth;
431
432    if (!(file = filestream_open(path,
433          RETRO_VFS_FILE_ACCESS_READ,
434          RETRO_VFS_FILE_ACCESS_HINT_NONE)))
435    {
436       free(conf->path);
437       return 1;
438    }
439
440    while (!filestream_eof(file))
441    {
442       char *line                     = NULL;
443       struct config_entry_list *list = (struct config_entry_list*)
444          malloc(sizeof(*list));
445
446       if (!list)
447       {
448          filestream_close(file);
449          return -1;
450       }
451
452       list->readonly  = false;
453       list->key       = NULL;
454       list->value     = NULL;
455       list->next      = NULL;
456
457       line            = filestream_getline(file);
458
459       if (!line)
460       {
461          free(list);
462          continue;
463       }
464
465       if ( 
466               !string_is_empty(line) 
467             && config_file_parse_line(conf, list, line, cb))
468       {
469          if (conf->entries)
470             conf->tail->next = list;
471          else
472             conf->entries    = list;
473
474          conf->tail = list;
475
476          if (list->key)
477          {
478             /* Only add entry to the map if an entry
479              * with the specified value does not
480              * already exist */
481             uint32_t hash = rhmap_hash_string(list->key);
482
483             if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key))
484             {
485                RHMAP_SET_FULL(conf->entries_map, hash, list->key, list);
486
487                if (cb && list->value)
488                   cb->config_file_new_entry_cb(list->key, list->value);
489             }
490          }
491       }
492
493       free(line);
494
495       if (list != conf->tail)
496          free(list);
497    }
498
499    filestream_close(file);
500
501    return 0;
502 }
503
504 static bool config_file_parse_line(config_file_t *conf,
505       struct config_entry_list *list, char *line, config_file_cb_t *cb)
506 {
507    size_t cur_size       = 32;
508    size_t idx            = 0;
509    char *key             = NULL;
510    char *key_tmp         = NULL;
511    /* Remove any comment text */
512    char *comment         = config_file_strip_comment(line);
513
514    /* Check whether entire line is a comment */
515    if (comment)
516    {
517       char *path               = NULL;
518       bool include_found       = string_starts_with_size(comment,
519             "include ",
520             STRLEN_CONST("include "));
521       bool reference_found     = string_starts_with_size(comment,
522             "reference ",
523             STRLEN_CONST("reference "));
524
525       /* All comments except those starting with the include or 
526        * reference directive are ignored */
527       if (!include_found && !reference_found)
528          return false;
529
530       /* Starting a line with an 'include' directive
531        * appends a sub-config file */
532       if (include_found)
533       {
534          config_file_t sub_conf;
535          char real_path[PATH_MAX_LENGTH];
536          char *include_line = comment + STRLEN_CONST("include ");
537
538          if (string_is_empty(include_line))
539             return false;
540
541          if (!(path = config_file_extract_value(include_line)))
542             return false;
543
544          if (     string_is_empty(path)
545                || conf->include_depth >= MAX_INCLUDE_DEPTH)
546          {
547             free(path);
548             return false;
549          }
550
551          config_file_add_sub_conf(conf, path,
552             real_path, sizeof(real_path), cb);
553
554          config_file_initialize(&sub_conf);
555
556          switch (config_file_load_internal(&sub_conf, real_path,
557             conf->include_depth + 1, cb))
558          {
559             case 0:
560                /* Pilfer internal list. */
561                config_file_add_child_list(conf, &sub_conf);
562                /* fall-through to deinitialize */
563             case -1:
564                config_file_deinitialize(&sub_conf);
565                break;
566             case 1:
567             default:
568                break;
569          }
570       }
571
572       /* Starting a line with an 'reference' directive
573        * sets the reference path */
574       if (reference_found)
575       {
576          char *reference_line = comment + STRLEN_CONST("reference ");
577
578          if (string_is_empty(reference_line))
579             return false;
580
581          if (!(path = config_file_extract_value(reference_line)))
582             return false;
583
584          config_file_add_reference(conf, path);
585
586          if (!path)
587             return false;
588       }
589
590       free(path);
591       return true;
592    }
593
594    /* Skip to first non-space character */
595    while (ISSPACE((int)*line))
596       line++;
597
598    /* Allocate storage for key */
599    if (!(key = (char*)malloc(cur_size + 1)))
600       return false;
601
602    /* Copy line contents into key until we
603     * reach the next space character */
604    while (isgraph((int)*line))
605    {
606       /* If current key storage is too small,
607        * double its size */
608       if (idx == cur_size)
609       {
610          cur_size *= 2;
611          if (!(key_tmp   = (char*)realloc(key, cur_size + 1)))
612          {
613             free(key);
614             return false;
615          }
616
617          key     = key_tmp;
618       }
619
620       key[idx++] = *line++;
621    }
622    key[idx]      = '\0';
623
624    /* Add key and value entries to list */
625    list->key     = key;
626
627    /* An entry without a value is invalid */
628    while (ISSPACE((int)*line))
629       line++;
630
631    /* If we don't have an equal sign here,
632     * we've got an invalid string. */
633    if (*line != '=')
634    {
635       list->value = NULL;
636       goto error;
637    }
638
639    line++;
640
641    if (!(list->value   = config_file_extract_value(line)))
642       goto error;
643
644    return true;
645
646 error:
647    list->key   = NULL;
648    free(key);
649    return false;
650 }
651
652 static int config_file_from_string_internal(
653       struct config_file *conf,
654       char *from_string,
655       const char *path)
656 {
657    char *lines                    = from_string;
658    char *save_ptr                 = NULL;
659    char *line                     = NULL;
660
661    if (!string_is_empty(path))
662       conf->path                  = strdup(path);
663    if (string_is_empty(lines))
664       return 0;
665
666    /* Get first line of config file */
667    line = strtok_r(lines, "\n", &save_ptr);
668
669    while (line)
670    {
671       struct config_entry_list *list = (struct config_entry_list*)
672             malloc(sizeof(*list));
673
674       if (!list)
675          return -1;
676
677       list->readonly  = false;
678       list->key       = NULL;
679       list->value     = NULL;
680       list->next      = NULL;
681
682       /* Parse current line */
683       if (
684               !string_is_empty(line)
685             && config_file_parse_line(conf, list, line, NULL))
686       {
687          if (conf->entries)
688             conf->tail->next = list;
689          else
690             conf->entries    = list;
691
692          conf->tail          = list;
693
694          if (list->key)
695          {
696             /* Only add entry to the map if an entry
697              * with the specified value does not
698              * already exist */
699             uint32_t hash = rhmap_hash_string(list->key);
700             if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key))
701                RHMAP_SET_FULL(conf->entries_map, hash, list->key, list);
702          }
703       }
704
705       if (list != conf->tail)
706          free(list);
707
708       /* Get next line of config file */
709       line = strtok_r(NULL, "\n", &save_ptr);
710    }
711    
712    return 0;
713 }
714
715
716 bool config_file_deinitialize(config_file_t *conf)
717 {
718    struct config_include_list *inc_tmp = NULL;
719    struct config_entry_list *tmp       = NULL;
720
721    if (!conf)
722       return false;
723
724    tmp = conf->entries;
725    while (tmp)
726    {
727       struct config_entry_list *hold = NULL;
728       if (tmp->key)
729          free(tmp->key);
730       if (tmp->value)
731          free(tmp->value);
732
733       tmp->value = NULL;
734       tmp->key   = NULL;
735
736       hold       = tmp;
737       tmp        = tmp->next;
738
739       if (hold)
740          free(hold);
741    }
742
743    inc_tmp = (struct config_include_list*)conf->includes;
744    while (inc_tmp)
745    {
746       struct config_include_list *hold = NULL;
747       if (inc_tmp->path)
748          free(inc_tmp->path);
749       hold    = (struct config_include_list*)inc_tmp;
750       inc_tmp = inc_tmp->next;
751       if (hold)
752          free(hold);
753    }
754
755    path_linked_list_free(conf->references);
756
757    if (conf->path)
758       free(conf->path);
759
760    RHMAP_FREE(conf->entries_map);
761
762    return true;
763 }
764
765 /**
766  * config_file_free:
767  *
768  * Frees config file.
769  **/
770 void config_file_free(config_file_t *conf)
771 {
772    if (config_file_deinitialize(conf))
773       free(conf);
774 }
775
776 /**
777  * config_append_file:
778  *
779  * Loads a new config, and appends its data to @conf.
780  * The key-value pairs of the new config file takes priority over the old.
781  **/
782 bool config_append_file(config_file_t *conf, const char *path)
783 {
784    size_t i, cap;
785    config_file_t *new_conf = config_file_new_from_path_to_string(path);
786
787    if (!new_conf)
788       return false;
789
790    /* Update hash map */
791    for (i = 0, cap = RHMAP_CAP(new_conf->entries_map); i != cap; i++)
792    {
793       uint32_t new_hash   = RHMAP_KEY(new_conf->entries_map, i);
794       const char *new_key = RHMAP_KEY_STR(new_conf->entries_map, i);
795
796       if (new_hash && new_key)
797       {
798          struct config_entry_list *entry = new_conf->entries_map[i];
799
800          if (entry)
801             RHMAP_SET_FULL(conf->entries_map, new_hash, new_key, entry);
802       }
803    }
804
805    if (new_conf->tail)
806    {
807       new_conf->tail->next = conf->entries;
808       conf->entries        = new_conf->entries; /* Pilfer. */
809       new_conf->entries    = NULL;
810    }
811
812    config_file_free(new_conf);
813    return true;
814 }
815
816 /**
817  * config_file_new_from_string:
818  *
819  * Load a config file from a string.
820  *
821  * NOTE: This will modify @from_string.
822  * Pass a copy of source string if original
823  * contents must be preserved
824  **/
825 config_file_t *config_file_new_from_string(char *from_string,
826       const char *path)
827 {
828    struct config_file *conf      = config_file_new_alloc();
829    if (     conf 
830          && config_file_from_string_internal(
831             conf, from_string, path) != -1)
832       return conf;
833    if (conf)
834       config_file_free(conf);
835    return NULL;
836 }
837
838 config_file_t *config_file_new_from_path_to_string(const char *path)
839 {
840    if (path_is_valid(path))
841    {
842            uint8_t *ret_buf                 = NULL;
843       int64_t length                   = 0;
844       if (filestream_read_file(path, (void**)&ret_buf, &length))
845       {
846          config_file_t *conf           = NULL;
847          /* Note: 'ret_buf' is not used outside this
848           * function - we do not care that it will be
849           * modified by config_file_new_from_string() */
850          if (length >= 0)
851             conf = config_file_new_from_string((char*)ret_buf, path);
852
853          if ((void*)ret_buf)
854             free((void*)ret_buf);
855
856          return conf;
857       }
858    }
859
860    return NULL;
861 }
862
863 /**
864  * config_file_new_with_callback:
865  *
866  * Loads a config file.
867  * If @path is NULL, will create an empty config file.
868  * Includes cb callbacks  to run custom code during config file processing.
869  *
870  * @return Returns NULL if file doesn't exist.
871  **/
872 config_file_t *config_file_new_with_callback(
873       const char *path, config_file_cb_t *cb)
874 {
875    int ret                  = 0;
876    struct config_file *conf = config_file_new_alloc();
877    if (!path || !*path)
878       return conf;
879    if ((ret = config_file_load_internal(conf, path, 0, cb)) == -1)
880    {
881       config_file_free(conf);
882       return NULL;
883    }
884    else if (ret == 1)
885    {
886       free(conf);
887       return NULL;
888    }
889    return conf;
890 }
891
892 /**
893  * config_file_new:
894  *
895  * Loads a config file.
896  * If @path is NULL, will create an empty config file.
897  *
898  * @return Returns NULL if file doesn't exist.
899  **/
900 config_file_t *config_file_new(const char *path)
901 {
902    int ret                  = 0;
903    struct config_file *conf = config_file_new_alloc();
904    if (!path || !*path)
905       return conf;
906    if ((ret = config_file_load_internal(conf, path, 0, NULL)) == -1)
907    {
908       config_file_free(conf);
909       return NULL;
910    }
911    else if (ret == 1)
912    {
913       free(conf);
914       return NULL;
915    }
916    return conf;
917 }
918
919 /**
920  * config_file_initialize:
921  *
922  * Leaf function.
923  **/
924 void config_file_initialize(struct config_file *conf)
925 {
926    if (!conf)
927       return;
928
929    conf->path                     = NULL;
930    conf->entries_map              = NULL;
931    conf->entries                  = NULL;
932    conf->tail                     = NULL;
933    conf->last                     = NULL;
934    conf->references               = NULL;
935    conf->includes                 = NULL;
936    conf->include_depth            = 0;
937    conf->guaranteed_no_duplicates = false;
938    conf->modified                 = false;
939 }
940
941 config_file_t *config_file_new_alloc(void)
942 {
943    struct config_file *conf = (struct config_file*)malloc(sizeof(*conf));
944    if (!conf)
945       return NULL;
946    config_file_initialize(conf);
947    return conf;
948 }
949
950 /**
951  * config_get_entry_internal:
952  *
953  * Leaf function.
954  **/
955 static struct config_entry_list *config_get_entry_internal(
956       const config_file_t *conf,
957       const char *key, struct config_entry_list **prev)
958 {
959    struct config_entry_list *entry = RHMAP_GET_STR(conf->entries_map, key);
960
961    if (entry)
962       return entry;
963
964    if (prev)
965    {
966       struct config_entry_list *previous = *prev;
967       for (entry = conf->entries; entry; entry = entry->next)
968          previous = entry;
969
970       *prev = previous;
971    }
972
973    return NULL;
974 }
975
976 struct config_entry_list *config_get_entry(
977       const config_file_t *conf, const char *key)
978 {
979    return RHMAP_GET_STR(conf->entries_map, key);
980 }
981
982 /**
983  * config_get_double:
984  *
985  * Extracts a double from config file.
986  *
987  * @return true if found, otherwise false.
988  **/
989 bool config_get_double(config_file_t *conf, const char *key, double *in)
990 {
991    const struct config_entry_list *entry = config_get_entry(conf, key);
992
993    if (!entry)
994       return false;
995
996    *in = strtod(entry->value, NULL);
997    return true;
998 }
999
1000 /**
1001  * config_get_float:
1002  *
1003  * Extracts a float from config file.
1004  *
1005  * @return true if found, otherwise false.
1006  **/
1007 bool config_get_float(config_file_t *conf, const char *key, float *in)
1008 {
1009    const struct config_entry_list *entry = config_get_entry(conf, key);
1010
1011    if (!entry)
1012       return false;
1013
1014    /* strtof() is C99/POSIX. Just use the more portable kind. */
1015    *in = (float)strtod(entry->value, NULL);
1016    return true;
1017 }
1018
1019 bool config_get_int(config_file_t *conf, const char *key, int *in)
1020 {
1021    const struct config_entry_list *entry = config_get_entry(conf, key);
1022    errno = 0;
1023
1024    if (entry)
1025    {
1026       int val = (int)strtol(entry->value, NULL, 0);
1027
1028       if (errno == 0)
1029       {
1030          *in = val;
1031          return true;
1032       }
1033    }
1034
1035    return false;
1036 }
1037
1038 bool config_get_size_t(config_file_t *conf, const char *key, size_t *in)
1039 {
1040    const struct config_entry_list *entry = config_get_entry(conf, key);
1041    errno = 0;
1042
1043    if (entry)
1044    {
1045       size_t val = 0;
1046       if (sscanf(entry->value, "%" PRI_SIZET, &val) == 1)
1047       {
1048          *in = val;
1049          return true;
1050       }
1051    }
1052
1053    return false;
1054 }
1055
1056 #if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
1057 bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in)
1058 {
1059    const struct config_entry_list *entry = config_get_entry(conf, key);
1060    errno = 0;
1061
1062    if (entry)
1063    {
1064       uint64_t val = strtoull(entry->value, NULL, 0);
1065
1066       if (errno == 0)
1067       {
1068          *in = val;
1069          return true;
1070       }
1071    }
1072    return false;
1073 }
1074 #endif
1075
1076 bool config_get_uint(config_file_t *conf, const char *key, unsigned *in)
1077 {
1078    const struct config_entry_list *entry = config_get_entry(conf, key);
1079    errno = 0;
1080
1081    if (entry)
1082    {
1083       unsigned val = (unsigned)strtoul(entry->value, NULL, 0);
1084
1085       if (errno == 0)
1086       {
1087          *in = val;
1088          return true;
1089       }
1090    }
1091
1092    return false;
1093 }
1094
1095 bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
1096 {
1097    const struct config_entry_list *entry = config_get_entry(conf, key);
1098    errno = 0;
1099
1100    if (entry)
1101    {
1102       unsigned val = (unsigned)strtoul(entry->value, NULL, 16);
1103
1104       if (errno == 0)
1105       {
1106          *in = val;
1107          return true;
1108       }
1109    }
1110
1111    return false;
1112 }
1113
1114 /**
1115  * config_get_char:
1116  *
1117  * Extracts a single char from config file.
1118  * If value consists of several chars, this is an error.
1119  *
1120  * @return true if found, otherwise false.
1121  **/
1122 bool config_get_char(config_file_t *conf, const char *key, char *in)
1123 {
1124    const struct config_entry_list *entry = config_get_entry(conf, key);
1125
1126    if (entry)
1127    {
1128       if (entry->value[0] && entry->value[1])
1129          return false;
1130
1131       *in = *entry->value;
1132       return true;
1133    }
1134
1135    return false;
1136 }
1137
1138 /**
1139  * config_get_string:
1140  *
1141  * Extracts an allocated string in *in. This must be free()-d if
1142  * this function succeeds.
1143  *
1144  * @return true if found, otherwise false.
1145  **/
1146 bool config_get_string(config_file_t *conf, const char *key, char **str)
1147 {
1148    const struct config_entry_list *entry = config_get_entry(conf, key);
1149
1150    if (!entry || !entry->value)
1151       return false;
1152
1153    *str = strdup(entry->value);
1154    return true;
1155 }
1156
1157 /**
1158   * config_get_config_path:
1159   *
1160   * Extracts a string to a preallocated buffer.
1161   * Avoid memory allocation.
1162   **/
1163 bool config_get_config_path(config_file_t *conf, char *s, size_t len)
1164 {
1165    if (conf)
1166       return strlcpy(s, conf->path, len);
1167    return false;
1168 }
1169
1170 bool config_get_array(config_file_t *conf, const char *key,
1171       char *buf, size_t size)
1172 {
1173    const struct config_entry_list *entry = config_get_entry(conf, key);
1174    if (entry)
1175       return strlcpy(buf, entry->value, size) < size;
1176    return false;
1177 }
1178
1179 bool config_get_path(config_file_t *conf, const char *key,
1180       char *buf, size_t size)
1181 {
1182 #if defined(RARCH_CONSOLE) || !defined(RARCH_INTERNAL)
1183    if (config_get_array(conf, key, buf, size))
1184       return true;
1185 #else
1186    const struct config_entry_list *entry = config_get_entry(conf, key);
1187    if (entry)
1188    {
1189       fill_pathname_expand_special(buf, entry->value, size);
1190       return true;
1191    }
1192 #endif
1193    return false;
1194 }
1195
1196 /**
1197  * config_get_bool:
1198  * 
1199  * Extracts a boolean from config.
1200  * Valid boolean true are "true" and "1". Valid false are "false" and "0".
1201  * Other values will be treated as an error.
1202  *
1203  * @return true if preconditions are true, otherwise false.
1204  **/
1205 bool config_get_bool(config_file_t *conf, const char *key, bool *in)
1206 {
1207    const struct config_entry_list *entry = config_get_entry(conf, key);
1208
1209    if (!entry)
1210       return false;
1211
1212    if      (
1213          (
1214             entry->value[0] == '1'
1215          && entry->value[1] == '\0'
1216          )
1217          || string_is_equal(entry->value, "true")
1218          )
1219       *in = true;
1220    else if (
1221          (
1222             entry->value[0] == '0'
1223          && entry->value[1] == '\0'
1224          )
1225          || string_is_equal(entry->value, "false")
1226          )
1227       *in = false;
1228    else
1229       return false;
1230
1231    return true;
1232 }
1233
1234 void config_set_string(config_file_t *conf, const char *key, const char *val)
1235 {
1236    struct config_entry_list *last  = NULL;
1237    struct config_entry_list *entry = NULL;
1238
1239    if (!conf || !key || !val)
1240       return;
1241
1242    last                            = conf->entries;
1243
1244    if (conf->guaranteed_no_duplicates)
1245    {
1246       if (conf->last)
1247          last                      = conf->last;
1248    }
1249    else
1250    {
1251       if ((entry = config_get_entry_internal(conf, key, &last)))
1252       {
1253          /* An entry corresponding to 'key' already exists
1254           * > Check whether value is currently set */
1255          if (entry->value)
1256          {
1257             /* Do nothing if value is unchanged */
1258             if (string_is_equal(entry->value, val))
1259                return;
1260
1261             /* Value is to be updated
1262              * > Free existing */
1263             free(entry->value);
1264          }
1265
1266          /* Update value
1267           * > Note that once a value is set, it
1268           *   is no longer considered 'read only' */
1269          entry->value    = strdup(val);
1270          entry->readonly = false;
1271          conf->modified  = true;
1272          return;
1273       }
1274    }
1275
1276    /* Entry corresponding to 'key' does not exist
1277     * > Create new entry */
1278    if (!(entry = (struct config_entry_list*)malloc(sizeof(*entry))))
1279       return;
1280
1281    entry->readonly  = false;
1282    entry->key       = strdup(key);
1283    entry->value     = strdup(val);
1284    entry->next      = NULL;
1285    conf->modified   = true;
1286
1287    if (last)
1288       last->next    = entry;
1289    else
1290       conf->entries = entry;
1291
1292    conf->last       = entry;
1293
1294    RHMAP_SET_STR(conf->entries_map, entry->key, entry);
1295 }
1296
1297 void config_unset(config_file_t *conf, const char *key)
1298 {
1299    struct config_entry_list *last  = NULL;
1300    struct config_entry_list *entry = NULL;
1301
1302    if (!conf || !key)
1303       return;
1304
1305    last  = conf->entries;
1306
1307    if (!(entry = config_get_entry_internal(conf, key, &last)))
1308       return;
1309
1310    (void)RHMAP_DEL_STR(conf->entries_map, entry->key);
1311
1312    if (entry->key)
1313       free(entry->key);
1314
1315    if (entry->value)
1316       free(entry->value);
1317
1318    entry->key     = NULL;
1319    entry->value   = NULL;
1320    conf->modified = true;
1321 }
1322
1323 void config_set_path(config_file_t *conf, const char *entry, const char *val)
1324 {
1325 #if defined(RARCH_CONSOLE) || !defined(RARCH_INTERNAL)
1326    config_set_string(conf, entry, val);
1327 #else
1328    char buf[PATH_MAX_LENGTH];
1329    fill_pathname_abbreviate_special(buf, val, sizeof(buf));
1330    config_set_string(conf, entry, buf);
1331 #endif
1332 }
1333
1334 void config_set_double(config_file_t *conf, const char *key, double val)
1335 {
1336    char buf[320];
1337 #ifdef __cplusplus
1338    snprintf(buf, sizeof(buf), "%f", (float)val);
1339 #elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
1340    snprintf(buf, sizeof(buf), "%lf", val);
1341 #else
1342    snprintf(buf, sizeof(buf), "%f", (float)val);
1343 #endif
1344    config_set_string(conf, key, buf);
1345 }
1346
1347 void config_set_float(config_file_t *conf, const char *key, float val)
1348 {
1349    char buf[64];
1350    snprintf(buf, sizeof(buf), "%f", val);
1351    config_set_string(conf, key, buf);
1352 }
1353
1354 void config_set_int(config_file_t *conf, const char *key, int val)
1355 {
1356    char buf[16];
1357    snprintf(buf, sizeof(buf), "%d", val);
1358    config_set_string(conf, key, buf);
1359 }
1360
1361 void config_set_uint(config_file_t *conf, const char *key, unsigned int val)
1362 {
1363    char buf[16];
1364    snprintf(buf, sizeof(buf), "%u", val);
1365    config_set_string(conf, key, buf);
1366 }
1367
1368 void config_set_hex(config_file_t *conf, const char *key, unsigned val)
1369 {
1370    char buf[16];
1371    snprintf(buf, sizeof(buf), "%x", val);
1372    config_set_string(conf, key, buf);
1373 }
1374
1375 void config_set_uint64(config_file_t *conf, const char *key, uint64_t val)
1376 {
1377    char buf[32];
1378    snprintf(buf, sizeof(buf), "%" PRIu64, val);
1379    config_set_string(conf, key, buf);
1380 }
1381
1382 void config_set_char(config_file_t *conf, const char *key, char val)
1383 {
1384    char buf[2];
1385    snprintf(buf, sizeof(buf), "%c", val);
1386    config_set_string(conf, key, buf);
1387 }
1388
1389 /**
1390  * config_set_bool:
1391
1392  * TODO/FIXME - could be turned into a trivial macro or removed
1393  **/
1394 void config_set_bool(config_file_t *conf, const char *key, bool val)
1395 {
1396    config_set_string(conf, key, val ? "true" : "false");
1397 }
1398
1399 /**
1400  * config_file_write:
1401  *
1402  * Write the current config to a file.
1403  **/
1404 bool config_file_write(config_file_t *conf, const char *path, bool sort)
1405 {
1406    if (!conf)
1407       return false;
1408
1409    if (!conf->modified)
1410       return true;
1411
1412    if (!string_is_empty(path))
1413    {
1414       void* buf  = NULL;
1415       FILE *file = (FILE*)fopen_utf8(path, "wb");
1416       if (!file)
1417          return false;
1418
1419       buf        = calloc(1, 0x4000);
1420       setvbuf(file, (char*)buf, _IOFBF, 0x4000);
1421
1422       config_file_dump(conf, file, sort);
1423
1424       if (file != stdout)
1425          fclose(file);
1426       if (buf)
1427          free(buf);
1428
1429       /* Only update modified flag if config file
1430        * is actually written to disk */
1431       conf->modified = false;
1432    }
1433    else
1434       config_file_dump(conf, stdout, sort);
1435
1436    return true;
1437 }
1438
1439 /**
1440  * config_file_dump:
1441  *
1442  * Dump the current config to an already opened file.
1443  * Does not close the file.
1444  **/
1445 void config_file_dump(config_file_t *conf, FILE *file, bool sort)
1446 {
1447    struct config_entry_list       *list = NULL;
1448    struct config_include_list *includes = conf->includes;
1449    struct path_linked_list *ref_tmp = conf->references;
1450
1451    while (ref_tmp)
1452    {
1453       pathname_make_slashes_portable(ref_tmp->path);
1454       fprintf(file, "#reference \"%s\"\n", ref_tmp->path);
1455       ref_tmp = ref_tmp->next;
1456    }
1457
1458    if (sort)
1459       list = config_file_merge_sort_linked_list(
1460             (struct config_entry_list*)conf->entries,
1461             config_file_sort_compare_func);
1462    else
1463       list = (struct config_entry_list*)conf->entries;
1464
1465    conf->entries = list;
1466
1467    while (list)
1468    {
1469       if (!list->readonly && list->key)
1470          fprintf(file, "%s = \"%s\"\n", list->key, list->value);
1471       list = list->next;
1472    }
1473
1474    /* Config files are read from the top down - if
1475     * duplicate entries are found then the topmost
1476     * one in the list takes precedence. This means
1477     * '#include' directives must go *after* individual
1478     * config entries, otherwise they will override
1479     * any custom-set values */
1480    while (includes)
1481    {
1482       fprintf(file, "#include \"%s\"\n", includes->path);
1483       includes = includes->next;
1484    }
1485 }
1486
1487 /**
1488  * config_get_entry_list_head:
1489  *
1490  * Leaf function.
1491  **/
1492 bool config_get_entry_list_head(config_file_t *conf,
1493       struct config_file_entry *entry)
1494 {
1495    const struct config_entry_list *head = conf->entries;
1496
1497    if (!head)
1498       return false;
1499
1500    entry->key   = head->key;
1501    entry->value = head->value;
1502    entry->next  = head->next;
1503    return true;
1504 }
1505
1506 /**
1507  * config_get_entry_list_next:
1508  *
1509  * Leaf function.
1510  **/
1511 bool config_get_entry_list_next(struct config_file_entry *entry)
1512 {
1513    const struct config_entry_list *next = entry->next;
1514
1515    if (!next)
1516       return false;
1517
1518    entry->key   = next->key;
1519    entry->value = next->value;
1520    entry->next  = next->next;
1521    return true;
1522 }