/* Copyright (c) 2003, 2011, Oracle and/or its affiliates.
Copyright ( c ) 2011 Monty Program Ab
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "strings_def.h"
#include "m_string.h"
#include "my_xml.h"
#include "my_sys.h"
#define MY_XML_UNKNOWN 'U'
#define MY_XML_EOF 'E'
#define MY_XML_STRING 'S'
#define MY_XML_IDENT 'I'
#define MY_XML_EQ '='
#define MY_XML_LT '<'
#define MY_XML_GT '>'
#define MY_XML_SLASH '/'
#define MY_XML_COMMENT 'C'
#define MY_XML_TEXT 'T'
#define MY_XML_PROCESSING_INSTRUCTION '?'
#define MY_XML_CDATA 'A'
#define MY_XML_DTD 'D'
typedef struct xml_attr_st
{
const char *beg;
const char *end;
} MY_XML_ATTR;
/*
XML ctype :
*/
#define MY_XML_ID0 0 x01 /* Identifier initial character */
#define MY_XML_ID1 0 x02 /* Identifier medial character */
#define MY_XML_SPC 0 x08 /* Spacing character */
/*
http : //www.w3.org/TR/REC-xml/
[ 4 ] NameChar : : = Letter | Digit | ' . ' | ' - ' | ' _ ' | ' : ' |
CombiningChar | Extender
[ 5 ] Name : : = ( Letter | ' _ ' | ' : ' ) ( NameChar ) *
*/
static char my_xml_ctype[256 ]=
{
/*00*/ 0,0,0,0,0,0,0,0,0,8,8,0,0,8,0,0,
/*10*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/*20*/ 8,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0, /* !"#$%&'()*+,-./ */
/*30*/ 2,2,2,2,2,2,2,2,2,2,3,0,0,0,0,0, /* 0123456789:;<=>? */
/*40*/ 0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* @ABCDEFGHIJKLMNO */
/*50*/ 3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3, /* PQRSTUVWXYZ[\]^_ */
/*60*/ 0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* `abcdefghijklmno */
/*70*/ 3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0, /* pqrstuvwxyz{|}~ */
/*80*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*90*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*A0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*B0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*C0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*D0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*E0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/*F0*/ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
};
#define my_xml_is_space(c) (my_xml_ctype[(uchar) (c)] & MY_XML_SPC)
#define my_xml_is_id0(c) (my_xml_ctype[(uchar) (c)] & MY_XML_ID0)
#define my_xml_is_id1(c) (my_xml_ctype[(uchar) (c)] & MY_XML_ID1)
static const char *lex2str(int lex)
{
switch (lex)
{
case MY_XML_EOF: return "END-OF-INPUT" ;
case MY_XML_STRING: return "STRING" ;
case MY_XML_IDENT: return "IDENT" ;
case MY_XML_CDATA: return "CDATA" ;
case MY_XML_EQ: return "'='" ;
case MY_XML_LT: return "'<'" ;
case MY_XML_GT: return "'>'" ;
case MY_XML_SLASH: return "'/'" ;
case MY_XML_COMMENT: return "COMMENT" ;
case MY_XML_TEXT: return "TEXT" ;
case MY_XML_PROCESSING_INSTRUCTION: return "'?'" ;
case MY_XML_DTD: return "DTD" ;
}
return "unknown token" ;
}
static void my_xml_norm_text(MY_XML_ATTR *a)
{
for ( ; (a->beg < a->end) && my_xml_is_space(a->beg[0 ]) ; a->beg++ );
for ( ; (a->beg < a->end) && my_xml_is_space(a->end[-1 ]) ; a->end-- );
}
static inline my_bool
my_xml_parser_prefix_cmp(MY_XML_PARSER *p, const char *s, size_t slen)
{
return (p->cur + slen > p->end) || memcmp(p->cur, s, slen);
}
static int my_xml_scan(MY_XML_PARSER *p,MY_XML_ATTR *a)
{
int lex;
for (; ( p->cur < p->end) && my_xml_is_space(p->cur[0 ]) ; p->cur++);
if (p->cur >= p->end)
{
a->beg=p->end;
a->end=p->end;
lex=MY_XML_EOF;
goto ret;
}
a->beg=p->cur;
a->end=p->cur;
if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<!--" )))
{
for (; p->cur < p->end; p->cur++)
{
if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("-->" )))
{
p->cur+= 3 ;
break ;
}
}
a->end=p->cur;
lex=MY_XML_COMMENT;
}
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<?" )))
{
lex= MY_XML_UNKNOWN;
p->cur+= 2 ;
a->beg= p->cur;
if (my_xml_is_id0(p->cur[0 ]))
{
int termination_expected= FALSE ;
p->cur++;
while (p->cur < p->end && my_xml_is_id1(p->cur[0 ]))
p->cur++;
a->end=p->cur;
/*
After the target name the syntax requires the
terminating ' ? > ' or some spaces then an
arbitrary string to the ' > ? ' .
*/
if (p->cur < p->end && !my_xml_is_space(p->cur[0 ]))
termination_expected= TRUE ;
while (p->cur < p->end)
{
if (my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("?>" )) == 0 )
{
lex= MY_XML_PROCESSING_INSTRUCTION;
p->cur+= 2 ;
break ;
}
else if (termination_expected)
break ;
p->cur++;
}
}
}
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<![CDATA[" )))
{
p->cur+= 9 ;
for (; p->cur < p->end - 2 ; p->cur++)
{
if (p->cur[0 ] == ']' && p->cur[1 ] == ']' && p->cur[2 ] == '>' )
{
p->cur+= 3 ;
a->end= p->cur;
break ;
}
}
lex= MY_XML_CDATA;
}
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<!DOCTYPE" )))
{
/* skip <!DOCTYPE part. TODO: add parser for the DTD */
int in_quote= 0 ;
int level= 1 ;
p->cur+= 9 ;
lex= MY_XML_UNKNOWN;
for (; p->cur < p->end; p->cur++)
{
if (p->cur[0 ] == '"' || p->cur[0 ] == '\' ')
{
if (!in_quote)
in_quote= p->cur[0 ];
else
{
if (p->cur[0 ] == in_quote)
in_quote= 0 ;
}
}
if (in_quote)
continue ;
switch (p->cur[0 ])
{
case MY_XML_LT:
{
level++;
break ;
}
case MY_XML_GT:
{
level--;
if (level == 0 )
{
lex= MY_XML_DTD;
p->cur++;
goto dtd_end;
}
}
default :;
}
}
dtd_end:
a->end= p->cur;
}
else if (strchr("=/<>" ,p->cur[0 ]))
{
p->cur++;
a->end=p->cur;
lex=a->beg[0 ];
}
else if ( (p->cur[0 ] == '"' ) || (p->cur[0 ] == '\' ') )
{
/*
" string " or ' string ' found .
Scan until the closing quote / doublequote , or until the END - OF - INPUT .
*/
p->cur++;
for (; ( p->cur < p->end ) && (p->cur[0 ] != a->beg[0 ]); p->cur++)
{}
a->end=p->cur;
if (p->cur < p->end) /* Closing quote or doublequote has been found */
p->cur++;
a->beg++;
if (!(p->flags & MY_XML_FLAG_SKIP_TEXT_NORMALIZATION))
my_xml_norm_text(a);
lex=MY_XML_STRING;
}
else if (my_xml_is_id0(p->cur[0 ]))
{
p->cur++;
while (p->cur < p->end && my_xml_is_id1(p->cur[0 ]))
p->cur++;
a->end=p->cur;
my_xml_norm_text(a);
lex=MY_XML_IDENT;
}
else
lex= MY_XML_UNKNOWN;
#if 0
printf("LEX=%s[%d]\n" ,lex2str(lex),a->end-a->beg);
#endif
ret:
return lex;
}
static int my_xml_value(MY_XML_PARSER *st, const char *str, size_t len)
{
if ((st->flags & MY_XML_FLAG_ASSERT_WELL_FORMED) &&
st->attr.end == st->attr.start)
{
/*
Values are not allowed on the root level of XML .
Though spaces are ok .
*/
while (len > 0 )
{
if (!my_xml_is_space(str[--len]))
return MY_XML_ERROR;
}
return MY_XML_OK;
}
return (st->value) ? (st->value)(st,str,len) : MY_XML_OK;
}
/**
Ensure the attr buffer is wide enough to hold the new value
Expand and / or allocate dynamic buffer as needed to hold the concatenated
path and the terminating zero .
@ attr st the parser instance
@ attr len the length of the attribute to be added
@ return state
@ retval 1 failed
@ retval 0 success
*/
static int my_xml_attr_ensure_space(MY_XML_PARSER *st, size_t len)
{
size_t ofs= st->attr.end - st->attr.start;
len++; // Add terminating zero.
if (ofs + len > st->attr.buffer_size)
{
st->attr.buffer_size= (SIZE_T_MAX - len) / 2 > st->attr.buffer_size ?
st->attr.buffer_size * 2 + len : SIZE_T_MAX;
if (!st->attr.buffer)
{
st->attr.buffer= (char *) my_malloc(PSI_INSTRUMENT_ME, st->attr.buffer_size, MYF(0 ));
if (st->attr.buffer)
memcpy(st->attr.buffer, st->attr.static_buffer, ofs + 1 /*term. zero */);
}
else
st->attr.buffer= (char *) my_realloc(PSI_INSTRUMENT_ME, st->attr.buffer,
st->attr.buffer_size, MYF(0 ));
st->attr.start= st->attr.buffer;
st->attr.end= st->attr.start + ofs;
return st->attr.buffer ? MY_XML_OK : MY_XML_ERROR;
}
return MY_XML_OK;
}
/** rewind the attr buffer to initial state */
static void my_xml_attr_rewind(MY_XML_PARSER *p)
{
/* keep the buffer already allocated */
p->attr.end= p->attr.start;
}
static int my_xml_enter(MY_XML_PARSER *st, const char *str, size_t len)
{
if (my_xml_attr_ensure_space(st, len + 1 /* the separator char */))
return MY_XML_ERROR;
if (st->attr.end > st->attr.start)
{
st->attr.end[0 ]= '/' ;
st->attr.end++;
}
else if ((st->flags & MY_XML_FLAG_ASSERT_WELL_FORMED) &&
st->current_node_type == MY_XML_NODE_TAG)
{
/* level 0 nesting of the XML document .*/
if (st->root_node_counter++)
return MY_XML_ERROR; /* no multiple root nodes allowed. */
st->item_counter++;
}
memcpy(st->attr.end, str, len);
st->attr.end+= len;
st->attr.end[0 ]= '\0' ;
if (st->flags & MY_XML_FLAG_RELATIVE_NAMES)
return st->enter ? st->enter(st, str, len) : MY_XML_OK;
else
return st->enter ?
st->enter(st, st->attr.start, st->attr.end - st->attr.start) : MY_XML_OK;
}
static void mstr(char *s,const char *src,size_t l1, size_t l2)
{
l1 = l1<l2 ? l1 : l2;
memcpy(s,src,l1);
s[l1]='\0' ;
}
static int my_xml_leave(MY_XML_PARSER *p, const char *str, size_t slen)
{
char *e, *tag;
size_t glen;
char s[32 ];
char g[32 ];
int rc;
/* Find previous '/' or beginning */
for (e= p->attr.end; (e > p->attr.start) && (e[0 ] != '/' ) ; e--);
glen= (size_t) ((e[0 ] == '/' ) ? (p->attr.end - e - 1 ) : p->attr.end - e);
tag= e[0 ] == '/' ? e + 1 : e;
if (str && (slen != glen || memcmp(str, tag, slen)))
{
mstr(s,str,sizeof (s)-1 ,slen);
if (glen)
{
mstr(g, tag, sizeof (g)-1 , glen);
snprintf(p->errstr,sizeof (p->errstr),"'</%s>' unexpected ('</%s>' wanted)" ,s,g);
}
else
snprintf(p->errstr,sizeof (p->errstr),"'</%s>' unexpected (END-OF-INPUT wanted)" , s);
return MY_XML_ERROR;
}
if (p->flags & MY_XML_FLAG_RELATIVE_NAMES)
rc= p->leave_xml ? p->leave_xml(p, str, slen) : MY_XML_OK;
else
rc= (p->leave_xml ?
p->leave_xml(p, p->attr.start, p->attr.end - p->attr.start) :
MY_XML_OK);
*e='\0' ;
p->attr.end= e;
return rc;
}
int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
{
my_xml_attr_rewind(p);
p->beg=str;
p->cur=str;
p->end=str+len;
while ( p->cur < p->end )
{
MY_XML_ATTR a;
if (p->cur[0 ] == '<' )
{
int lex;
lex=my_xml_scan(p,&a);
if (MY_XML_COMMENT == lex ||
MY_XML_DTD == lex)
{
p->item_counter++;
continue ;
}
if (MY_XML_PROCESSING_INSTRUCTION == lex)
{
size_t tag_len= (size_t) (a.end - a.beg);
if ((p->flags & MY_XML_FLAG_ASSERT_WELL_FORMED) &&
tag_len == 3 && memcmp(a.beg, "xml" , 3 ) == 0 )
{
/*
The < ? xml . . ? > processing instruction has special meaning .
If appears it must be first line of the XML document .
*/
if (p->item_counter)
return MY_XML_ERROR;
}
p->item_counter++;
if (p->processing_instruction &&
MY_XML_OK != p->processing_instruction(p,
a.beg, (size_t) (a.end-a.beg),
a.end, (size_t) (p->cur - a.end - 2 )))
return MY_XML_ERROR;
continue ;
}
if (lex == MY_XML_CDATA)
{
a.beg+= 9 ;
a.end-= 3 ;
if (MY_XML_OK != my_xml_value(p, a.beg, (size_t) (a.end-a.beg)))
return MY_XML_ERROR;
continue ;
}
lex=my_xml_scan(p,&a);
if (MY_XML_SLASH == lex)
{
if (MY_XML_IDENT != (lex=my_xml_scan(p,&a)))
{
snprintf(p->errstr,sizeof (p->errstr),"%s unexpected (ident wanted)" ,lex2str(lex));
return MY_XML_ERROR;
}
if (MY_XML_OK != my_xml_leave(p,a.beg,(size_t) (a.end-a.beg)))
return MY_XML_ERROR;
lex=my_xml_scan(p,&a);
goto gt;
}
if (MY_XML_IDENT == lex)
{
p->current_node_type= MY_XML_NODE_TAG;
if (MY_XML_OK != my_xml_enter(p,a.beg,(size_t) (a.end-a.beg)))
return MY_XML_ERROR;
}
else
{
snprintf(p->errstr,sizeof (p->errstr),"%s unexpected (ident or '/' wanted)" ,
lex2str(lex));
return MY_XML_ERROR;
}
while (MY_XML_IDENT == (lex=my_xml_scan(p,&a)))
{
MY_XML_ATTR b;
if (MY_XML_EQ == (lex=my_xml_scan(p,&b)))
{
lex=my_xml_scan(p,&b);
if ( (lex == MY_XML_IDENT) || (lex == MY_XML_STRING) )
{
p->current_node_type= MY_XML_NODE_ATTR;
if ((MY_XML_OK != my_xml_enter(p,a.beg,(size_t) (a.end-a.beg))) ||
(MY_XML_OK != my_xml_value(p,b.beg,(size_t) (b.end-b.beg))) ||
(MY_XML_OK != my_xml_leave(p,a.beg,(size_t) (a.end-a.beg))))
return MY_XML_ERROR;
}
else
{
snprintf(p->errstr,sizeof (p->errstr),"%s unexpected (ident or string wanted)" ,
lex2str(lex));
return MY_XML_ERROR;
}
}
else if (MY_XML_IDENT == lex)
{
p->current_node_type= MY_XML_NODE_ATTR;
if ((MY_XML_OK != my_xml_enter(p,a.beg,(size_t) (a.end-a.beg))) ||
(MY_XML_OK != my_xml_leave(p,a.beg,(size_t) (a.end-a.beg))))
return MY_XML_ERROR;
}
else
break ;
}
if (lex == MY_XML_SLASH)
{
if (MY_XML_OK != my_xml_leave(p,NULL,0 ))
return MY_XML_ERROR;
lex=my_xml_scan(p,&a);
}
gt:
if (lex != MY_XML_GT)
{
snprintf(p->errstr,sizeof (p->errstr),"%s unexpected ('>' wanted)" ,lex2str(lex));
return MY_XML_ERROR;
}
}
else
{
a.beg=p->cur;
for ( ; (p->cur < p->end) && (p->cur[0 ] != '<' ) ; p->cur++);
a.end=p->cur;
if (!(p->flags & MY_XML_FLAG_SKIP_TEXT_NORMALIZATION))
my_xml_norm_text(&a);
if (a.beg != a.end)
{
if (MY_XML_OK != my_xml_value(p, a.beg, (size_t) (a.end-a.beg)))
return MY_XML_ERROR;
}
}
}
if (p->attr.start[0 ] ||
((p->flags & MY_XML_FLAG_ASSERT_WELL_FORMED) &&
p->root_node_counter == 0 ))
{
snprintf(p->errstr,sizeof (p->errstr),"unexpected END-OF-INPUT" );
return MY_XML_ERROR;
}
return MY_XML_OK;
}
void my_xml_parser_create(MY_XML_PARSER *p)
{
memset(p, 0 , sizeof (p[0 ]));
/*
Use static buffer while it ' s sufficient .
*/
p->attr.start= p->attr.end= p->attr.static_buffer;
p->attr.buffer_size= sizeof (p->attr.static_buffer);
}
void my_xml_parser_free(MY_XML_PARSER *p)
{
if (p->attr.buffer)
{
my_free(p->attr.buffer);
p->attr.buffer= NULL;
}
}
void my_xml_set_value_handler(MY_XML_PARSER *p,
int (*action)(MY_XML_PARSER *p, const char *s,
size_t l))
{
p->value=action;
}
void my_xml_set_enter_handler(MY_XML_PARSER *p,
int (*action)(MY_XML_PARSER *p, const char *s,
size_t l))
{
p->enter=action;
}
void my_xml_set_leave_handler(MY_XML_PARSER *p,
int (*action)(MY_XML_PARSER *p, const char *s,
size_t l))
{
p->leave_xml=action;
}
void my_xml_set_processing_instruction_handler(MY_XML_PARSER *p,
int (*p_i)(MY_XML_PARSER *, const char *, size_t,
const char *, size_t))
{
p->processing_instruction= p_i;
}
void my_xml_set_user_data(MY_XML_PARSER *p, void *user_data)
{
p->user_data=user_data;
}
const char *my_xml_error_string(MY_XML_PARSER *p)
{
return p->errstr;
}
size_t my_xml_error_pos(MY_XML_PARSER *p)
{
const char *beg=p->beg;
const char *s;
for ( s=p->beg ; s<p->cur; s++)
{
if (s[0 ] == '\n' )
beg=s;
}
return (size_t) (p->cur-beg);
}
uint my_xml_error_lineno(MY_XML_PARSER *p)
{
uint res=0 ;
const char *s;
for (s=p->beg ; s<p->cur; s++)
{
if (s[0 ] == '\n' )
res++;
}
return res;
}
Messung V0.5 in Prozent C=95 H=86 G=90
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland