Skip to content

Commit

Permalink
Fix real number support in non-English locales (Issue #311)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelrsweet committed Mar 6, 2024
1 parent 9973668 commit 77d609e
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 58 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 18 additions & 28 deletions mxml-entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@

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


if (global->num_entity_cbs < (sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
{
global->entity_cbs[global->num_entity_cbs] = cb;
global->entity_cbs[global->num_entity_cbs] = cb;
global->entity_cbdatas[global->num_entity_cbs] = cbdata;
global->num_entity_cbs ++;

return (true);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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);
return (-1);
}
151 changes: 138 additions & 13 deletions mxml-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}


Expand All @@ -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_cb = cb;
global->error_cbdata = cbdata;
}


Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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 :
Expand All @@ -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);
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions mxml-private.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

void
_mxml_error(const char *format, // I - Printf-style format string
...) // I - Additional arguments as needed
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to arguments
char s[1024]; // Message string
Expand All @@ -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);
}
Expand Down
21 changes: 14 additions & 7 deletions mxml-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# define MXML_PRIVATE_H
# include "config.h"
# include "mxml.h"
# include <locale.h>


//
Expand Down Expand Up @@ -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;


Expand All @@ -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);

Expand Down
Loading

0 comments on commit 77d609e

Please sign in to comment.