2 /***************************************
3 $Header: /home/amb/routino/src/RCS/xmlparse.l,v 1.20 2010/10/09 11:05:28 amb Exp $
5 A simple generic XML parser where the structure comes from the function parameters.
6 Not intended to be fully conforming to XML staandard or a validating parser but
7 sufficient to parse OSM XML and simple program configuration files.
9 Part of the Routino routing software.
10 ******************/ /******************
11 This file Copyright 2010 Andrew M. Bishop
13 This program is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Affero General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Affero General Public License for more details.
23 You should have received a copy of the GNU Affero General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ***************************************/
40 #define LEX_TAG_BEGIN 1
41 #define LEX_XML_DECL_BEGIN 2
43 #define LEX_TAG_PUSH 4
44 #define LEX_XML_DECL_FINISH 6
45 #define LEX_TAG_FINISH 7
46 #define LEX_ATTR_KEY 8
47 #define LEX_ATTR_VAL 9
51 #define LEX_ERROR_TAG_START 101
52 #define LEX_ERROR_XML_DECL_START 102
53 #define LEX_ERROR_TAG 103
54 #define LEX_ERROR_XML_DECL 104
55 #define LEX_ERROR_ATTR 105
56 #define LEX_ERROR_END_TAG 106
57 #define LEX_ERROR_COMMENT 107
58 #define LEX_ERROR_CLOSE 108
59 #define LEX_ERROR_ATTR_VAL 109
60 #define LEX_ERROR_ENTITY_REF 110
61 #define LEX_ERROR_CHAR_REF 111
63 #define LEX_ERROR_UNEXP_TAG 201
64 #define LEX_ERROR_UNBALANCED 202
65 #define LEX_ERROR_NO_START 203
66 #define LEX_ERROR_UNEXP_ATT 204
67 #define LEX_ERROR_UNEXP_EOF 205
68 #define LEX_ERROR_XML_NOT_FIRST 206
70 #define LEX_ERROR_CALLBACK 255
73 /* Lexer definitions */
75 #define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */
77 /*+ Needed in lex but does nothing. +*/
81 /*+ Reset the current string. +*/
82 #define reset_string \
83 if(!string) string=(char*)malloc(16); \
87 /*+ append information to the current string. +*/
88 #define append_string(xx) \
90 if((stringused+newlen)>=stringlen) \
91 string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \
92 strcpy(string+stringused,xx); \
98 /* Lexer functions and variables */
100 extern int yylex(void);
102 static char *yylval=NULL;
104 static int xmlparse_options;
119 /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII tags not Unicode. */
123 U1 [\x09\x0A\x0D\x20-\x7F]
124 U2 [\xC2-\xDF][\x80-\xBF]
125 U3a \xE0[\xA0-\xBF][\x80-\xBF]
126 U3b [\xE1-\xEC][\x80-\xBF][\x80-\xBF]
127 U3c \xED[\x80-\x9F][\x80-\xBF]
128 U3d [\xEE-\xEF][\x80-\xBF][\x80-\xBF]
129 U3 {U3a}|{U3b}|{U3c}|{U3d}
130 U4a \xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]
131 U4b [\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]
132 U4c \xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]
135 U ({U1}|{U2}|{U3}|{U4})
136 UquotedS ([\x09\x0A\x0D\x20-\x25\x28-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4})
137 UquotedD ([\x09\x0A\x0D\x20-\x21\x23-\x25\x27-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4})
145 namechar ({letter}|{digit}|[-._:])
146 name ({letter}|[_:]){namechar}*
149 charref &#({digit}+|x{xdigit}+);
155 %x XML_DECL_START XML_DECL
162 /* Must use static variables since the parser returns often. */
163 static char *string=NULL;
164 static int stringlen=0,stringused=0;
165 static int after_attr=0;
169 /* Handle top level entities */
171 "<!--" { BEGIN(COMMENT); }
172 "<![CDATA[" { BEGIN(CDATA); }
173 "<!DOCTYPE" { BEGIN(DOCTYPE); doctype_depth=0; }
174 "</" { BEGIN(END_TAG1); }
175 "<?" { BEGIN(XML_DECL_START); }
176 "<" { BEGIN(TAG_START); }
177 ">" { return(LEX_ERROR_CLOSE); }
182 <COMMENT>"--->" { return(LEX_ERROR_COMMENT); }
183 <COMMENT>"-->" { BEGIN(INITIAL); }
184 <COMMENT>"--"[^->]+ { }
190 <CDATA>"]]>" { BEGIN(INITIAL); }
196 <DOCTYPE>"<" { doctype_depth++; }
197 <DOCTYPE>">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; }
200 /* XML Declaration start */
202 <XML_DECL_START>xml { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); }
203 <XML_DECL_START>.|{N} { return(LEX_ERROR_XML_DECL_START); }
207 <XML_DECL>"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); }
209 <XML_DECL>{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
210 <XML_DECL>.|{N} { return(LEX_ERROR_XML_DECL); }
214 <TAG_START>{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); }
215 <TAG_START>.|{N} { return(LEX_ERROR_TAG_START); }
219 <END_TAG1>{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); }
220 <END_TAG1>.|{N} { return(LEX_ERROR_END_TAG); }
222 <END_TAG2>">" { BEGIN(INITIAL); }
223 <END_TAG2>.|{N} { return(LEX_ERROR_END_TAG); }
227 <TAG>"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); }
228 <TAG>">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); }
230 <TAG>{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
231 <TAG>.|{N} { return(LEX_ERROR_TAG); }
235 <ATTR_KEY>= { BEGIN(ATTR_VAL); }
236 <ATTR_KEY>.|{N} { return(LEX_ERROR_ATTR); }
238 <ATTR_VAL>\" { BEGIN(DQUOTED); reset_string; }
239 <ATTR_VAL>\' { BEGIN(SQUOTED); reset_string; }
240 <ATTR_VAL>.|{N} { return(LEX_ERROR_ATTR); }
244 <DQUOTED>\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
245 <DQUOTED>{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
246 else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
247 <DQUOTED>{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
248 else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
249 <DQUOTED>[<>&\"] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
250 <DQUOTED>{UquotedD}+ { append_string(yytext); }
251 <DQUOTED>. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
253 <SQUOTED>\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
254 <SQUOTED>{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
255 else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
256 <SQUOTED>{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
257 else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
258 <SQUOTED>[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
259 <SQUOTED>{UquotedS}+ { append_string(yytext); }
260 <SQUOTED>. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
264 <<EOF>> { free(string); string=NULL; stringlen=stringused=0; BEGIN(INITIAL); return(LEX_EOF); }
269 /*++++++++++++++++++++++++++++++++++++++
270 A function to call the callback function with the parameters needed.
272 int call_callback Returns 1 if the callback returned with an error.
274 const char *name The name of the tag.
276 int (*callback)() The callback function.
278 int type The type of tag (start and/or end).
280 int nattributes The number of attributes collected.
282 char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes.
283 ++++++++++++++++++++++++++++++++++++++*/
285 static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS])
289 case 0: return (*callback)(name,type);
290 case 1: return (*callback)(name,type,attributes[0]);
291 case 2: return (*callback)(name,type,attributes[0],attributes[1]);
292 case 3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]);
293 case 4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]);
294 case 5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]);
295 case 6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]);
296 case 7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]);
297 case 8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]);
298 case 9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]);
299 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]);
300 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]);
301 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]);
302 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]);
303 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]);
304 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]);
305 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]);
308 fprintf(stderr,"XML Parser: Error on line %d: too many attributes for tag '%s' source code needs changing.\n",yylineno,name);
314 /*++++++++++++++++++++++++++++++++++++++
315 Parse the XML and call the functions for each tag as seen.
317 int ParseXML Returns 0 if OK or something else in case of an error.
319 FILE *file The file to parse.
321 xmltag **tags The array of pointers to tags for the top level.
323 int options A list of XML Parser options OR-ed together.
324 ++++++++++++++++++++++++++++++++++++++*/
326 int ParseXML(FILE *file,xmltag **tags,int options)
330 char *attributes[XMLPARSE_MAX_ATTRS]={NULL};
333 int stackdepth=0,stackused=0;
334 xmltag ***tags_stack=NULL;
335 xmltag **tag_stack=NULL;
338 /* The actual parser. */
340 xmlparse_options=options;
356 /* The start of a tag for an XML declaration */
358 case LEX_XML_DECL_BEGIN:
362 fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno);
363 yychar=LEX_ERROR_XML_NOT_FIRST;
367 /* The start of a tag for an element */
374 if(!strcasecmp(yylval,tags[i]->name))
378 for(i=0;i<tag->nattributes;i++)
390 fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval);
391 yychar=LEX_ERROR_UNEXP_TAG;
396 /* The end of the start-tag for an element */
400 if(stackused==stackdepth)
402 tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*));
403 tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**));
406 tag_stack [stackused]=tag;
407 tags_stack[stackused]=tags;
411 if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes))
412 yychar=LEX_ERROR_CALLBACK;
418 /* The end of the empty-element-tag for an XML declaration */
420 case LEX_XML_DECL_FINISH:
422 /* The end of the empty-element-tag for an element */
427 if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes))
428 yychar=LEX_ERROR_CALLBACK;
431 tag=tag_stack[stackused-1];
437 /* The end of the end-tag for an element */
442 tags=tags_stack[stackused];
443 tag =tag_stack [stackused];
445 if(strcmp(tag->name,yylval))
447 fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name);
448 yychar=LEX_ERROR_UNBALANCED;
453 fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval);
454 yychar=LEX_ERROR_NO_START;
457 for(i=0;i<tag->nattributes;i++)
465 if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes))
466 yychar=LEX_ERROR_CALLBACK;
469 tag=tag_stack[stackused-1];
475 /* An attribute key */
481 for(i=0;i<tag->nattributes;i++)
482 if(!strcasecmp(yylval,tag->attributes[i]))
491 if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR ||
492 ((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr(yylval,':')))
494 fprintf(stderr,"XML Parser: Error on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
495 yychar=LEX_ERROR_UNEXP_ATT;
497 else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN)
498 fprintf(stderr,"XML Parser: Warning on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
503 /* An attribute value */
507 if(tag->callback && attribute!=-1 && yylval)
508 attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval);
518 fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag '</%s>'.\n",yylineno,tag->name);
519 yychar=LEX_ERROR_UNEXP_EOF;
524 case LEX_ERROR_TAG_START:
525 fprintf(stderr,"XML Parser: Error on line %d: character '<' seen not at start of tag.\n",yylineno);
528 case LEX_ERROR_XML_DECL_START:
529 fprintf(stderr,"XML Parser: Error on line %d: characters '<?' seen not at start of XML declaration.\n",yylineno);
533 fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside tag '<%s...>'.\n",yylineno,tag->name);
536 case LEX_ERROR_XML_DECL:
537 fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside XML declaration '<?%s...>'.\n",yylineno,tag->name);
541 fprintf(stderr,"XML Parser: Error on line %d: invalid attribute definition seen in tag.\n",yylineno);
544 case LEX_ERROR_END_TAG:
545 fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in end-tag.\n",yylineno);
548 case LEX_ERROR_COMMENT:
549 fprintf(stderr,"XML Parser: Error on line %d: invalid comment seen.\n",yylineno);
552 case LEX_ERROR_CLOSE:
553 fprintf(stderr,"XML Parser: Error on line %d: character '>' seen not at end of tag.\n",yylineno);
556 case LEX_ERROR_ATTR_VAL:
557 fprintf(stderr,"XML Parser: Error on line %d: invalid character '%s' seen in attribute value.\n",yylineno,yylval);
560 case LEX_ERROR_ENTITY_REF:
561 fprintf(stderr,"XML Parser: Error on line %d: invalid entity reference '%s' seen in attribute value.\n",yylineno,yylval);
564 case LEX_ERROR_CHAR_REF:
565 fprintf(stderr,"XML Parser: Error on line %d: invalid character reference '%s' seen in attribute value.\n",yylineno,yylval);
569 while(yychar>LEX_EOF && yychar<LEX_ERROR);
571 /* Delete the tagdata */
573 for(i=0;i<XMLPARSE_MAX_ATTRS;i++)
587 /*++++++++++++++++++++++++++++++++++++++
588 Return the current parser line number.
590 unsigned long ParseXML_LineNumber Returns the line number.
591 ++++++++++++++++++++++++++++++++++++++*/
593 unsigned long ParseXML_LineNumber(void)
599 /*++++++++++++++++++++++++++++++++++++++
600 Convert an XML entity reference into an ASCII string.
602 char *ParseXML_Decode_Entity_Ref Returns a pointer to the replacement decoded string.
604 const char *string The entity reference string.
605 ++++++++++++++++++++++++++++++++++++++*/
607 char *ParseXML_Decode_Entity_Ref(const char *string)
609 if(!strcmp(string,"&")) return("&");
610 if(!strcmp(string,"<")) return("<");
611 if(!strcmp(string,">")) return(">");
612 if(!strcmp(string,"'")) return("'");
613 if(!strcmp(string,""")) return("\"");
618 /*++++++++++++++++++++++++++++++++++++++
619 Convert an XML character reference into an ASCII string.
621 char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string.
623 const char *string The character reference string.
624 ++++++++++++++++++++++++++++++++++++++*/
626 char *ParseXML_Decode_Char_Ref(const char *string)
628 static char result[2]=" ";
631 if(string[2]=='x') val=strtol(string+3,NULL,16);
632 else val=strtol(string+2,NULL,10);
643 /*++++++++++++++++++++++++++++++++++++++
644 Convert a string into something that is safe to output in an XML file.
646 char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed).
648 const char *string The string to convert.
649 ++++++++++++++++++++++++++++++++++++++*/
651 char *ParseXML_Encode_Safe_XML(const char *string)
653 static const char hexstring[17]="0123456789ABCDEF";
657 for(i=0;string[i];i++)
658 if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || (unsigned char)string[i]>127)
662 return((char*)string);
666 result=(char*)malloc(len+7);
667 strncpy(result,string,j=i);
671 for(;j<len && string[i];i++)
679 else if(string[i]=='>')
686 else if(string[i]=='&')
694 else if(string[i]=='\'')
703 else if(string[i]=='"')
712 else if(string[i]>=32 && (unsigned char)string[i]<=127)
713 result[j++]=string[i];
716 unsigned int unicode;
718 /* Decode the UTF-8 */
720 if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80)
722 /* 0000 0080-0000 07FF 110xxxxx 10xxxxxx */
723 unicode =(string[i++]&0x1F)<<6;
724 unicode|= string[i ]&0x3F;
726 else if((string[i]&0xF0)==0xE0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80)
728 /* 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx */
729 unicode =(string[i++]&0x0F)<<12;
730 unicode|=(string[i++]&0x3F)<<6;
731 unicode|= string[i ]&0x3F;
733 else if((string[i]&0xF8)==0xF0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80 && (string[i+3]&0xC0)==0x80)
735 /* 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
736 unicode =(string[i++]&0x07)<<18;
737 unicode|=(string[i++]&0x3F)<<12;
738 unicode|=(string[i++]&0x3F)<<6;
739 unicode|= string[i ]&0x3F;
744 /* Output the character entity */
750 if(unicode&0x00FF0000)
752 result[j++]=hexstring[((unicode>>16)&0xf0)>>4];
753 result[j++]=hexstring[((unicode>>16)&0x0f) ];
755 if(unicode&0x00FFFF00)
757 result[j++]=hexstring[((unicode>>8)&0xf0)>>4];
758 result[j++]=hexstring[((unicode>>8)&0x0f) ];
760 result[j++]=hexstring[(unicode&0xf0)>>4];
761 result[j++]=hexstring[(unicode&0x0f) ];
766 if(string[i]) /* Not finished */
769 result=(char*)realloc((void*)result,len+7);
780 /*++++++++++++++++++++++++++++++++++++++
781 Convert a string to a integer (checking that it really is a integer).
783 int ParseXML_GetInteger Returns 1 if a integer could be found or 0 otherwise.
785 const char *string The string to be parsed.
787 int *number Returns the number.
788 ++++++++++++++++++++++++++++++++++++++*/
790 int ParseXML_GetInteger(const char *string,int *number)
792 const char *p=string;
794 if(*p=='-' || *p=='+')
803 *number=atoi(string);
809 /*++++++++++++++++++++++++++++++++++++++
810 Convert a string to a floating point number (checking that it really is a number).
812 int ParseXML_GetFloating Returns 1 if a number could be found or 0 otherwise.
814 const char *string The string to be parsed.
816 int *number Returns the number.
817 ++++++++++++++++++++++++++++++++++++++*/
819 int ParseXML_GetFloating(const char *string,double *number)
821 const char *p=string;
823 if(*p=='-' || *p=='+')
826 while(isdigit(*p) || *p=='.')
829 if(*p=='e' || *p=='E')
833 if(*p=='-' || *p=='+')
843 *number=atof(string);