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!"
37 static int original_argc = 0;
38 static char **original_argv = NULL;
39 static const char *original_program_name =
"?";
44 static int configdb_finalized = 0;
45 static int config_file_was_never_open = 1;
47 static const char CONFIGDB_ERROR_MSG[] =
"Xemu internal ConfigDB error:";
53 size_t len = strlen(
value);
56 memcpy(*ptr,
value, len);
68 if (*
p->opt_str.pp == NULL)
69 return p->opt_str.defval == NULL ? 1 : 0;
70 if (
p->opt_str.defval == NULL)
72 return !strcmp(*
p->opt_str.pp,
p->opt_str.defval);
78 static char error_str[128];
82 FATAL(
"%s option %s gets value to set as NULL", CONFIGDB_ERROR_MSG,
p->name);
96 if (i >
p->opt_num.max) {
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
108 if (i != 0 && i != 1) {
110 snprintf(error_str,
sizeof error_str,
"requires boolean value 0 or 1, your value was %d", i);
112 *(
int*)
p->opt_bool.p = !!i;
117 f =
p->opt_float.min;
120 if (f >
p->opt_float.max) {
121 f =
p->opt_float.max;
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
137 if (!configdb_finalized)
138 FATAL(
"%s Invalid default value at defining option: %s", CONFIGDB_ERROR_MSG, error_str);
152 if (!strncasecmp(s,
"0x", 2)) {
155 }
else if (s[0] ==
'$') {
158 }
else if (!strncasecmp(s,
"0b", 2)) {
161 }
else if (s[0] ==
'%') {
164 }
else if (s[0] !=
'\0' && !strcasecmp(s + strlen(s) - 1,
"h")) {
166 }
else if (!strncasecmp(s,
"0o", 2)) {
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")))
187 double res = strtod(s, &endptr);
189 if (((res == HUGE_VAL || res == -HUGE_VAL || res == 0) && errno == ERANGE) || res <= (
double)INT_MIN || res >= (
double)INT_MAX || endptr == s || *endptr !=
'\0')
198 if (!strcasecmp(s,
"yes") || !strcasecmp(s,
"on") || !strcmp(s,
"1") || !strcasecmp(s,
"true")) {
202 if (!strcasecmp(s,
"no") || !strcasecmp(s,
"off") || !strcmp(s,
"0") || !strcasecmp(s,
"false")) {
210 static const char *set_option_value_from_string (
struct xemutools_config_st *
p,
const char *
value,
const char *optname_fullspec )
218 return set_option_value(
p, (
void*)
value);
221 return "integer value has invalid syntax";
222 return set_option_value(
p, &i);
225 return "float value has invalid syntax";
226 return set_option_value(
p, &f);
229 return "boolean value must be yes/on/1/true OR no/off/0/false";
230 return set_option_value(
p, &i);
235 return p->opt_proc.p(
p, optname_fullspec,
value);
237 return "unknown code-path";
241 static void define_core_options (
void );
248 static int core_definitions = 0;
249 if (!core_definitions) {
252 core_definitions = 1;
253 define_core_options();
255 if (configdb_finalized)
256 FATAL(
"%s cannot define new option '%s', DB is already finalized!", CONFIGDB_ERROR_MSG, optname);
258 FATAL(
"%s optname = NULL for define_new_option()", CONFIGDB_ERROR_MSG);
260 FATAL(
"%s optname is too long string for define_new_option() at defining option %s", CONFIGDB_ERROR_MSG, optname);
262 FATAL(
"%s storage = NULL for define_new_option() at defining option %s", CONFIGDB_ERROR_MSG, optname);
265 config_head = config_current;
266 config_current->
next = NULL;
271 int ret = strcasecmp(optname,
p->name);
273 FATAL(
"%s trying to re-define option '%s'", CONFIGDB_ERROR_MSG, optname);
275 config_current->
next =
p;
277 p_prev->next = config_current;
279 config_head = config_current;
286 p->next = config_current;
287 config_current->
next = NULL;
293 static const char no_help[] =
"(no help)";
294 config_current->
name = optname;
297 config_current->
flags = 0;
298 config_current->
opt_ANY.p = storage;
300 *config_current->
opt_str.pp = NULL;
310 (void)set_option_value(config_current, &
defval);
319 static void xemucfg_define_dummy_option (
const char *optname,
const char *
help,
int flags )
321 static int dummy = 0;
324 config_current->
opt_bool.defval = 0;
329 (void)set_option_value(config_current,
defval);
336 FATAL(
"%s cannot define new option '%s', given numeric range is irreal: %d ... %d", CONFIGDB_ERROR_MSG, optname,
min,
max);
340 (void)set_option_value(config_current, &
defval);
347 FATAL(
"%s cannot define new option '%s', given float range is irreal: %f ... %f", CONFIGDB_ERROR_MSG, optname,
min,
max);
351 (void)set_option_value(config_current, &
defval);
363 for (
int i = 0;
p[i].optname; i++)
367 for (
int i = 0;
p[i].optname; i++)
371 for (
int i = 0;
p[i].optname; i++)
375 for (
int i = 0;
p[i].optname; i++)
379 for (
int i = 0;
p[i].optname; i++)
383 for (
int i = 0;
p[i].optname; i++)
391 char *s = strchr(
name,
'@');
392 int l = s ? s -
name : strlen(
name);
394 if (!strncasecmp(
name,
p->name, l) &&
p->name[l] == 0 && (
name[l] == 0 ||
name[l] ==
'@'))
402 static void dump_help (
void )
404 printf(
"Available command line options:" NL NL);
421 printf(
"-%-16s %s%s" NL,
p->name, t,
p->help);
423 printf(
NL "Bool(-ean) options can be written without parameter if it means (1/on/yes/true)." NL);
427 static char *get_config_string_representation (
const char *initial_part )
429 char *out =
xemu_strdup(initial_part ? initial_part :
"");
433 char buffer[256 + PATH_MAX];
440 r = snprintf(buffer,
sizeof buffer,
NL "## %s" NL "## (string param)" NL "%s%s = \"%s\"" NL,
442 is_str_opt_default(
p) ?
"#" :
"",
444 *
p->opt_str.pp ? *
p->opt_str.pp :
""
448 r = snprintf(buffer,
sizeof buffer,
NL "## %s" NL "## (boolean param)" NL "%s%s = %s" NL,
450 *
p->opt_bool.p ==
p->opt_bool.defval ?
"#" :
"",
452 *
p->opt_bool.p ?
"true" :
"false"
456 r = snprintf(buffer,
sizeof buffer,
NL "## %s" NL "## (integer param)" NL "%s%s = %d" NL,
458 *
p->opt_num.p ==
p->opt_num.defval ?
"#" :
"",
464 r = snprintf(buffer,
sizeof buffer,
NL "## %s" NL "## (real-num param)" NL "%s%s = %f" NL,
466 *
p->opt_float.p ==
p->opt_float.defval ?
"#" :
"",
472 r = snprintf(buffer,
sizeof buffer,
NL "## %s" NL "## (SPECIAL)" NL "#%s" NL,
478 if (
r >=
sizeof(buffer) - 1) {
480 FATAL(
"%s Too large result for dumping config option '%s'!", CONFIGDB_ERROR_MSG,
p->name);
482 out =
xemu_realloc(out, strlen(out) + strlen(buffer) + 1);
483 strcpy(out + strlen(out), buffer);
487 FATAL(
"%s Too large config file generated in %s", CONFIGDB_ERROR_MSG, __func__);
495 char *out = get_config_string_representation(initial_part);
496 int ret = out[0] ?
xemu_save_file(filename, out, strlen(out), cry) : 0;
502 static char *get_config_template_string_representation (
const char *template_fn,
const char *default_fn )
506 "# Config template for XEMU/%s %s (%s) file %s" 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
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
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
539 return get_config_string_representation(templ);
543 static inline void finalize_configdb (
void )
545 configdb_finalized = 1;
556 config_file_was_never_open = 0;
558 if (strlen(cfgmem) != lineno) {
567 while (*
p ==
' ' || *
p ==
'\t')
570 p1 = strstr(
p,
"\r\n");
573 p1 = strstr(
p,
"\n\r");
576 p1 = strchr(
p,
'\n');
579 p1 = strchr(
p,
'\r');
580 pn = p1 ? p1 + 1 : NULL;
589 while (p1 >
p && (p1[-1] ==
'\t' || p1[-1] ==
' '))
596 while (*p1 && *p1 !=
'\t' && *p1 !=
' ' && *p1 !=
'=')
600 while (*p1 ==
'\t' || *p1 ==
' ' || *p1 ==
'=')
606 DEBUG(
"Line#%d = \"%s\",\"%s\"" NL, lineno,
p, p1 ? p1 :
"<no-specified>");
607 o = search_option(
p);
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);
624 if (!strcmp(p1,
"<NULL>"))
631 size_t i = strlen(p1);
632 if (i && p1[i - 1] ==
'"')
636 s = set_option_value_from_string(o, p1,
p);
650 #define OPT_ERROR_CMDLINE(...) do { ERROR_WINDOW("Command line error: " __VA_ARGS__); return 1; } while(0)
653 static int parse_commandline (
int argc,
char **argv )
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);
663 OPT_ERROR_CMDLINE(
"Unknown option '%s'", *argv);
665 OPT_ERROR_CMDLINE(
"'%s' must be the first option", *argv);
669 (argc && argv[0][0] ==
'-') ||
672 set_option_value(
p, (
void*)&
ONE_INT);
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);
678 OPT_ERROR_CMDLINE(
"Option '%s' error:\n%s",
p->name, s);
684 DEBUGPRINT(
"CFG: CLI parsing is done without error." NL);
689 static const char *custom_config_file_reader (
struct xemutools_config_st *opt,
const char *optname,
const char *optvalue )
696 static const char skipconfigfile_option_name[] =
"skipconfigfile";
699 static void define_core_options (
void )
716 *exec_name_ptr = original_program_name;
718 *argc_ptr = original_argc;
720 *argv_ptr = original_argv;
726 static char *storage = NULL;
736 static const char *xemucfg_get_template_config_file_name (
void )
738 static char *storage = NULL;
751 original_program_name = argv[0];
758 if (argc && !strncmp(argv[0],
"-psn_", 5) && !macos_gui_started) {
759 DEBUGPRINT(
"MACOS: -psn MacOS-madness detected, throwing in some workaround ..." NL);
762 macos_gui_started = 1;
769 original_argc = argc;
770 original_argv = argv;
773 int skip_config_file = 0;
775 const char *
p = argv[0];
776 while (*
p ==
'-' || *
p ==
'/' || *
p ==
'\\')
778 if (!strcasecmp(
p,
"h") || !strcasecmp(
p,
"help") || !strcasecmp(
p,
"?")) {
781 }
else if (!strcasecmp(
p,
"v") || !strcasecmp(
p,
"ver") || !strcasecmp(
p,
"version")) {
782 DEBUGPRINT(
"Exiting, because only version information was requested." NL);
784 }
else if (!strcasecmp(
p, skipconfigfile_option_name)) {
787 skip_config_file = 1;
798 const char *cfg_template_fn = xemucfg_get_template_config_file_name();
802 char *config_template_string = get_config_template_string_representation(cfg_template_fn + 1, cfg_default_fn + 1);
806 if (!skip_config_file) {
808 DEBUGPRINT(
"CFG: Default config file %s cannot be used" NL, cfg_default_fn);
810 DEBUGPRINT(
"CFG: Default config file %s has been used" NL, cfg_default_fn);
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
824 xemu_save_file(cfg_default_fn, empty_config_file_str,
size,
"Cannot save default config file");
827 DEBUGPRINT(
"CFG: Default config file is SKIPPED by user request!" NL);
829 const int ret = parse_commandline(argc, argv);
832 xemu_save_file(cfg_template_fn, config_template_string, strlen(config_template_string),
"Cannot save config template");
834 free(config_template_string);
844 char buffer[strlen(
value) + 1], *
p;
845 strcpy(buffer,
value);
846 p = strtok(buffer, delims);
850 result[num++] = atoi(
p);
851 p = strtok(NULL, delims);
857 #ifndef XEMU_RELEASE_BUILD
860 DEBUGPRINT(
"CFG: --- test dump of ConfigDB follows: %s" NL, msg ? msg :
"<no-info>");
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);
867 DEBUGPRINT(
"CFG: %s %p %d [%s]" NL,
p->name,
p->opt_bool.p, *
p->opt_bool.p,
p->help);
870 DEBUGPRINT(
"CFG: %s %p %d [%s]" NL,
p->name,
p->opt_num.p, *
p->opt_num.p,
p->help);
873 DEBUGPRINT(
"CFG: %s %p %f [%s]" NL,
p->name,
p->opt_float.p, *
p->opt_float.p,
p->help);