Contents of /trunk/src/qnd_xml.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 40 - (show annotations)
Sun Jan 18 19:43:20 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 11950 byte(s)
Option to use float math instead of double
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21 * qnd_xml - quick'n dirty xml is a very small and very fast implementation
22 * of a xml parser. The idea is to replace the usage of libxml2
23 * by this whenever performance is an issue. This is the case
24 * with reading the *.osm files on mobile devices. A powerful
25 * desktop will likely still use the libxml as it's just "better"
26 */
27
28 #include "appdata.h"
29
30 #include <ctype.h>
31 int isblank(int c);
32
33 typedef struct {
34 gpointer userdata;
35
36 int total, bytes_read;
37
38 char *buffer, *cur;
39
40 qnd_xml_stack_t *stack, *sp;
41 int mod; // modifier (?, !, /) in element
42 gboolean done;
43
44 qnd_xml_attribute_t *attributes;
45
46 } qnd_xml_context_t;
47
48
49 void stack_dump(qnd_xml_context_t *context) {
50 qnd_xml_stack_t *stack = context->stack;
51
52 printf("Stack:\n");
53 while(stack) {
54 if(stack == context->sp) printf(" *");
55 else printf(" ");
56
57 printf("%s\n", stack->entry->name);
58 stack = stack->next;
59 }
60 }
61
62 void stack_push(qnd_xml_context_t *context, qnd_xml_entry_t *entry) {
63 // printf("push %s\n", entry->name);
64
65 context->sp->next = g_new0(qnd_xml_stack_t, 1);
66 context->sp->next->prev = context->sp;
67 context->sp = context->sp->next;
68 context->sp->entry = entry;
69
70 // stack_dump(context);
71 }
72
73 qnd_xml_entry_t *stack_pop(qnd_xml_context_t *context) {
74 qnd_xml_entry_t *cur = context->sp->entry;
75
76 context->sp = context->sp->prev;
77 g_free(context->sp->next);
78 context->sp->next = NULL;
79
80 /* did we just empty the stack? if yes, we're done parsing */
81 if(context->sp == context->stack) {
82 printf("done parsing\n");
83 context->done = TRUE;
84 }
85
86 // printf("popped %s\n", cur->name);
87 // stack_dump(context);
88 return cur;
89 }
90
91 /*
92 utf8:
93 0xxxxxxx
94 110xxxxx 10xxxxxx
95 1110xxxx 10xxxxxx 10xxxxxx
96 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
97
98 Do we really need to handle this? Internally we are only
99 handling ascii characters (e.g. '<', '>', '/', '?' etc.)
100 thus it's only important to be able to skip utf8 characters
101 correctly. Since a subbyte of utf8 never equals a ascii character
102 it should be possible to parse the file correctly when ignoring utf8
103 */
104
105 /* TODO: this needs to be updated to cope with utf8 */
106 inline char current_char(qnd_xml_context_t *context) {
107 return *context->cur;
108 }
109
110 /* TODO: this needs to be updated to cope with utf8 */
111 inline gboolean skip_char(qnd_xml_context_t *context) {
112 context->cur++;
113 /* TODO: check buffer range */
114 return TRUE;
115 }
116
117 gboolean skip_to_char(qnd_xml_context_t *context, char *chrs) {
118 while(context->cur < context->buffer + context->total) {
119 if(strchr(chrs, current_char(context))) {
120 return skip_char(context);
121 }
122 if(!skip_char(context)) return FALSE;
123 }
124
125 /* if we get here the system was unable to fill the buffer */
126 return FALSE;
127 }
128
129 gboolean buffer_overflow(qnd_xml_context_t *context) {
130 return(!(context->cur < context->buffer + context->total));
131 }
132
133 gboolean get_element_name(qnd_xml_context_t *context) {
134
135 char *start = context->cur;
136
137 if(buffer_overflow(context) || !isalpha(current_char(context))) {
138 printf("invalid element name #1 (%c)\n", current_char(context));
139 return FALSE;
140 }
141
142 while(!buffer_overflow(context) && !isblank(current_char(context)) &&
143 (current_char(context) != '>')) {
144 if(!isalnum(current_char(context))) {
145 printf("invalid element name #2 (%c)\n", current_char(context));
146 return FALSE;
147 }
148 if(!skip_char(context)) return FALSE;
149 }
150
151 #if 0
152 char *format = g_strdup_printf("Element name = %%.%ds\n",
153 context->cur-start);
154 printf(format, start);
155 g_free(format);
156 #endif
157
158 /* handle special elements locally */
159 if(context->mod) {
160
161 } else {
162 qnd_xml_entry_t *entry = context->sp->entry, *hit = NULL;
163
164 int i=0;
165 for(i=0;!hit && i<entry->num_children;i++)
166 if(strncmp(entry->children[i]->name, start,
167 strlen(entry->children[i]->name)) == 0)
168 hit = entry->children[i];
169
170 if(hit)
171 stack_push(context, hit);
172 else {
173 printf("element search failed\n");
174 return FALSE;
175 }
176 }
177
178 return TRUE;
179 }
180
181 gboolean get_attribute_name(qnd_xml_context_t *context) {
182
183 char *start = context->cur;
184
185 if(buffer_overflow(context) || !isalpha(current_char(context))) {
186 printf("invalid attribute name\n");
187 return FALSE;
188 }
189
190 while(!buffer_overflow(context) && !isblank(current_char(context)) &&
191 !(current_char(context) == '=')) {
192 if(!isalnum(current_char(context))) {
193 printf("invalid attribute name\n");
194 return FALSE;
195 }
196 if(!skip_char(context)) return FALSE;
197 }
198
199 /* attach a new attribute to chain */
200 qnd_xml_attribute_t **attr = &context->attributes;
201 while(*attr) attr = &(*attr)->next;
202
203 /* terminate name at closing '=' */
204 *context->cur = '\0';
205
206 *attr = g_new0(qnd_xml_attribute_t, 1);
207 (*attr)->name = start;
208
209 return TRUE;
210 }
211
212 gboolean get_attribute_value(qnd_xml_context_t *context) {
213
214 char *start = context->cur;
215
216 while(!buffer_overflow(context) && !(current_char(context) == '\"'))
217 if(!skip_char(context)) return FALSE;
218
219 /* attach a new attribute to chain */
220 qnd_xml_attribute_t **attr = &context->attributes;
221 while((*attr) && (*attr)->next) attr = &(*attr)->next;
222
223 if(!(*attr) || (*attr)->value) {
224 printf("error storing attribute value\n");
225 return FALSE;
226 }
227
228 /* terminate value at closing '\"' */
229 *context->cur = '\0';
230 (*attr)->value = start;
231
232 return TRUE;
233 }
234
235 gboolean skip_white(qnd_xml_context_t *context) {
236 /* skip all white space */
237 while(!buffer_overflow(context) && isblank(current_char(context)))
238 if(!skip_char(context)) return FALSE;
239
240 if(isblank(current_char(context))) {
241 printf("error skipping white space\n");
242 return FALSE;
243 }
244
245 return TRUE;
246 }
247
248 gboolean get_attributes(qnd_xml_context_t *context) {
249 /* drop everything before element from buffer */
250
251 if(!skip_white(context)) return FALSE;
252
253 while(isalpha(current_char(context))) {
254
255 /* get attribute name */
256 if(!get_attribute_name(context)) return FALSE;
257
258 if(!skip_to_char(context, "=")) return FALSE;
259 if(!skip_to_char(context, "\"")) return FALSE;
260
261 if(!get_attribute_value(context)) return FALSE;
262 if(!skip_to_char(context, "\"")) return FALSE;
263
264 if(!skip_white(context)) return FALSE;
265 }
266 return TRUE;
267 }
268
269 void attributes_free(qnd_xml_context_t *context) {
270 qnd_xml_attribute_t *attr = context->attributes;
271
272 while(attr) {
273 qnd_xml_attribute_t *next = attr->next;
274 g_free(attr);
275 attr = next;
276 }
277
278 context->attributes = NULL;
279 }
280
281 void qnd_xml_cleanup(qnd_xml_context_t *context) {
282 /* todo: clean stack */
283
284 if(context->buffer) g_free(context->buffer);
285 g_free(context);
286 }
287
288 gboolean get_element(qnd_xml_context_t *context) {
289
290 /* skip all text */
291 if(!skip_to_char(context, "<")) return FALSE;
292
293 /* handle optional modifier */
294 if(current_char(context) == '?' || current_char(context) == '!') {
295 context->mod = current_char(context);
296 if(!skip_char(context)) return FALSE;
297 } else
298 context->mod = 0;
299
300 /* check for closing element */
301 if(current_char(context) == '/') {
302 context->mod = '/';
303 if(!skip_char(context)) return FALSE;
304 }
305
306 if(!get_element_name(context)) return FALSE;
307 if(!get_attributes(context)) return FALSE;
308
309 if(context->mod && context->mod != '/') {
310 if(current_char(context) != context->mod) {
311 printf("modifier mismatch\n");
312 return FALSE;
313 }
314
315 /* skip the modifier */
316 if(!skip_char(context)) return FALSE;
317 }
318
319 if(!skip_white(context)) return FALSE;
320
321 /* call callback now since the entry may be taken from stack */
322 if(!context->mod && context->sp->entry->cb)
323 if(!context->sp->entry->cb(context->sp,
324 context->attributes, context->userdata))
325 return FALSE;
326
327 if(context->mod == '/')
328 stack_pop(context);
329 else {
330 /* if this element closes here it's cleaned up immediately */
331 if(current_char(context) == '/') {
332 if(!skip_char(context)) return FALSE;
333 stack_pop(context);
334 }
335 }
336
337 if(current_char(context) != '>') {
338 printf("element closing error\n");
339 return FALSE;
340 }
341
342 if(!skip_char(context)) return FALSE;
343
344 attributes_free(context);
345
346 return TRUE;
347 }
348
349 gpointer qnd_xml_parse(char *name, qnd_xml_entry_t *root, gpointer userdata) {
350 FILE *file = NULL;
351 qnd_xml_context_t *context = g_new0(qnd_xml_context_t, 1);
352 context->cur = context->buffer;
353 context->userdata = userdata;
354
355 /* init stack by adding root entry */
356 context->sp = context->stack = g_new0(qnd_xml_stack_t, 1);
357 context->sp->entry = root;
358
359 /* check if file exists and is a regular file */
360 if(!g_file_test(name, G_FILE_TEST_IS_REGULAR)) {
361 printf("file doesn't exist or is not a regular file\n");
362 qnd_xml_cleanup(context);
363 return FALSE;
364 }
365
366 /* open file */
367 file = g_fopen(name, "r");
368 if(!file) {
369 printf("unable to open file\n");
370 qnd_xml_cleanup(context);
371 return FALSE;
372 }
373
374 printf("file is open\n");
375
376 /* get file length */
377 fseek(file, 0l, SEEK_END);
378 context->total = ftell(file);
379 fseek(file, 0l, SEEK_SET);
380
381 printf("file length is %d bytes\n", context->total);
382
383 /* load entire file into buffer */
384 context->buffer = g_malloc(context->total);
385 fread(context->buffer, 1l, context->total, file);
386 fclose(file);
387 context->cur = context->buffer;
388
389 setlocale(LC_NUMERIC, "C");
390
391 gboolean error = FALSE;
392 do
393 error = !get_element(context);
394 while(!error && !context->done);
395
396 setlocale(LC_NUMERIC, "");
397
398 if(error) printf("parser ended with error\n");
399 else printf("parser ended successfully\n");
400
401 printf("current bytes read: %d of %d\n",
402 context->bytes_read, context->total);
403 printf("current buffer offset: %d\n", context->cur - context->buffer);
404
405 /* user pointer[0] of root element is retval */
406 gpointer retval = error?NULL:context->stack->userdata[0];
407
408 /* close file and cleanup */
409 qnd_xml_cleanup(context);
410
411 return retval;
412 }
413
414 char *qnd_xml_get_prop(qnd_xml_attribute_t *attr, char *name) {
415 while(attr) {
416 if(strcasecmp(name, attr->name) == 0)
417 return attr->value;
418
419 attr = attr->next;
420 }
421 return NULL;
422 }
423
424 char *qnd_xml_get_prop_str(qnd_xml_attribute_t *attr, char *name) {
425 char *value = qnd_xml_get_prop(attr, name);
426 if(value) return g_strdup(value);
427 return NULL;
428 }
429
430 /* strtof this is c99 */
431 float strtof(const char *nptr, char **endptr);
432
433 gboolean qnd_xml_get_prop_float(qnd_xml_attribute_t *attr, char *name,
434 float *dest) {
435 char *value = qnd_xml_get_prop(attr, name);
436 if(!value) return FALSE;
437 *dest = strtof(value, NULL);
438 return TRUE;
439 }
440
441 gboolean qnd_xml_get_prop_double(qnd_xml_attribute_t *attr, char *name,
442 double *dest) {
443 char *value = qnd_xml_get_prop(attr, name);
444 if(!value) return FALSE;
445
446 *dest = g_ascii_strtod(value, NULL);
447 return TRUE;
448 }
449
450 gboolean qnd_xml_get_prop_gulong(qnd_xml_attribute_t *attr, char *name,
451 gulong *dest) {
452 char *value = qnd_xml_get_prop(attr, name);
453 if(!value) return FALSE;
454
455 *dest = strtoul(value, NULL, 10);
456 return TRUE;
457 }
458
459 gboolean qnd_xml_get_prop_is(qnd_xml_attribute_t *attr, char *name,
460 char *ref) {
461 char *value = qnd_xml_get_prop(attr, name);
462 if(!value) return FALSE;
463
464 return g_strcasecmp(ref, value);
465 }