Add line numbers to error messages (Issue #230)

This commit is contained in:
Michael R Sweet 2019-01-05 12:23:17 -05:00
parent a21f711191
commit 621a4de9b2
No known key found for this signature in database
GPG Key ID: 999559A027815955
2 changed files with 130 additions and 94 deletions

View File

@ -7,6 +7,7 @@
(Issue #233)
- Moved `mxmldoc` to a new `codedoc` project whose focus is on generating
code documentation (Issue #235, Issue #236, Issue #237)
- Error messages now include the line number of the error (Issue #230)
# Changes in Mini-XML 2.12

View File

@ -55,42 +55,26 @@ typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/
* Local functions...
*/
static int mxml_add_char(int ch, char **ptr, char **buffer,
int *bufsize);
static int mxml_add_char(int ch, char **ptr, char **buffer, int *bufsize);
static int mxml_fd_getc(void *p, int *encoding);
static int mxml_fd_putc(int ch, void *p);
static int mxml_fd_read(_mxml_fdbuf_t *buf);
static int mxml_fd_write(_mxml_fdbuf_t *buf);
static int mxml_file_getc(void *p, int *encoding);
static int mxml_file_putc(int ch, void *p);
static int mxml_get_entity(mxml_node_t *parent, void *p,
int *encoding,
_mxml_getc_cb_t getc_cb);
static int mxml_get_entity(mxml_node_t *parent, void *p, int *encoding, _mxml_getc_cb_t getc_cb, int *line);
static inline int mxml_isspace(int ch)
{
return (ch == ' ' || ch == '\t' || ch == '\r' ||
ch == '\n');
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p,
mxml_load_cb_t cb,
_mxml_getc_cb_t getc_cb,
mxml_sax_cb_t sax_cb, void *sax_data);
static int mxml_parse_element(mxml_node_t *node, void *p,
int *encoding,
_mxml_getc_cb_t getc_cb);
static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, mxml_load_cb_t cb, _mxml_getc_cb_t getc_cb, mxml_sax_cb_t sax_cb, void *sax_data);
static int mxml_parse_element(mxml_node_t *node, void *p, int *encoding, _mxml_getc_cb_t getc_cb, int *line);
static int mxml_string_getc(void *p, int *encoding);
static int mxml_string_putc(int ch, void *p);
static int mxml_write_name(const char *s, void *p,
_mxml_putc_cb_t putc_cb);
static int mxml_write_node(mxml_node_t *node, void *p,
mxml_save_cb_t cb, int col,
_mxml_putc_cb_t putc_cb,
_mxml_global_t *global);
static int mxml_write_string(const char *s, void *p,
_mxml_putc_cb_t putc_cb);
static int mxml_write_ws(mxml_node_t *node, void *p,
mxml_save_cb_t cb, int ws,
int col, _mxml_putc_cb_t putc_cb);
static int mxml_write_name(const char *s, void *p, _mxml_putc_cb_t putc_cb);
static int mxml_write_node(mxml_node_t *node, void *p, mxml_save_cb_t cb, int col, _mxml_putc_cb_t putc_cb, _mxml_global_t *global);
static int mxml_write_string(const char *s, void *p, _mxml_putc_cb_t putc_cb);
static int mxml_write_ws(mxml_node_t *node, void *p, mxml_save_cb_t cb, int ws, int col, _mxml_putc_cb_t putc_cb);
/*
@ -1315,8 +1299,9 @@ static int /* O - Character value or EOF on error */
mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
void *p, /* I - Pointer to source */
int *encoding, /* IO - Character encoding */
int (*getc_cb)(void *, int *))
int (*getc_cb)(void *, int *),
/* I - Get character function */
int *line) /* IO - Current line number */
{
int ch; /* Current character */
char entity[64], /* Entity string */
@ -1326,23 +1311,27 @@ mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
entptr = entity;
while ((ch = (*getc_cb)(p, encoding)) != EOF)
{
if (ch > 126 || (!isalnum(ch) && ch != '#'))
break;
else if (entptr < (entity + sizeof(entity) - 1))
*entptr++ = ch;
else
{
mxml_error("Entity name too long under parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Entity name too long under parent <%s> on line %d.", parent ? parent->value.element.name : "null", *line);
break;
}
}
*entptr = '\0';
if (ch != ';')
{
mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
entity, parent ? parent->value.element.name : "null");
mxml_error("Character entity '%s' not terminated under parent <%s> on line %d.", entity, parent ? parent->value.element.name : "null", *line);
if (ch == '\n')
(*line)++;
return (EOF);
}
@ -1354,13 +1343,11 @@ mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
ch = (int)strtol(entity + 1, NULL, 10);
}
else if ((ch = mxmlEntityGetValue(entity)) < 0)
mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
entity, parent ? parent->value.element.name : "null");
mxml_error("Entity name '%s;' not supported under parent <%s> on line %d.", entity, parent ? parent->value.element.name : "null", *line);
if (mxml_bad_char(ch))
{
mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
ch, parent ? parent->value.element.name : "null");
mxml_error("Bad control character 0x%02x under parent <%s> on line %d not allowed by XML standard.", ch, parent ? parent->value.element.name : "null", *line);
return (EOF);
}
@ -1384,7 +1371,8 @@ mxml_load_data(
mxml_node_t *node, /* Current node */
*first, /* First node added */
*parent; /* Current parent node */
int ch, /* Character from file */
int line = 1, /* Current line number */
ch, /* Character from file */
whitespace; /* Non-zero if whitespace seen */
char *buffer, /* String buffer */
*bufptr; /* Pointer into buffer */
@ -1469,8 +1457,7 @@ mxml_load_data(
if ((*global->custom_load_cb)(node, buffer))
{
mxml_error("Bad custom value '%s' in parent <%s>!",
buffer, parent ? parent->value.element.name : "null");
mxml_error("Bad custom value '%s' in parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "null", line);
mxmlDelete(node);
node = NULL;
}
@ -1488,9 +1475,7 @@ mxml_load_data(
* Bad integer/real number value...
*/
mxml_error("Bad %s value '%s' in parent <%s>!",
type == MXML_INTEGER ? "integer" : "real", buffer,
parent ? parent->value.element.name : "null");
mxml_error("Bad %s value '%s' in parent <%s> on line %d.", type == MXML_INTEGER ? "integer" : "real", buffer, parent ? parent->value.element.name : "null", line);
break;
}
@ -1503,8 +1488,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Unable to add value node of type %s to parent <%s>!",
types[type], parent ? parent->value.element.name : "null");
mxml_error("Unable to add value node of type %s to parent <%s> on line %d.", types[type], parent ? parent->value.element.name : "null", line);
goto error;
}
@ -1522,6 +1506,9 @@ mxml_load_data(
else if (mxml_isspace(ch) && type == MXML_TEXT)
whitespace = 1;
if (ch == '\n')
line ++;
/*
* Add lone whitespace node if we have an element and existing
* whitespace...
@ -1557,6 +1544,7 @@ mxml_load_data(
bufptr = buffer;
while ((ch = (*getc_cb)(p, &encoding)) != EOF)
{
if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
break;
else if (ch == '<')
@ -1566,7 +1554,7 @@ mxml_load_data(
}
else if (ch == '&')
{
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF)
goto error;
if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
@ -1581,6 +1569,10 @@ mxml_load_data(
((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
break;
if (ch == '\n')
line ++;
}
*bufptr = '\0';
if (!strcmp(buffer, "!--"))
@ -1596,6 +1588,9 @@ mxml_load_data(
break;
else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
goto error;
if (ch == '\n')
line ++;
}
/*
@ -1608,7 +1603,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Early EOF in comment node!");
mxml_error("Early EOF in comment node on line %d.", line);
goto error;
}
@ -1625,8 +1620,7 @@ mxml_load_data(
* There can only be one root element!
*/
mxml_error("<%s> cannot be a second root node after <%s>",
buffer, first->value.element.name);
mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line);
goto error;
}
@ -1636,8 +1630,7 @@ mxml_load_data(
* Just print error for now...
*/
mxml_error("Unable to add comment node to parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Unable to add comment node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line);
break;
}
@ -1671,6 +1664,9 @@ mxml_load_data(
}
else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
goto error;
if (ch == '\n')
line ++;
}
/*
@ -1683,7 +1679,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Early EOF in CDATA node!");
mxml_error("Early EOF in CDATA node on line %d.", line);
goto error;
}
@ -1700,8 +1696,7 @@ mxml_load_data(
* There can only be one root element!
*/
mxml_error("<%s> cannot be a second root node after <%s>",
buffer, first->value.element.name);
mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line);
goto error;
}
@ -1711,8 +1706,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Unable to add CDATA node to parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Unable to add CDATA node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line);
goto error;
}
@ -1739,6 +1733,9 @@ mxml_load_data(
break;
else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
goto error;
if (ch == '\n')
line ++;
}
/*
@ -1751,7 +1748,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Early EOF in processing instruction node!");
mxml_error("Early EOF in processing instruction node on line %d.", line);
goto error;
}
@ -1767,8 +1764,7 @@ mxml_load_data(
* There can only be one root element!
*/
mxml_error("<%s> cannot be a second root node after <%s>",
buffer, first->value.element.name);
mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line);
goto error;
}
@ -1778,8 +1774,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Unable to add processing instruction node to parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Unable to add processing instruction node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line);
goto error;
}
@ -1820,12 +1815,17 @@ mxml_load_data(
else
{
if (ch == '&')
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
{
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF)
goto error;
}
if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
goto error;
}
if (ch == '\n')
line ++;
}
while ((ch = (*getc_cb)(p, &encoding)) != EOF);
@ -1839,7 +1839,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Early EOF in declaration node!");
mxml_error("Early EOF in declaration node on line %d.", line);
goto error;
}
@ -1855,8 +1855,7 @@ mxml_load_data(
* There can only be one root element!
*/
mxml_error("<%s> cannot be a second root node after <%s>",
buffer, first->value.element.name);
mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line);
goto error;
}
@ -1866,8 +1865,7 @@ mxml_load_data(
* Print error and return...
*/
mxml_error("Unable to add declaration node to parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Unable to add declaration node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line);
goto error;
}
@ -1907,8 +1905,7 @@ mxml_load_data(
* Close tag doesn't match tree; print an error for now...
*/
mxml_error("Mismatched close tag <%s> under parent <%s>!",
buffer, parent ? parent->value.element.name : "(null)");
mxml_error("Mismatched close tag <%s> under parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "(null)", line);
goto error;
}
@ -1949,8 +1946,7 @@ mxml_load_data(
* There can only be one root element!
*/
mxml_error("<%s> cannot be a second root node after <%s>",
buffer, first->value.element.name);
mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line);
goto error;
}
@ -1960,22 +1956,20 @@ mxml_load_data(
* Just print error for now...
*/
mxml_error("Unable to add element node to parent <%s>!",
parent ? parent->value.element.name : "null");
mxml_error("Unable to add element node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line);
goto error;
}
if (mxml_isspace(ch))
{
if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
if ((ch = mxml_parse_element(node, p, &encoding, getc_cb, &line)) == EOF)
goto error;
}
else if (ch == '/')
{
if ((ch = (*getc_cb)(p, &encoding)) != '>')
{
mxml_error("Expected > but got '%c' instead for element <%s/>!",
ch, buffer);
mxml_error("Expected > but got '%c' instead for element <%s/> on line %d.", ch, buffer, line);
mxmlDelete(node);
goto error;
}
@ -2022,7 +2016,7 @@ mxml_load_data(
* Add character entity to current buffer...
*/
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF)
goto error;
if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
@ -2058,9 +2052,7 @@ mxml_load_data(
if (node != parent)
{
mxml_error("Missing close tag </%s> under parent <%s>!",
node->value.element.name,
node->parent ? node->parent->value.element.name : "(null)");
mxml_error("Missing close tag </%s> under parent <%s> on line %d.", node->value.element.name, node->parent ? node->parent->value.element.name : "(null)", line);
mxmlDelete(first);
@ -2077,7 +2069,7 @@ mxml_load_data(
* Common error return...
*/
error:
error:
mxmlDelete(first);
@ -2096,7 +2088,8 @@ mxml_parse_element(
mxml_node_t *node, /* I - Element node */
void *p, /* I - Data to read from */
int *encoding, /* IO - Encoding */
_mxml_getc_cb_t getc_cb) /* I - Data callback */
_mxml_getc_cb_t getc_cb, /* I - Data callback */
int *line) /* IO - Current line number */
{
int ch, /* Current character in file */
quote; /* Quoting character */
@ -2143,7 +2136,12 @@ mxml_parse_element(
*/
if (mxml_isspace(ch))
{
if (ch == '\n')
(*line)++;
continue;
}
/*
* Stop at /, ?, or >...
@ -2159,8 +2157,7 @@ mxml_parse_element(
if (quote != '>')
{
mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
ch, node->value.element.name, quote);
mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, node->value.element.name, quote, *line);
goto error;
}
@ -2168,7 +2165,7 @@ mxml_parse_element(
}
else if (ch == '<')
{
mxml_error("Bare < in element %s!", node->value.element.name);
mxml_error("Bare < in element %s on line %d.", node->value.element.name, *line);
goto error;
}
else if (ch == '>')
@ -2192,8 +2189,12 @@ mxml_parse_element(
while ((ch = (*getc_cb)(p, encoding)) != EOF)
{
if (ch == '&')
if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
{
if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF)
goto error;
}
else if (ch == '\n')
(*line)++;
if (mxml_add_char(ch, &ptr, &name, &namesize))
goto error;
@ -2209,40 +2210,59 @@ mxml_parse_element(
*/
while ((ch = (*getc_cb)(p, encoding)) != EOF)
{
if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
ch == '?')
{
if (ch == '\n')
(*line)++;
break;
}
else
{
if (ch == '&')
if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
{
if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF)
goto error;
}
if (mxml_add_char(ch, &ptr, &name, &namesize))
goto error;
}
}
}
*ptr = '\0';
if (mxmlElementGetAttr(node, name))
{
mxml_error("Duplicate attribute '%s' in element %s on line %d.", name, node->value.element.name, name, *line);
goto error;
}
while (ch != EOF && mxml_isspace(ch))
{
ch = (*getc_cb)(p, encoding);
if (ch == '\n')
(*line)++;
}
if (ch == '=')
{
/*
* Read the attribute value...
*/
while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch))
{
if (ch == '\n')
(*line)++;
}
if (ch == EOF)
{
mxml_error("Missing value for attribute '%s' in element %s!",
name, node->value.element.name);
mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, node->value.element.name, *line);
goto error;
}
@ -2256,17 +2276,25 @@ mxml_parse_element(
ptr = value;
while ((ch = (*getc_cb)(p, encoding)) != EOF)
{
if (ch == quote)
{
break;
}
else
{
if (ch == '&')
if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
{
if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF)
goto error;
}
else if (ch == '\n')
(*line)++;
if (mxml_add_char(ch, &ptr, &value, &valsize))
goto error;
}
}
*ptr = '\0';
}
@ -2280,17 +2308,26 @@ mxml_parse_element(
ptr = value + 1;
while ((ch = (*getc_cb)(p, encoding)) != EOF)
{
if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
{
if (ch == '\n')
(*line)++;
break;
}
else
{
if (ch == '&')
if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
{
if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF)
goto error;
}
if (mxml_add_char(ch, &ptr, &value, &valsize))
goto error;
}
}
*ptr = '\0';
}
@ -2303,8 +2340,7 @@ mxml_parse_element(
}
else
{
mxml_error("Missing value for attribute '%s' in element %s!",
name, node->value.element.name);
mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, node->value.element.name, *line);
goto error;
}
@ -2322,8 +2358,7 @@ mxml_parse_element(
if (quote != '>')
{
mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
ch, node->value.element.name, quote);
mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, node->value.element.name, quote, *line);
ch = EOF;
}
@ -2346,7 +2381,7 @@ mxml_parse_element(
* Common error return point...
*/
error:
error:
free(name);
free(value);