diff options
Diffstat (limited to 'src/xmlreader.cpp')
-rw-r--r-- | src/xmlreader.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/src/xmlreader.cpp b/src/xmlreader.cpp new file mode 100644 index 0000000..bb24157 --- /dev/null +++ b/src/xmlreader.cpp | |||
@@ -0,0 +1,412 @@ | |||
1 | #include "xmlreader.h" | ||
2 | #include <string.h> | ||
3 | |||
4 | XmlReader::XmlReader( bool bStrip ) | ||
5 | { | ||
6 | nError = 0; | ||
7 | this->bStrip = bStrip; | ||
8 | } | ||
9 | |||
10 | XmlReader::~XmlReader() | ||
11 | { | ||
12 | } | ||
13 | |||
14 | #define gcall( x ) if( x == false ) return false; | ||
15 | |||
16 | bool XmlReader::isws( char chr ) | ||
17 | { | ||
18 | return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' ); | ||
19 | } | ||
20 | |||
21 | bool XmlReader::ws() | ||
22 | { | ||
23 | while( true ) | ||
24 | { | ||
25 | char chr = getChar(); | ||
26 | if( isws( chr ) ) | ||
27 | { | ||
28 | usedChar(); | ||
29 | } | ||
30 | else | ||
31 | { | ||
32 | return true; | ||
33 | } | ||
34 | } | ||
35 | return true; | ||
36 | } | ||
37 | |||
38 | bool XmlReader::buildDoc() | ||
39 | { | ||
40 | // take care of initial whitespace | ||
41 | gcall( ws() ); | ||
42 | gcall( node() ); | ||
43 | |||
44 | return true; | ||
45 | } | ||
46 | |||
47 | bool XmlReader::node() | ||
48 | { | ||
49 | gcall( startNode() ) | ||
50 | |||
51 | // At this point, we are closing the startNode | ||
52 | char chr = getChar(); | ||
53 | if( chr == '>' ) | ||
54 | { | ||
55 | usedChar(); | ||
56 | |||
57 | // Now we process the guts of the node. | ||
58 | gcall( content() ); | ||
59 | } | ||
60 | else if( chr == '/' ) | ||
61 | { | ||
62 | // This is the tricky one, one more validation, then we close the node. | ||
63 | usedChar(); | ||
64 | if( getChar() == '>' ) | ||
65 | { | ||
66 | closeNode(); | ||
67 | usedChar(); | ||
68 | } | ||
69 | else | ||
70 | { | ||
71 | reportError("Close node in singleNode malformed!"); | ||
72 | return false; | ||
73 | } | ||
74 | } | ||
75 | else | ||
76 | { | ||
77 | reportError("Close node expected, but not found."); | ||
78 | return false; | ||
79 | } | ||
80 | |||
81 | return true; | ||
82 | } | ||
83 | |||
84 | bool XmlReader::startNode() | ||
85 | { | ||
86 | if( getChar() == '<' ) | ||
87 | { | ||
88 | usedChar(); | ||
89 | |||
90 | if( getChar() == '/' ) | ||
91 | { | ||
92 | // Heh, it's actually a close node, go figure | ||
93 | FlexBuf fbName; | ||
94 | usedChar(); | ||
95 | gcall( ws() ); | ||
96 | |||
97 | while( true ) | ||
98 | { | ||
99 | char chr = getChar(); | ||
100 | if( isws( chr ) || chr == '>' ) | ||
101 | { | ||
102 | // Here we actually compare the name we got to the name | ||
103 | // we already set, they have to match exactly. | ||
104 | if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) ) | ||
105 | { | ||
106 | closeNode(); | ||
107 | break; | ||
108 | } | ||
109 | else | ||
110 | { | ||
111 | reportError("Got a mismatched node close tag."); | ||
112 | return false; | ||
113 | } | ||
114 | } | ||
115 | else | ||
116 | { | ||
117 | fbName.appendData( chr ); | ||
118 | usedChar(); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | gcall( ws() ); | ||
123 | if( getChar() == '>' ) | ||
124 | { | ||
125 | // Everything is cool. | ||
126 | usedChar(); | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | reportError("Got extra junk data instead of node close tag."); | ||
131 | return false; | ||
132 | } | ||
133 | } | ||
134 | else | ||
135 | { | ||
136 | // We're good, format is consistant | ||
137 | addNode(); | ||
138 | |||
139 | // Skip extra whitespace | ||
140 | gcall( ws() ); | ||
141 | gcall( name() ); | ||
142 | gcall( ws() ); | ||
143 | gcall( paramlist() ); | ||
144 | gcall( ws() ); | ||
145 | } | ||
146 | } | ||
147 | else | ||
148 | { | ||
149 | reportError("Expected to find node opening char, '<'.\n"); | ||
150 | return false; | ||
151 | } | ||
152 | |||
153 | return true; | ||
154 | } | ||
155 | |||
156 | bool XmlReader::name() | ||
157 | { | ||
158 | FlexBuf fbName; | ||
159 | |||
160 | while( true ) | ||
161 | { | ||
162 | char chr = getChar(); | ||
163 | if( isws( chr ) || chr == '>' || chr == '/' ) | ||
164 | { | ||
165 | setName( fbName.getData() ); | ||
166 | return true; | ||
167 | } | ||
168 | else | ||
169 | { | ||
170 | fbName.appendData( chr ); | ||
171 | usedChar(); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | return true; | ||
176 | } | ||
177 | |||
178 | bool XmlReader::paramlist() | ||
179 | { | ||
180 | while( true ) | ||
181 | { | ||
182 | char chr = getChar(); | ||
183 | if( chr == '/' || chr == '>' ) | ||
184 | { | ||
185 | return true; | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | gcall( param() ); | ||
190 | gcall( ws() ); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | return true; | ||
195 | } | ||
196 | |||
197 | char XmlReader::getEscape() | ||
198 | { | ||
199 | // Right now, we just do # escapes... | ||
200 | if( getChar( 1 ) == '#' ) | ||
201 | { | ||
202 | usedChar(); | ||
203 | usedChar(); | ||
204 | char buf[4]; | ||
205 | int j = 0; | ||
206 | for( j = 0; getChar() != ';'; j++ ) | ||
207 | { | ||
208 | buf[j] = getChar(); | ||
209 | usedChar(); | ||
210 | } | ||
211 | usedChar(); | ||
212 | buf[j] = '\0'; | ||
213 | return (char)atoi( buf ); | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | return '\0'; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | bool XmlReader::param() | ||
222 | { | ||
223 | FlexBuf fbName; | ||
224 | FlexBuf fbValue; | ||
225 | |||
226 | while( true ) | ||
227 | { | ||
228 | char chr = getChar(); | ||
229 | if( isws( chr ) || chr == '=' ) | ||
230 | { | ||
231 | break; | ||
232 | } | ||
233 | else | ||
234 | { | ||
235 | fbName.appendData( chr ); | ||
236 | usedChar(); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | gcall( ws() ); | ||
241 | |||
242 | if( getChar() == '=' ) | ||
243 | { | ||
244 | usedChar(); | ||
245 | |||
246 | gcall( ws() ); | ||
247 | |||
248 | char chr = getChar(); | ||
249 | if( chr == '"' ) | ||
250 | { | ||
251 | // Better quoted rhs | ||
252 | usedChar(); | ||
253 | |||
254 | while( true ) | ||
255 | { | ||
256 | chr = getChar(); | ||
257 | if( chr == '"' ) | ||
258 | { | ||
259 | usedChar(); | ||
260 | addProperty( fbName.getData(), fbValue.getData() ); | ||
261 | return true; | ||
262 | } | ||
263 | else | ||
264 | { | ||
265 | if( chr == '&' ) | ||
266 | { | ||
267 | chr = getEscape(); | ||
268 | if( chr == '\0' ) return false; | ||
269 | fbValue.appendData( chr ); | ||
270 | } | ||
271 | else | ||
272 | { | ||
273 | fbValue.appendData( chr ); | ||
274 | usedChar(); | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | else | ||
280 | { | ||
281 | // Simple one-word rhs | ||
282 | while( true ) | ||
283 | { | ||
284 | chr = getChar(); | ||
285 | if( isws( chr ) || chr == '/' || chr == '>' ) | ||
286 | { | ||
287 | addProperty( fbName.getData(), fbValue.getData() ); | ||
288 | return true; | ||
289 | } | ||
290 | else | ||
291 | { | ||
292 | if( chr == '&' ) | ||
293 | { | ||
294 | chr = getEscape(); | ||
295 | if( chr == '\0' ) return false; | ||
296 | fbValue.appendData( chr ); | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | fbValue.appendData( chr ); | ||
301 | usedChar(); | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | else | ||
308 | { | ||
309 | reportError("Expected an equals to seperate the params."); | ||
310 | return false; | ||
311 | } | ||
312 | |||
313 | return true; | ||
314 | } | ||
315 | |||
316 | bool XmlReader::content() | ||
317 | { | ||
318 | FlexBuf fbContent; | ||
319 | |||
320 | if( bStrip ) gcall( ws() ); | ||
321 | |||
322 | while( true ) | ||
323 | { | ||
324 | char chr = getChar(); | ||
325 | if( chr == '<' ) | ||
326 | { | ||
327 | if( getChar(1) == '/' ) | ||
328 | { | ||
329 | if( fbContent.getLength() > 0 ) | ||
330 | { | ||
331 | if( bStrip ) | ||
332 | { | ||
333 | int j; | ||
334 | for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- ); | ||
335 | ((char *)fbContent.getData())[j+1] = '\0'; | ||
336 | } | ||
337 | setContent( fbContent.getData() ); | ||
338 | } | ||
339 | usedChar(); | ||
340 | usedChar(); | ||
341 | gcall( ws() ); | ||
342 | FlexBuf fbName; | ||
343 | while( true ) | ||
344 | { | ||
345 | chr = getChar(); | ||
346 | if( isws( chr ) || chr == '>' ) | ||
347 | { | ||
348 | if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) ) | ||
349 | { | ||
350 | closeNode(); | ||
351 | break; | ||
352 | } | ||
353 | else | ||
354 | { | ||
355 | reportError("Mismatched close tag found."); | ||
356 | return false; | ||
357 | } | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | fbName.appendData( chr ); | ||
362 | usedChar(); | ||
363 | } | ||
364 | } | ||
365 | gcall( ws() ); | ||
366 | if( getChar() == '>' ) | ||
367 | { | ||
368 | usedChar(); | ||
369 | return true; | ||
370 | } | ||
371 | else | ||
372 | { | ||
373 | reportError("Malformed close tag."); | ||
374 | return false; | ||
375 | } | ||
376 | } | ||
377 | else | ||
378 | { | ||
379 | if( fbContent.getLength() > 0 ) | ||
380 | { | ||
381 | if( bStrip ) | ||
382 | { | ||
383 | int j; | ||
384 | for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- ); | ||
385 | ((char *)fbContent.getData())[j+1] = '\0'; | ||
386 | } | ||
387 | setContent( fbContent.getData() ); | ||
388 | fbContent.clearData(); | ||
389 | } | ||
390 | gcall( node() ); | ||
391 | } | ||
392 | |||
393 | if( bStrip ) gcall( ws() ); | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | fbContent.appendData( chr ); | ||
398 | usedChar(); | ||
399 | } | ||
400 | } | ||
401 | } | ||
402 | |||
403 | void XmlReader::reportError( const char *sError ) | ||
404 | { | ||
405 | printf("XmlReader error: %s\n", sError ); | ||
406 | } | ||
407 | |||
408 | int XmlReader::getError() | ||
409 | { | ||
410 | return nError; | ||
411 | } | ||
412 | |||