Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
emutools_config.c
Go to the documentation of this file.
1 /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
2  Copyright (C)2016-2022 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17 
18 #ifndef XEMU_CONFIGDB_SUPPORT
19 #error "XEMU_CONFIGDB_SUPPORT was not defined in your xemu-target.h but xemu/emutools_config.c got compiled!"
20 #else
21 
22 #include "xemu/emutools.h"
23 #include "xemu/emutools_files.h"
24 #include "xemu/emutools_config.h"
25 #include <string.h>
26 #include <errno.h>
27 // About using strtod and maybe strtol too, some issues on Windows:
28 // ERROR: FIXME: windows does not now about HUGE_VALF and VALL ... it seems it want .._VAL with +/- sign?
29 // Also FIXME: widnows talks about EINVAL as well :-O
30 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtod-strtod-l-wcstod-wcstod-l?view=msvc-160
31 // ... but maybe that is for invalid paramers only (null pointer, invalid base, such like that)
32 // it seems windows needs math.h to have HUGE_VAL :-O
33 #include <math.h>
34 // Also, stdlib.h "should" be needed anyway
35 #include <stdlib.h>
36 
37 static int original_argc = 0;
38 static char **original_argv = NULL;
39 static const char *original_program_name = "?";
40 
41 static struct xemutools_config_st *config_head = NULL;
42 static struct xemutools_config_st *config_current;
43 
44 static int configdb_finalized = 0; // no new option can be defined if it's set to non-zero (after first use of any parse functionality)
45 static int config_file_was_never_open = 1;
46 
47 static const char CONFIGDB_ERROR_MSG[] = "Xemu internal ConfigDB error:";
48 
49 
50 void xemucfg_set_str ( char **ptr, const char *value )
51 {
52  if (value) {
53  size_t len = strlen(value);
54  *ptr = xemu_realloc(*ptr, len + 1); // need space for '\0' at the end of the string as well
55  if (len)
56  memcpy(*ptr, value, len); // probably memcpy() with len == 0 is not problematic, but who knows on some architectures ...
57  (*ptr)[len] = '\0';
58  } else if (*ptr) {
59  free(*ptr);
60  *ptr = NULL;
61  }
62 }
63 
64 
65 // Warning! No check, be sure to call this ONLY if "p" has really p->type == XEMUCFG_OPT_STR!
66 static int is_str_opt_default ( struct xemutools_config_st *p )
67 {
68  if (*p->opt_str.pp == NULL)
69  return p->opt_str.defval == NULL ? 1 : 0;
70  if (p->opt_str.defval == NULL)
71  return 0;
72  return !strcmp(*p->opt_str.pp, p->opt_str.defval);
73 }
74 
75 
76 static const char *set_option_value ( struct xemutools_config_st *p, const void *value )
77 {
78  static char error_str[128];
79  if ((p->flags & XEMUCFG_FLAG_DUMMY))
80  return NULL;
81  if (value == NULL && p->type != XEMUCFG_OPT_STR)
82  FATAL("%s option %s gets value to set as NULL", CONFIGDB_ERROR_MSG, p->name);
83  int error_flag = 0;
84  int i;
85  double f;
86  switch (p->type) {
87  case XEMUCFG_OPT_STR:
88  xemucfg_set_str(p->opt_str.pp, value);
89  break;
90  case XEMUCFG_OPT_NUM:
91  i = *(int*)value;
92  if (i < p->opt_num.min) {
93  i = p->opt_num.min;
94  error_flag = 1;
95  }
96  if (i > p->opt_num.max) {
97  i = p->opt_num.max;
98  error_flag = 1;
99  }
100  if (error_flag)
101  snprintf(error_str, sizeof error_str, "requires integer value between %d and %d, your value of %d is invalid",
102  p->opt_num.min, p->opt_num.max, *(int*)value
103  );
104  *p->opt_num.p = i;
105  break;
106  case XEMUCFG_OPT_BOOL:
107  i = *(int*)value;
108  if (i != 0 && i != 1) {
109  error_flag = 1;
110  snprintf(error_str, sizeof error_str, "requires boolean value 0 or 1, your value was %d", i);
111  }
112  *(int*)p->opt_bool.p = !!i;
113  break;
114  case XEMUCFG_OPT_FLOAT:
115  f = *(double*)value;
116  if (f < p->opt_float.min) {
117  f = p->opt_float.min;
118  error_flag = 1;
119  }
120  if (f > p->opt_float.max) {
121  f = p->opt_float.max;
122  error_flag = 1;
123  }
124  if (error_flag)
125  snprintf(error_str, sizeof error_str, "requires float value between %f and %f, your value of %f is invalid",
126  p->opt_float.min, p->opt_float.max, *(double*)value
127  );
128  *p->opt_float.p = f;
129  break;
130  case XEMUCFG_OPT_PROC:
131  p->opt_proc.p = value;
132  break;
133  }
134  if (error_flag) {
135  // Errors before finalization means, that the defined parameter values ARE invalid.
136  // Which should not happen, and must be treated as fatal errors, ASAP!
137  if (!configdb_finalized)
138  FATAL("%s Invalid default value at defining option: %s", CONFIGDB_ERROR_MSG, error_str);
139  return error_str;
140  } else
141  return NULL;
142 }
143 
144 
145 int xemucfg_str2int ( const char *s, int *result )
146 {
147  int base;
148  // Own logic to try to identify base-16 and base-2
149  // The base=0 for strtol() trick is not so nice, as it does not recognize
150  // "fancy" prefixes, but it also recognizes octal with starting of "0"
151  // which can be VERY confusing and I want to avoid!
152  if (!strncasecmp(s, "0x", 2)) {
153  base = 16;
154  s += 2;
155  } else if (s[0] == '$') {
156  base = 16;
157  s++;
158  } else if (!strncasecmp(s, "0b", 2)) {
159  base = 2;
160  s += 2;
161  } else if (s[0] == '%') {
162  base = 2;
163  s++;
164  } else if (s[0] != '\0' && !strcasecmp(s + strlen(s) - 1, "h")) {
165  base = 16;
166  } else if (!strncasecmp(s, "0o", 2)) { // heh, maybe somebody use octal, but please, no simple "0" prefix, that stupid! use "0o"
167  base = 8;
168  s += 2;
169  } else
170  base = 10;
171  char *endptr;
172  errno = 0;
173  // yes-yes, maybe your OS, your computer etc has "long int" the same size as "int" but it's not universal
174  // and for some reason there is no function strtoi() for int, just for longs ... Dunno why ...
175  long int res = strtol(s, &endptr, base);
176  if (((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE) || res < INT_MIN || res > INT_MAX || endptr == s || (*endptr != '\0' && strcasecmp(endptr ,"h")))
177  return 1;
178  *result = (int)res;
179  return 0;
180 }
181 
182 
183 int xemucfg_str2double ( const char *s, double *result )
184 {
185  char *endptr;
186  errno = 0;
187  double res = strtod(s, &endptr);
188  // Still we would like to detect problems, and want result which would fit into "int" as well, thush the INT_MIN and INT_MAX here
189  if (((res == HUGE_VAL || res == -HUGE_VAL || res == 0) && errno == ERANGE) || res <= (double)INT_MIN || res >= (double)INT_MAX || endptr == s || *endptr != '\0')
190  return 1;
191  *result = res;
192  return 0;
193 }
194 
195 
196 int xemucfg_str2bool ( const char *s, int *result )
197 {
198  if (!strcasecmp(s, "yes") || !strcasecmp(s, "on") || !strcmp(s, "1") || !strcasecmp(s, "true")) {
199  *result = 1;
200  return 0;
201  }
202  if (!strcasecmp(s, "no") || !strcasecmp(s, "off") || !strcmp(s, "0") || !strcasecmp(s, "false")) {
203  *result = 0;
204  return 0;
205  }
206  return 1;
207 }
208 
209 
210 static const char *set_option_value_from_string ( struct xemutools_config_st *p, const char *value, const char *optname_fullspec )
211 {
212  if ((p->flags & XEMUCFG_FLAG_DUMMY))
213  return NULL;
214  int i;
215  double f;
216  switch (p->type) {
217  case XEMUCFG_OPT_STR:
218  return set_option_value(p, (void*)value);
219  case XEMUCFG_OPT_NUM:
220  if (xemucfg_str2int(value, &i))
221  return "integer value has invalid syntax";
222  return set_option_value(p, &i);
223  case XEMUCFG_OPT_FLOAT:
224  if (xemucfg_str2double(value, &f))
225  return "float value has invalid syntax";
226  return set_option_value(p, &f);
227  case XEMUCFG_OPT_BOOL:
228  if (xemucfg_str2bool(value, &i))
229  return "boolean value must be yes/on/1/true OR no/off/0/false";
230  return set_option_value(p, &i);
231  case XEMUCFG_OPT_PROC:
232  // TODO: book all "proc" events (value and optname_full - this second one needed for @... prefixes)
233  // so config save can save those!
234  //return (*(xemucfg_parser_callback_func_t)(p->opt_proc.p))(p, optname_fullspec, value);
235  return p->opt_proc.p(p, optname_fullspec, value);
236  }
237  return "unknown code-path";
238 }
239 
240 
241 static void define_core_options ( void );
242 
243 
244 // This internal function may leaves some fields unfilled!
245 // The various xemcfg_define_..._option() functions must set those up!
246 static void define_new_option ( const char *optname, enum xemutools_option_type type, const char *help, void *storage )
247 {
248  static int core_definitions = 0;
249  if (!core_definitions) {
250  // Define some options BEFORE the requested one, if this is the first requested.
251  // Yay, define_core_options() will call this routine, be careful :-O
252  core_definitions = 1;
253  define_core_options();
254  }
255  if (configdb_finalized)
256  FATAL("%s cannot define new option '%s', DB is already finalized!", CONFIGDB_ERROR_MSG, optname);
257  if (optname == NULL)
258  FATAL("%s optname = NULL for define_new_option()", CONFIGDB_ERROR_MSG);
259  if (strlen(optname) > OPT_NAME_MAX_LENGTH)
260  FATAL("%s optname is too long string for define_new_option() at defining option %s", CONFIGDB_ERROR_MSG, optname);
261  if (storage == NULL)
262  FATAL("%s storage = NULL for define_new_option() at defining option %s", CONFIGDB_ERROR_MSG, optname);
263  config_current = xemu_malloc(sizeof(struct xemutools_config_st));
264  if (!config_head) {
265  config_head = config_current;
266  config_current->next = NULL;
267  } else {
268  struct xemutools_config_st *p = config_head, *p_prev = NULL;
269  for (;;) {
270  // we want to manage alphabet sorted list just for nice help output, and for checking re-definition assertion in one step as well
271  int ret = strcasecmp(optname, p->name);
272  if (!ret)
273  FATAL("%s trying to re-define option '%s'", CONFIGDB_ERROR_MSG, optname);
274  if (ret < 0) { // we want the first entry already later in alphabet than current one, insert new entry before that!
275  config_current->next = p;
276  if (p_prev)
277  p_prev->next = config_current;
278  else
279  config_head = config_current;
280  break;
281  }
282  if (p->next) {
283  p_prev = p;
284  p = p->next;
285  } else {
286  p->next = config_current;
287  config_current->next = NULL;
288  break;
289  }
290 
291  }
292  }
293  static const char no_help[] = "(no help)";
294  config_current->name = optname;
295  config_current->type = type;
296  config_current->help = (help && *help) ? help : no_help;
297  config_current->flags = 0;
298  config_current->opt_ANY.p = storage;
299  if (type == XEMUCFG_OPT_STR)
300  *config_current->opt_str.pp = NULL;
301 }
302 
303 
304 
305 void xemucfg_define_bool_option ( const char *optname, const int defval, const char *help, int *storage )
306 {
307  define_new_option(optname, XEMUCFG_OPT_BOOL, help, storage);
308  // return value (error message) is ignored since in define stage (configdb_finalized is zero) it will
309  // be fatal error anyway, triggered by set_option_value()
310  (void)set_option_value(config_current, &defval);
311  config_current->opt_bool.defval = *config_current->opt_bool.p;
312 }
313 
314 void xemucfg_define_switch_option ( const char *optname, const char *help, int *storage )
315 { // Really, this function defines a BOOL typed option, same as "bool" however the default value is hardcoded to be zero.
316  xemucfg_define_bool_option(optname, 0, help, storage);
317 }
318 
319 static void xemucfg_define_dummy_option ( const char *optname, const char *help, int flags )
320 {
321  static int dummy = 0;
322  define_new_option(optname, XEMUCFG_OPT_BOOL, help, &dummy);
323  config_current->flags = flags | XEMUCFG_FLAG_DUMMY;
324  config_current->opt_bool.defval = 0;
325 }
326 
327 void xemucfg_define_str_option ( const char *optname, const char *defval, const char *help, char **storage ) {
328  define_new_option(optname, XEMUCFG_OPT_STR, help, storage);
329  (void)set_option_value(config_current, defval);
330  config_current->opt_str.defval = defval;
331 }
332 
333 void xemucfg_define_num_option ( const char *optname, const int defval, const char *help, int *storage, int min, int max )
334 {
335  if (min >= max)
336  FATAL("%s cannot define new option '%s', given numeric range is irreal: %d ... %d", CONFIGDB_ERROR_MSG, optname, min, max);
337  define_new_option(optname, XEMUCFG_OPT_NUM, help, storage);
338  config_current->opt_num.min = min;
339  config_current->opt_num.max = max;
340  (void)set_option_value(config_current, &defval);
341  config_current->opt_num.defval = *config_current->opt_num.p;
342 }
343 
344 void xemucfg_define_float_option ( const char *optname, const double defval, const char *help, double *storage, double min, double max )
345 {
346  if (min >= max)
347  FATAL("%s cannot define new option '%s', given float range is irreal: %f ... %f", CONFIGDB_ERROR_MSG, optname, min, max);
348  define_new_option(optname, XEMUCFG_OPT_FLOAT, help, storage);
349  config_current->opt_float.min = min;
350  config_current->opt_float.max = max;
351  (void)set_option_value(config_current, &defval);
352  config_current->opt_float.defval = *config_current->opt_float.p;
353 }
354 
355 void xemucfg_define_proc_option ( const char *optname, xemucfg_parser_callback_func_t cb, const char *help )
356 {
357  define_new_option(optname, XEMUCFG_OPT_PROC, help, cb);
358 }
359 
360 
361 
363  for (int i = 0; p[i].optname; i++)
364  xemucfg_define_bool_option (p[i].optname, p[i].defval, p[i].help, p[i].store_here);
365 }
367  for (int i = 0; p[i].optname; i++)
368  xemucfg_define_switch_option(p[i].optname, p[i].help, p[i].store_here);
369 }
371  for (int i = 0; p[i].optname; i++)
372  xemucfg_define_str_option (p[i].optname, p[i].defval, p[i].help, p[i].store_here);
373 }
375  for (int i = 0; p[i].optname; i++)
376  xemucfg_define_num_option (p[i].optname, p[i].defval, p[i].help, p[i].store_here, p[i].min, p[i].max);
377 }
379  for (int i = 0; p[i].optname; i++)
380  xemucfg_define_float_option (p[i].optname, p[i].defval, p[i].help, p[i].store_here, p[i].min, p[i].max);
381 }
383  for (int i = 0; p[i].optname; i++)
384  xemucfg_define_proc_option (p[i].optname, p[i].cb, p[i].help);
385 }
386 
387 
388 static struct xemutools_config_st *search_option ( const char *name )
389 {
390  struct xemutools_config_st *p = config_head;
391  char *s = strchr(name, '@');
392  int l = s ? s - name : strlen(name);
393  while (p)
394  if (!strncasecmp(name, p->name, l) && p->name[l] == 0 && (name[l] == 0 || name[l] == '@'))
395  break;
396  else
397  p = p->next;
398  return (p && s && p->type != XEMUCFG_OPT_PROC) ? NULL : p;
399 }
400 
401 
402 static void dump_help ( void )
403 {
404  printf("Available command line options:" NL NL);
405  for (struct xemutools_config_st *p = config_head; p != NULL; p = p->next) {
406  if ((p->flags & XEMUCFG_FLAG_FILE_ONLY))
407  continue;
408  const char *t = "";
409  if ((p->flags & XEMUCFG_FLAG_FIRST_ONLY))
410  t = "(1st-arg!) ";
411  /*else if ((p->flags & XEMUCFG_FLAG_DUMMY))
412  t = " ";*/
413  else
414  switch (p->type) {
415  case XEMUCFG_OPT_BOOL: t = "(bool) "; break;
416  case XEMUCFG_OPT_NUM: t = "(int-num) "; break;
417  case XEMUCFG_OPT_FLOAT: t = "(float-num) "; break;
418  case XEMUCFG_OPT_STR: t = "(str) "; break;
419  case XEMUCFG_OPT_PROC: t = "(spec) "; break;
420  }
421  printf("-%-16s %s%s" NL, p->name, t, p->help);
422  }
423  printf(NL "Bool(-ean) options can be written without parameter if it means (1/on/yes/true)." NL);
424 }
425 
426 
427 static char *get_config_string_representation ( const char *initial_part )
428 {
429  char *out = xemu_strdup(initial_part ? initial_part : "");
430  for (struct xemutools_config_st *p = config_head; p != NULL; p = p->next) {
431  if ((p->flags & (XEMUCFG_FLAG_CLI_ONLY | XEMUCFG_FLAG_DUMMY)))
432  continue;
433  char buffer[256 + PATH_MAX];
434  int r = 0; // sigh, gcc is stupid, it must be set, even if it see all cases are handled later ...
435  // We want to dump configDB in a way, that:
436  // * includes the help
437  // * values left at their default values are just there as comment
438  switch (p->type) {
439  case XEMUCFG_OPT_STR:
440  r = snprintf(buffer, sizeof buffer, NL "## %s" NL "## (string param)" NL "%s%s = \"%s\"" NL,
441  p->help,
442  is_str_opt_default(p) ? "#" : "",
443  p->name,
444  *p->opt_str.pp ? *p->opt_str.pp : ""
445  );
446  break;
447  case XEMUCFG_OPT_BOOL:
448  r = snprintf(buffer, sizeof buffer, NL "## %s" NL "## (boolean param)" NL "%s%s = %s" NL,
449  p->help,
450  *p->opt_bool.p == p->opt_bool.defval ? "#" : "",
451  p->name,
452  *p->opt_bool.p ? "true" : "false"
453  );
454  break;
455  case XEMUCFG_OPT_NUM:
456  r = snprintf(buffer, sizeof buffer, NL "## %s" NL "## (integer param)" NL "%s%s = %d" NL,
457  p->help,
458  *p->opt_num.p == p->opt_num.defval ? "#" : "",
459  p->name,
460  *p->opt_num.p
461  );
462  break;
463  case XEMUCFG_OPT_FLOAT:
464  r = snprintf(buffer, sizeof buffer, NL "## %s" NL "## (real-num param)" NL "%s%s = %f" NL,
465  p->help,
466  *p->opt_float.p == p->opt_float.defval ? "#" : "",
467  p->name,
468  *p->opt_float.p
469  );
470  break;
471  case XEMUCFG_OPT_PROC:
472  r = snprintf(buffer, sizeof buffer, NL "## %s" NL "## (SPECIAL)" NL "#%s" NL,
473  p->help,
474  p->name
475  );
476  break;
477  }
478  if (r >= sizeof(buffer) - 1) {
479  free(out);
480  FATAL("%s Too large result for dumping config option '%s'!", CONFIGDB_ERROR_MSG, p->name);
481  }
482  out = xemu_realloc(out, strlen(out) + strlen(buffer) + 1);
483  strcpy(out + strlen(out), buffer);
484  }
485  if (strlen(out) > CONFIG_FILE_MAX_SIZE) {
486  free(out);
487  FATAL("%s Too large config file generated in %s", CONFIGDB_ERROR_MSG, __func__);
488  }
489  return out;
490 }
491 
492 
493 int xemucfg_save_config_file ( const char *filename, const char *initial_part, const char *cry )
494 {
495  char *out = get_config_string_representation(initial_part);
496  int ret = out[0] ? xemu_save_file(filename, out, strlen(out), cry) : 0;
497  free(out);
498  return ret;
499 }
500 
501 
502 static char *get_config_template_string_representation ( const char *template_fn, const char *default_fn )
503 {
504  char templ[4096];
505  sprintf(templ,
506  "# Config template for XEMU/%s %s (%s) file %s" NL
507  "# ----" NL
508  "# !!DO NOT EDIT THIS FILE - THIS WILL BE OVERWRITTEN EACH TIME YOU START XEMU!!" NL
509  "# This is only a *TEMPLATE* file, never loaded by Xemu. For actual configuration" NL
510  "# please edit the file %s instead. You can use this file to learn about the set" NL
511  "# of config options and format though." NL
512  "# ----" NL
513  "# Rules: basically option = value syntax." NL
514  "# String values optionally can be encolsed by '\"', and this is the only way" NL
515  "# to have empty string. Option values expecting file names can start with" NL
516  "# letters '@' or '#'. In case of '@' the rest of the filename/path is" NL
517  "# interpreted as relative to the preferences directory. In case of '#', the" NL
518  "# rest of the filename/path will be searched at some common places (including" NL
519  "# the preferences directory, but also in the same directory as the binary is, or" NL
520  "# in case of UNIX-like OS, even the common data directory)" NL
521  "# ----" NL
522  "# SDL preference directory for this installation: %s" NL
523  "# Binary base directory when generating this file: %s" NL
524 #ifndef XEMU_ARCH_WIN
525  "# Also common search directories:" NL
526  "# " UNIX_DATADIR_0 NL
527  "# " UNIX_DATADIR_1 NL
528  "# " UNIX_DATADIR_2 NL
529  "# " UNIX_DATADIR_3 NL
530 #endif
531  "# ----" NL NL NL,
532  /* args */
533  xemu_app_name, XEMU_BUILDINFO_CDATE, XEMU_ARCH_NAME,
534  template_fn,
535  default_fn,
536  sdl_pref_dir,
538  );
539  return get_config_string_representation(templ);
540 }
541 
542 
543 static inline void finalize_configdb ( void )
544 {
545  configdb_finalized = 1;
546 }
547 
548 
549 int xemucfg_parse_config_file ( const char *filename_in, const char *open_fail_msg )
550 {
551  finalize_configdb();
552  char *p, cfgmem[CONFIG_FILE_MAX_SIZE + 1];
553  int lineno = xemu_load_file(filename_in, cfgmem, 0, CONFIG_FILE_MAX_SIZE, open_fail_msg);
554  if (lineno < 0)
555  return 1;
556  config_file_was_never_open = 0;
557  cfgmem[lineno] = 0; // terminate string
558  if (strlen(cfgmem) != lineno) {
559  ERROR_WINDOW("Bad config file (%s),\ncontains '\\0' character (not a text file?)", xemu_load_filepath);
560  return 1;
561  }
562  p = cfgmem;
563  lineno = 1; // line number counter in read config file from now
564  do {
565  char *p1, *pn;
566  // Skip white-spaces at the beginning of the line
567  while (*p == ' ' || *p == '\t')
568  p++;
569  // Search for end of line (relaxed check, too much mixed line-endings I've seen already within ONE config file failed with simple fgets() etc method ...)
570  p1 = strstr(p, "\r\n");
571  if (p1) pn = p1 + 2;
572  else {
573  p1 = strstr(p, "\n\r");
574  if (p1) pn = p1 + 2;
575  else {
576  p1 = strchr(p, '\n');
577  if (p1) pn = p1 + 1;
578  else {
579  p1 = strchr(p, '\r');
580  pn = p1 ? p1 + 1 : NULL;
581  }
582  }
583  }
584  if (p1) *p1 = 0; // terminate line string at EOL marker
585  p1 = strchr(p, '#');
586  if (p1) *p1 = 0; // comment - if any - is also excluded
587  // Chop white-spaces off from the end of the line
588  p1 = p + strlen(p);
589  while (p1 > p && (p1[-1] == '\t' || p1[-1] == ' '))
590  *(--p1) = 0;
591  // If line is not empty, separate key/val (if there is) and see what it means
592  if (*p) {
593  struct xemutools_config_st *o;
594  const char *s;
595  p1 = p;
596  while (*p1 && *p1 != '\t' && *p1 != ' ' && *p1 != '=')
597  p1++;
598  if (*p1) {
599  *(p1++) = 0;
600  while (*p1 == '\t' || *p1 == ' ' || *p1 == '=')
601  p1++;
602  if (!*p1)
603  p1 = NULL;
604  } else
605  p1 = NULL;
606  DEBUG("Line#%d = \"%s\",\"%s\"" NL, lineno, p, p1 ? p1 : "<no-specified>");
607  o = search_option(p);
608  if (!o) {
609  ERROR_WINDOW("Config file (%s) error at line %d:\nkeyword '%s' is unknown.", xemu_load_filepath, lineno, p);
610  return 1;
611  }
612  if ((o->flags & XEMUCFG_FLAG_CLI_ONLY)) {
613  ERROR_WINDOW("Config file (%s) error at line %d:\nkeyword '%s' is a command-line only option, cannot be used in config files", xemu_load_filepath, lineno, p);
614  return 1;
615  }
616  if (o->type == XEMUCFG_OPT_BOOL && !p1) // Only for bool type, special case: if there is no parameter it means "1" (true)
617  p1 = "1"; // ... though let the common case parser to intepret the textual presentation to our desired value
618  else if (!p1) {
619  ERROR_WINDOW("Config file (%s) error at line %d:\nkeyword '%s' requires a value.", xemu_load_filepath, lineno, p);
620  return 1;
621  }
622  if (o->type == XEMUCFG_OPT_STR) {
623  // Special rules applies only for STR type params and only in config files
624  if (!strcmp(p1, "<NULL>")) // to force a NULL pointer (more like a debug feature!!)
625  p1 = NULL;
626  else {
627  // remove heading " char - if there is one (by moving the pointer itself)
628  if (*p1 == '"')
629  p1++;
630  // remove trailing " char - if there is one
631  size_t i = strlen(p1);
632  if (i && p1[i - 1] == '"')
633  p1[i - 1] = '\0';
634  }
635  }
636  s = set_option_value_from_string(o, p1, p);
637  if (s) {
638  ERROR_WINDOW("Config file (%s) error at line %d:\nkeyword '%s': %s", xemu_load_filepath, lineno, p, s);
639  return 1;
640  }
641  }
642  // Prepare for next line
643  p = pn; // start of next line (or EOF if NULL)
644  lineno++;
645  } while (p);
646  return 0;
647 }
648 
649 
650 #define OPT_ERROR_CMDLINE(...) do { ERROR_WINDOW("Command line error: " __VA_ARGS__); return 1; } while(0)
651 
652 
653 static int parse_commandline ( int argc, char **argv )
654 {
655  finalize_configdb();
656  while (argc) {
657  if (argv[0][0] != '-')
658  OPT_ERROR_CMDLINE("Invalid option '%s', must start with '-'", *argv);
659  if (argv[0][1] == '-')
660  OPT_ERROR_CMDLINE("Invalid option '%s', options start with '-' and not '--'", *argv);
661  struct xemutools_config_st *p = search_option(*argv + 1);
662  if (!p)
663  OPT_ERROR_CMDLINE("Unknown option '%s'", *argv);
664  if ((p->flags & XEMUCFG_FLAG_FIRST_ONLY))
665  OPT_ERROR_CMDLINE("'%s' must be the first option", *argv);
666  argc--;
667  argv++;
668  if (p->type == XEMUCFG_OPT_BOOL && (
669  (argc && argv[0][0] == '-') || // if next position in CLI seems to be an option (starting with '-')
670  (!argc) // ... or, we are at the end of CLI
671  )) {
672  set_option_value(p, (void*)&ONE_INT); // THEN, it's a boolean parameter without value meaning 'setting to one' (like "-fullscreen" versus "-fullscreen 1")
673  } else { // ELSE, we *NEED* some parameter for the option!
674  if (!argc)
675  OPT_ERROR_CMDLINE("Option '%s' requires a parameter, but end of command line detected.", argv[-1]);
676  const char *s = set_option_value_from_string(p, *argv, argv[-1] + 1);
677  if (s) {
678  OPT_ERROR_CMDLINE("Option '%s' error:\n%s", p->name, s);
679  }
680  argc--;
681  argv++;
682  }
683  }
684  DEBUGPRINT("CFG: CLI parsing is done without error." NL);
685  return 0;
686 }
687 
688 
689 static const char *custom_config_file_reader ( struct xemutools_config_st *opt, const char *optname, const char *optvalue )
690 {
691  xemucfg_parse_config_file(optvalue, "Cannot open/read selected config file");
692  return NULL; // hmm, call of xemucfg_parse_config_file() catched errors anyway (hopefully!)
693 }
694 
695 
696 static const char skipconfigfile_option_name[] = "skipconfigfile";
697 
698 
699 static void define_core_options ( void )
700 {
701  // Here must come some self-defining options (if we have any)
702  // we want to define anyway, regardless of what an Xemu emulator target defines or not
703  xemucfg_define_proc_option("config", custom_config_file_reader, "Use a given config file");
704  config_current->flags = XEMUCFG_FLAG_CLI_ONLY;
705  // These are for completness only as they are not handled "the normal way", but
706  // still nice if help can shows all the options.
707  xemucfg_define_dummy_option("help", "Shows this help screen", XEMUCFG_FLAG_CLI_ONLY | XEMUCFG_FLAG_FIRST_ONLY);
708  xemucfg_define_dummy_option("version", "Dump version info only and exit", XEMUCFG_FLAG_CLI_ONLY | XEMUCFG_FLAG_FIRST_ONLY);
709  xemucfg_define_dummy_option(skipconfigfile_option_name, "Skip loading the default config file", XEMUCFG_FLAG_CLI_ONLY | XEMUCFG_FLAG_FIRST_ONLY);
710 }
711 
712 
713 void xemucfg_get_cli_info ( const char **exec_name_ptr, int *argc_ptr, char ***argv_ptr )
714 {
715  if (exec_name_ptr)
716  *exec_name_ptr = original_program_name;
717  if (argc_ptr)
718  *argc_ptr = original_argc;
719  if (argv_ptr)
720  *argv_ptr = original_argv;
721 }
722 
723 
724 const char *xemucfg_get_default_config_file_name ( void )
725 {
726  static char *storage = NULL;
727  if (!storage) {
728  char buf[64];
729  snprintf(buf, sizeof buf, CONFIG_FILE_USE_NAME, xemu_app_name);
730  storage = xemu_strdup(buf);
731  }
732  return storage;
733 }
734 
735 
736 static const char *xemucfg_get_template_config_file_name ( void )
737 {
738  static char *storage = NULL;
739  if (!storage) {
740  char buf[64];
741  snprintf(buf, sizeof buf, CONFIG_FILE_TEMPL_NAME, xemu_app_name);
742  storage = xemu_strdup(buf);
743  }
744  return storage;
745 }
746 
747 
748 int xemucfg_parse_all ( int argc, char **argv )
749 {
750  if (argc > 0) // maybe overkill to CHECK, argv[0] should be there always ...
751  original_program_name = argv[0];
752  // Skip program name
753  argc--;
754  argv++;
755 #ifdef XEMU_ARCH_MAC
756  // Oh no, another MacOS miss-feature :-O it seems Finder passes a strange parameter to EVERY app it starts!!
757  // Skip that, otherwise the user will experience an error box about unknown option and Xemu exits ...
758  if (argc && !strncmp(argv[0], "-psn_", 5) && !macos_gui_started) {
759  DEBUGPRINT("MACOS: -psn MacOS-madness detected, throwing in some workaround ..." NL);
760  argc--;
761  argv++;
762  macos_gui_started = 1;
763  // FIXME: if we know it's a Mac GUI-started app (we have this -psn...)
764  // wouldn't it better to skip ALL of the command line parsing?
765  // Is there a way in Mac to start program from GUI (finder, or wtf) but
766  // still having CLI options what can make it legit not to do so?
767  }
768 #endif
769  original_argc = argc;
770  original_argv = argv;
771  // Check some hard-coded options (help, version ...)
772  // This is done by looking only for the FIRST command line parameter (if there's any ...)
773  int skip_config_file = 0;
774  if (argc) {
775  const char *p = argv[0];
776  while (*p == '-' || *p == '/' || *p == '\\') // skip various option markers, at this point, user may not have idea what is the right syntax yet
777  p++;
778  if (!strcasecmp(p, "h") || !strcasecmp(p, "help") || !strcasecmp(p, "?")) {
779  dump_help();
780  return 1;
781  } else if (!strcasecmp(p, "v") || !strcasecmp(p, "ver") || !strcasecmp(p, "version")) {
782  DEBUGPRINT("Exiting, because only version information was requested." NL);
783  return 1; // version info. Simply return, as it's already printed out ;)
784  } else if (!strcasecmp(p, skipconfigfile_option_name)) {
785  // Special option to skip config file parsing. Can be useful if there is an
786  // error in the default config file which prevents user to be able to start Xemu.
787  skip_config_file = 1;
788  // Also "remove" this option from the scope, so cli parser won't see it :)
789  argc--;
790  argv++;
791  // But continue ...
792  }
793  }
794  // Prepare filenames for default and template configuration files
795  // CONFIG_FILE_*_NAME are expected that the first character is '@' (denoted to pref dir) and contains a '%s' for xemu_app_name
796  // No worries, WLAs here should have anough space for the trailing '\0' because '%s' there is counted.
797  const char *cfg_default_fn = xemucfg_get_default_config_file_name();
798  const char *cfg_template_fn = xemucfg_get_template_config_file_name();
799  // Get config template now!
800  // We want to save this, but we must before parse, to have
801  // the default values. We save it later though!
802  char *config_template_string = get_config_template_string_representation(cfg_template_fn + 1, cfg_default_fn + 1);
803  // Next, we want to parse default config file! Since we want the command line
804  // override this optionally, we do command line parsing after this step only!
805  // ... unless user hasn't disabled parsing it, above ...
806  if (!skip_config_file) {
807  if (xemucfg_parse_config_file(cfg_default_fn, NULL))
808  DEBUGPRINT("CFG: Default config file %s cannot be used" NL, cfg_default_fn);
809  else
810  DEBUGPRINT("CFG: Default config file %s has been used" NL, cfg_default_fn);
811  // check if cfg_default_fn exits, if no (but only if!! - otherwise it would overwrite user's config!)
812  // write out an empty file with some comments, so user would find easily what to edit at all
813  if (config_file_was_never_open) {
814  DEBUGPRINT("CFG: default config file %s does not exist, creating an empty one" NL, cfg_default_fn);
815  char empty_config_file_str[512];
816  const int size = snprintf(empty_config_file_str, sizeof empty_config_file_str,
817  "# Use this (%s) file to put your configuration directives into." NL
818  "# This file is loaded by Xemu automatically during each start-up." NL
819  "# You can have a look on file %s to learn more about the syntax and options," NL
820  "# but you need to use THIS file to edit, not THAT (as that is overwritten" NL
821  "# each time Xemu starts up)." NL NL,
822  cfg_default_fn + 1, cfg_template_fn + 1
823  );
824  xemu_save_file(cfg_default_fn, empty_config_file_str, size, "Cannot save default config file");
825  }
826  } else
827  DEBUGPRINT("CFG: Default config file is SKIPPED by user request!" NL);
828  // And now, we want to parse command line arguments, now it can override already defined values by config file
829  const int ret = parse_commandline(argc, argv);
830  if (!ret) {
831  // save the template (only is cli parsing was OK, otherwise Xemu will exit later, anyway)
832  xemu_save_file(cfg_template_fn, config_template_string, strlen(config_template_string), "Cannot save config template");
833  }
834  free(config_template_string);
835  return ret;
836 }
837 
838 
839 int xemucfg_integer_list_from_string ( const char *value, int *result, int maxitems, const char *delims )
840 {
841  int num = 0;
842  if (!value)
843  return num;
844  char buffer[strlen(value) + 1], *p;
845  strcpy(buffer, value);
846  p = strtok(buffer, delims);
847  while (p) {
848  if (num == maxitems)
849  return -1;
850  result[num++] = atoi(p);
851  p = strtok(NULL, delims);
852  }
853  return num;
854 }
855 
856 
857 #ifndef XEMU_RELEASE_BUILD
858 void xemucfg_dump_db ( const char *msg )
859 {
860  DEBUGPRINT("CFG: --- test dump of ConfigDB follows: %s" NL, msg ? msg : "<no-info>");
861  for (struct xemutools_config_st *p = config_head; p != NULL; p = p->next) {
862  switch (p->type) {
863  case XEMUCFG_OPT_STR:
864  DEBUGPRINT("CFG: %s %p %p %s [%s]" NL, p->name, p->opt_str.pp, *p->opt_str.pp, *p->opt_str.pp, p->help);
865  break;
866  case XEMUCFG_OPT_BOOL:
867  DEBUGPRINT("CFG: %s %p %d [%s]" NL, p->name, p->opt_bool.p, *p->opt_bool.p, p->help);
868  break;
869  case XEMUCFG_OPT_NUM:
870  DEBUGPRINT("CFG: %s %p %d [%s]" NL, p->name, p->opt_num.p, *p->opt_num.p, p->help);
871  break;
872  case XEMUCFG_OPT_FLOAT:
873  DEBUGPRINT("CFG: %s %p %f [%s]" NL, p->name, p->opt_float.p, *p->opt_float.p, p->help);
874  break;
875  case XEMUCFG_OPT_PROC:
876  DEBUGPRINT("CFG: %s %p [%s]" NL, p->name, p->opt_proc.p, p->help);
877  break;
878  }
879  }
880  DEBUGPRINT("CFG: --- end of dump." NL);
881 }
882 #endif
883 
884 // XEMU_CONFIGDB_SUPPORT
885 #endif
xemucfg_define_str_option
void xemucfg_define_str_option(const char *optname, const char *defval, const char *help, char **storage)
xemutools_config_st::max
int max
Definition: emutools_config.h:61
emutools.h
XEMUCFG_OPT_STR
@ XEMUCFG_OPT_STR
Definition: emutools_config.h:29
flags
Uint8 flags
Definition: z8k1.c:126
xemutools_config_st::min
int min
Definition: emutools_config.h:61
sdl_base_dir
char * sdl_base_dir
Definition: emutools.c:97
xemucfg_integer_list_from_string
int xemucfg_integer_list_from_string(const char *value, int *result, int maxitems, const char *delims)
xemucfg_define_bool_option
void xemucfg_define_bool_option(const char *optname, const int defval, const char *help, int *storage)
xemucfg_define_num_option
void xemucfg_define_num_option(const char *optname, const int defval, const char *help, int *storage, int min, int max)
OPT_NAME_MAX_LENGTH
#define OPT_NAME_MAX_LENGTH
Definition: emutools_config.h:23
XEMUCFG_OPT_FLOAT
@ XEMUCFG_OPT_FLOAT
Definition: emutools_config.h:29
xemutools_configdef_str_st
Definition: emutools_config.h:85
xemucfg_define_proc_option_multi
void xemucfg_define_proc_option_multi(const struct xemutools_configdef_proc_st p[])
xemucfg_define_str_option_multi
void xemucfg_define_str_option_multi(const struct xemutools_configdef_str_st p[])
xemutools_config_st::type
enum xemutools_option_type type
Definition: emutools_config.h:47
ONE_INT
const int ONE_INT
Definition: emutools.c:59
xemucfg_define_bool_option_multi
void xemucfg_define_bool_option_multi(const struct xemutools_configdef_bool_st p[])
xemucfg_parse_all
int xemucfg_parse_all(int argc, char **argv)
xemucfg_define_switch_option_multi
void xemucfg_define_switch_option_multi(const struct xemutools_configdef_switch_st p[])
xemucfg_str2int
int xemucfg_str2int(const char *s, int *result)
xemutools_config_st::defval
int defval
Definition: emutools_config.h:57
XEMUCFG_FLAG_FIRST_ONLY
#define XEMUCFG_FLAG_FIRST_ONLY
Definition: emutools_config.h:40
xemutools_option_type
xemutools_option_type
Definition: emutools_config.h:28
CONFIG_FILE_MAX_SIZE
#define CONFIG_FILE_MAX_SIZE
Definition: emutools_config.h:21
xemucfg_save_config_file
int xemucfg_save_config_file(const char *filename, const char *initial_part, const char *cry)
UNIX_DATADIR_3
#define UNIX_DATADIR_3
Definition: emutools_files.h:93
XEMUCFG_FLAG_CLI_ONLY
#define XEMUCFG_FLAG_CLI_ONLY
Definition: emutools_config.h:39
emutools_files.h
xemu_malloc
void * xemu_malloc(size_t size)
Definition: emutools.c:226
xemu_app_name
char * xemu_app_name
Definition: emutools.c:85
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
xemu_save_file
int xemu_save_file(const char *filename_in, void *data, int size, const char *cry)
Definition: emutools_files.c:622
xemutools_configdef_proc_st
Definition: emutools_config.h:107
xemucfg_parse_config_file
int xemucfg_parse_config_file(const char *filename_in, const char *open_fail_msg)
xemucfg_define_num_option_multi
void xemucfg_define_num_option_multi(const struct xemutools_configdef_num_st p[])
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
xemutools_configdef_switch_st
Definition: emutools_config.h:112
xemutools_config_st::p
void * p
Definition: emutools_config.h:53
xemucfg_get_default_config_file_name
const char * xemucfg_get_default_config_file_name(void)
xemucfg_get_cli_info
void xemucfg_get_cli_info(const char **exec_name_ptr, int *argc_ptr, char ***argv_ptr)
NL
#define NL
Definition: fat32.c:37
xemucfg_define_proc_option
void xemucfg_define_proc_option(const char *optname, xemucfg_parser_callback_func_t cb, const char *help)
emutools_config.h
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
xemucfg_dump_db
void xemucfg_dump_db(const char *msg)
base
int base
Definition: dma65.c:82
xemutools_config_st
Definition: emutools_config.h:44
xemu_load_file
int xemu_load_file(const char *filename, void *store_to, int min_size, int max_size, const char *cry)
Definition: emutools_files.c:674
xemutools_configdef_float_st
Definition: emutools_config.h:99
xemutools_configdef_bool_st
Definition: emutools_config.h:79
UNIX_DATADIR_2
#define UNIX_DATADIR_2
Definition: emutools_files.h:92
xemutools_config_st::flags
unsigned int flags
Definition: emutools_config.h:49
xemucfg_define_float_option_multi
void xemucfg_define_float_option_multi(const struct xemutools_configdef_float_st p[])
xemucfg_str2double
int xemucfg_str2double(const char *s, double *result)
size
int size
Definition: inject.c:37
XEMU_BUILDINFO_CDATE
const char XEMU_BUILDINFO_CDATE[]
Definition: emutools_basicdefs.h:251
XEMUCFG_FLAG_FILE_ONLY
#define XEMUCFG_FLAG_FILE_ONLY
Definition: emutools_config.h:42
XEMUCFG_OPT_BOOL
@ XEMUCFG_OPT_BOOL
Definition: emutools_config.h:29
XEMUCFG_FLAG_DUMMY
#define XEMUCFG_FLAG_DUMMY
Definition: emutools_config.h:41
xemucfg_define_float_option
void xemucfg_define_float_option(const char *optname, const double defval, const char *help, double *storage, double min, double max)
xemutools_config_st::opt_float
struct xemutools_config_st::@52::@57 opt_float
xemutools_config_st::opt_ANY
struct xemutools_config_st::@52::@54 opt_ANY
xemutools_config_st::next
struct xemutools_config_st * next
Definition: emutools_config.h:45
xemu_strdup
char * xemu_strdup(const char *s)
Definition: emutools.c:278
xemutools_configdef_num_st
Definition: emutools_config.h:91
xemu_load_filepath
char xemu_load_filepath[PATH_MAX]
Definition: emutools_files.c:35
UNIX_DATADIR_1
#define UNIX_DATADIR_1
Definition: emutools_files.h:91
xemutools_config_st::opt_str
struct xemutools_config_st::@52::@58 opt_str
CONFIG_FILE_USE_NAME
#define CONFIG_FILE_USE_NAME
Definition: emutools_config.h:26
value
int value
Definition: dma65.c:90
XEMUCFG_OPT_PROC
@ XEMUCFG_OPT_PROC
Definition: emutools_config.h:29
name
const char * name
Definition: joystick.c:46
xemucfg_define_switch_option
void xemucfg_define_switch_option(const char *optname, const char *help, int *storage)
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
xemu_realloc
void * xemu_realloc(void *p, size_t size)
Definition: emutools.c:235
xemutools_config_st::name
const char * name
Definition: emutools_config.h:46
xemutools_config_st::help
const char * help
Definition: emutools_config.h:48
sdl_pref_dir
char * sdl_pref_dir
Definition: emutools.c:97
FATAL
#define FATAL(...)
Definition: xep128.h:117
xemutools_config_st::opt_num
struct xemutools_config_st::@52::@56 opt_num
CONFIG_FILE_TEMPL_NAME
#define CONFIG_FILE_TEMPL_NAME
Definition: emutools_config.h:25
XEMUCFG_OPT_NUM
@ XEMUCFG_OPT_NUM
Definition: emutools_config.h:29
xemucfg_parser_callback_func_t
EMUCFG_PARSER_CALLBACK_RET_TYPE(* xemucfg_parser_callback_func_t)(EMUCFG_PARSER_CALLBACK_ARG_LIST)
Definition: emutools_config.h:37
xemutools_config_st::opt_bool
struct xemutools_config_st::@52::@55 opt_bool
xemucfg_str2bool
int xemucfg_str2bool(const char *s, int *result)
UNIX_DATADIR_0
#define UNIX_DATADIR_0
Definition: emutools_files.h:90
xemucfg_set_str
void xemucfg_set_str(char **ptr, const char *value)
buf
Uint8 buf[512]
Definition: fat32.c:155