/* Licensed under GPL - see LICENSE file for details */\r
+\r
#include <stdlib.h>\r
#include <string.h>\r
#include <stdio.h>\r
#include "ttxml.h"\r
\r
\r
-XmlNode* xml_new(char * name, char * attrib)\r
+#define BUFFER 3264\r
+\r
+\r
+#define XML_LETTER 1\r
+#define XML_NUMBER 2\r
+#define XML_SPACE 4\r
+#define XML_SLASH 8\r
+#define XML_OPEN 16\r
+#define XML_EQUALS 32\r
+#define XML_CLOSE 64\r
+#define XML_QUOTE 128\r
+#define XML_OTHER 256\r
+\r
+#define XML_ALL 0xFFFFFFFF\r
+\r
+\r
+typedef struct XMLBUF\r
+{\r
+ FILE * fptr;\r
+ char * buf;\r
+ int len;\r
+ int read_index;\r
+ int eof;\r
+} XMLBUF;\r
+\r
+\r
+/* Allocate a new XmlNode */\r
+XmlNode* xml_new(char * name)\r
{\r
XmlNode * ret = malloc(sizeof(XmlNode));\r
if(!ret)return NULL;\r
return ret;\r
}\r
\r
-\r
+/* free a previously allocated XmlNode */\r
void xml_free(XmlNode *target)\r
{\r
int i;\r
free(target);\r
}\r
\r
-#define XML_LETTER 1\r
-#define XML_NUMBER 2\r
-#define XML_SPACE 4\r
-#define XML_SLASH 8\r
-#define XML_OPEN 16\r
-#define XML_EQUALS 32\r
-#define XML_CLOSE 64\r
-#define XML_QUOTE 128\r
-#define XML_OTHER 256\r
-\r
-#define XML_ALL 0xFFFFFFFF\r
-\r
-static int is_special(char item)\r
+/* raise flags if we have a character of special meaning\r
+ *\r
+ * This is where I've hidden the switch statements :-p\r
+ *\r
+ */\r
+int is_special(char item)\r
{\r
if((item >= 'a' && item <= 'z') || (item >= 'A' && item <='Z'))\r
return XML_LETTER;\r
return 128;\r
}\r
\r
-struct XMLBUF\r
+/* Refresh the buffer, expects not to be called when EOF */\r
+static void xml_read_file(XMLBUF *xml)\r
{\r
- FILE * fptr;\r
- char * buf;\r
- int len;\r
- int eof;\r
-};\r
-\r
-\r
-static void xml_consume(struct XMLBUF *xml, int offset)\r
-{\r
- int size, request, received;\r
+ int size;\r
\r
- size = xml->len - offset;\r
-\r
- if(!xml->len)\r
- return;\r
-\r
- if(size)\r
- {\r
-// printf("Size=%d, off=%d, len=%d\n", size, offset, xml->len);\r
- memmove(xml->buf, xml->buf + offset, size);\r
- }\r
-\r
- if(xml->eof)\r
+ size = fread( xml->buf, 1, xml->len, xml->fptr);\r
+ if( size != xml->len )\r
{\r
xml->len = size;\r
xml->buf[size]=0;\r
- return;\r
+ xml->eof = 1;\r
}\r
+}\r
\r
- request = xml->len - size;\r
- received = fread(xml->buf + size, 1, request, xml->fptr);\r
- if( received == request )\r
- return;\r
\r
- xml->len = size + received;\r
- xml->eof = 1;\r
- xml->buf[xml->len] = 0;\r
- return;\r
+/* All reading of the XML buffer done through these two functions */\r
+/*** read a byte without advancing the offset */\r
+static char xml_peek(XMLBUF *xml)\r
+{\r
+ return xml->buf[xml->read_index];\r
}\r
\r
-\r
-static void xml_skip( struct XMLBUF *xml, int mask )\r
+/*** read a byte and advance the offset */\r
+static char xml_read_byte(XMLBUF *xml)\r
{\r
- int offset = 0;\r
- if(!xml->len)return;\r
- while( is_special(xml->buf[offset]) & mask )\r
+ char ret = xml_peek(xml);\r
+ xml->read_index++;\r
+ if(xml->read_index >= xml->len)\r
{\r
- offset ++;\r
- if(offset == xml->len)\r
- {\r
- xml_consume(xml, offset);\r
- offset = 0;\r
- if(!xml->len)\r
- return;\r
- }\r
+ if(xml->eof)return ret;\r
+ xml->read_index = 0 ;\r
+ xml_read_file(xml);\r
}\r
- xml_consume(xml, offset);\r
+ return ret;\r
}\r
\r
+\r
+/* skip over bytes matching the is_special mask */\r
+static void xml_skip( XMLBUF *xml, int mask)\r
+{\r
+ printf("just called\n");\r
+ while( is_special(xml_peek(xml)) & mask && xml->len )\r
+ xml_read_byte(xml);\r
+}\r
+\r
+\r
+/* character matching tests for the feed functions */\r
static char quotechar = 0;\r
static int test_quote(const char x)\r
{\r
return !(is_special(x) & feed_mask);\r
}\r
\r
-static char* xml_feed( struct XMLBUF *xml, int (*test)(char) )\r
+/*\r
+ * char* xml_feed(x, test)\r
+ *\r
+ * Reads as many contiguous chars that pass test() into a newly allocated\r
+ * string.\r
+ *\r
+ * Instead of calling xml_read_byte and flogging realloc() for each byte,\r
+ * it checks the buffer itself.\r
+*/\r
+static char* xml_feed( XMLBUF *xml, int (*test)(char) )\r
{\r
- int offset = 0;\r
+ int offset = xml->read_index;\r
+ int delta;\r
char *ret = NULL;\r
int size = 0;\r
\r
+ /* perform first and N middle realloc()'s */\r
while( test(xml->buf[offset]) )\r
{\r
- offset++;\r
- if(offset == xml->len)\r
+ offset ++;\r
+\r
+ if(offset >= xml->len)\r
{\r
- ret = realloc(ret, size+offset+1);\r
- memcpy(ret+size, xml->buf, offset);\r
- size += offset;\r
+ delta = offset - xml->read_index;\r
+ ret = realloc(ret, size + delta + 1);\r
+ memcpy(ret+size, xml->buf + xml->read_index, delta);\r
+ size += delta;\r
ret[size]=0;\r
- xml_consume(xml, offset);\r
+ if(xml->eof)return ret;\r
+ xml_read_file(xml);\r
+ xml->read_index = 0;\r
offset = 0;\r
- if(!xml->len)return ret;\r
}\r
}\r
-\r
- if(offset)\r
+ /* perform final realloc() if needed */\r
+ if(offset > xml->read_index)\r
{\r
- ret = realloc(ret, size+offset+1);\r
- memcpy(ret+size, xml->buf, offset);\r
- size += offset;\r
+ delta = offset - xml->read_index;\r
+ ret = realloc(ret, size + delta + 1);\r
+ memcpy(ret+size, xml->buf + xml->read_index, delta);\r
+ xml->read_index = offset;\r
+ size += delta;\r
ret[size]=0;\r
- xml_consume(xml, offset);\r
}\r
return ret;\r
}\r
\r
+/* this reads attributes from tags, of the form...\r
+ *\r
+ * <tag attr1="some arguments" attr2=argument>\r
+ *\r
+ * It is aware of quotes, and will allow anything inside quoted arguments\r
+ */\r
static void xml_read_attr(struct XMLBUF *xml, XmlNode *node)\r
{\r
int n=0;\r
// how does this tag finish?\r
while(xml->len)\r
{\r
- if( is_special(xml->buf[0]) & (XML_CLOSE | XML_SLASH) )\r
+ if( is_special(xml_peek(xml)) & (XML_CLOSE | XML_SLASH) )\r
return;\r
\r
n = ++node->nattrib;\r
\r
feed_mask = XML_EQUALS | XML_SPACE | XML_CLOSE | XML_SLASH;\r
node->attrib[n*2] = xml_feed(xml, test_mask );\r
- if( xml->buf[0] == '=' )\r
+ if( xml_peek(xml) == '=' )\r
{\r
- if( is_special(xml->buf[1]) & XML_QUOTE )\r
+ xml_read_byte(xml);\r
+ if( is_special(xml_peek(xml)) & XML_QUOTE )\r
{\r
- quotechar = xml->buf[1];\r
- xml_consume(xml, 2);\r
+ quotechar = xml_read_byte(xml);\r
node->attrib[n*2+1] = xml_feed(xml, test_quote);\r
- xml_consume(xml, 1);\r
+ xml_read_byte(xml);\r
}\r
else\r
{\r
feed_mask = XML_SPACE | XML_CLOSE | XML_SLASH;\r
- xml_consume(xml, 1);\r
node->attrib[n*2+1] = xml_feed(xml, test_mask);\r
}\r
}\r
}\r
}\r
\r
+/* The big decision maker, is it a regular node, or a text node.\r
+ * If it's a node, it will check if it should have children, and if so\r
+ * will recurse over them.\r
+ * Text nodes don't have children, so no recursing.\r
+ */\r
static XmlNode* xml_parse(struct XMLBUF *xml)\r
{\r
int offset;\r
\r
xml_skip(xml, XML_SPACE); // skip whitespace\r
offset=0;\r
- while(xml->len)\r
+ while( (xml->read_index < xml->len) || !xml->eof )\r
{\r
- switch(is_special(xml->buf[offset]))\r
+ switch(is_special(xml_peek(xml)))\r
{\r
case XML_OPEN:\r
- xml_consume(xml, 1);\r
- if(xml->buf[offset] == '/')\r
+ xml_read_byte(xml);\r
+ if(xml_peek(xml) == '/')\r
return ret; // parents close tag\r
// read the tag name\r
feed_mask = XML_SPACE | XML_SLASH | XML_CLOSE;\r
- *this = xml_new( xml_feed(xml, test_mask), NULL );\r
+ *this = xml_new( xml_feed(xml, test_mask));\r
xml_skip(xml, XML_SPACE); // skip any whitespace\r
\r
xml_read_attr(xml, *this); // read attributes\r
\r
// how does this tag finish?\r
- switch(is_special(xml->buf[0]))\r
+ switch(is_special(xml_peek(xml)))\r
{\r
case XML_CLOSE: // child-nodes ahead\r
- xml_consume(xml, 1);\r
+ xml_read_byte(xml);\r
(*this)->child = xml_parse(xml);\r
xml_skip(xml, XML_ALL ^ XML_CLOSE);\r
- xml_consume(xml, 1);\r
+ xml_read_byte(xml);\r
break;\r
case XML_SLASH: // self closing tag\r
- xml_consume(xml, 2);\r
+ xml_read_byte(xml);\r
+ xml_read_byte(xml);\r
break;\r
}\r
break;\r
\r
default: // text node\r
- *this = xml_new(0, 0);\r
+ *this = xml_new(0);\r
xml_skip(xml, XML_SPACE); // skip any whitespace\r
feed_mask = XML_OPEN;\r
(*this)->nattrib=1;\r
(*this)->attrib = malloc(sizeof(char*)*2);\r
+ (*this)->attrib[1] = NULL;\r
tmp = (*this)->attrib[0] = xml_feed(xml, test_mask);\r
+\r
+ /* trim the whitespace off the end of text nodes,\r
+ * by overwriting the spaces will null termination. */\r
toff = strlen(tmp)-1;\r
while( ( is_special(tmp[toff]) & XML_SPACE ) )\r
{\r
toff --;\r
}\r
\r
- (*this)->attrib[1] = NULL;\r
break;\r
}\r
this = &(*this)->next; \r
}\r
\r
\r
-\r
-#define BUF 3264\r
+/* bootstrap the structures for xml_parse() to be able to get started */\r
XmlNode* xml_load(const char * filename)\r
{\r
struct XMLBUF xml;\r
XmlNode *ret = NULL;\r
\r
+// printf("xml_load(\"%s\");\n", filename);\r
+\r
xml.eof = 0;\r
+ xml.read_index = 0;\r
xml.fptr = fopen(filename, "rb");\r
if(!xml.fptr)\r
{\r
return NULL;\r
}\r
\r
- xml.buf = malloc(BUF);\r
+ xml.buf = malloc(BUFFER+1);\r
+ xml.buf[BUFFER]=0;\r
if(!xml.buf)\r
goto xml_load_fail_malloc_buf;\r
\r
- xml.len = fread(xml.buf, 1, BUF, xml.fptr);\r
- if(xml.len < BUF)\r
+ xml.len = fread(xml.buf, 1, BUFFER, xml.fptr);\r
+ if(xml.len < BUFFER)\r
xml.eof = 1;\r
\r
ret = xml_parse(&xml);\r
fclose(xml.fptr);\r
return ret;\r
}\r
-#undef BUF\r
\r
+/* very basic function that will get you the first node with a given name */\r
XmlNode * xml_find(XmlNode *xml, const char *name)\r
{\r
XmlNode * ret;\r
return NULL;\r
}\r
\r
-\r
+/* very basic attribute lookup function */\r
char* xml_attr(XmlNode *x, const char *name)\r
{\r
int i;\r
\r
\r
#ifdef TEST\r
+/* print out the heirarchy of an XML file, useful for debugging */\r
void xp(XmlNode *x, int level, int max)\r
{\r
int i;\r
if(x->name)\r
for(i=0; i<x->nattrib; i++)\r
printf("%s=\"%s\",", x->attrib[i*2], x->attrib[i*2+1]);\r
+ else printf("%s", x->attrib[0]);\r
printf("\n");\r
if(x->child)xp(x->child, level+1, max);\r
if(x->next)xp(x->next, level, max);\r
\r
int main(int argc, char *argv[])\r
{\r
- XmlNode *x;\r
+ XmlNode *x, *tmp;\r
+ int i;\r
\r
if(!argv[1])\r
{\r
return 1;\r
}\r
\r
- printf("Loading file \"%s\"\n", argv[1]);\r
-\r
- x = xml_load(argv[1]);\r
-\r
- if(!x)\r
+#ifdef PROFILE\r
+ for(i=0; i<1000; i++)\r
{\r
- printf("Failed to load.\n");\r
- return 2;\r
+#endif\r
+ x = xml_load(argv[1]);\r
+\r
+ if(!x)\r
+ {\r
+ printf("Failed to load.\n");\r
+ return 2;\r
+ }\r
+#ifndef PROFILE\r
+ xp(x, 1, 20);\r
+#endif\r
+ xml_free(x);\r
+#ifdef PROFILE\r
}\r
+#endif\r
\r
- xp(x, 1, 6);\r
- xml_free(x);\r
- printf("Happily free.\n");\r
+ \r
+// tmp = xml_find(x, "geometry");\r
+// xp(x, 1, 6);\r
+// printf("Happily free.\n");\r
return 0;\r
}\r
#endif\r