%{
/***************************************
$Header: /home/amb/CVS/routino/src/xmlparse.l,v 1.20 2010-10-09 11:05:28 amb Exp $
A simple generic XML parser where the structure comes from the function parameters.
Not intended to be fully conforming to XML staandard or a validating parser but
sufficient to parse OSM XML and simple program configuration files.
Part of the Routino routing software.
******************/ /******************
This file Copyright 2010 Andrew M. Bishop
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
***************************************/
#include
#include
#include
#include
#include "xmlparse.h"
/* Parser outputs */
#define LEX_EOF 0
#define LEX_TAG_BEGIN 1
#define LEX_XML_DECL_BEGIN 2
#define LEX_TAG_POP 3
#define LEX_TAG_PUSH 4
#define LEX_XML_DECL_FINISH 6
#define LEX_TAG_FINISH 7
#define LEX_ATTR_KEY 8
#define LEX_ATTR_VAL 9
#define LEX_ERROR 100
#define LEX_ERROR_TAG_START 101
#define LEX_ERROR_XML_DECL_START 102
#define LEX_ERROR_TAG 103
#define LEX_ERROR_XML_DECL 104
#define LEX_ERROR_ATTR 105
#define LEX_ERROR_END_TAG 106
#define LEX_ERROR_COMMENT 107
#define LEX_ERROR_CLOSE 108
#define LEX_ERROR_ATTR_VAL 109
#define LEX_ERROR_ENTITY_REF 110
#define LEX_ERROR_CHAR_REF 111
#define LEX_ERROR_UNEXP_TAG 201
#define LEX_ERROR_UNBALANCED 202
#define LEX_ERROR_NO_START 203
#define LEX_ERROR_UNEXP_ATT 204
#define LEX_ERROR_UNEXP_EOF 205
#define LEX_ERROR_XML_NOT_FIRST 206
#define LEX_ERROR_CALLBACK 255
/* Lexer definitions */
#define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */
#ifndef yywrap
/*+ Needed in lex but does nothing. +*/
#define yywrap() 1
#endif
/*+ Reset the current string. +*/
#define reset_string \
if(!string) string=(char*)malloc(16); \
*string=0; \
stringused=0;
/*+ append information to the current string. +*/
#define append_string(xx) \
newlen=strlen(xx); \
if((stringused+newlen)>=stringlen) \
string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \
strcpy(string+stringused,xx); \
stringused+=newlen;
#define YY_NO_INPUT
/* Lexer functions and variables */
extern int yylex(void);
static char *yylval=NULL;
static int xmlparse_options;
%}
%option 8bit
%option pointer
%option batch
%option yylineno
%option nodefault
%option perf-report
%option fast
%option nounput
/* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII tags not Unicode. */
S [ \t\r\n]
U1 [\x09\x0A\x0D\x20-\x7F]
U2 [\xC2-\xDF][\x80-\xBF]
U3a \xE0[\xA0-\xBF][\x80-\xBF]
U3b [\xE1-\xEC][\x80-\xBF][\x80-\xBF]
U3c \xED[\x80-\x9F][\x80-\xBF]
U3d [\xEE-\xEF][\x80-\xBF][\x80-\xBF]
U3 {U3a}|{U3b}|{U3c}|{U3d}
U4a \xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]
U4b [\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]
U4c \xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]
U4 {U4a}|{U4b}|{U4c}
U ({U1}|{U2}|{U3}|{U4})
UquotedS ([\x09\x0A\x0D\x20-\x25\x28-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4})
UquotedD ([\x09\x0A\x0D\x20-\x21\x23-\x25\x27-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4})
N (\n|\r\n)
letter [a-zA-Z]
digit [0-9]
xdigit [a-fA-F0-9]
namechar ({letter}|{digit}|[-._:])
name ({letter}|[_:]){namechar}*
entityref &{name};
charref ({digit}+|x{xdigit}+);
%x COMMENT
%x CDATA
%x DOCTYPE
%x XML_DECL_START XML_DECL
%x TAG_START TAG
%x ATTR_KEY ATTR_VAL
%x END_TAG1 END_TAG2
%x DQUOTED SQUOTED
%%
/* Must use static variables since the parser returns often. */
static char *string=NULL;
static int stringlen=0,stringused=0;
static int after_attr=0;
int newlen;
int doctype_depth=0;
/* Handle top level entities */
"" { return(LEX_ERROR_COMMENT); }
"-->" { BEGIN(INITIAL); }
"--"[^->]+ { }
[^-]+ { }
"-" { }
/* CDATA */
"]]>" { BEGIN(INITIAL); }
"]" { }
[^]]+ { }
/* CDATA */
"<" { doctype_depth++; }
">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; }
[^<>]+ { }
/* XML Declaration start */
xml { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); }
.|{N} { return(LEX_ERROR_XML_DECL_START); }
/* Tag middle */
"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); }
{S}+ { }
{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
.|{N} { return(LEX_ERROR_XML_DECL); }
/* Any tag start */
{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); }
.|{N} { return(LEX_ERROR_TAG_START); }
/* End-tag start */
{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); }
.|{N} { return(LEX_ERROR_END_TAG); }
">" { BEGIN(INITIAL); }
.|{N} { return(LEX_ERROR_END_TAG); }
/* Any tag middle */
"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); }
">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); }
{S}+ { }
{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
.|{N} { return(LEX_ERROR_TAG); }
/* Attributes */
= { BEGIN(ATTR_VAL); }
.|{N} { return(LEX_ERROR_ATTR); }
\" { BEGIN(DQUOTED); reset_string; }
\' { BEGIN(SQUOTED); reset_string; }
.|{N} { return(LEX_ERROR_ATTR); }
/* Quoted strings */
\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
[<>&\"] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
{UquotedD}+ { append_string(yytext); }
. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
{UquotedS}+ { append_string(yytext); }
. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
/* End of file */
<> { free(string); string=NULL; stringlen=stringused=0; BEGIN(INITIAL); return(LEX_EOF); }
%%
/*++++++++++++++++++++++++++++++++++++++
A function to call the callback function with the parameters needed.
int call_callback Returns 1 if the callback returned with an error.
const char *name The name of the tag.
int (*callback)() The callback function.
int type The type of tag (start and/or end).
int nattributes The number of attributes collected.
char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes.
++++++++++++++++++++++++++++++++++++++*/
static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS])
{
switch(nattributes)
{
case 0: return (*callback)(name,type);
case 1: return (*callback)(name,type,attributes[0]);
case 2: return (*callback)(name,type,attributes[0],attributes[1]);
case 3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]);
case 4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]);
case 5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]);
case 6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]);
case 7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]);
case 8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]);
case 9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]);
case 10: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]);
case 11: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]);
case 12: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11]);
case 13: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12]);
case 14: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13]);
case 15: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14]);
case 16: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14],attributes[15]);
default:
fprintf(stderr,"XML Parser: Error on line %d: too many attributes for tag '%s' source code needs changing.\n",yylineno,name);
exit(1);
}
}
/*++++++++++++++++++++++++++++++++++++++
Parse the XML and call the functions for each tag as seen.
int ParseXML Returns 0 if OK or something else in case of an error.
FILE *file The file to parse.
xmltag **tags The array of pointers to tags for the top level.
int options A list of XML Parser options OR-ed together.
++++++++++++++++++++++++++++++++++++++*/
int ParseXML(FILE *file,xmltag **tags,int options)
{
int yychar,i;
char *attributes[XMLPARSE_MAX_ATTRS]={NULL};
int attribute=0;
int stackdepth=0,stackused=0;
xmltag ***tags_stack=NULL;
xmltag **tag_stack=NULL;
xmltag *tag=NULL;
/* The actual parser. */
xmlparse_options=options;
yyin=file;
yyrestart(yyin);
yylineno=1;
BEGIN(INITIAL);
do
{
yychar=yylex();
switch(yychar)
{
/* The start of a tag for an XML declaration */
case LEX_XML_DECL_BEGIN:
if(tag_stack)
{
fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno);
yychar=LEX_ERROR_XML_NOT_FIRST;
break;
}
/* The start of a tag for an element */
case LEX_TAG_BEGIN:
tag=NULL;
for(i=0;tags[i];i++)
if(!strcasecmp(yylval,tags[i]->name))
{
tag=tags[i];
for(i=0;inattributes;i++)
if(attributes[i])
{
free(attributes[i]);
attributes[i]=NULL;
}
break;
}
if(tag==NULL)
{
fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval);
yychar=LEX_ERROR_UNEXP_TAG;
}
break;
/* The end of the start-tag for an element */
case LEX_TAG_PUSH:
if(stackused==stackdepth)
{
tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*));
tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**));
}
tag_stack [stackused]=tag;
tags_stack[stackused]=tags;
stackused++;
if(tag->callback)
if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes))
yychar=LEX_ERROR_CALLBACK;
tags=tag->subtags;
break;
/* The end of the empty-element-tag for an XML declaration */
case LEX_XML_DECL_FINISH:
/* The end of the empty-element-tag for an element */
case LEX_TAG_FINISH:
if(tag->callback)
if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes))
yychar=LEX_ERROR_CALLBACK;
if(stackused>0)
tag=tag_stack[stackused-1];
else
tag=NULL;
break;
/* The end of the end-tag for an element */
case LEX_TAG_POP:
stackused--;
tags=tags_stack[stackused];
tag =tag_stack [stackused];
if(strcmp(tag->name,yylval))
{
fprintf(stderr,"XML Parser: Error on line %d: end tag '%s>' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name);
yychar=LEX_ERROR_UNBALANCED;
}
if(stackused<0)
{
fprintf(stderr,"XML Parser: Error on line %d: end tag '%s>' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval);
yychar=LEX_ERROR_NO_START;
}
for(i=0;inattributes;i++)
if(attributes[i])
{
free(attributes[i]);
attributes[i]=NULL;
}
if(tag->callback)
if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes))
yychar=LEX_ERROR_CALLBACK;
if(stackused>0)
tag=tag_stack[stackused-1];
else
tag=NULL;
break;
/* An attribute key */
case LEX_ATTR_KEY:
attribute=-1;
for(i=0;inattributes;i++)
if(!strcasecmp(yylval,tag->attributes[i]))
{
attribute=i;
break;
}
if(attribute==-1)
{
if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR ||
((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr(yylval,':')))
{
fprintf(stderr,"XML Parser: Error on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
yychar=LEX_ERROR_UNEXP_ATT;
}
else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN)
fprintf(stderr,"XML Parser: Warning on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
}
break;
/* An attribute value */
case LEX_ATTR_VAL:
if(tag->callback && attribute!=-1 && yylval)
attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval);
break;
/* End of file */
case LEX_EOF:
if(tag)
{
fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag '%s>'.\n",yylineno,tag->name);
yychar=LEX_ERROR_UNEXP_EOF;
}
break;
case LEX_ERROR_TAG_START:
fprintf(stderr,"XML Parser: Error on line %d: character '<' seen not at start of tag.\n",yylineno);
break;
case LEX_ERROR_XML_DECL_START:
fprintf(stderr,"XML Parser: Error on line %d: characters '' seen not at start of XML declaration.\n",yylineno);
break;
case LEX_ERROR_TAG:
fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside tag '<%s...>'.\n",yylineno,tag->name);
break;
case LEX_ERROR_XML_DECL:
fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside XML declaration '%s...>'.\n",yylineno,tag->name);
break;
case LEX_ERROR_ATTR:
fprintf(stderr,"XML Parser: Error on line %d: invalid attribute definition seen in tag.\n",yylineno);
break;
case LEX_ERROR_END_TAG:
fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in end-tag.\n",yylineno);
break;
case LEX_ERROR_COMMENT:
fprintf(stderr,"XML Parser: Error on line %d: invalid comment seen.\n",yylineno);
break;
case LEX_ERROR_CLOSE:
fprintf(stderr,"XML Parser: Error on line %d: character '>' seen not at end of tag.\n",yylineno);
break;
case LEX_ERROR_ATTR_VAL:
fprintf(stderr,"XML Parser: Error on line %d: invalid character '%s' seen in attribute value.\n",yylineno,yylval);
break;
case LEX_ERROR_ENTITY_REF:
fprintf(stderr,"XML Parser: Error on line %d: invalid entity reference '%s' seen in attribute value.\n",yylineno,yylval);
break;
case LEX_ERROR_CHAR_REF:
fprintf(stderr,"XML Parser: Error on line %d: invalid character reference '%s' seen in attribute value.\n",yylineno,yylval);
break;
}
}
while(yychar>LEX_EOF && yychar");
if(!strcmp(string,"'")) return("'");
if(!strcmp(string,""")) return("\"");
return(NULL);
}
/*++++++++++++++++++++++++++++++++++++++
Convert an XML character reference into an ASCII string.
char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string.
const char *string The character reference string.
++++++++++++++++++++++++++++++++++++++*/
char *ParseXML_Decode_Char_Ref(const char *string)
{
static char result[2]=" ";
long int val;
if(string[2]=='x') val=strtol(string+3,NULL,16);
else val=strtol(string+2,NULL,10);
if(val<0 || val>255)
return(NULL);
result[0]=val&0xff;
return(result);
}
/*++++++++++++++++++++++++++++++++++++++
Convert a string into something that is safe to output in an XML file.
char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed).
const char *string The string to convert.
++++++++++++++++++++++++++++++++++++++*/
char *ParseXML_Encode_Safe_XML(const char *string)
{
static const char hexstring[17]="0123456789ABCDEF";
int i=0,j=0,len;
char *result;
for(i=0;string[i];i++)
if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || (unsigned char)string[i]>127)
break;
if(!string[i])
return((char*)string);
len=i+256-6;
result=(char*)malloc(len+7);
strncpy(result,string,j=i);
do
{
for(;j')
{
result[j++]='&';
result[j++]='g';
result[j++]='t';
result[j++]=';';
}
else if(string[i]=='&')
{
result[j++]='&';
result[j++]='a';
result[j++]='m';
result[j++]='p';
result[j++]=';';
}
else if(string[i]=='\'')
{
result[j++]='&';
result[j++]='a';
result[j++]='p';
result[j++]='o';
result[j++]='s';
result[j++]=';';
}
else if(string[i]=='"')
{
result[j++]='&';
result[j++]='q';
result[j++]='u';
result[j++]='o';
result[j++]='t';
result[j++]=';';
}
else if(string[i]>=32 && (unsigned char)string[i]<=127)
result[j++]=string[i];
else
{
unsigned int unicode;
/* Decode the UTF-8 */
if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80)
{
/* 0000 0080-0000 07FF 110xxxxx 10xxxxxx */
unicode =(string[i++]&0x1F)<<6;
unicode|= string[i ]&0x3F;
}
else if((string[i]&0xF0)==0xE0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80)
{
/* 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx */
unicode =(string[i++]&0x0F)<<12;
unicode|=(string[i++]&0x3F)<<6;
unicode|= string[i ]&0x3F;
}
else if((string[i]&0xF8)==0xF0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80 && (string[i+3]&0xC0)==0x80)
{
/* 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
unicode =(string[i++]&0x07)<<18;
unicode|=(string[i++]&0x3F)<<12;
unicode|=(string[i++]&0x3F)<<6;
unicode|= string[i ]&0x3F;
}
else
unicode=0xFFFD;
/* Output the character entity */
result[j++]='&';
result[j++]='#';
result[j++]='x';
if(unicode&0x00FF0000)
{
result[j++]=hexstring[((unicode>>16)&0xf0)>>4];
result[j++]=hexstring[((unicode>>16)&0x0f) ];
}
if(unicode&0x00FFFF00)
{
result[j++]=hexstring[((unicode>>8)&0xf0)>>4];
result[j++]=hexstring[((unicode>>8)&0x0f) ];
}
result[j++]=hexstring[(unicode&0xf0)>>4];
result[j++]=hexstring[(unicode&0x0f) ];
result[j++]=';';
}
if(string[i]) /* Not finished */
{
len+=256;
result=(char*)realloc((void*)result,len+7);
}
}
while(string[i]);
result[j]=0;
return(result);
}
/*++++++++++++++++++++++++++++++++++++++
Convert a string to a integer (checking that it really is a integer).
int ParseXML_GetInteger Returns 1 if a integer could be found or 0 otherwise.
const char *string The string to be parsed.
int *number Returns the number.
++++++++++++++++++++++++++++++++++++++*/
int ParseXML_GetInteger(const char *string,int *number)
{
const char *p=string;
if(*p=='-' || *p=='+')
p++;
while(isdigit(*p))
p++;
if(*p)
return(0);
*number=atoi(string);
return(1);
}
/*++++++++++++++++++++++++++++++++++++++
Convert a string to a floating point number (checking that it really is a number).
int ParseXML_GetFloating Returns 1 if a number could be found or 0 otherwise.
const char *string The string to be parsed.
int *number Returns the number.
++++++++++++++++++++++++++++++++++++++*/
int ParseXML_GetFloating(const char *string,double *number)
{
const char *p=string;
if(*p=='-' || *p=='+')
p++;
while(isdigit(*p) || *p=='.')
p++;
if(*p=='e' || *p=='E')
{
p++;
if(*p=='-' || *p=='+')
p++;
while(isdigit(*p))
p++;
}
if(*p)
return(0);
*number=atof(string);
return(1);
}