mirror of https://github.com/aseprite/aseprite.git
1351 lines
28 KiB
C
1351 lines
28 KiB
C
/* ______ ___ ___
|
|
* /\ _ \ /\_ \ /\_ \
|
|
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
|
|
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
|
|
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
|
|
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
|
|
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
|
|
* /\____/
|
|
* \_/__/
|
|
*
|
|
* Configuration routines.
|
|
*
|
|
* By Shawn Hargreaves.
|
|
*
|
|
* Hook functions added by Martijn Versteegh.
|
|
*
|
|
* Annie Testes lifted several hardcoded length limitations.
|
|
*
|
|
* Modified by David A. Capello to be used with ASE.
|
|
*
|
|
* See readme.txt for copyright information.
|
|
*/
|
|
|
|
#include "allegro.h"
|
|
|
|
/* Only for Allegro < 4.2 */
|
|
#if ((ALLEGRO_VERSION == 4 && ALLEGRO_SUB_VERSION < 2) || (ALLEGRO_VERSION < 4))
|
|
#include "allegro/internal/aintern.h"
|
|
|
|
|
|
|
|
typedef struct CONFIG_ENTRY
|
|
{
|
|
char *name; /* variable name (NULL if comment) */
|
|
char *data; /* variable value */
|
|
struct CONFIG_ENTRY *next; /* linked list */
|
|
} CONFIG_ENTRY;
|
|
|
|
|
|
typedef struct CONFIG
|
|
{
|
|
CONFIG_ENTRY *head; /* linked list of config entries */
|
|
char *filename; /* where were we loaded from? */
|
|
int dirty; /* has our data changed? */
|
|
} CONFIG;
|
|
|
|
|
|
typedef struct CONFIG_HOOK
|
|
{
|
|
char *section; /* hooked config section info */
|
|
int (*intgetter)(AL_CONST char *name, int def);
|
|
AL_CONST char *(*stringgetter)(AL_CONST char *name, AL_CONST char *def);
|
|
void (*stringsetter)(AL_CONST char *name, AL_CONST char *value);
|
|
struct CONFIG_HOOK *next;
|
|
} CONFIG_HOOK;
|
|
|
|
|
|
#define MAX_CONFIGS 4
|
|
|
|
static CONFIG *config[MAX_CONFIGS] = { NULL, NULL, NULL, NULL };
|
|
static CONFIG *config_override = NULL;
|
|
static CONFIG *config_language = NULL;
|
|
static CONFIG *system_config = NULL;
|
|
|
|
static CONFIG_HOOK *config_hook = NULL;
|
|
|
|
static int config_installed = FALSE;
|
|
|
|
|
|
|
|
/* flush_config:
|
|
* Writes out a config structure to disk if the contents
|
|
* have changed.
|
|
*/
|
|
static void flush_config(CONFIG *cfg)
|
|
{
|
|
CONFIG_ENTRY *pos;
|
|
PACKFILE *f;
|
|
char cr[16];
|
|
|
|
usetc(cr+usetc(cr, '\n'), 0);
|
|
|
|
if (cfg && cfg->filename && cfg->dirty) {
|
|
/* write changed data to disk */
|
|
f = pack_fopen(cfg->filename, F_WRITE);
|
|
|
|
if (f) {
|
|
pos = cfg->head;
|
|
|
|
while (pos) {
|
|
if (pos->name) {
|
|
pack_fputs(pos->name, f);
|
|
|
|
if (ugetc(pos->name) != '[') {
|
|
pack_putc(' ', f);
|
|
pack_putc('=', f);
|
|
pack_putc(' ', f);
|
|
}
|
|
}
|
|
|
|
if (pos->data)
|
|
pack_fputs(pos->data, f);
|
|
|
|
pack_fputs(cr, f);
|
|
|
|
pos = pos->next;
|
|
}
|
|
|
|
pack_fclose(f);
|
|
cfg->dirty = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* flush_config_file:
|
|
* Writes out the config file to disk if the contents
|
|
* have changed.
|
|
*/
|
|
void flush_config_file(void)
|
|
{
|
|
flush_config(config[0]);
|
|
}
|
|
|
|
|
|
|
|
/* destroy_config:
|
|
* Destroys a config structure, writing it out to disk if the contents
|
|
* have changed.
|
|
*/
|
|
static void destroy_config(CONFIG *cfg)
|
|
{
|
|
CONFIG_ENTRY *pos, *prev;
|
|
|
|
if (cfg) {
|
|
flush_config(cfg);
|
|
|
|
if (cfg->filename)
|
|
free(cfg->filename);
|
|
|
|
/* destroy the variable list */
|
|
pos = cfg->head;
|
|
|
|
while (pos) {
|
|
prev = pos;
|
|
pos = pos->next;
|
|
|
|
if (prev->name)
|
|
free(prev->name);
|
|
|
|
if (prev->data)
|
|
free(prev->data);
|
|
|
|
free(prev);
|
|
}
|
|
|
|
free(cfg);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* config_cleanup:
|
|
* Called at shutdown time to free memory being used by the config routines,
|
|
* and write any changed data out to disk.
|
|
*/
|
|
static void config_cleanup(void)
|
|
{
|
|
CONFIG_HOOK *hook, *nexthook;
|
|
int i;
|
|
|
|
for (i=0; i<MAX_CONFIGS; i++) {
|
|
if (config[i]) {
|
|
destroy_config(config[i]);
|
|
config[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (config_override) {
|
|
destroy_config(config_override);
|
|
config_override = NULL;
|
|
}
|
|
|
|
if (config_language) {
|
|
destroy_config(config_language);
|
|
config_language = NULL;
|
|
}
|
|
|
|
if (system_config) {
|
|
destroy_config(system_config);
|
|
system_config = NULL;
|
|
}
|
|
|
|
if (config_hook) {
|
|
hook = config_hook;
|
|
|
|
while (hook) {
|
|
if (hook->section)
|
|
free(hook->section);
|
|
|
|
nexthook = hook->next;
|
|
free(hook);
|
|
hook = nexthook;
|
|
}
|
|
|
|
config_hook = NULL;
|
|
}
|
|
|
|
_remove_exit_func(config_cleanup);
|
|
config_installed = FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* init_config:
|
|
* Sets up the configuration routines ready for use, also loading the
|
|
* default config file if the loaddata flag is set and no other config
|
|
* file is in memory.
|
|
*/
|
|
static void init_config(int loaddata)
|
|
{
|
|
char filename[1024], tmp[128], *cfg_name;
|
|
|
|
if (!config_installed) {
|
|
_add_exit_func(config_cleanup);
|
|
config_installed = TRUE;
|
|
}
|
|
|
|
if ((loaddata) && (!config[0])) {
|
|
cfg_name = uconvert_ascii("allegro.cfg", tmp);
|
|
|
|
if (find_allegro_resource(filename, cfg_name, NULL, NULL, NULL, NULL, NULL, sizeof(filename)) != 0) {
|
|
get_executable_name(filename, sizeof(filename));
|
|
usetc(get_filename(filename), 0);
|
|
ustrzcat(filename, sizeof(filename), cfg_name);
|
|
}
|
|
|
|
set_config_file(filename);
|
|
}
|
|
|
|
if (!system_config) {
|
|
system_config = malloc(sizeof(CONFIG));
|
|
if (system_config) {
|
|
system_config->head = NULL;
|
|
system_config->filename = NULL;
|
|
system_config->dirty = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* get_line:
|
|
* Helper for splitting files up into individual lines. Returns the length
|
|
* in bytes of the sequence of characters delimited by the first EOL marker
|
|
* in the array DATA of length LENGTH, and allocates NAME and VAL to record
|
|
* the name and the value of the config entry respectively; otherwise set
|
|
* NAME to NULL and returns a copy of the line through VAL if the line was
|
|
* blank or a comment. Returns -1 and set allegro_errno on failure.
|
|
*/
|
|
static int get_line(AL_CONST char *data, int length, char **name, char **val)
|
|
{
|
|
char *buf;
|
|
int buf_size=256;
|
|
int inpos, outpos, i, j;
|
|
int c, c2, w0;
|
|
|
|
inpos = 0;
|
|
outpos = 0;
|
|
w0 = ucwidth(0);
|
|
|
|
buf = malloc(buf_size);
|
|
if (!buf) {
|
|
*allegro_errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
/* search for an EOL marker */
|
|
while (inpos<length) {
|
|
c = ugetc(data+inpos);
|
|
if ((c == '\r') || (c == '\n')) {
|
|
inpos += uwidth(data+inpos);
|
|
if (inpos < length) {
|
|
c2 = ugetc(data+inpos);
|
|
if (((c == '\r') && (c2 == '\n')) || ((c == '\n') && (c2 == '\r')))
|
|
inpos += uwidth(data+inpos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* increase the buffer size if needed */
|
|
if (outpos>=(int)buf_size-w0) {
|
|
buf_size *= 2;
|
|
buf = _al_sane_realloc(buf, buf_size);
|
|
if (!buf) {
|
|
*allegro_errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
outpos += usetc(buf+outpos, c);
|
|
inpos += uwidth(data+inpos);
|
|
}
|
|
|
|
usetc(buf+outpos, 0);
|
|
|
|
/* skip leading spaces */
|
|
i = 0;
|
|
c = ugetc(buf);
|
|
|
|
while ((c) && (uisspace(c))) {
|
|
i += uwidth(buf+i);
|
|
c = ugetc(buf+i);
|
|
}
|
|
|
|
/* read name string */
|
|
j = 0;
|
|
|
|
/* compute name length */
|
|
while ((c) && (!uisspace(c)) && (c != '=') && (c != '#')) {
|
|
j += ucwidth(c);
|
|
i += uwidth(buf+i);
|
|
c = ugetc(buf+i);
|
|
}
|
|
|
|
if (j) {
|
|
/* got a variable */
|
|
*name = malloc(j+w0);
|
|
if (!(*name)) {
|
|
*allegro_errno = ENOMEM;
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
ustrzcpy(*name, j+w0, buf+i-j);
|
|
|
|
while ((c) && ((uisspace(c)) || (c == '='))) {
|
|
i += uwidth(buf+i);
|
|
c = ugetc(buf+i);
|
|
}
|
|
|
|
*val = ustrdup(buf+i);
|
|
if (!(*val)) {
|
|
free(name);
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
/* strip trailing spaces */
|
|
i = ustrlen(*val) - 1;
|
|
while ((i >= 0) && (uisspace(ugetat(*val, i))))
|
|
usetat(*val, i--, 0);
|
|
}
|
|
else {
|
|
/* blank line or comment */
|
|
*name = NULL;
|
|
*val = ustrdup(buf);
|
|
if (!(*val)) {
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return inpos;
|
|
}
|
|
|
|
|
|
|
|
/* set_config:
|
|
* Does the work of setting up a config structure.
|
|
*/
|
|
static void set_config(CONFIG **config, AL_CONST char *data, int length, AL_CONST char *filename)
|
|
{
|
|
CONFIG_ENTRY **prev, *p;
|
|
char *name, *val;
|
|
int ret, pos;
|
|
|
|
init_config(FALSE);
|
|
|
|
if (*config) {
|
|
destroy_config(*config);
|
|
*config = NULL;
|
|
}
|
|
|
|
*config = malloc(sizeof(CONFIG));
|
|
if (!(*config)) {
|
|
*allegro_errno = ENOMEM;
|
|
return;
|
|
}
|
|
|
|
(*config)->head = NULL;
|
|
(*config)->dirty = FALSE;
|
|
|
|
if (filename) {
|
|
(*config)->filename = ustrdup(filename);
|
|
if (!(*config)->filename) {
|
|
free(*config);
|
|
*config = NULL;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
(*config)->filename = NULL;
|
|
|
|
prev = &(*config)->head;
|
|
pos = 0;
|
|
|
|
while (pos < length) {
|
|
ret = get_line(data+pos, length-pos, &name, &val);
|
|
if (ret<0) {
|
|
free(*config);
|
|
*config = NULL;
|
|
return;
|
|
}
|
|
|
|
pos += ret;
|
|
|
|
p = malloc(sizeof(CONFIG_ENTRY));
|
|
if (!p) {
|
|
*allegro_errno = ENOMEM;
|
|
free(*config);
|
|
*config = NULL;
|
|
return;
|
|
}
|
|
|
|
p->name = name;
|
|
p->data = val;
|
|
|
|
p->next = NULL;
|
|
*prev = p;
|
|
prev = &p->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* load_config_file:
|
|
* Does the work of loading a config file.
|
|
*/
|
|
static void load_config_file(CONFIG **config, AL_CONST char *filename, AL_CONST char *savefile)
|
|
{
|
|
char *tmp, *tmp2;
|
|
int length;
|
|
|
|
if (*config) {
|
|
destroy_config(*config);
|
|
*config = NULL;
|
|
}
|
|
|
|
/* Special case when allegro_init has not been called yet. */
|
|
if (!system_driver) {
|
|
set_config(config, NULL, 0, savefile);
|
|
return;
|
|
}
|
|
|
|
#if (MAKE_VERSION(4, 2, 1) < MAKE_VERSION(ALLEGRO_VERSION, \
|
|
ALLEGRO_SUB_VERSION, \
|
|
ALLEGRO_WIP_VERSION))
|
|
length = file_size(filename);
|
|
#else
|
|
length = file_size_ex(filename);
|
|
#endif
|
|
|
|
if (length > 0) {
|
|
PACKFILE *f = pack_fopen(filename, F_READ);
|
|
|
|
if (f) {
|
|
tmp = malloc(length+1);
|
|
|
|
if (tmp) {
|
|
pack_fread(tmp, length, f);
|
|
tmp[length] = 0;
|
|
|
|
if (need_uconvert(tmp, U_UTF8, U_CURRENT)) {
|
|
length = uconvert_size(tmp, U_UTF8, U_CURRENT);
|
|
tmp2 = malloc(length);
|
|
|
|
if (tmp2)
|
|
do_uconvert(tmp, U_UTF8, tmp2, U_CURRENT, length);
|
|
|
|
length -= ucwidth(0);
|
|
}
|
|
else
|
|
tmp2 = tmp;
|
|
|
|
if (tmp2) {
|
|
set_config(config, tmp2, length, savefile);
|
|
|
|
if (tmp2 != tmp)
|
|
free(tmp2);
|
|
}
|
|
|
|
free(tmp);
|
|
}
|
|
else
|
|
set_config(config, NULL, 0, savefile);
|
|
|
|
pack_fclose(f);
|
|
}
|
|
else
|
|
set_config(config, NULL, 0, savefile);
|
|
}
|
|
else
|
|
set_config(config, NULL, 0, savefile);
|
|
}
|
|
|
|
|
|
|
|
/* set_config_file:
|
|
* Sets the file to be used for all future configuration operations.
|
|
*/
|
|
void set_config_file(AL_CONST char *filename)
|
|
{
|
|
ASSERT(filename);
|
|
load_config_file(&config[0], filename, filename);
|
|
}
|
|
|
|
|
|
|
|
/* set_config_data:
|
|
* Sets the block of data to be used for all future configuration
|
|
* operations.
|
|
*/
|
|
void set_config_data(AL_CONST char *data, int length)
|
|
{
|
|
ASSERT(data);
|
|
set_config(&config[0], data, length, NULL);
|
|
}
|
|
|
|
|
|
|
|
/* override_config_file:
|
|
* Sets the file that will override all future configuration operations.
|
|
*/
|
|
void override_config_file(AL_CONST char *filename)
|
|
{
|
|
/* load other configuration file to override settings */
|
|
if (filename)
|
|
load_config_file(&config_override, filename, filename);
|
|
/* destroy the current one */
|
|
else if (config_override) {
|
|
destroy_config(config_override);
|
|
config_override = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* override_config_data:
|
|
* Sets the block of data that will override all future configuration
|
|
* operations.
|
|
*/
|
|
void override_config_data(AL_CONST char *data, int length)
|
|
{
|
|
ASSERT(data);
|
|
set_config(&config_override, data, length, NULL);
|
|
}
|
|
|
|
|
|
|
|
/* push_config_state:
|
|
* Pushes the current config state onto the stack.
|
|
*/
|
|
void push_config_state(void)
|
|
{
|
|
int i;
|
|
|
|
if (config[MAX_CONFIGS-1])
|
|
destroy_config(config[MAX_CONFIGS-1]);
|
|
|
|
for (i=MAX_CONFIGS-1; i>0; i--)
|
|
config[i] = config[i-1];
|
|
|
|
config[0] = NULL;
|
|
}
|
|
|
|
|
|
|
|
/* pop_config_state:
|
|
* Pops the current config state off the stack.
|
|
*/
|
|
void pop_config_state(void)
|
|
{
|
|
int i;
|
|
|
|
if (config[0])
|
|
destroy_config(config[0]);
|
|
|
|
for (i=0; i<MAX_CONFIGS-1; i++)
|
|
config[i] = config[i+1];
|
|
|
|
config[MAX_CONFIGS-1] = NULL;
|
|
}
|
|
|
|
|
|
|
|
/* prettify_section_name:
|
|
* Helper for ensuring that a section name is enclosed by [ ] braces.
|
|
*/
|
|
static void prettify_section_name(AL_CONST char *in, char *out, int out_size)
|
|
{
|
|
int p;
|
|
|
|
if ((in) && (ustrlen(in))) {
|
|
if (ugetc(in) != '[') {
|
|
p = usetc(out, '[');
|
|
usetc(out+p, 0);
|
|
}
|
|
else
|
|
usetc(out, 0);
|
|
|
|
ustrzcat(out, out_size - ucwidth(']'), in);
|
|
|
|
out += uoffset(out, -1);
|
|
|
|
if (ugetc(out) != ']') {
|
|
out += uwidth(out);
|
|
out += usetc(out, ']');
|
|
usetc(out, 0);
|
|
}
|
|
}
|
|
else
|
|
usetc(out, 0);
|
|
}
|
|
|
|
|
|
|
|
/* hook_config_section:
|
|
* Hooks a config section to a set of getter/setter functions. This will
|
|
* override the normal table of values, and give the provider of the hooks
|
|
* complete control over that section.
|
|
*/
|
|
void hook_config_section(AL_CONST char *section, int (*intgetter)(AL_CONST char *, int), AL_CONST char *(*stringgetter)(AL_CONST char *, AL_CONST char *), void (*stringsetter)(AL_CONST char *, AL_CONST char *))
|
|
{
|
|
CONFIG_HOOK *hook, **prev;
|
|
char section_name[256];
|
|
|
|
init_config(FALSE);
|
|
|
|
prettify_section_name(section, section_name, sizeof(section_name));
|
|
|
|
hook = config_hook;
|
|
prev = &config_hook;
|
|
|
|
while (hook) {
|
|
if (ustricmp(section_name, hook->section) == 0) {
|
|
if ((intgetter) || (stringgetter) || (stringsetter)) {
|
|
/* modify existing hook */
|
|
hook->intgetter = intgetter;
|
|
hook->stringgetter = stringgetter;
|
|
hook->stringsetter = stringsetter;
|
|
}
|
|
else {
|
|
/* remove a hook */
|
|
*prev = hook->next;
|
|
free(hook->section);
|
|
free(hook);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
prev = &hook->next;
|
|
hook = hook->next;
|
|
}
|
|
|
|
/* add a new hook */
|
|
hook = malloc(sizeof(CONFIG_HOOK));
|
|
if (!hook)
|
|
return;
|
|
|
|
hook->section = ustrdup(section_name);
|
|
if (!(hook->section)) {
|
|
free(hook);
|
|
return;
|
|
}
|
|
|
|
hook->intgetter = intgetter;
|
|
hook->stringgetter = stringgetter;
|
|
hook->stringsetter = stringsetter;
|
|
|
|
hook->next = config_hook;
|
|
config_hook = hook;
|
|
}
|
|
|
|
|
|
|
|
/* is_config_hooked:
|
|
* Checks whether a specific section is hooked in any way.
|
|
*/
|
|
int config_is_hooked(AL_CONST char *section)
|
|
{
|
|
CONFIG_HOOK *hook = config_hook;
|
|
char section_name[256];
|
|
|
|
prettify_section_name(section, section_name, sizeof(section_name));
|
|
|
|
while (hook) {
|
|
if (ustricmp(section_name, hook->section) == 0)
|
|
return TRUE;
|
|
|
|
hook = hook->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* find_config_string:
|
|
* Helper for finding an entry in the configuration file.
|
|
*/
|
|
static CONFIG_ENTRY *find_config_string(CONFIG *config, AL_CONST char *section, AL_CONST char *name, CONFIG_ENTRY **prev)
|
|
{
|
|
CONFIG_ENTRY *p;
|
|
int in_section;
|
|
|
|
if (config) {
|
|
p = config->head;
|
|
|
|
if (prev)
|
|
*prev = NULL;
|
|
|
|
if (section && ugetc(section))
|
|
in_section = FALSE;
|
|
else
|
|
in_section = TRUE;
|
|
|
|
while (p) {
|
|
if (p->name) {
|
|
if ((section) && (ugetc(p->name) == '[') && (ugetat(p->name, -1) == ']')) {
|
|
/* change section */
|
|
in_section = (ustricmp(section, p->name) == 0);
|
|
}
|
|
if ((in_section) || (ugetc(name) == '[')) {
|
|
/* is this the one? */
|
|
if (ustricmp(p->name, name) == 0)
|
|
return p;
|
|
}
|
|
}
|
|
|
|
if (prev)
|
|
*prev = p;
|
|
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_string:
|
|
* Reads a string from the configuration file.
|
|
*/
|
|
AL_CONST char *get_config_string(AL_CONST char *section, AL_CONST char *name, AL_CONST char *def)
|
|
{
|
|
char section_name[256];
|
|
CONFIG_HOOK *hook;
|
|
CONFIG_ENTRY *p;
|
|
|
|
init_config(TRUE);
|
|
|
|
prettify_section_name(section, section_name, sizeof(section_name));
|
|
|
|
/* check for hooked sections */
|
|
hook = config_hook;
|
|
|
|
while (hook) {
|
|
if (ustricmp(section_name, hook->section) == 0) {
|
|
if (hook->stringgetter)
|
|
return hook->stringgetter(name, def);
|
|
else
|
|
return def;
|
|
}
|
|
hook = hook->next;
|
|
}
|
|
|
|
/* find the string */
|
|
p = find_config_string(config_override, section_name, name, NULL);
|
|
|
|
if (!p) {
|
|
if ((ugetc(name) == '#') || ((ugetc(section_name) == '[') && (ugetat(section_name, 1) == '#')))
|
|
p = find_config_string(system_config, section_name, name, NULL);
|
|
else
|
|
p = find_config_string(config[0], section_name, name, NULL);
|
|
}
|
|
|
|
if (p && p->data && (ustrlen(p->data) != 0))
|
|
return p->data;
|
|
else
|
|
return def;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_int:
|
|
* Reads an integer from the configuration file.
|
|
*/
|
|
int get_config_int(AL_CONST char *section, AL_CONST char *name, int def)
|
|
{
|
|
CONFIG_HOOK *hook;
|
|
char section_name[256];
|
|
AL_CONST char *s;
|
|
|
|
prettify_section_name(section, section_name, sizeof(section_name));
|
|
|
|
/* check for hooked sections */
|
|
hook = config_hook;
|
|
|
|
while (hook) {
|
|
if (ustricmp(section_name, hook->section) == 0) {
|
|
if (hook->intgetter) {
|
|
return hook->intgetter(name, def);
|
|
}
|
|
else if (hook->stringgetter) {
|
|
s = hook->stringgetter(name, NULL);
|
|
if ((s) && (ugetc(s)))
|
|
return ustrtol(s, NULL, 0);
|
|
else
|
|
return def;
|
|
}
|
|
else
|
|
return def;
|
|
}
|
|
hook = hook->next;
|
|
}
|
|
|
|
/* read normal data */
|
|
s = get_config_string(section_name, name, NULL);
|
|
|
|
if ((s) && (ugetc(s)))
|
|
return ustrtol(s, NULL, 0);
|
|
|
|
return def;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_hex:
|
|
* Reads a hexadecimal integer from the configuration file.
|
|
*/
|
|
int get_config_hex(AL_CONST char *section, AL_CONST char *name, int def)
|
|
{
|
|
AL_CONST char *s = get_config_string(section, name, NULL);
|
|
char tmp[64];
|
|
int i;
|
|
|
|
if ((s) && (ugetc(s))) {
|
|
i = ustrtol(s, NULL, 16);
|
|
if ((i == 0x7FFFFFFF) && (ustricmp(s, uconvert_ascii("7FFFFFFF", tmp)) != 0))
|
|
i = -1;
|
|
return i;
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_float:
|
|
* Reads a float from the configuration file.
|
|
*/
|
|
float get_config_float(AL_CONST char *section, AL_CONST char *name, float def)
|
|
{
|
|
AL_CONST char* s = get_config_string(section, name, NULL);
|
|
|
|
if ((s) && (ugetc(s)))
|
|
return uatof(s);
|
|
|
|
return def;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_id:
|
|
* Reads a driver ID number from the configuration file.
|
|
*/
|
|
int get_config_id(AL_CONST char *section, AL_CONST char *name, int def)
|
|
{
|
|
AL_CONST char *s = get_config_string(section, name, NULL);
|
|
char tmp[4];
|
|
char* endp;
|
|
int val, i;
|
|
|
|
if ((s) && (ugetc(s))) {
|
|
val = ustrtol(s, &endp, 0);
|
|
if (!ugetc(endp))
|
|
return val;
|
|
|
|
tmp[0] = tmp[1] = tmp[2] = tmp[3] = ' ';
|
|
|
|
for (i=0; i<4; i++) {
|
|
if (ugetat(s, i))
|
|
tmp[i] = utoupper(ugetat(s ,i));
|
|
else
|
|
break;
|
|
}
|
|
|
|
return AL_ID(tmp[0], tmp[1], tmp[2], tmp[3]);
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
|
|
|
|
/* get_config_argv:
|
|
* Reads an argc/argv style token list from the configuration file.
|
|
*/
|
|
char **get_config_argv(AL_CONST char *section, AL_CONST char *name, int *argc)
|
|
{
|
|
#define MAX_ARGV 16
|
|
|
|
static char *buf = NULL;
|
|
static int buf_size = 0;
|
|
static char *argv[MAX_ARGV];
|
|
int pos, ac, q, c;
|
|
int s_size;
|
|
|
|
AL_CONST char *s = get_config_string(section, name, NULL);
|
|
|
|
if (!s) {
|
|
*argc = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/* increase the buffer size if needed */
|
|
s_size = ustrsizez(s);
|
|
if (s_size>buf_size) {
|
|
buf_size = s_size;
|
|
buf = _al_sane_realloc(buf, buf_size);
|
|
if (!buf) {
|
|
*allegro_errno = ENOMEM;
|
|
*argc = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ustrzcpy(buf, buf_size, s);
|
|
pos = 0;
|
|
ac = 0;
|
|
|
|
c = ugetc(buf);
|
|
|
|
while ((ac<MAX_ARGV) && (c) && (c != '#')) {
|
|
while ((c) && (uisspace(c))) {
|
|
pos += ucwidth(c);
|
|
c = ugetc(buf+pos);
|
|
}
|
|
|
|
if ((c) && (c != '#')) {
|
|
if ((c == '\'') || (c == '"')) {
|
|
q = c;
|
|
pos += ucwidth(c);
|
|
c = ugetc(buf+pos);
|
|
}
|
|
else
|
|
q = 0;
|
|
|
|
argv[ac++] = buf+pos;
|
|
|
|
while ((c) && ((q) ? (c != q) : (!uisspace(c)))) {
|
|
pos += ucwidth(c);
|
|
c = ugetc(buf+pos);
|
|
}
|
|
|
|
if (c) {
|
|
usetat(buf+pos, 0, 0);
|
|
pos += ucwidth(0);
|
|
c = ugetc(buf+pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
*argc = ac;
|
|
return argv;
|
|
}
|
|
|
|
|
|
|
|
/* insert_variable:
|
|
* Helper for inserting a new variable into a configuration file.
|
|
*/
|
|
static CONFIG_ENTRY *insert_variable(CONFIG *the_config, CONFIG_ENTRY *p, AL_CONST char *name, AL_CONST char *data)
|
|
{
|
|
CONFIG_ENTRY *n = malloc(sizeof(CONFIG_ENTRY));
|
|
|
|
if (!n)
|
|
return NULL;
|
|
|
|
if (name)
|
|
n->name = ustrdup(name);
|
|
else
|
|
n->name = NULL;
|
|
|
|
if (data)
|
|
n->data = ustrdup(data);
|
|
else
|
|
n->data = NULL;
|
|
|
|
if (p) {
|
|
n->next = p->next;
|
|
p->next = n;
|
|
}
|
|
else {
|
|
n->next = NULL;
|
|
the_config->head = n;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
|
|
/* set_config_string:
|
|
* Writes a string to the configuration file.
|
|
*/
|
|
void set_config_string(AL_CONST char *section, AL_CONST char *name, AL_CONST char *val)
|
|
{
|
|
CONFIG *the_config;
|
|
CONFIG_HOOK *hook;
|
|
CONFIG_ENTRY *p, *prev;
|
|
char section_name[256];
|
|
|
|
init_config(TRUE);
|
|
|
|
prettify_section_name(section, section_name, sizeof(section_name));
|
|
|
|
/* check for hooked sections */
|
|
hook = config_hook;
|
|
|
|
while (hook) {
|
|
if (ustricmp(section_name, hook->section) == 0) {
|
|
if (hook->stringsetter)
|
|
hook->stringsetter(name, val);
|
|
return;
|
|
}
|
|
hook = hook->next;
|
|
}
|
|
|
|
/* decide which config file to use */
|
|
if ((ugetc(name) == '#') || ((ugetc(section_name) == '[') && (ugetat(section_name, 1) == '#')))
|
|
the_config = system_config;
|
|
else if (config_override)
|
|
the_config = config_override;
|
|
else
|
|
the_config = config[0];
|
|
|
|
if (the_config) {
|
|
p = find_config_string(the_config, section_name, name, &prev);
|
|
|
|
if (p) {
|
|
if ((val) && (ugetc(val))) {
|
|
/* modify existing variable */
|
|
if (p->data)
|
|
free(p->data);
|
|
|
|
p->data = ustrdup(val);
|
|
}
|
|
else {
|
|
/* delete variable */
|
|
if (p->name)
|
|
free(p->name);
|
|
|
|
if (p->data)
|
|
free(p->data);
|
|
|
|
if (prev)
|
|
prev->next = p->next;
|
|
else
|
|
the_config->head = p->next;
|
|
|
|
free(p);
|
|
}
|
|
}
|
|
else {
|
|
if ((val) && (ugetc(val))) {
|
|
/* add a new variable */
|
|
if (ugetc(section_name)) {
|
|
p = find_config_string(the_config, NULL, section_name, &prev);
|
|
|
|
if (!p) {
|
|
/* create a new section */
|
|
p = the_config->head;
|
|
while ((p) && (p->next))
|
|
p = p->next;
|
|
|
|
if ((p) && (p->data) && (ugetc(p->data)))
|
|
p = insert_variable(the_config, p, NULL, NULL);
|
|
|
|
p = insert_variable(the_config, p, section_name, NULL);
|
|
}
|
|
|
|
/* append to the end of the section */
|
|
while ((p) && (p->next) &&
|
|
(((p->next->name) && (ugetc(p->next->name))) ||
|
|
((p->next->data) && (ugetc(p->next->data)))))
|
|
p = p->next;
|
|
|
|
p = insert_variable(the_config, p, name, val);
|
|
}
|
|
else {
|
|
/* global variable */
|
|
p = the_config->head;
|
|
insert_variable(the_config, NULL, name, val);
|
|
the_config->head->next = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
the_config->dirty = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* set_config_int:
|
|
* Writes an integer to the configuration file.
|
|
*/
|
|
void set_config_int(AL_CONST char *section, AL_CONST char *name, int val)
|
|
{
|
|
char buf[32], tmp[32];
|
|
uszprintf(buf, sizeof(buf), uconvert_ascii("%d", tmp), val);
|
|
set_config_string(section, name, buf);
|
|
}
|
|
|
|
|
|
|
|
/* set_config_hex:
|
|
* Writes a hexadecimal integer to the configuration file.
|
|
*/
|
|
void set_config_hex(AL_CONST char *section, AL_CONST char *name, int val)
|
|
{
|
|
char buf[32], tmp[32];
|
|
|
|
if (val >= 0) {
|
|
uszprintf(buf, sizeof(buf), uconvert_ascii("%X", tmp), val);
|
|
set_config_string(section, name, buf);
|
|
}
|
|
else
|
|
set_config_string(section, name, uconvert_ascii("-1", buf));
|
|
}
|
|
|
|
|
|
|
|
/* set_config_float:
|
|
* Writes a float to the configuration file.
|
|
*/
|
|
void set_config_float(AL_CONST char *section, AL_CONST char *name, float val)
|
|
{
|
|
char buf[32], tmp[32];
|
|
uszprintf(buf, sizeof(buf), uconvert_ascii("%f", tmp), val);
|
|
set_config_string(section, name, buf);
|
|
}
|
|
|
|
|
|
|
|
/* set_config_id:
|
|
* Writes a driver ID to the configuration file.
|
|
*/
|
|
void set_config_id(AL_CONST char *section, AL_CONST char *name, int val)
|
|
{
|
|
char buf[32], tmp[32];
|
|
int v[4];
|
|
int pos = 0;
|
|
int i;
|
|
|
|
if (val < 256) {
|
|
uszprintf(buf, sizeof(buf), uconvert_ascii("%d", tmp), val);
|
|
}
|
|
else {
|
|
v[0] = (val>>24)&0xFF;
|
|
v[1] = (val>>16)&0xFF;
|
|
v[2] = (val>>8)&0xFF;
|
|
v[3] = val&0xFF;
|
|
|
|
for (i=0; (i<4) && (v[i]) && (v[i] != ' '); i++)
|
|
pos += usetc(buf+pos, v[i]);
|
|
|
|
usetc(buf+pos, 0);
|
|
}
|
|
|
|
set_config_string(section, name, buf);
|
|
}
|
|
|
|
|
|
|
|
/* reload_config_texts:
|
|
* Reads in a block of translated system text, looking for either a
|
|
* user-specified file, a ??text.cfg file, or a language.dat#??TEXT_CFG
|
|
* datafile object. If new_language is not NULL, the language config
|
|
* variable will be set to new_language before reloading the
|
|
* configuration files.
|
|
*/
|
|
void reload_config_texts(AL_CONST char *new_language)
|
|
{
|
|
char filename[1024], tmp1[128], tmp2[128];
|
|
AL_CONST char *name, *ext, *datafile;
|
|
char *namecpy;
|
|
|
|
if (config_language) {
|
|
destroy_config(config_language);
|
|
config_language = NULL;
|
|
}
|
|
|
|
if (new_language)
|
|
set_config_string("system", "language", new_language);
|
|
|
|
name = get_config_string(uconvert_ascii("system", tmp1), uconvert_ascii("language", tmp2), NULL);
|
|
|
|
if ((name) && (ugetc(name))) {
|
|
namecpy = ustrdup(name);
|
|
ustrlwr (namecpy);
|
|
if ((ustrlen(namecpy)<4) || (ustricmp(namecpy+uoffset(namecpy, -4), uconvert_ascii("text", tmp1)) != 0))
|
|
ext = uconvert_ascii("text.cfg", tmp1);
|
|
else
|
|
ext = uconvert_ascii(".cfg", tmp1);
|
|
|
|
datafile = uconvert_ascii("language.dat", tmp2);
|
|
|
|
if (find_allegro_resource(filename, namecpy, ext, datafile, NULL, NULL, NULL, sizeof(filename)) == 0) {
|
|
free(namecpy);
|
|
load_config_file(&config_language, filename, NULL);
|
|
return;
|
|
}
|
|
|
|
free(namecpy);
|
|
}
|
|
|
|
config_language = malloc(sizeof(CONFIG));
|
|
if (config_language ) {
|
|
config_language ->head = NULL;
|
|
config_language ->filename = NULL;
|
|
config_language ->dirty = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* get_config_text:
|
|
* Looks up a translated version of the specified English string,
|
|
* returning a suitable message in the current language if one is
|
|
* available, or a copy of the parameter if no translation can be found.
|
|
*/
|
|
AL_CONST char *get_config_text(AL_CONST char *msg)
|
|
{
|
|
char tmp1[256];
|
|
AL_CONST char *section = uconvert_ascii("[language]", tmp1);
|
|
AL_CONST char *umsg;
|
|
AL_CONST char *s;
|
|
AL_CONST char *ret = NULL;
|
|
char *name;
|
|
CONFIG_HOOK *hook;
|
|
CONFIG_ENTRY *p;
|
|
int c, pos, size;
|
|
ASSERT(msg);
|
|
|
|
init_config(TRUE);
|
|
|
|
/* allocate memory and convert message to current encoding format */
|
|
if (need_uconvert(msg, U_ASCII, U_CURRENT)) {
|
|
size = uconvert_size(msg, U_ASCII, U_CURRENT);
|
|
umsg = malloc(size);
|
|
if (!umsg) {
|
|
*allegro_errno = ENOMEM;
|
|
return empty_string;
|
|
}
|
|
|
|
name = malloc(size);
|
|
if (!name) {
|
|
free((char *)umsg); /* remove constness */
|
|
*allegro_errno = ENOMEM;
|
|
return empty_string;
|
|
}
|
|
|
|
do_uconvert(msg, U_ASCII, (char*)umsg, U_CURRENT, size);
|
|
}
|
|
else {
|
|
umsg = msg;
|
|
name = malloc(ustrsizez(msg));
|
|
if (!name) {
|
|
*allegro_errno = ENOMEM;
|
|
return empty_string;
|
|
}
|
|
}
|
|
|
|
s = umsg;
|
|
pos = 0;
|
|
|
|
while ((c = ugetxc(&s)) != 0) {
|
|
if ((uisspace(c)) || (c == '=') || (c == '#'))
|
|
pos += usetc(name+pos, '_');
|
|
else
|
|
pos += usetc(name+pos, c);
|
|
}
|
|
|
|
usetc(name+pos, 0);
|
|
|
|
/* check for hooked sections */
|
|
hook = config_hook;
|
|
|
|
while (hook) {
|
|
if (ustricmp(section, hook->section) == 0) {
|
|
if (hook->stringgetter) {
|
|
ret = hook->stringgetter(name, umsg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
hook = hook->next;
|
|
}
|
|
|
|
if (!ret) {
|
|
/* find the string */
|
|
p = find_config_string(config_override, section, name, NULL);
|
|
|
|
if (!p) {
|
|
p = find_config_string(config[0], section, name, NULL);
|
|
|
|
if (!p)
|
|
p = find_config_string(config_language, section, name, NULL);
|
|
}
|
|
|
|
if (p) {
|
|
ret = (p->data ? p->data : empty_string);
|
|
}
|
|
else {
|
|
/* no translation, so store off this value in the file */
|
|
p = config_language->head;
|
|
insert_variable(config_language, NULL, name, umsg);
|
|
config_language->head->next = p;
|
|
ret = config_language->head->data;
|
|
}
|
|
}
|
|
|
|
/* free memory */
|
|
if (umsg!=msg)
|
|
free((char*) umsg); /* remove constness */
|
|
|
|
free(name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|