mirror of
https://github.com/michaelrsweet/mxml.git
synced 2024-11-24 03:15:30 +00:00
Fix real number support in non-English locales (Issue #311)
This commit is contained in:
parent
99736682f1
commit
77d609e8e8
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
149
mxml-file.c
149
mxml-file.c
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
14
mxml.h
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user