1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #include <fs/base.h>
6 #include <fs/conf.h>
7 #include <fs/filesys.h>
8 #include <fs/glib.h>
9 #include <fs/inifile.h>
10 #include <fs/log.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 static int g_initialized = 0;
15 GHashTable *g_hash_table = NULL;
16 
17 #define LOG_LINE \
18 "----------------------------------------------------------------------------\n"
19 
initialize(void)20 static void initialize(void)
21 {
22     if (g_initialized) {
23         return;
24     }
25     g_initialized = 1;
26     g_hash_table = g_hash_table_new_full(
27         g_str_hash, g_str_equal, g_free, g_free);
28 }
29 
fse_init_conf(void)30 void fse_init_conf(void)
31 {
32     initialize();
33 }
34 
fs_config_exists(const char * key)35 bool fs_config_exists(const char *key)
36 {
37     return fs_config_get_const_string(key) != NULL;
38 }
39 
fs_config_check_auto(const char * key,const char * value)40 bool fs_config_check_auto(const char *key, const char *value)
41 {
42     const char *s = fs_config_get_const_string(key);
43     if (s == NULL) {
44         return true;
45     }
46     if (value && strcasecmp(s, value) == 0) {
47         return true;
48     }
49     return false;
50 }
51 
fs_config_check_enabled(const char * key,const char * value)52 bool fs_config_check_enabled(const char *key, const char *value)
53 {
54     const char *s = fs_config_get_const_string(key);
55     if (s == NULL) {
56         return false;
57     }
58     if (value && strcasecmp(s, value) == 0) {
59         return true;
60     }
61     if (strcasecmp(s, "1") == 0) {
62         return true;
63     }
64     return false;
65 }
66 
fs_config_check_disabled(const char * key,const char * value)67 bool fs_config_check_disabled(const char *key, const char *value)
68 {
69     const char *s = fs_config_get_const_string(key);
70     if (s == NULL) {
71         return false;
72     }
73     if (value && strcasecmp(s, value) == 0) {
74         return true;
75     }
76     if (strcasecmp(s, "0") == 0) {
77         return true;
78     }
79     return false;
80 }
81 
fs_config_get_const_string(const char * key)82 const char *fs_config_get_const_string(const char *key)
83 {
84     if (!g_initialized) {
85         initialize();
86     }
87     const char *value = (const char *) g_hash_table_lookup(g_hash_table, key);
88     if (value && !value[0]) {
89         // an empty string is treated as non-existing (unset value)
90         return NULL;
91     }
92     return value;
93 }
94 
fs_config_get_string(const char * key)95 char *fs_config_get_string(const char *key)
96 {
97     const char* value = fs_config_get_const_string(key);
98     if (value) {
99         return g_strdup(value);
100     }
101     return NULL;
102 }
103 
process_key_value(const char * key,char * value,int force)104 static void process_key_value(const char *key, char *value, int force)
105 {
106     char *key_lower = g_ascii_strdown(key, -1);
107     g_strdelimit (key_lower, "-", '_');
108     /* Using fs_config_get_const_string here instead of just
109      * g_hash_table_lookup, since that also checks for empty strings, which
110      * should be treated as non-existing keys. */
111     if (!force && fs_config_get_const_string(key_lower)) {
112         fs_log("%s = %s (ignored)\n", key_lower, value);
113         g_free(key_lower);
114         g_free(value);
115     } else {
116         g_strstrip(value);
117         fs_log("%s = %s\n", key_lower, value);
118         /* Hash table now owns both key_lower and value. */
119         g_hash_table_insert(g_hash_table, key_lower, value);
120     }
121 }
122 
fs_config_set_string(const char * key,const char * value)123 void fs_config_set_string(const char *key, const char *value)
124 {
125     process_key_value(key, g_strdup(value), 1);
126 }
127 
fs_config_set_string_if_unset(const char * key,const char * value)128 void fs_config_set_string_if_unset(const char *key, const char *value)
129 {
130     if (fs_config_get_const_string(key) == NULL) {
131         fs_config_set_string(key, value);
132     }
133 }
134 
135 #ifdef FSUAE
136 #define USE_INIFILE
137 #endif
138 
139 #ifdef USE_INIFILE
140 
fs_config_parse_ini_file(fs_ini_file * ini_file,int force)141 void fs_config_parse_ini_file(fs_ini_file *ini_file, int force)
142 {
143     char **groups = fs_ini_file_get_groups(ini_file, NULL);
144     for (char **group = groups; *group; group++) {
145         const char *prefix = "";
146         if (strcmp(*group, "theme") == 0) {
147             prefix = "theme_";
148         }
149         char **keys = fs_ini_file_get_keys(ini_file, *group, NULL);
150         for (char **key = keys; *key; key++) {
151             char *value = fs_ini_file_get_value(ini_file, *group, *key);
152             if (value) {
153                 char *key2 = g_strconcat(prefix, *key, NULL);
154                 process_key_value(key2, value, force);
155                 g_free(key2);
156             }
157         }
158         g_strfreev(keys);
159     }
160     g_strfreev(groups);
161 }
162 
163 #endif
164 
fs_config_read_file(const char * path,int force)165 int fs_config_read_file(const char *path, int force)
166 {
167     if (!g_initialized) {
168         initialize();
169     }
170     fs_log("\n");
171     fs_log(LOG_LINE);
172     fs_log("config (%s)\n", path);
173     fs_log(LOG_LINE);
174     fs_log("\n");
175 
176     if (!force && fs_config_get_boolean("end_config") == 1) {
177         fs_log("end_config is set, ignoring this config file\n");
178         return 1;
179     }
180     if (!fs_path_is_file(path)) {
181         fs_log("config file %s does not exist\n", path);
182         return 0;
183     }
184 
185 #ifdef USE_INIFILE
186     fs_ini_file *ini_file = fs_ini_file_open(path);
187     if (ini_file == NULL) {
188         fs_log("error loading config file\n");
189         return 0;
190     }
191     fs_config_parse_ini_file(ini_file, force);
192     fs_ini_file_destroy(ini_file);
193     return 1;
194 #else
195     return 0;
196 #endif
197 }
198 
fs_config_get_boolean(const char * key)199 int fs_config_get_boolean(const char *key)
200 {
201     return fs_config_get_int(key);
202 }
203 
fs_config_get_int(const char * key)204 int fs_config_get_int(const char *key)
205 {
206     const char *value = fs_config_get_const_string(key);
207     if (value == NULL) {
208         return FS_CONFIG_NONE;
209     }
210     return atoi(value);
211 }
212 
fs_config_get_int_clamped(const char * key,int min,int max)213 int fs_config_get_int_clamped(const char *key, int min, int max)
214 {
215     int value = fs_config_get_int(key);
216     if (value == FS_CONFIG_NONE) {
217         return value;
218     }
219     if (value < min) {
220         fs_log("clamping value %d for key %s to %d\n", value, key, min);
221         return min;
222     }
223     if (value > max) {
224         fs_log("clamping value %d for key %s to %d\n", value, key, max);
225         return max;
226     }
227     return value;
228 }
229 
fs_config_get_double(const char * key)230 double fs_config_get_double(const char *key)
231 {
232     const char *value = fs_config_get_const_string(key);
233     if (value == NULL) {
234         return FS_CONFIG_NONE;
235     }
236     return g_ascii_strtod(value, NULL);
237 }
238 
fs_config_get_double_clamped(const char * key,double min,double max)239 double fs_config_get_double_clamped(const char *key, double min, double max)
240 {
241     double value = fs_config_get_double(key);
242     if (value == FS_CONFIG_NONE) {
243         return value;
244     }
245     if (value < min) {
246         fs_log("clamping value %d for key %s to %d\n", value, key, min);
247         return min;
248     }
249     if (value > max) {
250         fs_log("clamping value %d for key %s to %d\n", value, key, max);
251         return max;
252     }
253     return value;
254 }
255 
fs_config_parse_options(int argc,char ** argv)256 void fs_config_parse_options(int argc, char **argv)
257 {
258     if (!g_initialized) {
259         initialize();
260     }
261     int first = 1;
262     for (int i = 0; i < argc; i++) {
263         char *arg = argv[i];
264         if (!g_str_has_prefix(arg, "--")) {
265             continue;
266         }
267         char *key = arg + 2;
268         char *value = strchr(arg, '=');
269         char *k, *v;
270         if (value) {
271             k = g_strndup(key, value - key);
272             v = g_strdup(value + 1);
273         } else {
274             if (g_str_has_prefix(key, "no-")) {
275                 k = g_strdup(key + 3);
276                 v = g_strdup("0");
277             } else {
278                 k = g_strdup(key);
279                 v = g_strdup("1");
280             }
281         }
282 
283         if (first) {
284             fs_log("\n");
285             fs_log(LOG_LINE);
286             fs_log("config (command line arguments)\n");
287             fs_log(LOG_LINE);
288             fs_log("\n");
289             first = 0;
290         }
291         process_key_value(k, v, 0);
292         g_free(k);
293         /* v is owned by process_key_file, do not free here! */
294     }
295 }
296