For whatever fucking reason, glibc's functions dealing with decimal numbers apparently can only accept either commas or dots in strings, but not both. Meanwhile, both Windows and macOS have no apparent issues accepting both. I will never understand why they decided to even consider such behaviour acceptable, especially since those ARE used for parsing decimal numbers in many programs, but I guess it's their own version of Not Invented Here syndrome that they (or anyone else) can't be bothered to deal with. This is not how good C standard libraries are written, at all.
1042 lines
24 KiB
C
1042 lines
24 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Configuration file handler.
|
|
*
|
|
*
|
|
*
|
|
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
* Overdoze,
|
|
* David Hrdlička, <hrdlickadavid@outlook.com>
|
|
*
|
|
* Copyright 2008-2019 Sarah Walker.
|
|
* Copyright 2016-2019 Miran Grca.
|
|
* Copyright 2017-2019 Fred N. van Kempen.
|
|
* Copyright 2018-2019 David Hrdlička.
|
|
*
|
|
* NOTE: Forcing config files to be in Unicode encoding breaks
|
|
* it on Windows XP, and possibly also Vista. Use the
|
|
* -DANSI_CFG for use on these systems.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <wctype.h>
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/ini.h>
|
|
#include <86box/plat.h>
|
|
|
|
typedef struct _list_ {
|
|
struct _list_ *next;
|
|
} list_t;
|
|
|
|
typedef struct section_t {
|
|
list_t list;
|
|
|
|
char name[128];
|
|
|
|
list_t entry_head;
|
|
} section_t;
|
|
|
|
typedef struct entry_t {
|
|
list_t list;
|
|
|
|
char name[128];
|
|
char data[512];
|
|
wchar_t wdata[512];
|
|
} entry_t;
|
|
|
|
#define list_add(new, head) \
|
|
{ \
|
|
list_t *next = head; \
|
|
\
|
|
while (next->next != NULL) \
|
|
next = next->next; \
|
|
\
|
|
(next)->next = new; \
|
|
(new)->next = NULL; \
|
|
}
|
|
|
|
#define list_delete(old, head) \
|
|
{ \
|
|
list_t *next = head; \
|
|
\
|
|
while ((next)->next != old) { \
|
|
next = (next)->next; \
|
|
} \
|
|
\
|
|
(next)->next = (old)->next; \
|
|
if ((next) == (head)) \
|
|
(head)->next = (old)->next; \
|
|
}
|
|
|
|
#ifdef ENABLE_INI_LOG
|
|
int ini_do_log = ENABLE_INI_LOG;
|
|
|
|
static void
|
|
ini_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (ini_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define ini_log(fmt, ...)
|
|
#endif
|
|
|
|
static section_t *
|
|
find_section(list_t *head, const char *name)
|
|
{
|
|
section_t *sec = (section_t *) head->next;
|
|
const char blank[] = "";
|
|
|
|
if (name == NULL)
|
|
name = blank;
|
|
|
|
while (sec != NULL) {
|
|
if (!strncmp(sec->name, name, sizeof(sec->name)))
|
|
return sec;
|
|
|
|
sec = (section_t *) sec->list.next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ini_section_t
|
|
ini_find_section(ini_t ini, const char *name)
|
|
{
|
|
if (ini == NULL)
|
|
return NULL;
|
|
|
|
return (ini_section_t) find_section((list_t *) ini, name);
|
|
}
|
|
|
|
void
|
|
ini_rename_section(ini_section_t section, const char *name)
|
|
{
|
|
section_t *sec = (section_t *) section;
|
|
|
|
if (sec == NULL)
|
|
return;
|
|
|
|
memset(sec->name, 0x00, sizeof(sec->name));
|
|
memcpy(sec->name, name, MIN(128, strlen(name) + 1));
|
|
}
|
|
|
|
static entry_t *
|
|
find_entry(section_t *section, const char *name)
|
|
{
|
|
entry_t *ent;
|
|
|
|
ent = (entry_t *) section->entry_head.next;
|
|
|
|
while (ent != NULL) {
|
|
if (!strncmp(ent->name, name, sizeof(ent->name)))
|
|
return ent;
|
|
|
|
ent = (entry_t *) ent->list.next;
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
ini_has_entry(ini_section_t self, const char *name)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
|
|
if (section == NULL)
|
|
return 0;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
entries_num(section_t *section)
|
|
{
|
|
entry_t *ent;
|
|
int i = 0;
|
|
|
|
ent = (entry_t *) section->entry_head.next;
|
|
|
|
while (ent != NULL) {
|
|
if (strlen(ent->name) > 0)
|
|
i++;
|
|
|
|
ent = (entry_t *) ent->list.next;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
delete_section_if_empty(list_t *head, section_t *section)
|
|
{
|
|
if (section == NULL)
|
|
return;
|
|
|
|
int n = entries_num(section);
|
|
|
|
if (n > 0) {
|
|
int i = 0;
|
|
entry_t *i_ent = (entry_t *) section->entry_head.next;
|
|
|
|
while (i_ent != NULL) {
|
|
int i_nlen = strlen(i_ent->name);
|
|
entry_t* i_next = (entry_t *) i_ent->list.next;
|
|
|
|
if (i_nlen > 0) {
|
|
int j = 0;
|
|
entry_t *j_ent = (entry_t *) section->entry_head.next;
|
|
|
|
while (j_ent != NULL) {
|
|
int j_nlen = strlen(j_ent->name);
|
|
entry_t* j_next = (entry_t *) j_ent->list.next;
|
|
if (j_nlen > 0) {
|
|
if ((j != i) && (strcmp(j_ent->name, i_ent->name) > 0)) {
|
|
entry_t t_ent = { 0 };
|
|
memcpy(&t_ent, j_ent, sizeof(entry_t));
|
|
/* J: Contents of I, list of J */
|
|
memcpy(j_ent->name, i_ent->name, sizeof(entry_t) - sizeof(i_ent->list));
|
|
/* I: Contents of J, list of I */
|
|
memcpy(i_ent->name, t_ent.name, sizeof(entry_t) - sizeof(i_ent->list));
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
j_ent = (entry_t *) j_next;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
i_ent = (entry_t *) i_next;
|
|
}
|
|
} else {
|
|
list_delete(§ion->list, head);
|
|
free(section);
|
|
}
|
|
}
|
|
|
|
void
|
|
ini_delete_section_if_empty(ini_t ini, ini_section_t section)
|
|
{
|
|
if (ini == NULL || section == NULL)
|
|
return;
|
|
|
|
delete_section_if_empty((list_t *) ini, (section_t *) section);
|
|
}
|
|
|
|
static section_t *
|
|
create_section(list_t *head, const char *name)
|
|
{
|
|
section_t *ns = calloc(1, sizeof(section_t));
|
|
|
|
memcpy(ns->name, name, strlen(name) + 1);
|
|
list_add(&ns->list, head);
|
|
|
|
return ns;
|
|
}
|
|
|
|
ini_section_t
|
|
ini_find_or_create_section(ini_t ini, const char *name)
|
|
{
|
|
if (ini == NULL)
|
|
return NULL;
|
|
|
|
section_t *section = find_section((list_t *) ini, name);
|
|
if (section == NULL)
|
|
section = create_section((list_t *) ini, name);
|
|
|
|
return (ini_section_t) section;
|
|
}
|
|
|
|
static entry_t *
|
|
create_entry(section_t *section, const char *name)
|
|
{
|
|
entry_t *ne = calloc(1, sizeof(entry_t));
|
|
|
|
memcpy(ne->name, name, strlen(name) + 1);
|
|
list_add(&ne->list, §ion->entry_head);
|
|
|
|
return ne;
|
|
}
|
|
|
|
void
|
|
ini_close(ini_t ini)
|
|
{
|
|
section_t *sec;
|
|
section_t *ns;
|
|
entry_t *ent;
|
|
list_t *list = (list_t *) ini;
|
|
|
|
if (list == NULL)
|
|
return;
|
|
|
|
sec = (section_t *) list->next;
|
|
while (sec != NULL) {
|
|
ns = (section_t *) sec->list.next;
|
|
ent = (entry_t *) sec->entry_head.next;
|
|
|
|
while (ent != NULL) {
|
|
entry_t *nent = (entry_t *) ent->list.next;
|
|
|
|
free(ent);
|
|
ent = nent;
|
|
}
|
|
|
|
free(sec);
|
|
sec = ns;
|
|
}
|
|
|
|
free(list);
|
|
}
|
|
|
|
static int
|
|
ini_detect_bom(const char *fn)
|
|
{
|
|
FILE *fp;
|
|
unsigned char bom[4] = { 0, 0, 0, 0 };
|
|
|
|
#if defined(ANSI_CFG) || !defined(_WIN32)
|
|
fp = plat_fopen(fn, "rt");
|
|
#else
|
|
fp = plat_fopen(fn, "rt, ccs=UTF-8");
|
|
#endif
|
|
if (fp == NULL)
|
|
return 0;
|
|
(void) !fread(bom, 1, 3, fp);
|
|
if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __HAIKU__
|
|
/* Local version of fgetws to avoid a crash */
|
|
static wchar_t *
|
|
ini_fgetws(wchar_t *str, int count, FILE *stream)
|
|
{
|
|
int i = 0;
|
|
if (feof(stream))
|
|
return NULL;
|
|
for (i = 0; i < count; i++) {
|
|
wint_t curChar = fgetwc(stream);
|
|
if (curChar == WEOF) {
|
|
if (i + 1 < count)
|
|
str[i + 1] = 0;
|
|
return feof(stream) ? str : NULL;
|
|
}
|
|
str[i] = curChar;
|
|
if (curChar == '\n')
|
|
break;
|
|
}
|
|
if (i + 1 < count)
|
|
str[i + 1] = 0;
|
|
return str;
|
|
}
|
|
#endif
|
|
|
|
/* Read and parse the configuration file into memory. */
|
|
ini_t
|
|
ini_read(const char *fn)
|
|
{
|
|
char sname[128];
|
|
char ename[128];
|
|
wchar_t buff[1024];
|
|
section_t *sec;
|
|
section_t *ns;
|
|
entry_t *ne;
|
|
int c;
|
|
int d;
|
|
int bom;
|
|
FILE *fp;
|
|
list_t *head;
|
|
|
|
bom = ini_detect_bom(fn);
|
|
#if defined(ANSI_CFG) || !defined(_WIN32)
|
|
fp = plat_fopen(fn, "rt");
|
|
#else
|
|
fp = plat_fopen(fn, "rt, ccs=UTF-8");
|
|
#endif
|
|
if (fp == NULL)
|
|
return NULL;
|
|
|
|
head = calloc(1, sizeof(list_t));
|
|
sec = calloc(1, sizeof(section_t));
|
|
|
|
list_add(&sec->list, head);
|
|
if (bom)
|
|
fseek(fp, 3, SEEK_SET);
|
|
|
|
while (1) {
|
|
memset(buff, 0x00, sizeof(buff));
|
|
#ifdef __HAIKU__
|
|
ini_fgetws(buff, sizeof_w(buff), fp);
|
|
#else
|
|
(void) !fgetws(buff, sizeof_w(buff), fp);
|
|
#endif
|
|
if (feof(fp))
|
|
break;
|
|
|
|
/* Make sure there are no stray newlines or hard-returns in there. */
|
|
if (wcslen(buff) > 0)
|
|
if (buff[wcslen(buff) - 1] == L'\n')
|
|
buff[wcslen(buff) - 1] = L'\0';
|
|
if (wcslen(buff) > 0)
|
|
if (buff[wcslen(buff) - 1] == L'\r')
|
|
buff[wcslen(buff) - 1] = L'\0';
|
|
|
|
/* Skip any leading whitespace. */
|
|
c = 0;
|
|
while ((buff[c] == L' ') || (buff[c] == L'\t'))
|
|
c++;
|
|
|
|
/* Skip empty lines. */
|
|
if (buff[c] == L'\0')
|
|
continue;
|
|
|
|
/* Skip lines that (only) have a comment. */
|
|
if ((buff[c] == L'#') || (buff[c] == L';'))
|
|
continue;
|
|
|
|
if (buff[c] == L'[') { /*Section*/
|
|
c++;
|
|
d = 0;
|
|
while (buff[c] != L']' && buff[c])
|
|
(void) !wctomb(&(sname[d++]), buff[c++]);
|
|
sname[d] = L'\0';
|
|
|
|
/* Is the section name properly terminated? */
|
|
if (buff[c] != L']')
|
|
continue;
|
|
|
|
/* Create a new section and insert it. */
|
|
ns = malloc(sizeof(section_t));
|
|
memset(ns, 0x00, sizeof(section_t));
|
|
memcpy(ns->name, sname, 128);
|
|
list_add(&ns->list, head);
|
|
|
|
/* New section is now the current one. */
|
|
sec = ns;
|
|
continue;
|
|
}
|
|
|
|
/* Get the variable name. */
|
|
d = 0;
|
|
while ((buff[c] != L'=') && (buff[c] != L' ') && buff[c])
|
|
(void) !wctomb(&(ename[d++]), buff[c++]);
|
|
ename[d] = L'\0';
|
|
|
|
/* Skip incomplete lines. */
|
|
if (buff[c] == L'\0')
|
|
continue;
|
|
|
|
/* Look for =, skip whitespace. */
|
|
while ((buff[c] == L'=' || buff[c] == L' ') && buff[c])
|
|
c++;
|
|
|
|
/* Skip incomplete lines. */
|
|
if (buff[c] == L'\0')
|
|
continue;
|
|
|
|
/* This is where the value part starts. */
|
|
d = c;
|
|
|
|
/* Allocate a new variable entry.. */
|
|
ne = calloc(1, sizeof(entry_t));
|
|
memset(ne, 0x00, sizeof(entry_t));
|
|
memcpy(ne->name, ename, 128);
|
|
wcsncpy(ne->wdata, &buff[d], sizeof_w(ne->wdata) - 1);
|
|
ne->wdata[sizeof_w(ne->wdata) - 1] = L'\0';
|
|
#ifdef _WIN32 /* Make sure the string is converted to UTF-8 rather than a legacy codepage */
|
|
c16stombs(ne->data, ne->wdata, sizeof(ne->data));
|
|
#else
|
|
wcstombs(ne->data, ne->wdata, sizeof(ne->data));
|
|
#endif
|
|
ne->data[sizeof(ne->data) - 1] = '\0';
|
|
|
|
/* .. and insert it. */
|
|
list_add(&ne->list, &sec->entry_head);
|
|
}
|
|
|
|
(void) fclose(fp);
|
|
|
|
return (ini_t) head;
|
|
}
|
|
|
|
/* Write the in-memory configuration to disk. */
|
|
void
|
|
ini_write(ini_t ini, const char *fn)
|
|
{
|
|
wchar_t wtemp[512];
|
|
list_t *list = (list_t *) ini;
|
|
section_t *sec;
|
|
FILE *fp;
|
|
int fl = 0;
|
|
|
|
if (list == NULL)
|
|
return;
|
|
|
|
sec = (section_t *) list->next;
|
|
|
|
#if defined(ANSI_CFG) || !defined(_WIN32)
|
|
fp = plat_fopen(fn, "wt");
|
|
#else
|
|
fp = plat_fopen(fn, "wt, ccs=UTF-8");
|
|
#endif
|
|
if (fp == NULL)
|
|
return;
|
|
|
|
while (sec != NULL) {
|
|
entry_t *ent;
|
|
|
|
if (sec->name[0]) {
|
|
mbstowcs(wtemp, sec->name, strlen(sec->name) + 1);
|
|
if (fl)
|
|
fwprintf(fp, L"\n[%ls]\n", wtemp);
|
|
else
|
|
fwprintf(fp, L"[%ls]\n", wtemp);
|
|
fl++;
|
|
}
|
|
|
|
ent = (entry_t *) sec->entry_head.next;
|
|
while (ent != NULL) {
|
|
if (ent->name[0] != '\0') {
|
|
mbstowcs(wtemp, ent->name, 128);
|
|
if (ent->wdata[0] == L'\0')
|
|
fwprintf(fp, L"%ls = \n", wtemp);
|
|
else
|
|
fwprintf(fp, L"%ls = %ls\n", wtemp, ent->wdata);
|
|
fl++;
|
|
}
|
|
|
|
ent = (entry_t *) ent->list.next;
|
|
}
|
|
|
|
sec = (section_t *) sec->list.next;
|
|
}
|
|
|
|
(void) fclose(fp);
|
|
}
|
|
|
|
/* Wide-character version of "trim" */
|
|
wchar_t *
|
|
trim_w(wchar_t *str)
|
|
{
|
|
size_t len = 0;
|
|
wchar_t *frontp = str;
|
|
wchar_t *endp = NULL;
|
|
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
if (str[0] == L'\0') {
|
|
return str;
|
|
}
|
|
|
|
len = wcslen(str);
|
|
endp = str + len;
|
|
|
|
/* Move the front and back pointers to address the first non-whitespace
|
|
* characters from each end.
|
|
*/
|
|
while (iswspace((wint_t) *frontp)) {
|
|
++frontp;
|
|
}
|
|
if (endp != frontp) {
|
|
while (iswspace((wint_t) *(--endp)) && endp != frontp) { }
|
|
}
|
|
|
|
if (frontp != str && endp == frontp)
|
|
*str = L'\0';
|
|
else if ((str + len - 1) != endp)
|
|
*(endp + 1) = L'\0';
|
|
|
|
/* Shift the string so that it starts at str so that if it's dynamically
|
|
* allocated, we can still free it on the returned pointer. Note the reuse
|
|
* of endp to mean the front of the string buffer now.
|
|
*/
|
|
endp = str;
|
|
if (frontp != str) {
|
|
while (*frontp) {
|
|
*endp++ = *frontp++;
|
|
}
|
|
*endp = L'\0';
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
extern char* trim(char* str);
|
|
|
|
void
|
|
ini_strip_quotes(ini_t ini)
|
|
{
|
|
list_t *list = (list_t *) ini;
|
|
section_t *sec;
|
|
|
|
sec = (section_t *) list->next;
|
|
|
|
while (sec != NULL) {
|
|
entry_t *ent;
|
|
|
|
ent = (entry_t *) sec->entry_head.next;
|
|
while (ent != NULL) {
|
|
if (ent->name[0] != '\0') {
|
|
int trailing_hash = strcspn(ent->data, "#");
|
|
int trailing_quote;
|
|
ent->wdata[trailing_hash] = 0;
|
|
ent->data[trailing_hash] = 0;
|
|
if (ent->wdata[0] == L'\"') {
|
|
memmove(ent->wdata, &ent->wdata[1], sizeof(ent->wdata) - sizeof(wchar_t));
|
|
}
|
|
if (ent->wdata[wcslen(ent->wdata) - 1] == L'\"') {
|
|
ent->wdata[wcslen(ent->wdata) - 1] = 0;
|
|
}
|
|
|
|
if (ent->data[0] == '\"') {
|
|
memmove(ent->data, &ent->data[1], sizeof(ent->data) - sizeof(char));
|
|
}
|
|
if (ent->data[strlen(ent->data) - 1] == '\"') {
|
|
ent->data[strlen(ent->data) - 1] = 0;
|
|
}
|
|
|
|
trailing_quote = strcspn(ent->data, "\"");
|
|
ent->wdata[trailing_quote] = 0;
|
|
ent->data[trailing_quote] = 0;
|
|
|
|
trim_w(ent->wdata);
|
|
trim(ent->data);
|
|
}
|
|
|
|
ent = (entry_t *) ent->list.next;
|
|
}
|
|
|
|
sec = (section_t *) sec->list.next;
|
|
}
|
|
}
|
|
|
|
ini_t
|
|
ini_new(void)
|
|
{
|
|
ini_t ini = malloc(sizeof(list_t));
|
|
memset(ini, 0, sizeof(list_t));
|
|
return ini;
|
|
}
|
|
|
|
void
|
|
ini_dump(ini_t ini)
|
|
{
|
|
section_t *sec = (section_t *) ini;
|
|
while (sec != NULL) {
|
|
entry_t *ent;
|
|
|
|
if (sec->name[0])
|
|
ini_log("[%s]\n", sec->name);
|
|
|
|
ent = (entry_t *) sec->entry_head.next;
|
|
while (ent != NULL) {
|
|
ini_log("%s = %s\n", ent->name, ent->data);
|
|
|
|
ent = (entry_t *) ent->list.next;
|
|
}
|
|
|
|
sec = (section_t *) sec->list.next;
|
|
}
|
|
}
|
|
|
|
void
|
|
ini_section_delete_var(ini_section_t self, const char *name)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *entry;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry != NULL) {
|
|
list_delete(&entry->list, §ion->entry_head);
|
|
free(entry);
|
|
}
|
|
}
|
|
|
|
int
|
|
ini_section_get_int(ini_section_t self, const char *name, int def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
int value = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
if (stricmp(entry->data, "true") == 0)
|
|
return 1;
|
|
if (stricmp(entry->data, "false") == 0)
|
|
return 0;
|
|
|
|
sscanf(entry->data, "%i", &value);
|
|
|
|
return value;
|
|
}
|
|
|
|
uint32_t
|
|
ini_section_get_uint(ini_section_t self, const char *name, uint32_t def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
uint32_t value = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
sscanf(entry->data, "%u", &value);
|
|
|
|
return value;
|
|
}
|
|
|
|
#if 0
|
|
float
|
|
ini_section_get_float(ini_section_t self, const char *name, float def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
float value = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
sscanf(entry->data, "%g", &value);
|
|
|
|
return value;
|
|
}
|
|
#endif
|
|
|
|
double
|
|
ini_section_get_double(ini_section_t self, const char *name, double def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *entry;
|
|
double value = 0;
|
|
int res = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
res = sscanf(entry->data, "%lg", &value);
|
|
if (res == EOF || res <= 0) {
|
|
int i = 0;
|
|
for (i = 0; i < strlen(entry->data); i++) {
|
|
if (entry->data[i] == ',') {
|
|
entry->data[i] = '.';
|
|
entry->wdata[i] = L'.';
|
|
}
|
|
}
|
|
(void)sscanf(entry->data, "%lg", &value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
int
|
|
ini_section_get_hex16(ini_section_t self, const char *name, int def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
unsigned int value = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
sscanf(entry->data, "%04X", &value);
|
|
|
|
return value;
|
|
}
|
|
|
|
int
|
|
ini_section_get_hex20(ini_section_t self, const char *name, int def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
unsigned int value = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
sscanf(entry->data, "%05X", &value);
|
|
|
|
return value;
|
|
}
|
|
|
|
int
|
|
ini_section_get_mac(ini_section_t self, const char *name, int def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
const entry_t *entry;
|
|
unsigned int val0 = 0;
|
|
unsigned int val1 = 0;
|
|
unsigned int val2 = 0;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
sscanf(entry->data, "%02x:%02x:%02x", &val0, &val1, &val2);
|
|
|
|
return ((val0 << 16) + (val1 << 8) + val2);
|
|
}
|
|
|
|
char *
|
|
ini_section_get_string(ini_section_t self, const char *name, char *def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *entry;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
return (entry->data);
|
|
}
|
|
|
|
wchar_t *
|
|
ini_section_get_wstring(ini_section_t self, const char *name, wchar_t *def)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *entry;
|
|
|
|
if (section == NULL)
|
|
return def;
|
|
|
|
entry = find_entry(section, name);
|
|
if (entry == NULL)
|
|
return def;
|
|
|
|
return (entry->wdata);
|
|
}
|
|
|
|
void
|
|
ini_section_set_int(ini_section_t self, const char *name, int val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%i", val);
|
|
mbstowcs(ent->wdata, ent->data, 512);
|
|
}
|
|
|
|
void
|
|
ini_section_set_uint(ini_section_t self, const char *name, uint32_t val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%i", val);
|
|
mbstowcs(ent->wdata, ent->data, 512);
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
ini_section_set_float(ini_section_t self, const char *name, float val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%g", val);
|
|
mbstowcs(ent->wdata, ent->data, 512);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
ini_section_set_double(ini_section_t self, const char *name, double val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%lg", val);
|
|
mbstowcs(ent->wdata, ent->data, 512);
|
|
}
|
|
|
|
void
|
|
ini_section_set_hex16(ini_section_t self, const char *name, int val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%04X", val);
|
|
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
|
|
}
|
|
|
|
void
|
|
ini_section_set_hex20(ini_section_t self, const char *name, int val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%05X", val);
|
|
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
|
|
}
|
|
|
|
void
|
|
ini_section_set_mac(ini_section_t self, const char *name, int val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
sprintf(ent->data, "%02x:%02x:%02x",
|
|
(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
|
|
mbstowcs(ent->wdata, ent->data, 512);
|
|
}
|
|
|
|
void
|
|
ini_section_set_string(ini_section_t self, const char *name, const char *val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
if ((strlen(val) + 1) <= sizeof(ent->data))
|
|
memcpy(ent->data, val, strlen(val) + 1);
|
|
else
|
|
memcpy(ent->data, val, sizeof(ent->data));
|
|
#ifdef _WIN32 /* Make sure the string is converted from UTF-8 rather than a legacy codepage */
|
|
mbstoc16s(ent->wdata, ent->data, sizeof_w(ent->wdata));
|
|
#else
|
|
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ini_section_set_wstring(ini_section_t self, const char *name, wchar_t *val)
|
|
{
|
|
section_t *section = (section_t *) self;
|
|
entry_t *ent;
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
ent = find_entry(section, name);
|
|
if (ent == NULL)
|
|
ent = create_entry(section, name);
|
|
|
|
memcpy(ent->wdata, val, sizeof_w(ent->wdata));
|
|
#ifdef _WIN32 /* Make sure the string is converted to UTF-8 rather than a legacy codepage */
|
|
c16stombs(ent->data, ent->wdata, sizeof(ent->data));
|
|
#else
|
|
wcstombs(ent->data, ent->wdata, sizeof(ent->data));
|
|
#endif
|
|
}
|