ttxml: new module.
[ccan] / ccan / ttxml / ttxml.c
1 \r
2 #include <stdlib.h>\r
3 #include <string.h>\r
4 #include <stdio.h>\r
5 \r
6 #include "ttxml.h"\r
7 \r
8 \r
9 XmlNode* xml_new(char * name, char * attrib)\r
10 {\r
11         XmlNode * ret = malloc(sizeof(XmlNode));\r
12         if(!ret)return NULL;\r
13 \r
14         ret->attrib = NULL;\r
15         ret->nattrib = 0;\r
16         ret->child = ret->next = NULL;\r
17 \r
18         ret->name = name;\r
19         return ret;\r
20 }\r
21 \r
22 \r
23 void xml_free(XmlNode *target)\r
24 {\r
25         int i;\r
26         for(i=0; i<target->nattrib*2; i++)\r
27                 if(target->attrib[i])\r
28                         free(target->attrib[i]);\r
29 \r
30         if(target->attrib)free(target->attrib);\r
31         if(target->child)xml_free(target->child);\r
32         if(target->next)xml_free(target->next);\r
33         free(target->name);\r
34         free(target);\r
35 }\r
36 \r
37 #define XML_LETTER      1\r
38 #define XML_NUMBER      2\r
39 #define XML_SPACE       4\r
40 #define XML_SLASH       8\r
41 #define XML_OPEN        16\r
42 #define XML_EQUALS      32\r
43 #define XML_CLOSE       64\r
44 #define XML_QUOTE       128\r
45 #define XML_OTHER       256\r
46 \r
47 #define XML_ALL 0xFFFFFFFF\r
48 \r
49 static int is_special(char item)\r
50 {\r
51         if((item >= 'a' && item <= 'z') || (item >= 'A' && item <='Z'))\r
52                 return XML_LETTER;\r
53         if( item >= '0' && item <='9' )\r
54                 return XML_NUMBER;\r
55         if( item == 0x20 || item == '\t' ||     item == 0x0D || item == 0x0A )\r
56                 return XML_SPACE;\r
57         if( item == '/' )\r
58                 return XML_SLASH;\r
59         if( item == '<' )\r
60                 return XML_OPEN;\r
61         if( item == '=' )\r
62                 return XML_EQUALS;\r
63         if( item == '>' )\r
64                 return XML_CLOSE;\r
65         if( item == '"' || item == '\'' )\r
66                 return XML_QUOTE;\r
67         return 128;\r
68 }\r
69 \r
70 struct XMLBUF\r
71 {\r
72         FILE * fptr;\r
73         char * buf;\r
74         int len;\r
75         int eof;\r
76 };\r
77 \r
78 \r
79 static void xml_consume(struct XMLBUF *xml, int offset)\r
80 {\r
81         int size, request, received;\r
82         \r
83         size = xml->len - offset;\r
84 \r
85         if(!xml->len)\r
86                 return;\r
87 \r
88         if(size)\r
89         {\r
90 //              printf("Size=%d, off=%d, len=%d\n", size, offset, xml->len);\r
91                 memmove(xml->buf, xml->buf + offset, size);\r
92         }\r
93 \r
94         if(xml->eof)\r
95         {\r
96                 xml->len = size;\r
97                 xml->buf[size]=0;\r
98                 return;\r
99         }\r
100 \r
101         request = xml->len - size;\r
102         received = fread(xml->buf + size, 1, request, xml->fptr);\r
103         if( received == request )\r
104                 return;\r
105 \r
106         xml->len = size + received;\r
107         xml->eof = 1;\r
108         xml->buf[xml->len] = 0;\r
109         return;\r
110 }\r
111 \r
112 \r
113 static void xml_skip( struct XMLBUF *xml, int mask )\r
114 {\r
115         int offset = 0;\r
116         if(!xml->len)return;\r
117         while( is_special(xml->buf[offset]) & mask )\r
118         {\r
119                 offset ++;\r
120                 if(offset == xml->len)\r
121                 {\r
122                         xml_consume(xml, offset);\r
123                         offset = 0;\r
124                         if(!xml->len)\r
125                                 return;\r
126                 }\r
127         }\r
128         xml_consume(xml, offset);\r
129 }\r
130 \r
131 static char quotechar = 0;\r
132 static int test_quote(const char x)\r
133 {\r
134         static int escaped=0;\r
135         if( escaped || '\\' == x )\r
136         {\r
137                 escaped = !escaped;\r
138                 return 1;\r
139         }\r
140         if( x != quotechar )\r
141                 return 1;\r
142         return 0;\r
143 }\r
144 \r
145 static int feed_mask = 0;\r
146 static int test_mask(const char x)\r
147 {\r
148         return !(is_special(x) & feed_mask);\r
149 }\r
150 \r
151 static char* xml_feed( struct XMLBUF *xml, int (*test)(char) )\r
152 {\r
153         int offset = 0;\r
154         char *ret = NULL;\r
155         int size = 0;\r
156 \r
157         while( test(xml->buf[offset]) )\r
158         {\r
159                 offset++;\r
160                 if(offset == xml->len)\r
161                 {\r
162                         ret = realloc(ret, size+offset+1);\r
163                         memcpy(ret+size, xml->buf, offset);\r
164                         size += offset;\r
165                         ret[size]=0;\r
166                         xml_consume(xml, offset);\r
167                         offset = 0;\r
168                         if(!xml->len)return ret;\r
169                 }\r
170         }\r
171 \r
172         if(offset)\r
173         {\r
174                 ret = realloc(ret, size+offset+1);\r
175                 memcpy(ret+size, xml->buf, offset);\r
176                 size += offset;\r
177                 ret[size]=0;\r
178                 xml_consume(xml, offset);\r
179         }\r
180         return ret;\r
181 }\r
182 \r
183 static void xml_read_attr(struct XMLBUF *xml, XmlNode *node)\r
184 {\r
185         int n=0;\r
186 \r
187         // how does this tag finish?\r
188         while(xml->len)\r
189         {\r
190                 if( is_special(xml->buf[0]) & (XML_CLOSE | XML_SLASH) )\r
191                         return;\r
192 \r
193                 n = ++node->nattrib;\r
194                 node->attrib = realloc(node->attrib, n * 2 * sizeof(char*) );\r
195                 node->attrib[--n*2+1] = 0;\r
196                 \r
197                 feed_mask = XML_EQUALS | XML_SPACE | XML_CLOSE | XML_SLASH;\r
198                 node->attrib[n*2] = xml_feed(xml, test_mask );\r
199                 if( xml->buf[0] == '=' )\r
200                 {\r
201                         if( is_special(xml->buf[1]) & XML_QUOTE )\r
202                         {\r
203                                 quotechar = xml->buf[1];\r
204                                 xml_consume(xml, 2);\r
205                                 node->attrib[n*2+1] = xml_feed(xml, test_quote);\r
206                                 xml_consume(xml, 1);\r
207                         }\r
208                         else\r
209                         {\r
210                                 feed_mask = XML_SPACE | XML_CLOSE | XML_SLASH;\r
211                                 xml_consume(xml, 1);\r
212                                 node->attrib[n*2+1] = xml_feed(xml, test_mask);\r
213                         }\r
214                 }\r
215                 xml_skip(xml, XML_SPACE);\r
216         }\r
217 }\r
218 \r
219 static XmlNode* xml_parse(struct XMLBUF *xml)\r
220 {\r
221         int offset;\r
222         int toff;\r
223         char *tmp;\r
224         XmlNode **this, *ret = NULL;\r
225         \r
226         this = &ret;\r
227 \r
228         xml_skip(xml, XML_SPACE);       // skip whitespace\r
229         offset=0;\r
230         while(xml->len)\r
231         {\r
232                 switch(is_special(xml->buf[offset]))\r
233                 {\r
234                         case XML_OPEN:\r
235                                 xml_consume(xml, 1);\r
236                                 if(xml->buf[offset] == '/')\r
237                                         return ret;             // parents close tag\r
238                                 // read the tag name\r
239                                 feed_mask = XML_SPACE | XML_SLASH | XML_CLOSE;\r
240                                 *this = xml_new( xml_feed(xml, test_mask), NULL );\r
241                                 xml_skip(xml, XML_SPACE);       // skip any whitespace\r
242 \r
243                                 xml_read_attr(xml, *this);      // read attributes\r
244 \r
245                                 // how does this tag finish?\r
246                                 switch(is_special(xml->buf[0]))\r
247                                 {\r
248                                         case XML_CLOSE:         // child-nodes ahead\r
249                                                 xml_consume(xml, 1);\r
250                                                 (*this)->child = xml_parse(xml);\r
251                                                 xml_skip(xml, XML_ALL ^ XML_CLOSE);\r
252                                                 xml_consume(xml, 1);\r
253                                                 break;\r
254                                         case XML_SLASH:         // self closing tag\r
255                                                 xml_consume(xml, 2);\r
256                                                 break;\r
257                                 }\r
258                                 break;\r
259 \r
260                         default:        // text node\r
261                                 *this = xml_new(0, 0);\r
262                                 xml_skip(xml, XML_SPACE);       // skip any whitespace\r
263                                 feed_mask = XML_OPEN;\r
264                                 (*this)->nattrib=1;\r
265                                 (*this)->attrib = malloc(sizeof(char*)*2);\r
266                                 tmp = (*this)->attrib[0] = xml_feed(xml, test_mask);\r
267                                 toff = strlen(tmp)-1;\r
268                                 while( ( is_special(tmp[toff]) & XML_SPACE ) )\r
269                                 {\r
270                                         tmp[toff] = 0;\r
271                                         toff --;\r
272                                 }\r
273 \r
274                                 (*this)->attrib[1] = NULL;\r
275                                 break;\r
276                 }\r
277                 this = &(*this)->next; \r
278                 xml_skip(xml, XML_SPACE);       // skip whitespace\r
279         }       \r
280 \r
281         return ret;\r
282 }\r
283 \r
284 \r
285 \r
286 #define BUF 3264\r
287 XmlNode* xml_load(const char * filename)\r
288 {\r
289         struct XMLBUF xml;\r
290         XmlNode *ret = NULL;\r
291 \r
292         xml.eof = 0;\r
293         xml.fptr = fopen(filename, "rb");\r
294         if(!xml.fptr)\r
295         {\r
296                 printf("Opening file failed\n");\r
297                 return NULL;\r
298         }\r
299 \r
300         xml.buf = malloc(BUF);\r
301         if(!xml.buf)\r
302                 goto xml_load_fail_malloc_buf;\r
303         \r
304         xml.len = fread(xml.buf, 1, BUF, xml.fptr);\r
305         if(xml.len < BUF)\r
306                 xml.eof = 1;\r
307 \r
308         ret = xml_parse(&xml);\r
309 \r
310         free(xml.buf);\r
311 xml_load_fail_malloc_buf:\r
312         fclose(xml.fptr);\r
313         return ret;\r
314 }\r
315 #undef BUF\r
316 \r
317 XmlNode * xml_find(XmlNode *xml, const char *name)\r
318 {\r
319         XmlNode * ret;\r
320         if(xml->name)if(!strcmp(xml->name, name))return xml;\r
321         if(xml->child)\r
322         {\r
323                 ret = xml_find(xml->child, name);\r
324                 if(ret)return ret;\r
325         }\r
326         if(xml->next)\r
327         {\r
328                 ret = xml_find(xml->next, name);\r
329                 if(ret)return ret;\r
330         }\r
331         return NULL;\r
332 }\r
333 \r
334 \r
335 char* xml_attr(XmlNode *x, const char *name)\r
336 {\r
337         int i;\r
338         for(i=0; i<x->nattrib; i++)\r
339                 if(x->attrib[i*2])\r
340                         if(!strcmp(x->attrib[i*2], name))\r
341                                 return x->attrib[i*2+1];\r
342         return 0;\r
343 }\r
344 \r
345 \r
346 #ifdef TEST\r
347 void xp(XmlNode *x, int level, int max)\r
348 {\r
349         int i;\r
350         char text[] = "text";\r
351         char *name = text;\r
352         if(level > max)return;\r
353         if(!x)return;\r
354         if(x->name)name = x->name;\r
355         for(i=0; i<level; i++)printf("    ");\r
356         printf("%s:", name);\r
357         if(x->name)\r
358         for(i=0; i<x->nattrib; i++)\r
359                 printf("%s=\"%s\",", x->attrib[i*2], x->attrib[i*2+1]);\r
360         printf("\n");\r
361         if(x->child)xp(x->child, level+1, max);\r
362         if(x->next)xp(x->next, level, max);\r
363 }\r
364 \r
365 \r
366 int main(int argc, char *argv[])\r
367 {\r
368         XmlNode *x;\r
369 \r
370         if(!argv[1])\r
371         {\r
372                 printf("USAGE: %s name\n\t reads name where name is an XML file.\n",\r
373                                 argv[0]);\r
374                 return 1;\r
375         }\r
376 \r
377         printf("Loading file \"%s\"\n", argv[1]);\r
378 \r
379         x = xml_load(argv[1]);\r
380 \r
381         if(!x)\r
382         {\r
383                 printf("Failed to load.\n");\r
384                 return 2;\r
385         }\r
386 \r
387         xp(x, 1, 6);\r
388         xml_free(x);\r
389         printf("Happily free.\n");\r
390         return 0;\r
391 }\r
392 #endif\r
393 \r