Fix real number support in non-English locales (Issue #311)

This commit is contained in:
Michael R Sweet 2024-03-06 18:18:29 -05:00
parent 99736682f1
commit 77d609e8e8
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
7 changed files with 181 additions and 58 deletions

View File

@ -15,6 +15,7 @@
(Issue #51)
- Updated the load and save callbacks to include a context pointer (Issue #106)
- Fixed some warnings (Issue #301)
- Fixed real number support in non-English locales (Issue #311)
# Changes in Mini-XML 3.3.2

View File

@ -18,7 +18,8 @@
bool // O - `true` on success, `false` on failure
mxmlEntityAddCallback(
mxml_entity_cb_t cb) // I - Callback function to add
mxml_entity_cb_t cb, // I - Callback function to add
void *cbdata) // I - Callback data
{
_mxml_global_t *global = _mxml_global();
// Global data
@ -27,6 +28,7 @@ mxmlEntityAddCallback(
if (global->num_entity_cbs < (sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
{
global->entity_cbs[global->num_entity_cbs] = cb;
global->entity_cbdatas[global->num_entity_cbs] = cbdata;
global->num_entity_cbs ++;
return (true);
@ -85,7 +87,7 @@ mxmlEntityGetValue(const char *name) // I - Entity name
for (i = 0; i < global->num_entity_cbs; i ++)
{
if ((ch = (global->entity_cbs[i])(name)) >= 0)
if ((ch = (global->entity_cbs[i])(global->entity_cbdatas[i], name)) >= 0)
return (ch);
}
@ -114,8 +116,10 @@ mxmlEntityRemoveCallback(
global->num_entity_cbs --;
if (i < global->num_entity_cbs)
{
memmove(global->entity_cbs + i, global->entity_cbs + i + 1, (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0]));
memmove(global->entity_cbdatas + i, global->entity_cbdatas + i + 1, (global->num_entity_cbs - i) * sizeof(global->entity_cbdatas[0]));
}
return;
}
}
@ -127,12 +131,11 @@ mxmlEntityRemoveCallback(
//
int // O - Unicode value or -1
_mxml_entity_cb(const char *name) // I - Entity name
_mxml_entity_cb(void *cbdata, // I - Callback data (unused)
const char *name) // I - Entity name
{
int diff; // Difference between names
size_t current, // Current entity in search
first, // First entity in search
last; // Last entity in search
size_t i; // Looping var
int diff; // Difference
static const struct
{
const char *name; // Entity name
@ -399,27 +402,14 @@ _mxml_entity_cb(const char *name) // I - Entity name
};
// Do a binary search for the named entity...
first = 0;
last = sizeof(entities) / sizeof(entities[0]) - 1;
while ((last - first) > 1)
// Do a linear search for the named entity...
for (i = 0; i < (sizeof(entities) / sizeof(entities[0])); i ++)
{
current = (first + last) / 2;
if ((diff = strcmp(name, entities[current].name)) == 0)
return (entities[current].val);
if ((diff = strcmp(name, entities[i].name)) == 0)
return (entities[i].val);
else if (diff < 0)
last = current;
else
first = current;
break;
}
// If we get here, there is a small chance that there is still a match; check first and last...
if (!strcmp(name, entities[first].name))
return (entities[first].val);
else if (!strcmp(name, entities[last].name))
return (entities[last].val);
else
return (-1);
}

View File

@ -58,6 +58,8 @@ static int mxml_parse_element(mxml_read_cb_t read_cb, void *read_cbdata, mxml_n
static ssize_t mxml_read_cb_fd(int *fd, void *buffer, size_t bytes);
static ssize_t mxml_read_cb_file(FILE *fp, void *buffer, size_t bytes);
static ssize_t mxml_read_cb_string(_mxml_stringbuf_t *sb, void *buffer, size_t bytes);
static void mxml_set_loc(_mxml_global_t *global);
static double mxml_strtod(_mxml_global_t *global, const char *buffer, char **bufptr);
static ssize_t mxml_write_cb_fd(int *fd, const void *buffer, size_t bytes);
static ssize_t mxml_write_cb_file(FILE *fp, const void *buffer, size_t bytes);
static ssize_t mxml_write_cb_string(_mxml_stringbuf_t *sb, const void *buffer, size_t bytes);
@ -536,16 +538,18 @@ mxmlSaveString(
//
void
mxmlSetCustomHandlers(
mxml_custom_load_cb_t load, // I - Load function
mxml_custom_save_cb_t save) // I - Save function
mxmlSetCustomCallbacks(
mxml_custom_load_cb_t load_cb, // I - Load callback function
mxml_custom_save_cb_t save_cb, // I - Save callback function
void *cbdata) // I - Callback data
{
_mxml_global_t *global = _mxml_global();
// Global data
global->custom_load_cb = load;
global->custom_save_cb = save;
global->custom_load_cb = load_cb;
global->custom_save_cb = save_cb;
global->custom_cbdata = cbdata;
}
@ -554,13 +558,16 @@ mxmlSetCustomHandlers(
//
void
mxmlSetErrorCallback(mxml_error_cb_t cb)// I - Error callback function
mxmlSetErrorCallback(
mxml_error_cb_t cb, // I - Error callback function
void *cbdata) // I - Error callback data
{
_mxml_global_t *global = _mxml_global();
// Global data
global->error_cb = cb;
global->error_cbdata = cbdata;
}
@ -702,7 +709,8 @@ mxml_get_entity(
}
else if ((ch = mxmlEntityGetValue(entity)) < 0)
{
_mxml_error("Entity name '%s;' not supported under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line);
_mxml_error("Entity '&%s;' not supported under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line);
return (EOF);
}
if (mxml_bad_char(ch))
@ -989,7 +997,7 @@ mxml_load_data(
break;
case MXML_TYPE_REAL :
node = mxmlNewReal(parent, strtod(buffer, &bufptr));
node = mxmlNewReal(parent, mxml_strtod(global, buffer, &bufptr));
break;
case MXML_TYPE_TEXT :
@ -1002,7 +1010,7 @@ mxml_load_data(
// Use the callback to fill in the custom data...
node = mxmlNewCustom(parent, NULL, NULL);
if (!(*global->custom_load_cb)(node, buffer))
if (!(*global->custom_load_cb)(global->custom_cbdata, node, buffer))
{
_mxml_error("Bad custom value '%s' in parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "null", line);
mxmlDelete(node);
@ -1881,6 +1889,106 @@ mxml_read_cb_string(
}
//
// 'mxml_set_loc()' - Set the locale values for numbers.
//
static void
mxml_set_loc(_mxml_global_t *global) // I - Global data
{
if ((global->loc = localeconv()) != NULL)
{
if (!global->loc->decimal_point || !strcmp(global->loc->decimal_point, "."))
global->loc = NULL;
else
global->loc_declen = strlen(global->loc->decimal_point);
}
global->loc_set = true;
}
//
// 'mxml_strtod()' - Convert a string to a double without respect to the locale.
//
static double // O - Real number
mxml_strtod(_mxml_global_t *global, // I - Global data
const char *buffer, // I - String
char **bufend) // O - End of number in string
{
const char *bufptr; // Pointer into buffer
char temp[64], // Temporary buffer
*tempptr; // Pointer into temporary buffer
// See if the locale has a special decimal point string...
if (!global->loc_set)
mxml_set_loc(global);
if (!global->loc)
return (strtod(buffer, bufend));
// Copy leading sign, numbers, period, and then numbers...
tempptr = temp;
temp[sizeof(temp) - 1] = '\0';
if (*bufptr == '-' || *bufptr == '+')
*tempptr++ = *bufptr++;
for (bufptr = buffer; *bufptr && isdigit(*bufptr & 255); bufptr ++)
{
if (tempptr < (temp + sizeof(temp) - 1))
{
*tempptr++ = *bufptr;
}
else
{
*bufend = (char *)bufptr;
return (0.0);
}
}
if (*bufptr == '.')
{
// Convert decimal point to locale equivalent...
size_t declen = strlen(global->loc->decimal_point);
// Length of decimal point
bufptr ++;
if (declen <= (sizeof(temp) - (size_t)(tempptr - temp)))
{
memcpy(tempptr, global->loc->decimal_point, declen);
tempptr += declen;
}
else
{
*bufend = (char *)bufptr;
return (0.0);
}
}
// Copy any remaining characters...
while (*bufptr && isdigit(*bufptr & 255))
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *bufptr++;
else
break;
}
*bufend = (char *)bufptr;
if (*bufptr)
return (0.0);
// Nul-terminate the temporary string and convert the string...
*tempptr = '\0';
return (strtod(temp, NULL));
}
//
// 'mxml_write_cb_fd()' - Write bytes to a file descriptor.
//
@ -2079,8 +2187,25 @@ mxml_write_node(
}
// Write real number...
// TODO: Provide locale-neutral formatting/scanning code for REAL
snprintf(s, sizeof(s), "%f", current->value.real);
snprintf(s, sizeof(s), "%g", current->value.real);
if (!global->loc_set)
mxml_set_loc(global);
if (global->loc)
{
char *sptr; // Pointer into string
if ((sptr = strstr(s, global->loc->decimal_point)) != NULL)
{
// Convert locale decimal point to "."
if (global->loc_declen > 1)
memmove(sptr + 1, sptr + global->loc_declen, strlen(sptr + global->loc_declen) + 1);
*sptr = '.';
}
}
col = mxml_write_string(write_cb, write_cbdata, s, /*use_entities*/true, col);
break;
@ -2103,7 +2228,7 @@ mxml_write_node(
if (!global->custom_save_cb)
return (-1);
if ((data = (*global->custom_save_cb)(current)) == NULL)
if ((data = (*global->custom_save_cb)(global->custom_cbdata, current)) == NULL)
return (-1);
col = mxml_write_string(write_cb, write_cbdata, data, /*use_entities*/true, col);

View File

@ -64,7 +64,7 @@ _mxml_error(const char *format, // I - Printf-style format string
// And then display the error message...
if (global->error_cb)
(*global->error_cb)(s);
(*global->error_cb)(global->error_cbdata, s);
else
fprintf(stderr, "%s\n", s);
}

View File

@ -13,6 +13,7 @@
# define MXML_PRIVATE_H
# include "config.h"
# include "mxml.h"
# include <locale.h>
//
@ -94,12 +95,18 @@ struct _mxml_index_s // An XML node index.
typedef struct _mxml_global_s // Global, per-thread data
{
void (*error_cb)(const char *);
size_t num_entity_cbs;
int (*entity_cbs[100])(const char *name);
int wrap;
mxml_custom_load_cb_t custom_load_cb;
mxml_custom_save_cb_t custom_save_cb;
mxml_custom_load_cb_t custom_load_cb; // Custom load callback function
mxml_custom_save_cb_t custom_save_cb; // Custom save callback function
void *custom_cbdata; // Custom callback data
mxml_error_cb_t error_cb; // Error callback function
void *error_cbdata; // Error callback data
size_t num_entity_cbs; // Number of entity callbacks
mxml_entity_cb_t entity_cbs[100]; // Entity callback functions
void *entity_cbdatas[100]; // Entity callback data
struct lconv *loc; // Locale data
size_t loc_declen; // Length of decimal point string
bool loc_set; // Locale data set?
int wrap; // Wrap margin
} _mxml_global_t;
@ -108,7 +115,7 @@ typedef struct _mxml_global_s // Global, per-thread data
//
extern _mxml_global_t *_mxml_global(void);
extern int _mxml_entity_cb(const char *name);
extern int _mxml_entity_cb(void *cbdata, const char *name);
extern const char *_mxml_entity_string(int ch);
extern void _mxml_error(const char *format, ...) MXML_FORMAT(1,2);

14
mxml.h
View File

@ -95,7 +95,7 @@ typedef enum mxml_ws_e // Whitespace periods
typedef void (*mxml_custom_destroy_cb_t)(void *);
// Custom data destructor
typedef void (*mxml_error_cb_t)(const char *);
typedef void (*mxml_error_cb_t)(void *cbdata, const char *message);
// Error callback function
typedef struct _mxml_node_s mxml_node_t;// An XML node.
@ -103,13 +103,13 @@ typedef struct _mxml_node_s mxml_node_t;// An XML node.
typedef struct _mxml_index_s mxml_index_t;
// An XML node index.
typedef bool (*mxml_custom_load_cb_t)(mxml_node_t *node, const char *s);
typedef bool (*mxml_custom_load_cb_t)(void *cbdata, mxml_node_t *node, const char *s);
// Custom data load callback function
typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *node);
typedef char *(*mxml_custom_save_cb_t)(void *cbdata, mxml_node_t *node);
// Custom data save callback function
typedef int (*mxml_entity_cb_t)(const char *name);
typedef int (*mxml_entity_cb_t)(void *cbdata, const char *name);
// Entity callback function
typedef mxml_type_t (*mxml_load_cb_t)(void *cbdata, mxml_node_t *node);
@ -142,7 +142,7 @@ extern const char *mxmlElementGetAttrByIndex(mxml_node_t *node, int idx, c
extern size_t mxmlElementGetAttrCount(mxml_node_t *node);
extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, const char *value);
extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, const char *format, ...) MXML_FORMAT(3,4);
extern bool mxmlEntityAddCallback(mxml_entity_cb_t cb);
extern bool mxmlEntityAddCallback(mxml_entity_cb_t cb, void *cbdata);
extern int mxmlEntityGetValue(const char *name);
extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb);
@ -218,9 +218,9 @@ extern bool mxmlSetDeclarationf(mxml_node_t *node, const char *format, ...) MXM
extern bool mxmlSetDirective(mxml_node_t *node, const char *directive);
extern bool mxmlSetDirectivef(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3);
extern bool mxmlSetCustom(mxml_node_t *node, void *data, mxml_custom_destroy_cb_t destroy);
extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, mxml_custom_save_cb_t save);
extern void mxmlSetCustomCallbacks(mxml_custom_load_cb_t load_cb, mxml_custom_save_cb_t save_cb, void *cbdata);
extern bool mxmlSetElement(mxml_node_t *node, const char *name);
extern void mxmlSetErrorCallback(mxml_error_cb_t cb);
extern void mxmlSetErrorCallback(mxml_error_cb_t cb, void *cbdata);
extern bool mxmlSetInteger(mxml_node_t *node, long integer);
extern bool mxmlSetOpaque(mxml_node_t *node, const char *opaque);
extern bool mxmlSetOpaquef(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3);

View File

@ -4,7 +4,7 @@
<keyword type="opaque">InputSlot</keyword>
<default type="opaque">Auto</default>
<text>Media Source</text>
<order type="real">10.000000</order>
<order type="real">10.42</order>
<choice>
<keyword type="opaque">Auto</keyword>
<text>Auto Tray Selection</text>