Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino
Annotation of /trunk/src/xmlparse.l
Parent Directory
|
Revision Log
Revision 363 -
(hide annotations)
(download)
Sun Apr 11 13:01:38 2010 UTC (14 years, 11 months ago) by amb
File size: 22955 byte(s)
Sun Apr 11 13:01:38 2010 UTC (14 years, 11 months ago) by amb
File size: 22955 byte(s)
Added helper functions for parsing strings into numbers. Added macros to perform common error checking. Change XML parser callback functions to return an error status.
1 | amb | 334 | %{ |
2 | /*************************************** | ||
3 | amb | 363 | $Header: /home/amb/CVS/routino/src/xmlparse.l,v 1.7 2010-04-11 13:01:24 amb Exp $ |
4 | amb | 334 | |
5 | A simple generic XML parser where the structure comes from the function parameters. | ||
6 | amb | 348 | 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. | ||
8 | amb | 334 | |
9 | Part of the Routino routing software. | ||
10 | ******************/ /****************** | ||
11 | This file Copyright 2010 Andrew M. Bishop | ||
12 | |||
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. | ||
17 | |||
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. | ||
22 | |||
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 | ***************************************/ | ||
26 | |||
27 | |||
28 | #include <stdio.h> | ||
29 | #include <stdlib.h> | ||
30 | amb | 363 | #include <ctype.h> |
31 | amb | 334 | #include <string.h> |
32 | |||
33 | amb | 356 | #include "xmlparse.h" |
34 | |||
35 | |||
36 | amb | 334 | /* Parser outputs */ |
37 | |||
38 | amb | 348 | #define LEX_EOF 0 |
39 | amb | 334 | |
40 | amb | 348 | #define LEX_TAG_BEGIN 1 |
41 | #define LEX_XML_DECL_BEGIN 2 | ||
42 | #define LEX_TAG_POP 3 | ||
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 | ||
48 | amb | 334 | |
49 | amb | 348 | #define LEX_ERROR 100 |
50 | |||
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 | amb | 351 | #define LEX_ERROR_ATTR_VAL 109 |
60 | amb | 356 | #define LEX_ERROR_ENTITY_REF 110 |
61 | #define LEX_ERROR_CHAR_REF 111 | ||
62 | amb | 348 | |
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 | ||
69 | |||
70 | amb | 363 | #define LEX_ERROR_CALLBACK 255 |
71 | amb | 348 | |
72 | amb | 363 | |
73 | amb | 334 | /* Lexer definitions */ |
74 | |||
75 | #define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */ | ||
76 | #ifndef yywrap | ||
77 | /*+ Needed in lex but does nothing. +*/ | ||
78 | #define yywrap() 1 | ||
79 | #endif | ||
80 | |||
81 | /*+ Reset the current string. +*/ | ||
82 | #define reset_string \ | ||
83 | if(string) *string=0; \ | ||
84 | stringused=0; | ||
85 | |||
86 | /*+ append information to the current string. +*/ | ||
87 | #define append_string(xx) \ | ||
88 | newlen=strlen(xx); \ | ||
89 | if((stringused+newlen)>=stringlen) \ | ||
90 | string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \ | ||
91 | strcpy(string+stringused,xx); \ | ||
92 | stringused+=newlen; | ||
93 | |||
94 | #define YY_NO_INPUT | ||
95 | |||
96 | |||
97 | /* Lexer functions and variables */ | ||
98 | |||
99 | extern int yylex(void); | ||
100 | |||
101 | static char *yylval=NULL; | ||
102 | |||
103 | %} | ||
104 | |||
105 | amb | 344 | %option 8bit |
106 | amb | 348 | %option pointer |
107 | amb | 344 | %option batch |
108 | %option yylineno | ||
109 | amb | 348 | |
110 | amb | 344 | %option nodefault |
111 | %option perf-report | ||
112 | %option fast | ||
113 | amb | 348 | %option nounput |
114 | amb | 344 | |
115 | amb | 334 | |
116 | amb | 348 | /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII not Unicode. */ |
117 | amb | 334 | |
118 | amb | 348 | S [ \t\r\n] |
119 | amb | 334 | |
120 | amb | 348 | letter [a-zA-Z] |
121 | digit [0-9] | ||
122 | xdigit [a-fA-F0-9] | ||
123 | |||
124 | namechar ({letter}|{digit}|[-._:]) | ||
125 | name ({letter}|[_:]){namechar}* | ||
126 | |||
127 | entityref &{name}; | ||
128 | charref &#({digit}+|x{xdigit}+); | ||
129 | |||
130 | |||
131 | amb | 334 | %x COMMENT |
132 | amb | 348 | %x CDATA |
133 | %x DOCTYPE | ||
134 | %x XML_DECL_START XML_DECL | ||
135 | %x TAG_START TAG | ||
136 | %x ATTR_KEY ATTR_VAL | ||
137 | %x END_TAG1 END_TAG2 | ||
138 | amb | 334 | %x DQUOTED SQUOTED |
139 | |||
140 | %% | ||
141 | /* Must use static variables since the parser returns often. */ | ||
142 | static char *string=NULL; | ||
143 | static int stringlen=0,stringused=0; | ||
144 | amb | 348 | static int after_attr=0; |
145 | amb | 334 | int newlen; |
146 | amb | 348 | int doctype_depth=0; |
147 | amb | 334 | |
148 | amb | 348 | /* Handle top level entities */ |
149 | amb | 334 | |
150 | "<!--" { BEGIN(COMMENT); } | ||
151 | amb | 348 | "<![CDATA[" { BEGIN(CDATA); } |
152 | "<!DOCTYPE" { BEGIN(DOCTYPE); doctype_depth=0; } | ||
153 | "</" { BEGIN(END_TAG1); } | ||
154 | "<?" { BEGIN(XML_DECL_START); } | ||
155 | amb | 334 | "<" { BEGIN(TAG_START); } |
156 | amb | 348 | ">" { return(LEX_ERROR_CLOSE); } |
157 | [^<>]+ { } | ||
158 | amb | 334 | |
159 | amb | 348 | /* Comments */ |
160 | amb | 334 | |
161 | amb | 348 | <COMMENT>"--->" { return(LEX_ERROR_COMMENT); } |
162 | <COMMENT>"-->" { BEGIN(INITIAL); } | ||
163 | <COMMENT>"--"[^->]+ { } | ||
164 | <COMMENT>[^-]+ { } | ||
165 | amb | 334 | <COMMENT>"-" { } |
166 | |||
167 | amb | 348 | /* CDATA */ |
168 | amb | 334 | |
169 | amb | 348 | <CDATA>"]]>" { BEGIN(INITIAL); } |
170 | <CDATA>"]" { } | ||
171 | <CDATA>[^]]+ { } | ||
172 | amb | 334 | |
173 | amb | 348 | /* CDATA */ |
174 | amb | 334 | |
175 | amb | 348 | <DOCTYPE>"<" { doctype_depth++; } |
176 | <DOCTYPE>">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; } | ||
177 | <DOCTYPE>[^<>]+ { } | ||
178 | amb | 334 | |
179 | amb | 348 | /* XML Declaration start */ |
180 | |||
181 | <XML_DECL_START>{name} { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); } | ||
182 | <XML_DECL_START>.|\n { return(LEX_ERROR_XML_DECL_START); } | ||
183 | |||
184 | /* Tag middle */ | ||
185 | |||
186 | <XML_DECL>"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); } | ||
187 | <XML_DECL>{S}+ { } | ||
188 | <XML_DECL>{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } | ||
189 | <XML_DECL>.|\n { return(LEX_ERROR_XML_DECL); } | ||
190 | |||
191 | /* Any tag start */ | ||
192 | |||
193 | <TAG_START>{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); } | ||
194 | <TAG_START>.|\n { return(LEX_ERROR_TAG_START); } | ||
195 | |||
196 | /* End-tag start */ | ||
197 | |||
198 | <END_TAG1>{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); } | ||
199 | <END_TAG1>.|\n { return(LEX_ERROR_END_TAG); } | ||
200 | |||
201 | <END_TAG2>">" { BEGIN(INITIAL); } | ||
202 | <END_TAG2>.|\n { return(LEX_ERROR_END_TAG); } | ||
203 | |||
204 | /* Any tag middle */ | ||
205 | |||
206 | amb | 334 | <TAG>"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); } |
207 | <TAG>">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); } | ||
208 | amb | 348 | <TAG>{S}+ { } |
209 | <TAG>{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } | ||
210 | <TAG>.|\n { return(LEX_ERROR_TAG); } | ||
211 | amb | 334 | |
212 | amb | 348 | /* Attributes */ |
213 | amb | 334 | |
214 | amb | 348 | <ATTR_KEY>= { BEGIN(ATTR_VAL); } |
215 | <ATTR_KEY>.|\n { return(LEX_ERROR_ATTR); } | ||
216 | amb | 334 | |
217 | amb | 348 | <ATTR_VAL>\" { BEGIN(DQUOTED); reset_string; } |
218 | <ATTR_VAL>\' { BEGIN(SQUOTED); reset_string; } | ||
219 | <ATTR_VAL>.|\n { return(LEX_ERROR_ATTR); } | ||
220 | |||
221 | amb | 334 | /* Quoted strings */ |
222 | |||
223 | amb | 348 | <DQUOTED>\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } |
224 | amb | 356 | <DQUOTED>{entityref} { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } |
225 | <DQUOTED>{charref} { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } | ||
226 | <DQUOTED>[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } | ||
227 | amb | 348 | <DQUOTED>[^<>&\"]+ { append_string(yytext); } |
228 | amb | 334 | |
229 | amb | 348 | <SQUOTED>\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } |
230 | amb | 356 | <SQUOTED>{entityref} { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } |
231 | <SQUOTED>{charref} { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } | ||
232 | <SQUOTED>[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } | ||
233 | amb | 348 | <SQUOTED>[^<>&\']+ { append_string(yytext); } |
234 | amb | 334 | |
235 | /* End of file */ | ||
236 | |||
237 | amb | 348 | <<EOF>> { free(string); string=NULL; BEGIN(INITIAL); return(LEX_EOF); } |
238 | amb | 334 | |
239 | %% | ||
240 | |||
241 | |||
242 | amb | 348 | /*++++++++++++++++++++++++++++++++++++++ |
243 | A function to call the callback function with the parameters needed. | ||
244 | |||
245 | amb | 363 | int call_callback Returns 1 if the callback returned with an error. |
246 | |||
247 | amb | 356 | const char *name The name of the tag. |
248 | amb | 348 | |
249 | amb | 363 | int (*callback)() The callback function. |
250 | amb | 348 | |
251 | int type The type of tag (start and/or end). | ||
252 | |||
253 | int nattributes The number of attributes collected. | ||
254 | |||
255 | char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes. | ||
256 | ++++++++++++++++++++++++++++++++++++++*/ | ||
257 | |||
258 | amb | 363 | static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS]) |
259 | amb | 344 | { |
260 | switch(nattributes) | ||
261 | { | ||
262 | amb | 363 | case 0: return (*callback)(type); |
263 | case 1: return (*callback)(type,attributes[0]); | ||
264 | case 2: return (*callback)(type,attributes[0],attributes[1]); | ||
265 | case 3: return (*callback)(type,attributes[0],attributes[1],attributes[2]); | ||
266 | case 4: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3]); | ||
267 | case 5: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]); | ||
268 | case 6: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]); | ||
269 | case 7: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]); | ||
270 | case 8: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]); | ||
271 | case 9: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]); | ||
272 | case 10: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]); | ||
273 | case 11: return (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]); | ||
274 | case 12: return (*callback)(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]); | ||
275 | case 13: return (*callback)(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]); | ||
276 | case 14: return (*callback)(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]); | ||
277 | case 15: return (*callback)(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]); | ||
278 | case 16: return (*callback)(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]); | ||
279 | amb | 334 | |
280 | amb | 344 | default: |
281 | amb | 363 | fprintf(stderr,"XML Parser: Error on line %d: too many attributes for tag '%s' source code needs changing.\n",yylineno,name); |
282 | amb | 344 | exit(1); |
283 | } | ||
284 | } | ||
285 | |||
286 | |||
287 | amb | 334 | /*++++++++++++++++++++++++++++++++++++++ |
288 | Parse the XML and call the functions for each tag as seen. | ||
289 | |||
290 | amb | 348 | int ParseXML Returns 0 if OK or something else in case of an error. |
291 | |||
292 | amb | 334 | FILE *file The file to parse. |
293 | |||
294 | xmltag **tags The array of pointers to tags for the top level. | ||
295 | amb | 337 | |
296 | int ignore_unknown_attributes If set to 0 then exit if unknown attribute is seen, if sete to 1 then warn, if set to 2 then ignore. | ||
297 | amb | 334 | ++++++++++++++++++++++++++++++++++++++*/ |
298 | |||
299 | amb | 348 | int ParseXML(FILE *file,xmltag **tags,int ignore_unknown_attributes) |
300 | amb | 334 | { |
301 | int yychar,i; | ||
302 | |||
303 | amb | 351 | char *attributes[XMLPARSE_MAX_ATTRS]={NULL}; |
304 | amb | 334 | int attribute=0; |
305 | |||
306 | int stackdepth=0,stackused=0; | ||
307 | amb | 344 | xmltag ***tags_stack=NULL; |
308 | xmltag **tag_stack=NULL; | ||
309 | amb | 334 | xmltag *tag=NULL; |
310 | |||
311 | static int first=1; | ||
312 | |||
313 | /* Parser (re)-initialisation */ | ||
314 | |||
315 | yyin=file; | ||
316 | |||
317 | if(!first) | ||
318 | yyrestart(NULL); | ||
319 | |||
320 | first=0; | ||
321 | |||
322 | /* The actual parser. */ | ||
323 | |||
324 | amb | 348 | do |
325 | { | ||
326 | yychar=yylex(); | ||
327 | |||
328 | amb | 334 | switch(yychar) |
329 | { | ||
330 | amb | 348 | /* The start of a tag for an XML declaration */ |
331 | |||
332 | case LEX_XML_DECL_BEGIN: | ||
333 | |||
334 | if(tag_stack) | ||
335 | { | ||
336 | fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno); | ||
337 | yychar=LEX_ERROR_XML_NOT_FIRST; | ||
338 | break; | ||
339 | } | ||
340 | |||
341 | amb | 334 | /* The start of a tag for an element */ |
342 | |||
343 | case LEX_TAG_BEGIN: | ||
344 | |||
345 | tag=NULL; | ||
346 | |||
347 | for(i=0;tags[i];i++) | ||
348 | if(!strcasecmp(yylval,tags[i]->name)) | ||
349 | { | ||
350 | tag=tags[i]; | ||
351 | |||
352 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
353 | amb | 351 | if(attributes[i]) |
354 | { | ||
355 | free(attributes[i]); | ||
356 | attributes[i]=NULL; | ||
357 | } | ||
358 | amb | 334 | |
359 | break; | ||
360 | } | ||
361 | |||
362 | if(tag==NULL) | ||
363 | { | ||
364 | amb | 348 | fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval); |
365 | yychar=LEX_ERROR_UNEXP_TAG; | ||
366 | amb | 334 | } |
367 | amb | 348 | |
368 | amb | 334 | break; |
369 | |||
370 | /* The end of the start-tag for an element */ | ||
371 | |||
372 | case LEX_TAG_PUSH: | ||
373 | |||
374 | if(stackused==stackdepth) | ||
375 | amb | 344 | { |
376 | tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*)); | ||
377 | tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**)); | ||
378 | } | ||
379 | amb | 334 | |
380 | amb | 344 | tag_stack [stackused]=tag; |
381 | tags_stack[stackused]=tags; | ||
382 | stackused++; | ||
383 | |||
384 | if(tag->callback) | ||
385 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes)) |
386 | yychar=LEX_ERROR_CALLBACK; | ||
387 | amb | 344 | |
388 | amb | 334 | tags=tag->subtags; |
389 | |||
390 | amb | 344 | break; |
391 | |||
392 | amb | 348 | /* The end of the empty-element-tag for an XML declaration */ |
393 | |||
394 | case LEX_XML_DECL_FINISH: | ||
395 | |||
396 | amb | 334 | /* The end of the empty-element-tag for an element */ |
397 | |||
398 | case LEX_TAG_FINISH: | ||
399 | |||
400 | if(tag->callback) | ||
401 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes)) |
402 | yychar=LEX_ERROR_CALLBACK; | ||
403 | amb | 334 | |
404 | amb | 344 | if(stackused>0) |
405 | tag=tag_stack[stackused-1]; | ||
406 | else | ||
407 | tag=NULL; | ||
408 | amb | 334 | |
409 | break; | ||
410 | |||
411 | /* The end of the end-tag for an element */ | ||
412 | |||
413 | case LEX_TAG_POP: | ||
414 | |||
415 | amb | 344 | stackused--; |
416 | tags=tags_stack[stackused]; | ||
417 | tag =tag_stack [stackused]; | ||
418 | amb | 334 | |
419 | amb | 348 | if(strcmp(tag->name,yylval)) |
420 | { | ||
421 | fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name); | ||
422 | yychar=LEX_ERROR_UNBALANCED; | ||
423 | } | ||
424 | |||
425 | if(stackused<0) | ||
426 | { | ||
427 | fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval); | ||
428 | yychar=LEX_ERROR_NO_START; | ||
429 | } | ||
430 | |||
431 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
432 | amb | 351 | if(attributes[i]) |
433 | { | ||
434 | free(attributes[i]); | ||
435 | attributes[i]=NULL; | ||
436 | } | ||
437 | |||
438 | amb | 344 | if(tag->callback) |
439 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes)) |
440 | yychar=LEX_ERROR_CALLBACK; | ||
441 | amb | 344 | |
442 | amb | 348 | if(stackused>0) |
443 | tag=tag_stack[stackused-1]; | ||
444 | else | ||
445 | tag=NULL; | ||
446 | amb | 344 | |
447 | amb | 334 | break; |
448 | |||
449 | /* An attribute key */ | ||
450 | |||
451 | case LEX_ATTR_KEY: | ||
452 | |||
453 | attribute=-1; | ||
454 | |||
455 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
456 | amb | 334 | if(!strcasecmp(yylval,tag->attributes[i])) |
457 | { | ||
458 | attribute=i; | ||
459 | |||
460 | break; | ||
461 | } | ||
462 | |||
463 | if(attribute==-1) | ||
464 | { | ||
465 | amb | 337 | if(ignore_unknown_attributes==0) |
466 | { | ||
467 | amb | 348 | fprintf(stderr,"XML Parser: Error on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name); |
468 | yychar=LEX_ERROR_UNEXP_ATT; | ||
469 | amb | 337 | } |
470 | else if(ignore_unknown_attributes==1) | ||
471 | amb | 348 | fprintf(stderr,"XML Parser: Warning on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name); |
472 | amb | 334 | } |
473 | amb | 348 | |
474 | amb | 334 | break; |
475 | |||
476 | /* An attribute value */ | ||
477 | |||
478 | case LEX_ATTR_VAL: | ||
479 | |||
480 | amb | 363 | if(tag->callback && attribute!=-1 && yylval) |
481 | amb | 334 | attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval); |
482 | amb | 348 | |
483 | break; | ||
484 | |||
485 | /* End of file */ | ||
486 | |||
487 | case LEX_EOF: | ||
488 | |||
489 | if(tag) | ||
490 | { | ||
491 | fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag '</%s>'.\n",yylineno,tag->name); | ||
492 | yychar=LEX_ERROR_UNEXP_EOF; | ||
493 | } | ||
494 | |||
495 | break; | ||
496 | |||
497 | case LEX_ERROR_TAG_START: | ||
498 | fprintf(stderr,"XML Parser: Error on line %d: character '<' seen not at start of tag.\n",yylineno); | ||
499 | break; | ||
500 | |||
501 | case LEX_ERROR_XML_DECL_START: | ||
502 | fprintf(stderr,"XML Parser: Error on line %d: characters '<?' seen not at start of XML declaration.\n",yylineno); | ||
503 | break; | ||
504 | |||
505 | case LEX_ERROR_TAG: | ||
506 | fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside tag '<%s...>'.\n",yylineno,tag->name); | ||
507 | break; | ||
508 | |||
509 | case LEX_ERROR_XML_DECL: | ||
510 | fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside XML declaration '<?%s...>'.\n",yylineno,tag->name); | ||
511 | break; | ||
512 | |||
513 | case LEX_ERROR_ATTR: | ||
514 | fprintf(stderr,"XML Parser: Error on line %d: invalid attribute definition seen in tag.\n",yylineno); | ||
515 | break; | ||
516 | |||
517 | case LEX_ERROR_END_TAG: | ||
518 | fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in end-tag.\n",yylineno); | ||
519 | break; | ||
520 | |||
521 | case LEX_ERROR_COMMENT: | ||
522 | fprintf(stderr,"XML Parser: Error on line %d: invalid comment seen.\n",yylineno); | ||
523 | break; | ||
524 | |||
525 | case LEX_ERROR_CLOSE: | ||
526 | fprintf(stderr,"XML Parser: Error on line %d: character '>' seen not at end of tag.\n",yylineno); | ||
527 | break; | ||
528 | amb | 351 | |
529 | case LEX_ERROR_ATTR_VAL: | ||
530 | amb | 356 | fprintf(stderr,"XML Parser: Error on line %d: invalid character '%s' seen in attribute value.\n",yylineno,yylval); |
531 | amb | 351 | break; |
532 | amb | 356 | |
533 | case LEX_ERROR_ENTITY_REF: | ||
534 | fprintf(stderr,"XML Parser: Error on line %d: invalid entity reference '%s' seen in attribute value.\n",yylineno,yylval); | ||
535 | break; | ||
536 | |||
537 | case LEX_ERROR_CHAR_REF: | ||
538 | fprintf(stderr,"XML Parser: Error on line %d: invalid character reference '%s' seen in attribute value.\n",yylineno,yylval); | ||
539 | break; | ||
540 | amb | 334 | } |
541 | amb | 348 | } |
542 | while(yychar>LEX_EOF && yychar<LEX_ERROR); | ||
543 | amb | 334 | |
544 | /* Delete the tagdata */ | ||
545 | |||
546 | amb | 356 | for(i=0;i<XMLPARSE_MAX_ATTRS;i++) |
547 | amb | 334 | if(attributes[i]) |
548 | free(attributes[i]); | ||
549 | |||
550 | if(stackdepth) | ||
551 | amb | 344 | free(tags_stack); |
552 | amb | 348 | |
553 | return(yychar); | ||
554 | amb | 334 | } |
555 | amb | 348 | |
556 | |||
557 | /*++++++++++++++++++++++++++++++++++++++ | ||
558 | Return the current parser line number. | ||
559 | |||
560 | int ParseXML_LineNumber Returns the line number. | ||
561 | ++++++++++++++++++++++++++++++++++++++*/ | ||
562 | |||
563 | int ParseXML_LineNumber(void) | ||
564 | { | ||
565 | return(yylineno); | ||
566 | } | ||
567 | amb | 356 | |
568 | |||
569 | /*++++++++++++++++++++++++++++++++++++++ | ||
570 | Convert an XML entity reference into an ASCII string. | ||
571 | |||
572 | const char *ParseXML_Decode_Entity_Ref Returns a pointer to the replacement decoded string. | ||
573 | |||
574 | const char *string The entity reference string. | ||
575 | ++++++++++++++++++++++++++++++++++++++*/ | ||
576 | |||
577 | const char *ParseXML_Decode_Entity_Ref(const char *string) | ||
578 | { | ||
579 | if(!strcmp(string,"&")) return("&"); | ||
580 | if(!strcmp(string,"<")) return("<"); | ||
581 | if(!strcmp(string,">")) return(">"); | ||
582 | if(!strcmp(string,"'")) return("'"); | ||
583 | if(!strcmp(string,""")) return("\""); | ||
584 | return(NULL); | ||
585 | } | ||
586 | |||
587 | |||
588 | /*++++++++++++++++++++++++++++++++++++++ | ||
589 | Convert an XML character reference into an ASCII string. | ||
590 | |||
591 | const char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string. | ||
592 | |||
593 | const char *string The character reference string. | ||
594 | ++++++++++++++++++++++++++++++++++++++*/ | ||
595 | |||
596 | const char *ParseXML_Decode_Char_Ref(const char *string) | ||
597 | { | ||
598 | static char result[2]=" "; | ||
599 | long int val; | ||
600 | |||
601 | if(string[2]=='x') val=strtol(string+3,NULL,16); | ||
602 | else val=strtol(string+2,NULL,10); | ||
603 | |||
604 | if(val<0 || val>255) | ||
605 | return(NULL); | ||
606 | |||
607 | result[0]=val&0xff; | ||
608 | |||
609 | return(result); | ||
610 | } | ||
611 | |||
612 | |||
613 | /*++++++++++++++++++++++++++++++++++++++ | ||
614 | Convert a string into something that is safe to output in an XML file. | ||
615 | |||
616 | const char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed). | ||
617 | |||
618 | const char *string The string to convert. | ||
619 | ++++++++++++++++++++++++++++++++++++++*/ | ||
620 | |||
621 | const char *ParseXML_Encode_Safe_XML(const char *string) | ||
622 | { | ||
623 | static const char hexstring[17]="0123456789ABCDEF"; | ||
624 | int i=0,j=0,len; | ||
625 | char *result; | ||
626 | |||
627 | for(i=0;string[i];i++) | ||
628 | if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || string[i]>126) | ||
629 | break; | ||
630 | |||
631 | if(!string[i]) | ||
632 | return(string); | ||
633 | |||
634 | len=i+256-6; | ||
635 | |||
636 | result=(char*)malloc(len+7); | ||
637 | strncpy(result,string,i); | ||
638 | |||
639 | do | ||
640 | { | ||
641 | for(;j<len && string[i];i++) | ||
642 | if(string[i]=='<') | ||
643 | { | ||
644 | result[j++]='&'; | ||
645 | result[j++]='l'; | ||
646 | result[j++]='t'; | ||
647 | result[j++]=';'; | ||
648 | } | ||
649 | else if(string[i]=='>') | ||
650 | { | ||
651 | result[j++]='&'; | ||
652 | result[j++]='g'; | ||
653 | result[j++]='t'; | ||
654 | result[j++]=';'; | ||
655 | } | ||
656 | else if(string[i]=='&') | ||
657 | { | ||
658 | result[j++]='&'; | ||
659 | result[j++]='a'; | ||
660 | result[j++]='m'; | ||
661 | result[j++]='p'; | ||
662 | result[j++]=';'; | ||
663 | } | ||
664 | else if(string[i]=='\'') | ||
665 | { | ||
666 | result[j++]='&'; | ||
667 | result[j++]='a'; | ||
668 | result[j++]='p'; | ||
669 | result[j++]='o'; | ||
670 | result[j++]='s'; | ||
671 | result[j++]=';'; | ||
672 | } | ||
673 | else if(string[i]=='"') | ||
674 | { | ||
675 | result[j++]='&'; | ||
676 | result[j++]='q'; | ||
677 | result[j++]='u'; | ||
678 | result[j++]='o'; | ||
679 | result[j++]='t'; | ||
680 | result[j++]=';'; | ||
681 | } | ||
682 | else if(string[i]<32 || string[i]>126) | ||
683 | { | ||
684 | result[j++]='&'; | ||
685 | result[j++]='#'; | ||
686 | result[j++]='x'; | ||
687 | result[j++]=hexstring[(string[i]&0xf0)>>4]; | ||
688 | result[j++]=hexstring[ string[i]&0x0f ]; | ||
689 | result[j++]=';'; | ||
690 | } | ||
691 | else | ||
692 | result[j++]=string[i]; | ||
693 | |||
694 | if(string[i]) /* Not finished */ | ||
695 | { | ||
696 | len+=256; | ||
697 | result=(char*)realloc((void*)result,len+7); | ||
698 | } | ||
699 | } | ||
700 | while(string[i]); | ||
701 | |||
702 | result[j]=0; | ||
703 | |||
704 | return(result); | ||
705 | } | ||
706 | amb | 363 | |
707 | |||
708 | /*++++++++++++++++++++++++++++++++++++++ | ||
709 | Convert a string to a integer (checking that it really is a integer). | ||
710 | |||
711 | int ParseXML_GetInteger Returns 1 if a integer could be found or 0 otherwise. | ||
712 | |||
713 | const char *string The string to be parsed. | ||
714 | |||
715 | int *number Returns the number. | ||
716 | ++++++++++++++++++++++++++++++++++++++*/ | ||
717 | |||
718 | int ParseXML_GetInteger(const char *string,int *number) | ||
719 | { | ||
720 | const char *p=string; | ||
721 | |||
722 | if(*p=='-' || *p=='+') | ||
723 | p++; | ||
724 | |||
725 | while(isdigit(*p)) | ||
726 | p++; | ||
727 | |||
728 | if(*p) | ||
729 | return(0); | ||
730 | |||
731 | *number=atoi(string); | ||
732 | |||
733 | return(1); | ||
734 | } | ||
735 | |||
736 | |||
737 | /*++++++++++++++++++++++++++++++++++++++ | ||
738 | Convert a string to a floating point number (checking that it really is a number). | ||
739 | |||
740 | int ParseXML_GetFloating Returns 1 if a number could be found or 0 otherwise. | ||
741 | |||
742 | const char *string The string to be parsed. | ||
743 | |||
744 | int *number Returns the number. | ||
745 | ++++++++++++++++++++++++++++++++++++++*/ | ||
746 | |||
747 | int ParseXML_GetFloating(const char *string,double *number) | ||
748 | { | ||
749 | const char *p=string; | ||
750 | |||
751 | if(*p=='-' || *p=='+') | ||
752 | p++; | ||
753 | |||
754 | while(isdigit(*p) || *p=='.') | ||
755 | p++; | ||
756 | |||
757 | if(*p) | ||
758 | return(0); | ||
759 | |||
760 | *number=atof(string); | ||
761 | |||
762 | return(1); | ||
763 | } |
Properties
Name | Value |
---|---|
cvs:description | A simple generic XML parser. |