Routino SVN Repository Browser

Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino

ViewVC logotype

Annotation of /trunk/src/xmlparse.l

Parent Directory Parent Directory | Revision Log Revision Log


Revision 351 - (hide annotations) (download)
Tue Apr 6 18:48:07 2010 UTC (15 years ago) by amb
File size: 17358 byte(s)
Change error message for bad character in a quoted string.
Make sure attribute values are cleared before calling tag function (for
end-tags).

1 amb 334 %{
2     /***************************************
3 amb 351 $Header: /home/amb/CVS/routino/src/xmlparse.l,v 1.5 2010-04-06 18:48:07 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     #include <string.h>
31    
32     /* Parser outputs */
33    
34 amb 348 #define LEX_EOF 0
35 amb 334
36 amb 348 #define LEX_TAG_BEGIN 1
37     #define LEX_XML_DECL_BEGIN 2
38     #define LEX_TAG_POP 3
39     #define LEX_TAG_PUSH 4
40     #define LEX_XML_DECL_FINISH 6
41     #define LEX_TAG_FINISH 7
42     #define LEX_ATTR_KEY 8
43     #define LEX_ATTR_VAL 9
44 amb 334
45 amb 348 #define LEX_ERROR 100
46    
47     #define LEX_ERROR_TAG_START 101
48     #define LEX_ERROR_XML_DECL_START 102
49     #define LEX_ERROR_TAG 103
50     #define LEX_ERROR_XML_DECL 104
51     #define LEX_ERROR_ATTR 105
52     #define LEX_ERROR_END_TAG 106
53     #define LEX_ERROR_COMMENT 107
54     #define LEX_ERROR_CLOSE 108
55 amb 351 #define LEX_ERROR_ATTR_VAL 109
56 amb 348
57     #define LEX_ERROR_UNEXP_TAG 201
58     #define LEX_ERROR_UNBALANCED 202
59     #define LEX_ERROR_NO_START 203
60     #define LEX_ERROR_UNEXP_ATT 204
61     #define LEX_ERROR_UNEXP_EOF 205
62     #define LEX_ERROR_XML_NOT_FIRST 206
63    
64    
65 amb 334 /* Lexer definitions */
66    
67     #define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */
68     #ifndef yywrap
69     /*+ Needed in lex but does nothing. +*/
70     #define yywrap() 1
71     #endif
72    
73     /*+ Reset the current string. +*/
74     #define reset_string \
75     if(string) *string=0; \
76     stringused=0;
77    
78     /*+ append information to the current string. +*/
79     #define append_string(xx) \
80     newlen=strlen(xx); \
81     if((stringused+newlen)>=stringlen) \
82     string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \
83     strcpy(string+stringused,xx); \
84     stringused+=newlen;
85    
86     #define YY_NO_INPUT
87    
88    
89     /* Lexer functions and variables */
90    
91     extern int yylex(void);
92    
93     static char *yylval=NULL;
94    
95     %}
96    
97 amb 344 %option 8bit
98 amb 348 %option pointer
99 amb 344 %option batch
100     %option yylineno
101 amb 348
102 amb 344 %option nodefault
103     %option perf-report
104     %option fast
105 amb 348 %option nounput
106 amb 344
107 amb 334
108 amb 348 /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII not Unicode. */
109 amb 334
110 amb 348 S [ \t\r\n]
111 amb 334
112 amb 348 letter [a-zA-Z]
113     digit [0-9]
114     xdigit [a-fA-F0-9]
115    
116     namechar ({letter}|{digit}|[-._:])
117     name ({letter}|[_:]){namechar}*
118    
119     entityref &{name};
120     charref &#({digit}+|x{xdigit}+);
121    
122    
123 amb 334 %x COMMENT
124 amb 348 %x CDATA
125     %x DOCTYPE
126     %x XML_DECL_START XML_DECL
127     %x TAG_START TAG
128     %x ATTR_KEY ATTR_VAL
129     %x END_TAG1 END_TAG2
130 amb 334 %x DQUOTED SQUOTED
131    
132     %%
133     /* Must use static variables since the parser returns often. */
134     static char *string=NULL;
135     static int stringlen=0,stringused=0;
136 amb 348 static int after_attr=0;
137 amb 334 int newlen;
138 amb 348 int doctype_depth=0;
139 amb 334
140 amb 348 /* Handle top level entities */
141 amb 334
142     "<!--" { BEGIN(COMMENT); }
143 amb 348 "<![CDATA[" { BEGIN(CDATA); }
144     "<!DOCTYPE" { BEGIN(DOCTYPE); doctype_depth=0; }
145     "</" { BEGIN(END_TAG1); }
146     "<?" { BEGIN(XML_DECL_START); }
147 amb 334 "<" { BEGIN(TAG_START); }
148 amb 348 ">" { return(LEX_ERROR_CLOSE); }
149     [^<>]+ { }
150 amb 334
151 amb 348 /* Comments */
152 amb 334
153 amb 348 <COMMENT>"--->" { return(LEX_ERROR_COMMENT); }
154     <COMMENT>"-->" { BEGIN(INITIAL); }
155     <COMMENT>"--"[^->]+ { }
156     <COMMENT>[^-]+ { }
157 amb 334 <COMMENT>"-" { }
158    
159 amb 348 /* CDATA */
160 amb 334
161 amb 348 <CDATA>"]]>" { BEGIN(INITIAL); }
162     <CDATA>"]" { }
163     <CDATA>[^]]+ { }
164 amb 334
165 amb 348 /* CDATA */
166 amb 334
167 amb 348 <DOCTYPE>"<" { doctype_depth++; }
168     <DOCTYPE>">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; }
169     <DOCTYPE>[^<>]+ { }
170 amb 334
171 amb 348 /* XML Declaration start */
172    
173     <XML_DECL_START>{name} { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); }
174     <XML_DECL_START>.|\n { return(LEX_ERROR_XML_DECL_START); }
175    
176     /* Tag middle */
177    
178     <XML_DECL>"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); }
179     <XML_DECL>{S}+ { }
180     <XML_DECL>{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
181     <XML_DECL>.|\n { return(LEX_ERROR_XML_DECL); }
182    
183     /* Any tag start */
184    
185     <TAG_START>{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); }
186     <TAG_START>.|\n { return(LEX_ERROR_TAG_START); }
187    
188     /* End-tag start */
189    
190     <END_TAG1>{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); }
191     <END_TAG1>.|\n { return(LEX_ERROR_END_TAG); }
192    
193     <END_TAG2>">" { BEGIN(INITIAL); }
194     <END_TAG2>.|\n { return(LEX_ERROR_END_TAG); }
195    
196     /* Any tag middle */
197    
198 amb 334 <TAG>"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); }
199     <TAG>">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); }
200 amb 348 <TAG>{S}+ { }
201     <TAG>{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
202     <TAG>.|\n { return(LEX_ERROR_TAG); }
203 amb 334
204 amb 348 /* Attributes */
205 amb 334
206 amb 348 <ATTR_KEY>= { BEGIN(ATTR_VAL); }
207     <ATTR_KEY>.|\n { return(LEX_ERROR_ATTR); }
208 amb 334
209 amb 348 <ATTR_VAL>\" { BEGIN(DQUOTED); reset_string; }
210     <ATTR_VAL>\' { BEGIN(SQUOTED); reset_string; }
211     <ATTR_VAL>.|\n { return(LEX_ERROR_ATTR); }
212    
213 amb 334 /* Quoted strings */
214    
215 amb 348 <DQUOTED>\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
216     <DQUOTED>{entityref} { append_string(yytext); }
217     <DQUOTED>{charref} { append_string(yytext); }
218 amb 351 <DQUOTED>[<>&] { return(LEX_ERROR_ATTR_VAL); }
219 amb 348 <DQUOTED>[^<>&\"]+ { append_string(yytext); }
220 amb 334
221 amb 348 <SQUOTED>\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); }
222     <SQUOTED>{entityref} { append_string(yytext); }
223     <SQUOTED>{charref} { append_string(yytext); }
224 amb 351 <SQUOTED>[<>&] { return(LEX_ERROR_ATTR_VAL); }
225 amb 348 <SQUOTED>[^<>&\']+ { append_string(yytext); }
226 amb 334
227     /* End of file */
228    
229 amb 348 <<EOF>> { free(string); string=NULL; BEGIN(INITIAL); return(LEX_EOF); }
230 amb 334
231     %%
232    
233     #include "xmlparse.h"
234    
235 amb 348
236     /*++++++++++++++++++++++++++++++++++++++
237     A function to call the callback function with the parameters needed.
238    
239     char *name The name of the tag.
240    
241     void (*callback)() The callback function.
242    
243     int type The type of tag (start and/or end).
244    
245     int nattributes The number of attributes collected.
246    
247     char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes.
248     ++++++++++++++++++++++++++++++++++++++*/
249    
250 amb 344 static inline void call_callback(char *name,void (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS])
251     {
252     switch(nattributes)
253     {
254     case 0: (*callback)(type); break;
255     case 1: (*callback)(type,attributes[0]); break;
256     case 2: (*callback)(type,attributes[0],attributes[1]); break;
257     case 3: (*callback)(type,attributes[0],attributes[1],attributes[2]); break;
258     case 4: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3]); break;
259     case 5: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]); break;
260     case 6: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]); break;
261     case 7: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]); break;
262     case 8: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]); break;
263     case 9: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]); break;
264     case 10: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]); break;
265     case 11: (*callback)(type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]); break;
266     case 12: (*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]); break;
267     case 13: (*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]); break;
268     case 14: (*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]); break;
269     case 15: (*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]); break;
270     case 16: (*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]); break;
271 amb 334
272 amb 344 default:
273 amb 348 fprintf(stderr,"XML Parser: Error on line %d: too many attributes for tag '%s'.\n",yylineno,name);
274 amb 344 exit(1);
275     }
276     }
277    
278    
279 amb 334 /*++++++++++++++++++++++++++++++++++++++
280     Parse the XML and call the functions for each tag as seen.
281    
282 amb 348 int ParseXML Returns 0 if OK or something else in case of an error.
283    
284 amb 334 FILE *file The file to parse.
285    
286     xmltag **tags The array of pointers to tags for the top level.
287 amb 337
288     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.
289 amb 334 ++++++++++++++++++++++++++++++++++++++*/
290    
291 amb 348 int ParseXML(FILE *file,xmltag **tags,int ignore_unknown_attributes)
292 amb 334 {
293     int yychar,i;
294    
295     int nattributes=0;
296 amb 351 char *attributes[XMLPARSE_MAX_ATTRS]={NULL};
297 amb 334 int attribute=0;
298    
299     int stackdepth=0,stackused=0;
300 amb 344 xmltag ***tags_stack=NULL;
301     xmltag **tag_stack=NULL;
302 amb 334 xmltag *tag=NULL;
303    
304     static int first=1;
305    
306     /* Parser (re)-initialisation */
307    
308     yyin=file;
309    
310     if(!first)
311     yyrestart(NULL);
312    
313     first=0;
314    
315     /* The actual parser. */
316    
317 amb 348 do
318     {
319     yychar=yylex();
320    
321 amb 334 switch(yychar)
322     {
323 amb 348 /* The start of a tag for an XML declaration */
324    
325     case LEX_XML_DECL_BEGIN:
326    
327     if(tag_stack)
328     {
329     fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno);
330     yychar=LEX_ERROR_XML_NOT_FIRST;
331     break;
332     }
333    
334 amb 334 /* The start of a tag for an element */
335    
336     case LEX_TAG_BEGIN:
337    
338     tag=NULL;
339    
340     for(i=0;tags[i];i++)
341     if(!strcasecmp(yylval,tags[i]->name))
342     {
343     tag=tags[i];
344    
345 amb 351 for(i=0;i<XMLPARSE_MAX_ATTRS;i++)
346     if(attributes[i])
347     {
348     free(attributes[i]);
349     attributes[i]=NULL;
350     }
351 amb 334
352     for(i=0;i<XMLPARSE_MAX_ATTRS;i++)
353     if(!tag->attributes[i])
354     break;
355    
356     nattributes=i;
357    
358     break;
359     }
360    
361     if(tag==NULL)
362     {
363 amb 348 fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval);
364     yychar=LEX_ERROR_UNEXP_TAG;
365 amb 334 }
366 amb 348
367 amb 334 break;
368    
369     /* The end of the start-tag for an element */
370    
371     case LEX_TAG_PUSH:
372    
373     if(stackused==stackdepth)
374 amb 344 {
375     tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*));
376     tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**));
377     }
378 amb 334
379 amb 344 tag_stack [stackused]=tag;
380     tags_stack[stackused]=tags;
381     stackused++;
382    
383     if(tag->callback)
384     call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,nattributes,attributes);
385    
386 amb 334 tags=tag->subtags;
387    
388 amb 344 break;
389    
390 amb 348 /* The end of the empty-element-tag for an XML declaration */
391    
392     case LEX_XML_DECL_FINISH:
393    
394 amb 334 /* The end of the empty-element-tag for an element */
395    
396     case LEX_TAG_FINISH:
397    
398     if(tag->callback)
399 amb 344 call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,nattributes,attributes);
400 amb 334
401 amb 344 if(stackused>0)
402     tag=tag_stack[stackused-1];
403     else
404     tag=NULL;
405 amb 334
406     break;
407    
408     /* The end of the end-tag for an element */
409    
410     case LEX_TAG_POP:
411    
412 amb 344 stackused--;
413     tags=tags_stack[stackused];
414     tag =tag_stack [stackused];
415 amb 334
416 amb 348 if(strcmp(tag->name,yylval))
417     {
418     fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name);
419     yychar=LEX_ERROR_UNBALANCED;
420     }
421    
422     if(stackused<0)
423     {
424     fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval);
425     yychar=LEX_ERROR_NO_START;
426     }
427    
428 amb 351 for(i=0;i<XMLPARSE_MAX_ATTRS;i++)
429     if(attributes[i])
430     {
431     free(attributes[i]);
432     attributes[i]=NULL;
433     }
434    
435 amb 344 if(tag->callback)
436     call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,nattributes,attributes);
437    
438 amb 348 if(stackused>0)
439     tag=tag_stack[stackused-1];
440     else
441     tag=NULL;
442 amb 344
443 amb 334 break;
444    
445     /* An attribute key */
446    
447     case LEX_ATTR_KEY:
448    
449     attribute=-1;
450    
451     for(i=0;i<nattributes;i++)
452     if(!strcasecmp(yylval,tag->attributes[i]))
453     {
454     attribute=i;
455    
456     break;
457     }
458    
459     if(attribute==-1)
460     {
461 amb 337 if(ignore_unknown_attributes==0)
462     {
463 amb 348 fprintf(stderr,"XML Parser: Error on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
464     yychar=LEX_ERROR_UNEXP_ATT;
465 amb 337 }
466     else if(ignore_unknown_attributes==1)
467 amb 348 fprintf(stderr,"XML Parser: Warning on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
468 amb 334 }
469 amb 348
470 amb 334 break;
471    
472     /* An attribute value */
473    
474     case LEX_ATTR_VAL:
475    
476 amb 337 if(yylval && attribute!=-1)
477 amb 334 attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval);
478 amb 348
479     break;
480    
481     /* End of file */
482    
483     case LEX_EOF:
484    
485     if(tag)
486     {
487     fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag '</%s>'.\n",yylineno,tag->name);
488     yychar=LEX_ERROR_UNEXP_EOF;
489     }
490    
491     break;
492    
493     case LEX_ERROR_TAG_START:
494     fprintf(stderr,"XML Parser: Error on line %d: character '<' seen not at start of tag.\n",yylineno);
495     break;
496    
497     case LEX_ERROR_XML_DECL_START:
498     fprintf(stderr,"XML Parser: Error on line %d: characters '<?' seen not at start of XML declaration.\n",yylineno);
499     break;
500    
501     case LEX_ERROR_TAG:
502     fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside tag '<%s...>'.\n",yylineno,tag->name);
503     break;
504    
505     case LEX_ERROR_XML_DECL:
506     fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside XML declaration '<?%s...>'.\n",yylineno,tag->name);
507     break;
508    
509     case LEX_ERROR_ATTR:
510     fprintf(stderr,"XML Parser: Error on line %d: invalid attribute definition seen in tag.\n",yylineno);
511     break;
512    
513     case LEX_ERROR_END_TAG:
514     fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in end-tag.\n",yylineno);
515     break;
516    
517     case LEX_ERROR_COMMENT:
518     fprintf(stderr,"XML Parser: Error on line %d: invalid comment seen.\n",yylineno);
519     break;
520    
521     case LEX_ERROR_CLOSE:
522     fprintf(stderr,"XML Parser: Error on line %d: character '>' seen not at end of tag.\n",yylineno);
523     break;
524 amb 351
525     case LEX_ERROR_ATTR_VAL:
526     fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in attribute value.\n",yylineno);
527     break;
528 amb 334 }
529 amb 348 }
530     while(yychar>LEX_EOF && yychar<LEX_ERROR);
531 amb 334
532     /* Delete the tagdata */
533    
534     for(i=0;i<nattributes;i++)
535     if(attributes[i])
536     free(attributes[i]);
537    
538     if(stackdepth)
539 amb 344 free(tags_stack);
540 amb 348
541     return(yychar);
542 amb 334 }
543 amb 348
544    
545     /*++++++++++++++++++++++++++++++++++++++
546     Return the current parser line number.
547    
548     int ParseXML_LineNumber Returns the line number.
549     ++++++++++++++++++++++++++++++++++++++*/
550    
551     int ParseXML_LineNumber(void)
552     {
553     return(yylineno);
554     }

Properties

Name Value
cvs:description A simple generic XML parser.