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 791 -
(hide annotations)
(download)
Sat Jun 18 18:36:40 2011 UTC (13 years, 9 months ago) by amb
File size: 27649 byte(s)
Sat Jun 18 18:36:40 2011 UTC (13 years, 9 months ago) by amb
File size: 27649 byte(s)
Don't use the flex yylineno but keep track with an unsigned long long line counter instead (if there are more than 2^31 nodes then there are more than 2^31 lines as well).
1 | amb | 334 | %{ |
2 | /*************************************** | ||
3 | A simple generic XML parser where the structure comes from the function parameters. | ||
4 | amb | 348 | Not intended to be fully conforming to XML staandard or a validating parser but |
5 | sufficient to parse OSM XML and simple program configuration files. | ||
6 | amb | 334 | |
7 | Part of the Routino routing software. | ||
8 | ******************/ /****************** | ||
9 | amb | 773 | This file Copyright 2010-2011 Andrew M. Bishop |
10 | amb | 334 | |
11 | This program is free software: you can redistribute it and/or modify | ||
12 | it under the terms of the GNU Affero General Public License as published by | ||
13 | the Free Software Foundation, either version 3 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU Affero General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU Affero General Public License | ||
22 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
23 | ***************************************/ | ||
24 | |||
25 | |||
26 | #include <stdio.h> | ||
27 | #include <stdlib.h> | ||
28 | amb | 363 | #include <ctype.h> |
29 | amb | 334 | #include <string.h> |
30 | amb | 787 | #include <strings.h> |
31 | amb | 334 | |
32 | amb | 356 | #include "xmlparse.h" |
33 | |||
34 | |||
35 | amb | 334 | /* Parser outputs */ |
36 | |||
37 | amb | 348 | #define LEX_EOF 0 |
38 | amb | 334 | |
39 | amb | 348 | #define LEX_TAG_BEGIN 1 |
40 | #define LEX_XML_DECL_BEGIN 2 | ||
41 | #define LEX_TAG_POP 3 | ||
42 | #define LEX_TAG_PUSH 4 | ||
43 | #define LEX_XML_DECL_FINISH 6 | ||
44 | #define LEX_TAG_FINISH 7 | ||
45 | #define LEX_ATTR_KEY 8 | ||
46 | #define LEX_ATTR_VAL 9 | ||
47 | amb | 334 | |
48 | amb | 348 | #define LEX_ERROR 100 |
49 | |||
50 | #define LEX_ERROR_TAG_START 101 | ||
51 | #define LEX_ERROR_XML_DECL_START 102 | ||
52 | #define LEX_ERROR_TAG 103 | ||
53 | #define LEX_ERROR_XML_DECL 104 | ||
54 | #define LEX_ERROR_ATTR 105 | ||
55 | #define LEX_ERROR_END_TAG 106 | ||
56 | #define LEX_ERROR_COMMENT 107 | ||
57 | #define LEX_ERROR_CLOSE 108 | ||
58 | amb | 351 | #define LEX_ERROR_ATTR_VAL 109 |
59 | amb | 356 | #define LEX_ERROR_ENTITY_REF 110 |
60 | #define LEX_ERROR_CHAR_REF 111 | ||
61 | amb | 348 | |
62 | #define LEX_ERROR_UNEXP_TAG 201 | ||
63 | #define LEX_ERROR_UNBALANCED 202 | ||
64 | #define LEX_ERROR_NO_START 203 | ||
65 | #define LEX_ERROR_UNEXP_ATT 204 | ||
66 | #define LEX_ERROR_UNEXP_EOF 205 | ||
67 | #define LEX_ERROR_XML_NOT_FIRST 206 | ||
68 | |||
69 | amb | 363 | #define LEX_ERROR_CALLBACK 255 |
70 | amb | 348 | |
71 | amb | 363 | |
72 | amb | 334 | /* Lexer definitions */ |
73 | |||
74 | /*+ Reset the current string. +*/ | ||
75 | #define reset_string \ | ||
76 | amb | 389 | if(!string) string=(char*)malloc(16); \ |
77 | *string=0; \ | ||
78 | amb | 334 | stringused=0; |
79 | |||
80 | /*+ append information to the current string. +*/ | ||
81 | #define append_string(xx) \ | ||
82 | newlen=strlen(xx); \ | ||
83 | if((stringused+newlen)>=stringlen) \ | ||
84 | string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \ | ||
85 | strcpy(string+stringused,xx); \ | ||
86 | stringused+=newlen; | ||
87 | |||
88 | |||
89 | /* Lexer functions and variables */ | ||
90 | |||
91 | extern int yylex(void); | ||
92 | |||
93 | static char *yylval=NULL; | ||
94 | |||
95 | amb | 374 | static int xmlparse_options; |
96 | |||
97 | amb | 791 | static unsigned long long lineno; |
98 | |||
99 | amb | 334 | %} |
100 | |||
101 | amb | 344 | %option 8bit |
102 | amb | 348 | %option pointer |
103 | amb | 344 | %option batch |
104 | amb | 787 | %option never-interactive |
105 | |||
106 | amb | 344 | %option nodefault |
107 | %option perf-report | ||
108 | %option fast | ||
109 | amb | 787 | %option noread |
110 | |||
111 | amb | 348 | %option nounput |
112 | amb | 787 | %option noinput |
113 | %option noyywrap | ||
114 | amb | 344 | |
115 | amb | 334 | |
116 | amb | 487 | /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII tags not Unicode. */ |
117 | amb | 334 | |
118 | amb | 791 | S [ \t] |
119 | amb | 334 | |
120 | amb | 487 | U1 [\x09\x0A\x0D\x20-\x7F] |
121 | amb | 483 | U2 [\xC2-\xDF][\x80-\xBF] |
122 | amb | 487 | U3a \xE0[\xA0-\xBF][\x80-\xBF] |
123 | U3b [\xE1-\xEC][\x80-\xBF][\x80-\xBF] | ||
124 | U3c \xED[\x80-\x9F][\x80-\xBF] | ||
125 | U3d [\xEE-\xEF][\x80-\xBF][\x80-\xBF] | ||
126 | U3 {U3a}|{U3b}|{U3c}|{U3d} | ||
127 | U4a \xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF] | ||
128 | U4b [\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] | ||
129 | U4c \xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF] | ||
130 | U4 {U4a}|{U4b}|{U4c} | ||
131 | amb | 483 | |
132 | U ({U1}|{U2}|{U3}|{U4}) | ||
133 | amb | 487 | UquotedS ([\x09\x0A\x0D\x20-\x25\x28-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4}) |
134 | UquotedD ([\x09\x0A\x0D\x20-\x21\x23-\x25\x27-\x3B\x3D\x3F-\x7F]|{U2}|{U3}|{U4}) | ||
135 | amb | 483 | |
136 | N (\n|\r\n) | ||
137 | |||
138 | amb | 348 | letter [a-zA-Z] |
139 | digit [0-9] | ||
140 | xdigit [a-fA-F0-9] | ||
141 | |||
142 | namechar ({letter}|{digit}|[-._:]) | ||
143 | name ({letter}|[_:]){namechar}* | ||
144 | |||
145 | entityref &{name}; | ||
146 | charref &#({digit}+|x{xdigit}+); | ||
147 | |||
148 | |||
149 | amb | 334 | %x COMMENT |
150 | amb | 348 | %x CDATA |
151 | %x DOCTYPE | ||
152 | %x XML_DECL_START XML_DECL | ||
153 | %x TAG_START TAG | ||
154 | %x ATTR_KEY ATTR_VAL | ||
155 | %x END_TAG1 END_TAG2 | ||
156 | amb | 334 | %x DQUOTED SQUOTED |
157 | |||
158 | %% | ||
159 | /* Must use static variables since the parser returns often. */ | ||
160 | static char *string=NULL; | ||
161 | static int stringlen=0,stringused=0; | ||
162 | amb | 348 | static int after_attr=0; |
163 | amb | 334 | int newlen; |
164 | amb | 348 | int doctype_depth=0; |
165 | amb | 334 | |
166 | amb | 348 | /* Handle top level entities */ |
167 | amb | 334 | |
168 | "<!--" { BEGIN(COMMENT); } | ||
169 | amb | 348 | "<![CDATA[" { BEGIN(CDATA); } |
170 | "<!DOCTYPE" { BEGIN(DOCTYPE); doctype_depth=0; } | ||
171 | "</" { BEGIN(END_TAG1); } | ||
172 | "<?" { BEGIN(XML_DECL_START); } | ||
173 | amb | 334 | "<" { BEGIN(TAG_START); } |
174 | amb | 348 | ">" { return(LEX_ERROR_CLOSE); } |
175 | amb | 791 | {N} { lineno++; } |
176 | [^<>] { } | ||
177 | amb | 334 | |
178 | amb | 348 | /* Comments */ |
179 | amb | 334 | |
180 | amb | 348 | <COMMENT>"--->" { return(LEX_ERROR_COMMENT); } |
181 | <COMMENT>"-->" { BEGIN(INITIAL); } | ||
182 | amb | 791 | <COMMENT>"--"[^->] { } |
183 | <COMMENT>{N} { lineno++; } | ||
184 | <COMMENT>[^-] { } | ||
185 | amb | 334 | <COMMENT>"-" { } |
186 | |||
187 | amb | 348 | /* CDATA */ |
188 | amb | 334 | |
189 | amb | 348 | <CDATA>"]]>" { BEGIN(INITIAL); } |
190 | <CDATA>"]" { } | ||
191 | amb | 791 | <CDATA>{N} { lineno++; } |
192 | <CDATA>[^]] { } | ||
193 | amb | 334 | |
194 | amb | 348 | /* CDATA */ |
195 | amb | 334 | |
196 | amb | 348 | <DOCTYPE>"<" { doctype_depth++; } |
197 | <DOCTYPE>">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; } | ||
198 | amb | 791 | <DOCTYPE>{N} { lineno++; } |
199 | <DOCTYPE>[^<>] { } | ||
200 | amb | 334 | |
201 | amb | 348 | /* XML Declaration start */ |
202 | |||
203 | amb | 483 | <XML_DECL_START>xml { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); } |
204 | amb | 791 | <XML_DECL_START>{N} { /* lineno++; */ return(LEX_ERROR_XML_DECL_START); } |
205 | <XML_DECL_START>. { return(LEX_ERROR_XML_DECL_START); } | ||
206 | amb | 348 | |
207 | /* Tag middle */ | ||
208 | |||
209 | <XML_DECL>"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); } | ||
210 | <XML_DECL>{S}+ { } | ||
211 | amb | 791 | <XML_DECL>{N} { lineno++; } |
212 | amb | 348 | <XML_DECL>{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } |
213 | amb | 791 | <XML_DECL>. { return(LEX_ERROR_XML_DECL); } |
214 | amb | 348 | |
215 | /* Any tag start */ | ||
216 | |||
217 | <TAG_START>{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); } | ||
218 | amb | 791 | <TAG_START>{N} { /* lineno++; */ return(LEX_ERROR_TAG_START); } |
219 | <TAG_START>. { return(LEX_ERROR_TAG_START); } | ||
220 | amb | 348 | |
221 | /* End-tag start */ | ||
222 | |||
223 | <END_TAG1>{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); } | ||
224 | amb | 791 | <END_TAG1>{N} { /* lineno++; */ return(LEX_ERROR_END_TAG); } |
225 | <END_TAG1>. { return(LEX_ERROR_END_TAG); } | ||
226 | amb | 348 | |
227 | <END_TAG2>">" { BEGIN(INITIAL); } | ||
228 | amb | 791 | <END_TAG2>{N} { /* lineno++; */ return(LEX_ERROR_END_TAG); } |
229 | <END_TAG2>. { return(LEX_ERROR_END_TAG); } | ||
230 | amb | 348 | |
231 | /* Any tag middle */ | ||
232 | |||
233 | amb | 334 | <TAG>"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); } |
234 | <TAG>">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); } | ||
235 | amb | 348 | <TAG>{S}+ { } |
236 | amb | 791 | <TAG>{N} { lineno++; } |
237 | amb | 348 | <TAG>{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } |
238 | amb | 791 | <TAG>. { return(LEX_ERROR_TAG); } |
239 | amb | 334 | |
240 | amb | 348 | /* Attributes */ |
241 | amb | 334 | |
242 | amb | 348 | <ATTR_KEY>= { BEGIN(ATTR_VAL); } |
243 | amb | 791 | <ATTR_KEY>{N} { /* lineno++; */ return(LEX_ERROR_ATTR); } |
244 | <ATTR_KEY>. { return(LEX_ERROR_ATTR); } | ||
245 | amb | 334 | |
246 | amb | 348 | <ATTR_VAL>\" { BEGIN(DQUOTED); reset_string; } |
247 | <ATTR_VAL>\' { BEGIN(SQUOTED); reset_string; } | ||
248 | amb | 791 | <ATTR_VAL>{N} { /* lineno++; */ return(LEX_ERROR_ATTR); } |
249 | <ATTR_VAL>. { return(LEX_ERROR_ATTR); } | ||
250 | amb | 348 | |
251 | amb | 334 | /* Quoted strings */ |
252 | |||
253 | amb | 348 | <DQUOTED>\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } |
254 | amb | 374 | <DQUOTED>{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 | <DQUOTED>{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 | amb | 487 | <DQUOTED>[<>&\"] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } |
259 | <DQUOTED>{UquotedD}+ { append_string(yytext); } | ||
260 | <DQUOTED>. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } | ||
261 | amb | 334 | |
262 | amb | 348 | <SQUOTED>\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } |
263 | amb | 374 | <SQUOTED>{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} |
264 | else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } } | ||
265 | <SQUOTED>{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} | ||
266 | else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } } | ||
267 | amb | 356 | <SQUOTED>[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } |
268 | amb | 487 | <SQUOTED>{UquotedS}+ { append_string(yytext); } |
269 | <SQUOTED>. { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } | ||
270 | amb | 334 | |
271 | /* End of file */ | ||
272 | |||
273 | amb | 368 | <<EOF>> { free(string); string=NULL; stringlen=stringused=0; BEGIN(INITIAL); return(LEX_EOF); } |
274 | amb | 334 | |
275 | %% | ||
276 | |||
277 | |||
278 | amb | 348 | /*++++++++++++++++++++++++++++++++++++++ |
279 | A function to call the callback function with the parameters needed. | ||
280 | |||
281 | amb | 363 | int call_callback Returns 1 if the callback returned with an error. |
282 | |||
283 | amb | 356 | const char *name The name of the tag. |
284 | amb | 348 | |
285 | amb | 363 | int (*callback)() The callback function. |
286 | amb | 348 | |
287 | int type The type of tag (start and/or end). | ||
288 | |||
289 | int nattributes The number of attributes collected. | ||
290 | |||
291 | char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes. | ||
292 | ++++++++++++++++++++++++++++++++++++++*/ | ||
293 | |||
294 | amb | 363 | static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS]) |
295 | amb | 344 | { |
296 | switch(nattributes) | ||
297 | { | ||
298 | amb | 373 | case 0: return (*callback)(name,type); |
299 | case 1: return (*callback)(name,type,attributes[0]); | ||
300 | case 2: return (*callback)(name,type,attributes[0],attributes[1]); | ||
301 | case 3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]); | ||
302 | case 4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]); | ||
303 | case 5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]); | ||
304 | case 6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]); | ||
305 | case 7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]); | ||
306 | case 8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]); | ||
307 | case 9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]); | ||
308 | 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]); | ||
309 | 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]); | ||
310 | 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]); | ||
311 | 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]); | ||
312 | 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]); | ||
313 | 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]); | ||
314 | 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]); | ||
315 | amb | 334 | |
316 | amb | 344 | default: |
317 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: too many attributes for tag '%s' source code needs changing.\n",lineno,name); |
318 | amb | 344 | exit(1); |
319 | } | ||
320 | } | ||
321 | |||
322 | |||
323 | amb | 334 | /*++++++++++++++++++++++++++++++++++++++ |
324 | Parse the XML and call the functions for each tag as seen. | ||
325 | |||
326 | amb | 348 | int ParseXML Returns 0 if OK or something else in case of an error. |
327 | |||
328 | amb | 334 | FILE *file The file to parse. |
329 | |||
330 | xmltag **tags The array of pointers to tags for the top level. | ||
331 | amb | 337 | |
332 | amb | 366 | int options A list of XML Parser options OR-ed together. |
333 | amb | 334 | ++++++++++++++++++++++++++++++++++++++*/ |
334 | |||
335 | amb | 366 | int ParseXML(FILE *file,xmltag **tags,int options) |
336 | amb | 334 | { |
337 | int yychar,i; | ||
338 | |||
339 | amb | 351 | char *attributes[XMLPARSE_MAX_ATTRS]={NULL}; |
340 | amb | 334 | int attribute=0; |
341 | |||
342 | int stackdepth=0,stackused=0; | ||
343 | amb | 344 | xmltag ***tags_stack=NULL; |
344 | xmltag **tag_stack=NULL; | ||
345 | amb | 334 | xmltag *tag=NULL; |
346 | |||
347 | amb | 368 | /* The actual parser. */ |
348 | amb | 334 | |
349 | amb | 374 | xmlparse_options=options; |
350 | |||
351 | amb | 334 | yyin=file; |
352 | |||
353 | amb | 368 | yyrestart(yyin); |
354 | amb | 334 | |
355 | amb | 791 | lineno=1; |
356 | amb | 396 | |
357 | amb | 368 | BEGIN(INITIAL); |
358 | amb | 334 | |
359 | amb | 348 | do |
360 | { | ||
361 | yychar=yylex(); | ||
362 | |||
363 | amb | 334 | switch(yychar) |
364 | { | ||
365 | amb | 348 | /* The start of a tag for an XML declaration */ |
366 | |||
367 | case LEX_XML_DECL_BEGIN: | ||
368 | |||
369 | if(tag_stack) | ||
370 | { | ||
371 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: XML declaration not before all other tags.\n",lineno); |
372 | amb | 348 | yychar=LEX_ERROR_XML_NOT_FIRST; |
373 | break; | ||
374 | } | ||
375 | |||
376 | amb | 334 | /* The start of a tag for an element */ |
377 | |||
378 | case LEX_TAG_BEGIN: | ||
379 | |||
380 | tag=NULL; | ||
381 | |||
382 | for(i=0;tags[i];i++) | ||
383 | if(!strcasecmp(yylval,tags[i]->name)) | ||
384 | { | ||
385 | tag=tags[i]; | ||
386 | |||
387 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
388 | amb | 351 | if(attributes[i]) |
389 | { | ||
390 | free(attributes[i]); | ||
391 | attributes[i]=NULL; | ||
392 | } | ||
393 | amb | 334 | |
394 | break; | ||
395 | } | ||
396 | |||
397 | if(tag==NULL) | ||
398 | { | ||
399 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: unexpected tag '%s'.\n",lineno,yylval); |
400 | amb | 348 | yychar=LEX_ERROR_UNEXP_TAG; |
401 | amb | 334 | } |
402 | amb | 348 | |
403 | amb | 334 | break; |
404 | |||
405 | /* The end of the start-tag for an element */ | ||
406 | |||
407 | case LEX_TAG_PUSH: | ||
408 | |||
409 | if(stackused==stackdepth) | ||
410 | amb | 344 | { |
411 | tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*)); | ||
412 | tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**)); | ||
413 | } | ||
414 | amb | 334 | |
415 | amb | 344 | tag_stack [stackused]=tag; |
416 | tags_stack[stackused]=tags; | ||
417 | stackused++; | ||
418 | |||
419 | if(tag->callback) | ||
420 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes)) |
421 | yychar=LEX_ERROR_CALLBACK; | ||
422 | amb | 344 | |
423 | amb | 334 | tags=tag->subtags; |
424 | |||
425 | amb | 344 | break; |
426 | |||
427 | amb | 348 | /* The end of the empty-element-tag for an XML declaration */ |
428 | |||
429 | case LEX_XML_DECL_FINISH: | ||
430 | |||
431 | amb | 334 | /* The end of the empty-element-tag for an element */ |
432 | |||
433 | case LEX_TAG_FINISH: | ||
434 | |||
435 | if(tag->callback) | ||
436 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes)) |
437 | yychar=LEX_ERROR_CALLBACK; | ||
438 | amb | 334 | |
439 | amb | 344 | if(stackused>0) |
440 | tag=tag_stack[stackused-1]; | ||
441 | else | ||
442 | tag=NULL; | ||
443 | amb | 334 | |
444 | break; | ||
445 | |||
446 | /* The end of the end-tag for an element */ | ||
447 | |||
448 | case LEX_TAG_POP: | ||
449 | |||
450 | amb | 344 | stackused--; |
451 | tags=tags_stack[stackused]; | ||
452 | tag =tag_stack [stackused]; | ||
453 | amb | 334 | |
454 | amb | 348 | if(strcmp(tag->name,yylval)) |
455 | { | ||
456 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",lineno,yylval,tag->name); |
457 | amb | 348 | yychar=LEX_ERROR_UNBALANCED; |
458 | } | ||
459 | |||
460 | if(stackused<0) | ||
461 | { | ||
462 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",lineno,yylval,yylval); |
463 | amb | 348 | yychar=LEX_ERROR_NO_START; |
464 | } | ||
465 | |||
466 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
467 | amb | 351 | if(attributes[i]) |
468 | { | ||
469 | free(attributes[i]); | ||
470 | attributes[i]=NULL; | ||
471 | } | ||
472 | |||
473 | amb | 344 | if(tag->callback) |
474 | amb | 363 | if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes)) |
475 | yychar=LEX_ERROR_CALLBACK; | ||
476 | amb | 344 | |
477 | amb | 348 | if(stackused>0) |
478 | tag=tag_stack[stackused-1]; | ||
479 | else | ||
480 | tag=NULL; | ||
481 | amb | 344 | |
482 | amb | 334 | break; |
483 | |||
484 | /* An attribute key */ | ||
485 | |||
486 | case LEX_ATTR_KEY: | ||
487 | |||
488 | attribute=-1; | ||
489 | |||
490 | amb | 356 | for(i=0;i<tag->nattributes;i++) |
491 | amb | 334 | if(!strcasecmp(yylval,tag->attributes[i])) |
492 | { | ||
493 | attribute=i; | ||
494 | |||
495 | break; | ||
496 | } | ||
497 | |||
498 | if(attribute==-1) | ||
499 | { | ||
500 | amb | 366 | if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR || |
501 | ((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr(yylval,':'))) | ||
502 | amb | 337 | { |
503 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: unexpected attribute '%s' for tag '%s'.\n",lineno,yylval,tag->name); |
504 | amb | 348 | yychar=LEX_ERROR_UNEXP_ATT; |
505 | amb | 337 | } |
506 | amb | 366 | else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN) |
507 | amb | 791 | fprintf(stderr,"XML Parser: Warning on line %llu: unexpected attribute '%s' for tag '%s'.\n",lineno,yylval,tag->name); |
508 | amb | 334 | } |
509 | amb | 348 | |
510 | amb | 334 | break; |
511 | |||
512 | /* An attribute value */ | ||
513 | |||
514 | case LEX_ATTR_VAL: | ||
515 | |||
516 | amb | 363 | if(tag->callback && attribute!=-1 && yylval) |
517 | amb | 334 | attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval); |
518 | amb | 348 | |
519 | break; | ||
520 | |||
521 | /* End of file */ | ||
522 | |||
523 | case LEX_EOF: | ||
524 | |||
525 | if(tag) | ||
526 | { | ||
527 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: end of file seen without end tag '</%s>'.\n",lineno,tag->name); |
528 | amb | 348 | yychar=LEX_ERROR_UNEXP_EOF; |
529 | } | ||
530 | |||
531 | break; | ||
532 | |||
533 | case LEX_ERROR_TAG_START: | ||
534 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: character '<' seen not at start of tag.\n",lineno); |
535 | amb | 348 | break; |
536 | |||
537 | case LEX_ERROR_XML_DECL_START: | ||
538 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: characters '<?' seen not at start of XML declaration.\n",lineno); |
539 | amb | 348 | break; |
540 | |||
541 | case LEX_ERROR_TAG: | ||
542 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen inside tag '<%s...>'.\n",lineno,tag->name); |
543 | amb | 348 | break; |
544 | |||
545 | case LEX_ERROR_XML_DECL: | ||
546 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen inside XML declaration '<?%s...>'.\n",lineno,tag->name); |
547 | amb | 348 | break; |
548 | |||
549 | case LEX_ERROR_ATTR: | ||
550 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid attribute definition seen in tag.\n",lineno); |
551 | amb | 348 | break; |
552 | |||
553 | case LEX_ERROR_END_TAG: | ||
554 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen in end-tag.\n",lineno); |
555 | amb | 348 | break; |
556 | |||
557 | case LEX_ERROR_COMMENT: | ||
558 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid comment seen.\n",lineno); |
559 | amb | 348 | break; |
560 | |||
561 | case LEX_ERROR_CLOSE: | ||
562 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: character '>' seen not at end of tag.\n",lineno); |
563 | amb | 348 | break; |
564 | amb | 351 | |
565 | case LEX_ERROR_ATTR_VAL: | ||
566 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid character '%s' seen in attribute value.\n",lineno,yylval); |
567 | amb | 351 | break; |
568 | amb | 356 | |
569 | case LEX_ERROR_ENTITY_REF: | ||
570 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid entity reference '%s' seen in attribute value.\n",lineno,yylval); |
571 | amb | 356 | break; |
572 | |||
573 | case LEX_ERROR_CHAR_REF: | ||
574 | amb | 791 | fprintf(stderr,"XML Parser: Error on line %llu: invalid character reference '%s' seen in attribute value.\n",lineno,yylval); |
575 | amb | 356 | break; |
576 | amb | 334 | } |
577 | amb | 348 | } |
578 | while(yychar>LEX_EOF && yychar<LEX_ERROR); | ||
579 | amb | 334 | |
580 | /* Delete the tagdata */ | ||
581 | |||
582 | amb | 356 | for(i=0;i<XMLPARSE_MAX_ATTRS;i++) |
583 | amb | 334 | if(attributes[i]) |
584 | free(attributes[i]); | ||
585 | |||
586 | if(stackdepth) | ||
587 | amb | 401 | { |
588 | free(tag_stack); | ||
589 | amb | 344 | free(tags_stack); |
590 | amb | 401 | } |
591 | amb | 348 | |
592 | return(yychar); | ||
593 | amb | 334 | } |
594 | amb | 348 | |
595 | |||
596 | /*++++++++++++++++++++++++++++++++++++++ | ||
597 | Return the current parser line number. | ||
598 | |||
599 | amb | 791 | unsigned long long ParseXML_LineNumber Returns the line number. |
600 | amb | 348 | ++++++++++++++++++++++++++++++++++++++*/ |
601 | |||
602 | amb | 791 | unsigned long long ParseXML_LineNumber(void) |
603 | amb | 348 | { |
604 | amb | 791 | return(lineno); |
605 | amb | 348 | } |
606 | amb | 356 | |
607 | |||
608 | /*++++++++++++++++++++++++++++++++++++++ | ||
609 | Convert an XML entity reference into an ASCII string. | ||
610 | |||
611 | amb | 377 | char *ParseXML_Decode_Entity_Ref Returns a pointer to the replacement decoded string. |
612 | amb | 356 | |
613 | const char *string The entity reference string. | ||
614 | ++++++++++++++++++++++++++++++++++++++*/ | ||
615 | |||
616 | amb | 377 | char *ParseXML_Decode_Entity_Ref(const char *string) |
617 | amb | 356 | { |
618 | if(!strcmp(string,"&")) return("&"); | ||
619 | if(!strcmp(string,"<")) return("<"); | ||
620 | if(!strcmp(string,">")) return(">"); | ||
621 | if(!strcmp(string,"'")) return("'"); | ||
622 | if(!strcmp(string,""")) return("\""); | ||
623 | return(NULL); | ||
624 | } | ||
625 | |||
626 | |||
627 | /*++++++++++++++++++++++++++++++++++++++ | ||
628 | Convert an XML character reference into an ASCII string. | ||
629 | |||
630 | amb | 377 | char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string. |
631 | amb | 356 | |
632 | const char *string The character reference string. | ||
633 | ++++++++++++++++++++++++++++++++++++++*/ | ||
634 | |||
635 | amb | 377 | char *ParseXML_Decode_Char_Ref(const char *string) |
636 | amb | 356 | { |
637 | amb | 662 | static char result[5]=""; |
638 | long int unicode; | ||
639 | amb | 356 | |
640 | amb | 662 | if(string[2]=='x') unicode=strtol(string+3,NULL,16); |
641 | else unicode=strtol(string+2,NULL,10); | ||
642 | amb | 356 | |
643 | amb | 662 | if(unicode<0x80) |
644 | { | ||
645 | /* 0000 0000-0000 007F => 0xxxxxxx */ | ||
646 | result[0]=unicode; | ||
647 | result[1]=0; | ||
648 | } | ||
649 | else if(unicode<0x07FF) | ||
650 | { | ||
651 | /* 0000 0080-0000 07FF => 110xxxxx 10xxxxxx */ | ||
652 | result[0]=0xC0+((unicode&0x07C0)>>6); | ||
653 | result[1]=0x80+ (unicode&0x003F); | ||
654 | result[2]=0; | ||
655 | } | ||
656 | else if(unicode<0xFFFF) | ||
657 | { | ||
658 | /* 0000 0800-0000 FFFF => 1110xxxx 10xxxxxx 10xxxxxx */ | ||
659 | result[0]=0xE0+((unicode&0xF000)>>12); | ||
660 | result[1]=0x80+((unicode&0x0FC0)>>6); | ||
661 | result[2]=0x80+ (unicode&0x003F); | ||
662 | result[3]=0; | ||
663 | } | ||
664 | else if(unicode<0x1FFFFF) | ||
665 | { | ||
666 | /* 0001 0000-001F FFFF => 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ | ||
667 | result[0]=0xF0+((unicode&0x1C0000)>>18); | ||
668 | result[1]=0x80+((unicode&0x03F000)>>12); | ||
669 | result[2]=0x80+((unicode&0x000FC0)>>6); | ||
670 | result[3]=0x80+ (unicode&0x00003F); | ||
671 | result[4]=0; | ||
672 | } | ||
673 | else | ||
674 | { | ||
675 | result[0]=0xFF; | ||
676 | result[1]=0xFD; | ||
677 | result[2]=0; | ||
678 | } | ||
679 | amb | 356 | |
680 | return(result); | ||
681 | } | ||
682 | |||
683 | |||
684 | /*++++++++++++++++++++++++++++++++++++++ | ||
685 | Convert a string into something that is safe to output in an XML file. | ||
686 | |||
687 | amb | 377 | char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed). |
688 | amb | 356 | |
689 | const char *string The string to convert. | ||
690 | ++++++++++++++++++++++++++++++++++++++*/ | ||
691 | |||
692 | amb | 377 | char *ParseXML_Encode_Safe_XML(const char *string) |
693 | amb | 356 | { |
694 | static const char hexstring[17]="0123456789ABCDEF"; | ||
695 | int i=0,j=0,len; | ||
696 | char *result; | ||
697 | |||
698 | for(i=0;string[i];i++) | ||
699 | amb | 509 | if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || (unsigned char)string[i]>127) |
700 | amb | 356 | break; |
701 | |||
702 | if(!string[i]) | ||
703 | amb | 377 | return((char*)string); |
704 | amb | 356 | |
705 | len=i+256-6; | ||
706 | |||
707 | result=(char*)malloc(len+7); | ||
708 | amb | 404 | strncpy(result,string,j=i); |
709 | amb | 356 | |
710 | do | ||
711 | { | ||
712 | for(;j<len && string[i];i++) | ||
713 | if(string[i]=='<') | ||
714 | { | ||
715 | result[j++]='&'; | ||
716 | result[j++]='l'; | ||
717 | result[j++]='t'; | ||
718 | result[j++]=';'; | ||
719 | } | ||
720 | else if(string[i]=='>') | ||
721 | { | ||
722 | result[j++]='&'; | ||
723 | result[j++]='g'; | ||
724 | result[j++]='t'; | ||
725 | result[j++]=';'; | ||
726 | } | ||
727 | else if(string[i]=='&') | ||
728 | { | ||
729 | result[j++]='&'; | ||
730 | result[j++]='a'; | ||
731 | result[j++]='m'; | ||
732 | result[j++]='p'; | ||
733 | result[j++]=';'; | ||
734 | } | ||
735 | else if(string[i]=='\'') | ||
736 | { | ||
737 | result[j++]='&'; | ||
738 | result[j++]='a'; | ||
739 | result[j++]='p'; | ||
740 | result[j++]='o'; | ||
741 | result[j++]='s'; | ||
742 | result[j++]=';'; | ||
743 | } | ||
744 | else if(string[i]=='"') | ||
745 | { | ||
746 | result[j++]='&'; | ||
747 | result[j++]='q'; | ||
748 | result[j++]='u'; | ||
749 | result[j++]='o'; | ||
750 | result[j++]='t'; | ||
751 | result[j++]=';'; | ||
752 | } | ||
753 | amb | 509 | else if(string[i]>=32 && (unsigned char)string[i]<=127) |
754 | amb | 487 | result[j++]=string[i]; |
755 | else | ||
756 | amb | 356 | { |
757 | amb | 487 | unsigned int unicode; |
758 | |||
759 | /* Decode the UTF-8 */ | ||
760 | |||
761 | amb | 662 | if((string[i]&0x80)==0) |
762 | amb | 487 | { |
763 | amb | 662 | /* 0000 0000-0000 007F => 0xxxxxxx */ |
764 | unicode=string[i]; | ||
765 | } | ||
766 | else if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80) | ||
767 | { | ||
768 | /* 0000 0080-0000 07FF => 110xxxxx 10xxxxxx */ | ||
769 | amb | 487 | unicode =(string[i++]&0x1F)<<6; |
770 | unicode|= string[i ]&0x3F; | ||
771 | } | ||
772 | else if((string[i]&0xF0)==0xE0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80) | ||
773 | { | ||
774 | amb | 662 | /* 0000 0800-0000 FFFF => 1110xxxx 10xxxxxx 10xxxxxx */ |
775 | amb | 487 | unicode =(string[i++]&0x0F)<<12; |
776 | unicode|=(string[i++]&0x3F)<<6; | ||
777 | unicode|= string[i ]&0x3F; | ||
778 | } | ||
779 | else if((string[i]&0xF8)==0xF0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80 && (string[i+3]&0xC0)==0x80) | ||
780 | { | ||
781 | amb | 662 | /* 0001 0000-001F FFFF => 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
782 | amb | 487 | unicode =(string[i++]&0x07)<<18; |
783 | unicode|=(string[i++]&0x3F)<<12; | ||
784 | unicode|=(string[i++]&0x3F)<<6; | ||
785 | unicode|= string[i ]&0x3F; | ||
786 | } | ||
787 | else | ||
788 | unicode=0xFFFD; | ||
789 | |||
790 | /* Output the character entity */ | ||
791 | |||
792 | amb | 356 | result[j++]='&'; |
793 | result[j++]='#'; | ||
794 | result[j++]='x'; | ||
795 | amb | 487 | |
796 | if(unicode&0x00FF0000) | ||
797 | { | ||
798 | result[j++]=hexstring[((unicode>>16)&0xf0)>>4]; | ||
799 | result[j++]=hexstring[((unicode>>16)&0x0f) ]; | ||
800 | } | ||
801 | if(unicode&0x00FFFF00) | ||
802 | { | ||
803 | result[j++]=hexstring[((unicode>>8)&0xf0)>>4]; | ||
804 | result[j++]=hexstring[((unicode>>8)&0x0f) ]; | ||
805 | } | ||
806 | result[j++]=hexstring[(unicode&0xf0)>>4]; | ||
807 | result[j++]=hexstring[(unicode&0x0f) ]; | ||
808 | |||
809 | amb | 356 | result[j++]=';'; |
810 | } | ||
811 | |||
812 | if(string[i]) /* Not finished */ | ||
813 | { | ||
814 | len+=256; | ||
815 | result=(char*)realloc((void*)result,len+7); | ||
816 | } | ||
817 | } | ||
818 | while(string[i]); | ||
819 | |||
820 | result[j]=0; | ||
821 | |||
822 | return(result); | ||
823 | } | ||
824 | amb | 363 | |
825 | |||
826 | /*++++++++++++++++++++++++++++++++++++++ | ||
827 | amb | 773 | Check that a string really is an integer. |
828 | amb | 363 | |
829 | amb | 773 | int ParseXML_IsInteger Returns 1 if an integer could be found or 0 otherwise. |
830 | amb | 363 | |
831 | const char *string The string to be parsed. | ||
832 | ++++++++++++++++++++++++++++++++++++++*/ | ||
833 | |||
834 | amb | 773 | int ParseXML_IsInteger(const char *string) |
835 | amb | 363 | { |
836 | const char *p=string; | ||
837 | |||
838 | if(*p=='-' || *p=='+') | ||
839 | p++; | ||
840 | |||
841 | while(isdigit(*p)) | ||
842 | p++; | ||
843 | |||
844 | if(*p) | ||
845 | return(0); | ||
846 | amb | 773 | else |
847 | return(1); | ||
848 | amb | 363 | } |
849 | |||
850 | |||
851 | /*++++++++++++++++++++++++++++++++++++++ | ||
852 | amb | 773 | Check that a string really is a floating point number. |
853 | amb | 363 | |
854 | amb | 773 | int ParseXML_IsFloating Returns 1 if a floating point number could be found or 0 otherwise. |
855 | amb | 363 | |
856 | const char *string The string to be parsed. | ||
857 | ++++++++++++++++++++++++++++++++++++++*/ | ||
858 | |||
859 | amb | 773 | int ParseXML_IsFloating(const char *string) |
860 | amb | 363 | { |
861 | const char *p=string; | ||
862 | |||
863 | if(*p=='-' || *p=='+') | ||
864 | p++; | ||
865 | |||
866 | while(isdigit(*p) || *p=='.') | ||
867 | p++; | ||
868 | |||
869 | amb | 396 | if(*p=='e' || *p=='E') |
870 | { | ||
871 | p++; | ||
872 | |||
873 | if(*p=='-' || *p=='+') | ||
874 | p++; | ||
875 | |||
876 | while(isdigit(*p)) | ||
877 | p++; | ||
878 | } | ||
879 | |||
880 | amb | 363 | if(*p) |
881 | return(0); | ||
882 | amb | 773 | else |
883 | return(1); | ||
884 | amb | 363 | } |
Properties
Name | Value |
---|---|
cvs:description | A simple generic XML parser. |