Initial commit of libpnd 0.0.5 so we cna restart with GIT
authorskeezix <skeezix@flotsam-vm.(none)>
Wed, 18 Feb 2009 13:50:22 +0000 (08:50 -0500)
committerskeezix <skeezix@flotsam-vm.(none)>
Wed, 18 Feb 2009 13:50:22 +0000 (08:50 -0500)
35 files changed:
CHANGES [new file with mode: 0644]
Makefile [new file with mode: 0644]
include/pnd_apps.h [new file with mode: 0644]
include/pnd_conf.h [new file with mode: 0644]
include/pnd_container.h [new file with mode: 0644]
include/pnd_device.h [new file with mode: 0644]
include/pnd_discovery.h [new file with mode: 0644]
include/pnd_locate.h [new file with mode: 0644]
include/pnd_notify.h [new file with mode: 0644]
include/pnd_pxml.h [new file with mode: 0644]
include/pnd_version.h [new file with mode: 0644]
lib/pnd_conf.c [new file with mode: 0644]
lib/pnd_container.c [new file with mode: 0644]
lib/pnd_discovery.c [new file with mode: 0644]
lib/pnd_locate.c [new file with mode: 0644]
lib/pnd_notify.c [new file with mode: 0644]
lib/pnd_pathiter.h [new file with mode: 0644]
lib/pnd_pxml.c [new file with mode: 0644]
lib/pnd_tinyxml.cpp [new file with mode: 0644]
lib/tinyxml/tinystr.cpp [new file with mode: 0644]
lib/tinyxml/tinystr.h [new file with mode: 0644]
lib/tinyxml/tinyxml.cpp [new file with mode: 0644]
lib/tinyxml/tinyxml.h [new file with mode: 0644]
lib/tinyxml/tinyxmlerror.cpp [new file with mode: 0644]
lib/tinyxml/tinyxmlparser.cpp [new file with mode: 0644]
libpnd.txt [new file with mode: 0644]
test/conftest.c [new file with mode: 0644]
test/discotest.c [new file with mode: 0644]
test/locatetest.c [new file with mode: 0644]
test/notifytest.c [new file with mode: 0644]
testdata/apps-override/123shaboo.xml [new file with mode: 0644]
testdata/apps/sampleapp/PXML.xml [new file with mode: 0644]
testdata/apps/sampleapp/program.exe [new file with mode: 0644]
testdata/conf/apps [new file with mode: 0644]
testdata/scripts/pnd_run.sh [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..d6f49f7
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,14 @@
+
+0.0.3 REM: Slight fixup (if you can call it that) to replace copy/paste code with SEARCHPATH_PRE/POST pathiter.h
+      REM: Added .so to Makefile
+      REM: Added extern "C" wrappers for headers; just maybe this will help someone :P
+      REM: Added 'locatetest' shitty test tool to verify pnd_locate code pretends to work; needs the
+           shared lib version so you need to set your LD_LIBRARY_PATH, for what its worth
+
+0.0.2 REM: Working on INOTIFY in what little time I have before Christmas :)
+      REM: I'm sick as hell.
+      NEW: Added notifytest, pnd_notify.[ch]
+          Seems to work 'okay' in a brute force fashion; only watching a couple events right now and
+          didn't test yet against mount/unmount and such, but a start.
+
+0.0.1 Initial version
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..6113c61
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,46 @@
+
+#
+# libpnd Makefile
+#
+
+# tools
+CC = gcc
+AR = ar
+RANLIB = ranlib
+RM = rm
+
+# environment
+VPATH = lib test
+CFLAG_SO = -fPIC #-fPIC not always needed, but good to have
+CFLAGS = -Wall -I./include -g ${CFLAG_SO}
+
+# code
+LIB = libpnd.a 
+SOLIB = libpnd.so.1         # canonicle name
+SOLIB1 = libpnd.so.1.0.1    # versioned name
+XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o
+ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o
+
+all: ${SOLIB} ${LIB} conftest discotest notifytest locatetest
+
+clean:
+       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest bin/notifytest notifytest.o
+
+libpnd.a:      ${ALLOBJ} ${XMLOBJ}
+       ${AR} r ${LIB} ${ALLOBJ} ${XMLOBJ}
+       ${RANLIB} ${LIB}
+
+libpnd.so.1:   ${ALLOBJ} ${XMLOBJ}
+       ${CC} -shared -Wl,-soname,${SOLIB} -o ${SOLIB1} ${ALLOBJ} ${XMLOBJ}
+
+conftest:      conftest.o ${LIB}
+       ${CC} -lstdc++ -o bin/conftest conftest.o libpnd.a
+
+discotest:     discotest.o ${LIB}
+       ${CC} -lstdc++ -o bin/discotest discotest.o libpnd.a
+
+notifytest:    notifytest.o ${LIB}
+       ${CC} -lstdc++ -o bin/notifytest notifytest.o libpnd.a
+
+locatetest:    locatetest.o ${SOLIB1}
+       ${CC} -lstdc++ -o bin/locatetest locatetest.o ${SOLIB1}
diff --git a/include/pnd_apps.h b/include/pnd_apps.h
new file mode 100644 (file)
index 0000000..086796b
--- /dev/null
@@ -0,0 +1,36 @@
+
+#ifndef h_pnd_apps_h
+#define h_pnd_apps_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PND_APPS_SEARCHPATH "/mnt/sd1/pandora/apps:/mnt/sd2/pandora/apps:./testdata/apps"
+#define PND_APPS_KEY "autodiscovery.searchpath"
+
+#define PND_PNDRUN_SEARCHPATH "pnd.searchpath"
+#define PND_PNDRUN_KEY "pnd.default"
+#define PND_PNDRUN_FILENAME "pnd_run.sh"
+#define PND_PNDRUN_DEFAULT "./testdata/scripts/pnd_run.sh"
+
+#define PND_PXML_OVERRIDE_SEARCHPATH "~/pxml-overrides"
+#define PND_PXML_OVERRIDE_KEY "overrides.searchpath"
+
+#define PND_MOUNT_PATH "/mnt/apps/" /* all mounted PND images should be here.. /mnt/apps/myapp/... */
+
+/* pnd_apps_exec() is used to blindly launch an app, be it a .pnd file bundle or a plain executable
+ * (shell, bin, whatever.) pndrun specifies the full path to the pnd_run sh script, which should be
+ * found using searchpaths and locates.. see locatetest.c for a sample
+ *   if fullpath ends in PXML_FILENAME (PXML.xml) then pnd_run will be invoked (after extracting goodies
+ *     from PXML file as appropriate)
+ *   if fullpath ends in .pnd then pnd_run will be invoked (after inspecting embedded PXML or best-guess)
+ *   otherwise the fullpath will be executed as-is
+ */
+signed char pnd_apps_exec ( char *fullpath, char *pndrun );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_conf.h b/include/pnd_conf.h
new file mode 100644 (file)
index 0000000..a1d2193
--- /dev/null
@@ -0,0 +1,126 @@
+
+/*
+ * libpnd - tools for the Open Pandora device
+ * This code to be released under the terms of the GPLv2
+ */
+
+#ifndef h_pnd_conf_h
+#define h_pnd_conf_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Config file format
+ * Since we're sticking configs in homedirs and /etc, it seems inappropriate to use XML; further
+ * of course, XML means a heavy duty parser (expat or something at least) when people really just
+ * use grep on these guys. Still, XML would afford us unicode which would be nice..
+ * Anyway, the very basic format of the config file supported herein is derived from .ini really
+ * (sorry!)
+ *
+ * [section]
+ * key value #comment
+ *
+ * Comments and leading/trailing space is removed; embedded space is left.
+ *
+ * Thats it; internally, 'section' is prepended to all keys, so it looks something like this.
+ * Section can be [] to mean 'back to the top level.' I had
+ * considered having no decorations around [section] (which is to say, a key with no value) but
+ * I could see cases where folks want to have a valueless key, without something goofy like value
+ * 'present' 'on' '0', when they just want a key 'use-foo' and don't want 'use foo' value 'no'
+ * that feals yes/no-ey.
+ *
+ * NOTE: If a key is present in a config twice.. well, too bad. No warning is made.
+ *
+ * autodiscovery:
+ * path /mnt/sd1/my/path
+ *
+ * Internally becomes:
+ *
+ * autodiscovery.path -> /mnt/sd1/my/path
+ *
+ */
+
+/* certain paths must be 'givens'; ideally as few assumptions as possible, but to
+ * find further config files we need to look _somewhere_; note that searchpaths
+ * are searched in order, so the most-canonicle should be listed last.
+ *
+ * NOTE: This search path is used to find the basic config files, which in turn
+ * can specify a replacement config path .. it may be required to first locate the
+ * 'conf' config, and then use its suggested searchpath!
+ *
+ */
+#define PND_CONF_SEARCHPATH "/mnt/sd1/pandora/conf:/mnt/sd2/pandora/conf:/etc/pandora:./testdata/conf"
+
+/* within the base conf file 'conf', the key for the searchpath is 'conf.searchpath' */
+#define PND_CONF_FILE       "conf" /* a config file for config settings! */
+#define PND_CONF_KEY        "conf.searchpath" /* if not found, use PND_CONF_SEARCHPATH */
+
+/* we would like to open config files based on enums, so as to minimize specifying
+ * filenames; ie: It makes it easier to rename them later or display them in a native
+ * language, etc. It is optional as our API will allow direct open by filename as
+ * well.
+ */
+typedef enum {
+  pnd_conf_nil = 0,
+  pnd_conf_conf,           // provides settings for the config system
+  pnd_conf_apps,           // provides application search-path, pxml override location, etc.
+  pnd_conf_startup,        // provides list of startup applications, basic shell application, etc.
+} pnd_conf_filename_e;
+
+typedef struct {
+  pnd_conf_filename_e id;
+  char *filename;
+} pnd_conf_filename_t;
+
+extern pnd_conf_filename_t pnd_conf_filenames[];
+
+/* config file blackbox type
+ */
+typedef void* pnd_conf_handle;
+
+/*
+ * config FILE reading public API
+ */
+
+/* fetch_searchpath() - since near every app may wish to use this piece of code,
+ * it is encapsulated here.
+ * Returns a search-path to be used hereafter; free it when done with it!
+ */
+char *pnd_conf_query_searchpath ( void );
+
+/* fetch_by_id() will try to locate the config file as referred to by the enum'd 'id'. If it
+ * can be found and parsed a handle will be returned, otherwise a handle of 0 (NULL).
+ * Returns a 0 handle on fail, otherwise a useful handle.
+ */
+pnd_conf_handle pnd_conf_fetch_by_id ( pnd_conf_filename_e id, char *searchpath );
+
+/* if you don't wish to use an 'id', you can fetch by filename. Essentially the fetch_by_id()
+ * function simply uses this to do the work, but you could use it to load up alternate config-format
+ * files.
+ * Returns a 0 handle on fail, otherwise a useful handle.
+ */
+pnd_conf_handle pnd_conf_fetch_by_name ( char *filename, char *searchpath );
+
+/* fetch_by_path() will operate on a specific full filename; this essentially does the
+ * dirty work for the above functions, but can be used to pull in a specific path for
+ * whatever purpose.
+ * Returns a 0 handle on fail, otherwise a useful handle.
+ */
+pnd_conf_handle pnd_conf_fetch_by_path ( char *fullpath );
+
+/*
+ * config file accessor functions public API
+ */
+
+/* get_as_char() will attempt to locate the specified key string (of format section,key) in the
+ * provided config handle. Do not free up this value, it is considered read only.
+ * Returns NULL on error, otherwise a READ ONLY char* reference to the value.
+ */
+char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_container.h b/include/pnd_container.h
new file mode 100644 (file)
index 0000000..acb850e
--- /dev/null
@@ -0,0 +1,59 @@
+
+#ifndef h_pnd_container_h
+#define h_pnd_container_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// this interface defines a simple container for storing unsorted data; it is likely to just be
+// the worst linked list you've ever seen, but could be changed later and applications should only
+// need a recompile
+
+// this simple 'Box' (shorter to type) container is not intended to be super efficient or highly usable
+// and as such does not include delete-node functionality or othe useful APIs, nor internal data to
+// support such operatins yet. Should performance become important, a hash or somesuch might be
+// needing implementing.
+
+// The user may have a payload of a simple char*string, or a struct, or whatever they choose to stuff
+// in there.
+
+typedef void* pnd_box_handle;
+
+/* return a new container with no contained nodes.
+ * Returns NULL on failure.
+ */
+pnd_box_handle pnd_box_new ( char *name );
+
+/* delete the container and all of its contents. Note that of course you have to free up any memory
+ * referred to in advance of this operation, lest there be leaks!
+ * Assume success always (ie: the container is either destroyed or indeterminate after.)
+ */
+void pnd_box_delete ( pnd_box_handle box );
+
+/* allocinsert() is used to allocate a new entry in the container of the specified size; ie:
+ * the 'key' is a regular char* string; the 'size' refers to the requested size of the payload
+ * you need, not the size of the key.
+ * Returns a new payload pointer of the requested size, or NULL. Do not free this payload pointer
+ * ever by hand.
+ */
+void *pnd_box_allocinsert ( pnd_box_handle box, char *key, unsigned int size );
+
+/* find_by_key() will attempt to locate a payload based on the specified key.
+ * Returns the payload pointer or NULL if not found.
+ */
+void *pnd_box_find_by_key ( pnd_box_handle box, char *key );
+
+/* should the user want to walk around the container, a couple basic functions are
+ * provided.
+ */
+char *pnd_box_get_name ( pnd_box_handle box );
+void *pnd_box_get_head ( pnd_box_handle box );
+char *pnd_box_get_key ( void *node );
+void *pnd_box_get_next ( void *node );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_device.h b/include/pnd_device.h
new file mode 100644 (file)
index 0000000..6b05577
--- /dev/null
@@ -0,0 +1,22 @@
+
+#ifndef h_pnd_device_h
+#define h_pnd_device_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// do we have a 'minimal' lib yet anywhere formalized? if not, we could
+// attempt to include it here.
+
+// set clock speed
+
+// set LED on
+
+// suspend/hibernate/etc
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_discovery.h b/include/pnd_discovery.h
new file mode 100644 (file)
index 0000000..f14ba29
--- /dev/null
@@ -0,0 +1,87 @@
+
+#ifndef h_pnd_discovery_h
+#define h_pnd_discovery_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// this code is for doing the application discovery against a given directory and its children (ie: this
+// code could be called on appliction startup, or as a result of media eject/insert, and so forth.)
+
+/* disco_search() will walk the given search path (one or more paths, colon separated) in order left to
+ * right; as PXML's are found, basic analysis is performed to verfy validity. A list of valid applications
+ * is returned, the union of all matches in the search path
+ * If no matches are found, NULL is returned (to save you deleting the container)
+ * overridespath may be NULL if you do not wish to search for pxml overrides
+ */
+pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath );
+
+/* pnd_disco_t describes a given entry found by the discovery code; ie: the containers key is the path to
+ * the PXML file (since this is relatively unique), with the fields below detailing the executable path,
+ * application name, unique-id and so on
+ *
+ * NOTE: this struct is dynamicly populated; you are responsible for invoking the destroy() function to
+ * kill its contents, and the pnt_box_destroy() to kill the container at the end
+ *
+ * NOTE: The PXML path (or .pnd file path in bundle files) is used as a key since it is unique. The
+ * application 'unique id' will be unique for a given app, but that app could be in multiple directories
+ * or on multiple SD cards or wifi or whatever, so only the mounted path is truly unique. The key
+ * is only used internally so the consumer can refer to multiple versions of the same app without
+ * confusion.. it is not displayed. So no big deal.
+ */
+
+typedef struct\r
+{
+       char *title_en;
+       char *title_de;
+       char *title_it;
+       char *title_fr;
+       char *unique_id;
+       char *standalone;
+       char *icon;
+       char *description_en;
+       char *description_de;
+       char *description_it;
+       char *description_fr;
+       char *previewpic1;
+       char *previewpic2;
+       char *author_name;
+       char *author_website;
+       char *version_major;
+       char *version_minor;
+       char *version_release;
+       char *version_build;
+       char *exec;
+       char *main_category;
+       char *subcategory1;
+       char *subcategory2;
+       char *altcategory;
+       char *altsubcategory1;
+       char *altsubcategory2;
+       char *osversion_major;
+       char *osversion_minor;
+       char *osversion_release;
+       char *osversion_build;
+       char *associationitem1_name;
+       char *associationitem1_filetype;
+       char *associationitem1_parameter;
+       char *associationitem2_name;
+       char *associationitem2_filetype;
+       char *associationitem2_parameter;
+       char *associationitem3_name;
+       char *associationitem3_filetype;
+       char *associationitem3_parameter;
+       char *clockspeed;
+       char *background;
+       char *startdir;
+\r
+} pnd_disco_t;
+
+void pnd_disco_destroy ( pnd_disco_t *p ); // a function name that simply could not be avoided
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_locate.h b/include/pnd_locate.h
new file mode 100644 (file)
index 0000000..884fd2e
--- /dev/null
@@ -0,0 +1,17 @@
+
+#ifndef h_pnd_locate_h
+#define h_pnd_locate_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// given a filename and a searchpath, return the first path it is found at (or NULL if not found)
+// Returned path includes filename.
+char *pnd_locate_filename ( char *searchpath, char *filename );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_notify.h b/include/pnd_notify.h
new file mode 100644 (file)
index 0000000..e9b6e7b
--- /dev/null
@@ -0,0 +1,32 @@
+
+#ifndef h_pnd_notify_h
+#define h_pnd_notify_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* pnd_notify_handle;
+
+/* must invoke this to obtain a handle; other notify functions require it of course.
+ * Returns NULL on failure.
+ */
+pnd_notify_handle pnd_notify_init ( void );
+
+/* shutdown..
+ */
+void pnd_notify_shutdown ( pnd_notify_handle h );
+
+/* rescan_p (rescan predicate) -- wil return TRUE (>0) when the notify is reporting
+ * that we should do a re-discovery against the paths.
+ * NOTE:
+ * (Yes this is a little brute-forcey .. it should just collect notify events
+ * and suss-out the changes, and add/sub those from a working list of applications.)
+ */
+unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_pxml.h b/include/pnd_pxml.h
new file mode 100644 (file)
index 0000000..366326c
--- /dev/null
@@ -0,0 +1,103 @@
+
+#ifndef h_pnd_pxml_h
+#define h_pnd_pxml_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// this code is for very basic PXML.xml file parsing
+
+#define PXML_FILENAME "PXML.xml" /* a specification defined name */
+
+// use this handle to interact with PXML; this hides the mechanics of parsing a PXML file so that
+// it can be upgraded with impacting applications
+typedef void* pnd_pxml_handle;
+
+/* pxml_fetch() will return NULL on fail, otherwise a valid handle which may be further queried
+ */
+pnd_pxml_handle pnd_pxml_fetch ( char *fullpath );
+void pnd_pxml_delete ( pnd_pxml_handle h );
+
+/* overrides() allow for customization of a PXML that persists; ie: An application might be sitting
+ * on an SD card and popped out while we need to edit its personalized category; more to point, the
+ * PXML itself could be in a read-only SD or packed into an ISO, or just sitting in a directory.
+ * Rather than have a _second_ PXML in the same place or have to write back to read-only media or
+ * worry about losing customizations when an app is temporarily deleted, we can just keep the
+ * overrides themselves in NAND.
+ */
+/* merge_override() will attempt to locate an override of the given PXML, and will modify the
+ * PXML in-place to include any overrides found.
+ * Returns >0 if a merge was done, 0 if no merge was done, and <0 on error
+ * NOTE: For searchpath, should query configs for PND_PXML_OVERRIDES_KEY (or use PND_PXML_OVERRIDES_SEARCHPATH)
+ */
+signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath );
+
+/* these accessor functions will return READ ONLY char*s; do not free them or modify them.
+ */
+char *pnd_pxml_get_app_name ( pnd_pxml_handle h );
+char *pnd_pxml_get_icon_path ( pnd_pxml_handle h );
+char *pnd_pxml_get_unique_id ( pnd_pxml_handle h );
+char *pnd_pxml_get_primary_category ( pnd_pxml_handle h );
+char *pnd_pxml_get_exec_path ( pnd_pxml_handle h );
+char *pnd_pxml_get_clockspeed ( pnd_pxml_handle h );
+
+// for 'set' functions, pass NULL value to delete existing value without setting new one
+void pnd_pxml_set_app_name ( pnd_pxml_handle h, char *v );
+
+/* utilities
+ */
+unsigned char pnd_is_pxml_valid_app ( pnd_pxml_handle h ); // returns 1 when pxml seems like a valid application
+
+typedef struct\r
+{
+       char *title_en;
+       char *title_de;
+       char *title_it;
+       char *title_fr;
+       char *unique_id;
+       char *standalone;
+       char *icon;
+       char *description_en;
+       char *description_de;
+       char *description_it;
+       char *description_fr;
+       char *previewpic1;
+       char *previewpic2;
+       char *author_name;
+       char *author_website;
+       char *version_major;
+       char *version_minor;
+       char *version_release;
+       char *version_build;
+       char *exec;
+       char *main_category;
+       char *subcategory1;
+       char *subcategory2;
+       char *altcategory;
+       char *altsubcategory1;
+       char *altsubcategory2;
+       char *osversion_major;
+       char *osversion_minor;
+       char *osversion_release;
+       char *osversion_build;
+       char *associationitem1_name;
+       char *associationitem1_filetype;
+       char *associationitem1_parameter;
+       char *associationitem2_name;
+       char *associationitem2_filetype;
+       char *associationitem2_parameter;
+       char *associationitem3_name;
+       char *associationitem3_filetype;
+       char *associationitem3_parameter;
+       char *clockspeed;
+       char *background;
+       char *startdir;
+\r
+}  pnd_pxml_t;
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/include/pnd_version.h b/include/pnd_version.h
new file mode 100644 (file)
index 0000000..6b521bc
--- /dev/null
@@ -0,0 +1,15 @@
+
+#ifndef h_pnd_version_h
+#define h_pnd_version_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PND_VERSION "0.0.2"
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
diff --git a/lib/pnd_conf.c b/lib/pnd_conf.c
new file mode 100644 (file)
index 0000000..129d278
--- /dev/null
@@ -0,0 +1,239 @@
+
+#include <stdio.h> /* for NULL, printf, FILE, etc */
+#include <stdlib.h> /* for malloc */
+#include <string.h> /* for strdup */
+#include <ctype.h> /* for isspace */
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_pathiter.h"
+
+pnd_conf_filename_t pnd_conf_filenames[] = {
+  { pnd_conf_conf,         PND_CONF_FILE },
+  { pnd_conf_apps,         "apps" },
+  { pnd_conf_startup,      "startup" },
+  { pnd_conf_nil,          NULL },
+};
+
+char *pnd_conf_query_searchpath ( void ) {
+  pnd_conf_handle ch;
+
+  // fetch base config
+  ch = pnd_conf_fetch_by_id ( pnd_conf_conf, PND_CONF_SEARCHPATH );
+
+  if ( ! ch ) {
+    //printf ( "Couldn't locate base conf file '%s'\n", PND_CONF_FILE );
+    return ( strdup ( PND_CONF_SEARCHPATH ) );
+  }
+
+  // can we find a user-specified config path? if so, use it.. if not, fall back!
+  char *searchpath;
+  char *temp;
+
+  temp = pnd_conf_get_as_char ( ch, PND_CONF_KEY );
+
+  if ( searchpath ) {
+    searchpath = strdup ( temp );
+  } else {
+    searchpath = strdup ( PND_CONF_SEARCHPATH );
+  }
+
+  return ( searchpath );
+}
+
+pnd_conf_handle pnd_conf_fetch_by_id ( pnd_conf_filename_e id, char *searchpath ) {
+  pnd_conf_filename_t *p = pnd_conf_filenames;
+
+  while ( p -> filename ) {
+
+    /* found the filename associated to the id? */
+    if ( p -> id == id ) {
+      return ( pnd_conf_fetch_by_name ( p -> filename, searchpath ) );
+    }
+
+    /* next! */
+    p++;
+  } /* while */
+
+  return ( NULL );
+}
+
+pnd_conf_handle pnd_conf_fetch_by_name ( char *filename, char *searchpath ) {
+
+  /* the fun part here is that we get to cheat; while we have to search through all the directories
+   * listed in the search path, we can stop at the first matching file. Nothign really fancy going on, and
+   * no need for comprehensive directory crawling. yay!
+   */
+  pnd_conf_handle conf;
+
+  //printf ( "Search path: '%s'\n", searchpath );
+
+  SEARCHPATH_PRE
+  {
+
+    strncat ( buffer, "/", FILENAME_MAX - 1 );
+    strncat ( buffer, filename, FILENAME_MAX - 1 );
+    conf = pnd_conf_fetch_by_path ( buffer );
+
+    if ( conf ) {
+      return ( conf );
+    }
+
+  }
+  SEARCHPATH_POST
+
+  return ( NULL );
+}
+
+pnd_conf_handle pnd_conf_fetch_by_path ( char *fullpath ) {
+  FILE *f;
+  char section [ 256 ] = "";
+  char buffer [ FILENAME_MAX ];
+  char inbuffer [ FILENAME_MAX ];
+  char *c, *head, *tail, *mid;
+
+  //printf ( "Attempt to load config from fullpath '%s'\n", fullpath );
+
+  /* WARN:
+   * No check yet to verify the directory is actually mounted and readable; either way
+   * this should not block or take up much time, though SD cards might be slow to open over
+   * and over again .. perhaps need some smarts or caching of results or somesuch, since this
+   * call gets spammed over and over...
+   */
+  f = fopen ( fullpath, "r" );
+
+  if ( ! f ) {
+    return ( NULL );
+  }
+
+  // damn, we actually found a file, so need to try to parse it. Shucks. Give back a box-handle
+  // so the consumer has some lists to look at
+  pnd_box_handle h;
+  h = pnd_box_new ( fullpath );
+
+  // inhale file
+  while ( fgets ( inbuffer, FILENAME_MAX, f ) ) {
+
+    // strip line-endings and DOSisms
+    if ( ( c = strchr ( inbuffer, '\r' ) ) ) {
+      *c = '\0';
+    }
+
+    if ( ( c = strchr ( inbuffer, '\n' ) ) ) {
+      *c = '\0';
+    }
+
+    //printf ( "config line: '%s'\n", inbuffer );
+
+    // strip comments
+    if ( ( c = strchr ( inbuffer, '#' ) ) ) {
+      *c = '\0';
+    }
+
+    // strip leading and trailing spaces
+    head = inbuffer;
+    while ( *head && isspace ( *head ) ) {
+      head++;
+    }
+
+    if ( inbuffer [ 0 ] == '\0' ) {
+      //printf ( "  -> discard\n" );
+      continue; // skip, the line was pure comment or blank
+    }
+
+    tail = strchr ( inbuffer, '\0' ) - 1;
+    while ( *tail && isspace ( *tail ) ) {
+      *tail = '\0';
+      tail--;
+    }
+
+    if ( inbuffer [ 0 ] == '\0' ) {
+      //printf ( "  -> discard\n" );
+      continue; // skip, the line was pure comment or blank
+    }
+
+    // decorated, ie: a section?
+    if ( *head == '[' && *tail == ']' ) {
+      // note: handle the nil-section
+
+      memset ( section, '\0', 256 );
+
+      if ( tail == head + 1 ) {
+       section [ 0 ] = '\0';
+      } else {
+       strncpy ( section, head + 1, tail - head - 1 );
+      }
+
+      //printf ( " -> section '%s'\n", section );
+
+    } else {
+
+      // must be a key (and likely a value) .. find the division
+      mid = head;
+      while ( *mid && ! isspace ( *mid ) ) {
+       mid++;
+      }
+      *mid = '\0';
+      mid++;
+
+      //printf ( "key head: '%s'\n", head );
+      //printf ( "key mid: '%s'\n", mid );
+
+      // is thjis a key/value pair, or just a key?
+      if ( mid [ 0 ] ) {
+       // key/value pairing
+       char *v;
+
+       // form the actual new key
+       if ( section [ 0 ] ) {
+         snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
+       } else {
+         strncpy ( buffer, head, FILENAME_MAX - 1 );
+       }
+
+       //printf ( "Found key '%s' in config file\n", buffer );
+
+       // alloc node into the box
+       v = pnd_box_allocinsert ( h, buffer, strlen ( mid ) + 1 ); // allow for trailing null
+
+       if ( v ) {
+         strcpy ( v, mid );
+       } else {
+         return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
+       }
+
+      } else {
+       // key/value pairing
+       char *v;
+
+       // form the actual new key
+       if ( section [ 0 ] ) {
+         snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
+       } else {
+         strncpy ( buffer, head, FILENAME_MAX - 1 );
+       }
+
+       //printf ( "Found key with no value '%s' in config file\n", buffer );
+
+       // alloc node into the box
+       v = pnd_box_allocinsert ( h, buffer, 0 ); // zero b/c of no payload
+
+       if ( ! v ) {
+         return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
+       }
+
+      } // key or key/value?
+
+    } // section or key/value line?
+    
+  } // while
+
+  // clean up a trifle
+  fclose ( f );
+
+  return ( h );
+}
+
+char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key ) {
+  return ( pnd_box_find_by_key ( c, key ) );
+}
diff --git a/lib/pnd_container.c b/lib/pnd_container.c
new file mode 100644 (file)
index 0000000..6bd8236
--- /dev/null
@@ -0,0 +1,151 @@
+
+#include <stdlib.h> // for malloc, free, etc
+#include <string.h> // for strdup()
+
+#include "pnd_container.h"
+
+// This is implemented in a very vaguely 'class-like' fashion; sure, I could use union's and
+// void*'s to payload areas and so forth, but this method is pretty convenient instead. We allocate
+// a buffer of the size of the node PLUS the user requested payload, and then return the payload
+// part of it. We can go back to the node part with some basic pointer subtraction whenever we
+// want, and are reasonably assured the user won't monkey with anything in the mechanism.
+//  In short, we can change or replace the container code without impacting the user, and without
+// boring anyone with void*s or unions or sub-structs and the like.
+
+// a Box node is just a key-name and a pointer to the next; the payload is whatever data
+// follows this. The container itself doesn't care.
+typedef struct _pnd_box_node_t {
+  char *key;
+  struct _pnd_box_node_t *next;
+} pnd_box_node_t;
+
+typedef struct {
+  char *name;             // for when you're using gdb and wondering wtf you're looking at
+  pnd_box_node_t *head;   // the first node
+} pnd_box_t;
+
+#define PAYLOAD2NODE(x) ((pnd_box_node_t*)( ((unsigned char *)(x)) - sizeof(pnd_box_node_t) ))
+#define NODE2PAYLOAD(x) ( ((unsigned char *)(x)) + sizeof(pnd_box_node_t) )
+
+pnd_box_handle pnd_box_new ( char *name ) {
+
+  pnd_box_t *p = malloc ( sizeof(pnd_box_t) );
+
+  if ( ! p ) {
+    return ( NULL ); // youch!
+  }
+
+  if ( name ) {
+    p -> name = strdup ( name );
+  } else {
+    p -> name = NULL;
+  }
+
+  p -> head = NULL;
+
+  return ( p );
+}
+
+void pnd_box_delete ( pnd_box_handle box ) {
+  pnd_box_t *p = (pnd_box_t*) box;
+  pnd_box_node_t *n, *next;
+
+  /* free up the list
+   */
+
+  n = p -> head;
+
+  while ( n ) {
+
+    if ( n -> key ) {
+      free ( n -> key );
+    }
+
+    next = n -> next;
+
+    free ( n );
+
+    n = next;
+
+  } // while
+
+  /* free up the box itself
+   */
+
+  if ( p -> name ) {
+    free ( p -> name );
+  }
+
+  p -> head = (void*)123; // if you're looking at a stale pointer in gdb, this might tip you off
+
+  free ( p );
+
+  return;
+}
+
+void *pnd_box_allocinsert ( pnd_box_handle box, char *key, unsigned int size ) {
+  pnd_box_t *p = (pnd_box_t*) box;
+
+  pnd_box_node_t *n = malloc ( sizeof(pnd_box_node_t) + size );
+
+  if ( ! n ) {
+    return ( NULL ); // must be getting bloody tight!
+  }
+
+  if ( key ) {
+    n -> key = strdup ( key );
+  } else {
+    n -> key = NULL;
+  }
+
+  n -> next = p -> head;
+
+  p -> head = n;
+
+  return ( NODE2PAYLOAD(n) );
+}
+
+void *pnd_box_find_by_key ( pnd_box_handle box, char *key ) {
+  pnd_box_t *p = (pnd_box_t*) box;
+  pnd_box_node_t *n;
+
+  n = p -> head;
+
+  while ( n ) {
+
+    if ( strcmp ( n -> key, key ) == 0 ) {
+      return ( NODE2PAYLOAD(n) );
+    }
+
+    n = n -> next;
+  } // while
+
+  return ( NULL );
+}
+
+char *pnd_box_get_name ( pnd_box_handle box ) {
+  pnd_box_t *p = (pnd_box_t*) box;
+  return ( p -> name );
+}
+
+void *pnd_box_get_head ( pnd_box_handle box ) {
+  pnd_box_t *p = (pnd_box_t*) box;
+  if ( ! p -> head ) {
+    return ( NULL );
+  }
+  return ( NODE2PAYLOAD(p -> head) );
+}
+
+void *pnd_box_get_next ( void *node ) {
+  pnd_box_node_t *p = PAYLOAD2NODE(node);
+  p = p -> next;
+  if ( ! p ) {
+    return ( NULL );
+  }
+  return ( NODE2PAYLOAD(p) );
+}
+
+char *pnd_box_get_key ( void *node ) {
+  pnd_box_node_t *p = PAYLOAD2NODE(node);
+  return ( p -> key );
+}
diff --git a/lib/pnd_discovery.c b/lib/pnd_discovery.c
new file mode 100644 (file)
index 0000000..618bb36
--- /dev/null
@@ -0,0 +1,151 @@
+
+#include <stdio.h> /* for FILE etc */
+#include <stdlib.h> /* for malloc */
+#include <string.h> /* for making ftw.h happy */
+
+#define _XOPEN_SOURCE 500
+#define __USE_XOPEN_EXTENDED
+#include <ftw.h> /* for nftw, tree walker */
+
+#include "pnd_container.h"
+#include "pnd_pxml.h"
+#include "pnd_discovery.h"
+#include "pnd_pathiter.h"
+
+#warning "PND/PNZ support is not included yet; scripts need writing"
+#warning "  /usr/pandora/bin/pnd_valid.sh"
+#warning "  /usr/pandora/bin/pnd_prepare.sh"
+#warning "  /usr/pandora/bin/pnd_unprepare.sh"
+
+// need these 'globals' due to the way nftw and ftw work :/
+static pnd_box_handle disco_box;
+static char *disco_overrides = NULL;
+
+void pnd_disco_destroy ( pnd_disco_t *p ) {
+
+  if ( p -> title_en ) {
+    free ( p -> title_en );
+  }
+
+  if ( p -> icon ) {
+    free ( p -> icon );
+  }
+
+  if ( p -> exec ) {
+    free ( p -> exec );
+  }
+
+  if ( p -> unique_id ) {
+    free ( p -> unique_id );
+  }
+
+  if ( p -> main_category ) {
+    free ( p -> main_category );
+  }
+
+  if ( p -> clockspeed ) {
+    free ( p -> clockspeed );
+  }
+
+  return;
+}
+
+static int pnd_disco_callback ( const char *fpath, const struct stat *sb,
+                               int typeflag, struct FTW *ftwbuf )
+{
+  unsigned char valid = 0; // 1 for plaintext PXML, 2 for PND...
+
+  // PXML.xml is a possible application candidate (and not a dir named PXML.xml :)
+  if ( typeflag & FTW_D ) {
+    return ( 0 ); // skip directories and other non-regular files
+  }
+
+  if ( strcasecmp ( fpath + ftwbuf -> base, PXML_FILENAME ) == 0 ) {
+    valid = 1;
+  }
+
+  // PND file and others may be valid as well .. but lets leave that for now
+  //
+  // PND and PNZ and whatever
+  //
+
+  // if not a file of interest, just keep looking until we run out
+  if ( ! valid ) {
+    return ( 0 );
+  }
+
+  // potentially a valid application
+  if ( valid == 1 ) {
+    // Plaintext PXML file
+    //printf ( "disco callback encountered '%s'\n", fpath );
+
+    pnd_pxml_handle pxmlh;
+
+    // pick up the PXML if we can
+
+    pxmlh = pnd_pxml_fetch ( (char*) fpath );
+
+    if ( ! pxmlh ) {
+      return ( 0 ); // continue the scan
+    }
+
+    // look for any overrides, if requested
+#warning pnd_pxml_merge_override removed by Cpasjuste ...
+  //  pnd_pxml_merge_override ( pxmlh, disco_overrides );
+
+    // check for validity and add to resultset if it looks executable
+    if ( pnd_is_pxml_valid_app ( pxmlh ) ) {
+      pnd_disco_t *p;
+
+      p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
+      p -> title_en = strdup ( pnd_pxml_get_app_name ( pxmlh ) );
+      p -> icon = strdup ( pnd_pxml_get_icon_path ( pxmlh ) );
+      p -> exec = strdup ( pnd_pxml_get_exec_path ( pxmlh ) );
+      p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
+      p -> main_category = strdup ( pnd_pxml_get_primary_category ( pxmlh ) );
+      p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
+
+    }
+
+    // ditch pxml
+       pnd_pxml_delete ( pxmlh );
+
+       return 0;
+  } else if ( valid == 2 ) {
+    // PND ... ??
+  }
+
+  return ( 0 ); // continue the tree walk
+}
+
+pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
+
+  //printf ( "Searchpath to discover: '%s'\n", searchpath );
+
+  // alloc a container for the result set
+  disco_box = pnd_box_new ( "discovery" );
+  disco_overrides = overridespath;
+
+  /* iterate across the paths within the searchpath, attempting to locate applications
+   */
+
+  SEARCHPATH_PRE
+  {
+
+    // invoke the dir walking function; thankfully Linux includes a pretty good one
+    nftw ( buffer,               // path to descend
+          pnd_disco_callback,   // callback to do processing
+          10,                   // no more than X open fd's at once
+          FTW_PHYS );           // do not follow symlinks
+
+  }
+  SEARCHPATH_POST
+
+  // return whatever we found, or NULL if nada
+  if ( ! pnd_box_get_head ( disco_box ) ) {
+    pnd_box_delete ( disco_box );
+    disco_box = NULL;
+  }
+
+  return ( disco_box );
+}
diff --git a/lib/pnd_locate.c b/lib/pnd_locate.c
new file mode 100644 (file)
index 0000000..6e4afbb
--- /dev/null
@@ -0,0 +1,35 @@
+
+#include <stdio.h> /* for FILE etc */
+#include <stdlib.h> /* for malloc */
+#include <string.h> /* for making ftw.h happy */
+
+#include <sys/types.h> /* for stat(2) */
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "pnd_pathiter.h"
+#include "pnd_locate.h"
+
+static char pnd_locate_buffer [ FILENAME_MAX ]; // exceedingly lame
+
+char *pnd_locate_filename ( char *searchpath, char *filename ) {
+  struct stat foo;
+
+  SEARCHPATH_PRE
+  {
+
+    strncat ( buffer, "/", FILENAME_MAX );
+    strncat ( buffer, filename, FILENAME_MAX );
+
+    //printf ( "foo: %s\n", buffer );
+
+    if ( stat ( buffer, &foo ) == 0 ) {
+      strcpy ( pnd_locate_buffer, buffer );
+      return ( pnd_locate_buffer );
+    }
+
+  }
+  SEARCHPATH_POST
+
+  return ( NULL );
+}
diff --git a/lib/pnd_notify.c b/lib/pnd_notify.c
new file mode 100644 (file)
index 0000000..0c99458
--- /dev/null
@@ -0,0 +1,117 @@
+
+#include <sys/inotify.h>    // for INOTIFY duh
+#include <stdio.h>          // for stdio, NULL
+#include <stdlib.h>         // for malloc, etc
+#include <unistd.h>         // for close
+
+#include "pnd_notify.h"
+
+typedef struct {
+  int fd;              // notify API file descriptor
+} pnd_notify_t;
+
+static void pnd_notify_hookup ( int fd );
+
+pnd_notify_handle pnd_notify_init ( void ) {
+  int fd;
+  pnd_notify_t *p;
+
+  fd = inotify_init();
+
+  if ( fd < 0 ) {
+    return ( NULL );
+  }
+
+  p = malloc ( sizeof(pnd_notify_t) );
+
+  if ( ! p ) {
+    close ( fd );
+    return ( NULL ); // uhh..
+  }
+
+  p -> fd = fd;
+
+  // setup some default watches
+  pnd_notify_hookup ( fd );
+
+  return ( p );
+}
+
+void pnd_notify_shutdown ( pnd_notify_handle h ) {
+  pnd_notify_t *p = (pnd_notify_t*) h;
+
+  close ( p -> fd );
+
+  return;
+}
+
+static void pnd_notify_hookup ( int fd ) {
+
+  inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
+
+  return;
+}
+
+unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
+  pnd_notify_t *p = (pnd_notify_t*) h;
+
+  struct timeval t;
+  fd_set rfds;
+  int retcode;
+
+  // don't block for long..
+  t.tv_sec = 1;
+  t.tv_usec = 0; //5000;
+
+  // only for our useful fd
+  FD_ZERO ( &rfds );
+  FD_SET ( (p->fd), &rfds );
+
+  // wait and test
+  retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
+
+  if ( retcode < 0 ) {
+    return ( 0 ); // hmm.. need a better error code handler
+  } else if ( retcode == 0 ) {
+    return ( 0 ); // timeout
+  }
+
+  if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
+    return ( 0 );
+  }
+
+  // by this point, something must have happened on our watch
+#define BINBUFLEN ( 100 * ( sizeof(struct inotify_event) + 30 ) ) /* approx 100 events in a shot? */
+  unsigned char binbuf [ BINBUFLEN ];
+  int actuallen;
+
+  actuallen = read ( (p->fd), binbuf, BINBUFLEN );
+
+  if ( actuallen < 0 ) {
+    return ( 0 ); // error
+  } else if ( actuallen == 0 ) {
+    return ( 0 ); // nothing, or overflow, or .. whatever.
+  }
+
+  unsigned int i;
+  struct inotify_event *e;
+
+  while ( i < actuallen ) {
+    e = (struct inotify_event *) &binbuf [ i ];
+
+    /* do it!
+     */
+
+    if ( e -> len ) {
+      printf ( "Got event against '%s'\n", e -> name );
+    }
+
+    /* do it!
+     */
+
+    // next
+    i += ( sizeof(struct inotify_event) + e -> len );
+  } // while
+
+  return ( 1 );
+}
diff --git a/lib/pnd_pathiter.h b/lib/pnd_pathiter.h
new file mode 100644 (file)
index 0000000..4611e99
--- /dev/null
@@ -0,0 +1,41 @@
+
+#ifndef h_pnd_pathiter_h
+#define h_png_pathiter_h
+
+// man don'y you wish it was python, perl or c++ right about now?
+// perl: foreach i ( split ( ':', path ) ) would be sauce right now
+
+// this really should be replaced by a function pair .. one to
+// start a new search and one to go to next, bailing when done. Maybe
+// a state struct. Like we have time. OR perhaps just a single
+// func with a callback. Whatever.
+
+#define SEARCHPATH_PRE                            \
+  char *end, *colon;                              \
+  char *head = searchpath;                        \
+  char buffer [ FILENAME_MAX ];                   \
+                                                  \
+  while ( 1 ) {                                   \
+    colon = strchr ( head, ':' );                 \
+    end = strchr ( head, '\0' );                  \
+                                                  \
+    if ( colon && colon < end ) {                 \
+      memset ( buffer, '\0', FILENAME_MAX );      \
+      strncpy ( buffer, head, colon - head );     \
+    } else {                                      \
+      strncpy ( buffer, head, FILENAME_MAX - 1 ); \
+    }                                             \
+                                                  \
+    //printf ( "Path to search: '%s'\n", buffer );
+
+#define SEARCHPATH_POST                           \
+    /* next search path */                       \
+    if ( colon && colon < end ) {                 \
+      head = colon + 1;                           \
+    } else {                                      \
+      break; /* done! */                          \
+    }                                             \
+                                                  \
+  } // while
+
+#endif
diff --git a/lib/pnd_pxml.c b/lib/pnd_pxml.c
new file mode 100644 (file)
index 0000000..2c64d42
--- /dev/null
@@ -0,0 +1,139 @@
+
+#include <stdio.h> /* for FILE */
+#include <stdlib.h> /* for malloc */
+#include <string.h> /* for string ops */
+
+#include <sys/types.h> /* for stat */
+#include <sys/stat.h> /* for stat */
+#include <unistd.h> /* for stat */
+
+#include "pnd_pxml.h"
+#include "pnd_pathiter.h"
+
+void pnd_pxml_load(const char* pFilename, pnd_pxml_t *app);
+
+pnd_pxml_handle pnd_pxml_fetch ( char *fullpath ) {
+
+       pnd_pxml_t *p = malloc ( sizeof(pnd_pxml_t) );
+
+       pnd_pxml_load(fullpath, p);
+
+       return (p);
+}
+
+void pnd_pxml_delete ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+
+  if ( p -> title_en ) {
+    free ( p -> title_en );
+  }
+
+  if ( p -> icon ) {
+    free ( p -> icon );
+  }
+
+  if ( p -> exec ) {
+    free ( p -> exec );
+  }
+  if ( p -> main_category ) {
+    free ( p -> main_category );
+  }
+  if ( p -> unique_id ) {
+    free ( p -> unique_id );
+  }
+  if ( p -> clockspeed ) {
+    free ( p -> clockspeed );
+  }
+
+  return;
+}
+
+char *pnd_pxml_get_app_name ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> title_en );
+}
+
+char *pnd_pxml_get_icon_path ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> icon );
+}
+
+char *pnd_pxml_get_clockspeed ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> clockspeed );
+}
+
+void pnd_pxml_set_app_name ( pnd_pxml_handle h, char *v ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  if ( p -> title_en ) {
+    free ( p -> title_en );
+    p -> title_en = NULL;
+  }
+
+  if ( v ) {
+    p -> title_en = strdup ( v );
+  }
+
+  return;
+}
+
+char *pnd_pxml_get_unique_id ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> unique_id );
+}
+
+char *pnd_pxml_get_primary_category ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> main_category );
+}
+
+char *pnd_pxml_get_exec_path ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> exec );
+}
+
+unsigned char pnd_is_pxml_valid_app ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+
+  // for now, lets just verify the exec-path is valid
+  //printf ( "exec is '%s'\n", p -> exec );
+
+  struct stat buf;
+  if ( stat ( p -> exec, &buf ) == 0 ) {
+    return ( 1 ); // path is present
+  }
+
+  return ( 0 );
+}
+
+signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath ) {
+  // the pxml includes a unique-id; use this value to attempt to find an
+  // override in the given searchpath
+  signed char retval = 0;
+  pnd_pxml_handle mergeh;
+
+  SEARCHPATH_PRE
+  {
+
+    // do it
+    strncat ( buffer, "/", FILENAME_MAX );
+    strncat ( buffer, pnd_pxml_get_unique_id ( h ), FILENAME_MAX );
+    strncat ( buffer, ".xml", FILENAME_MAX );
+    //printf ( "  Path to seek merges: '%s'\n", buffer );
+
+    mergeh = pnd_pxml_fetch ( buffer );
+
+    if ( mergeh ) {
+
+      if ( pnd_pxml_get_app_name ( mergeh ) ) {
+       pnd_pxml_set_app_name ( h, pnd_pxml_get_app_name ( mergeh ) );
+      }
+
+      pnd_pxml_delete ( mergeh );
+    }
+
+  }
+  SEARCHPATH_POST
+
+  return ( retval );
+}
diff --git a/lib/pnd_tinyxml.cpp b/lib/pnd_tinyxml.cpp
new file mode 100644 (file)
index 0000000..b2c97ac
--- /dev/null
@@ -0,0 +1,282 @@
+#include "tinyxml/tinyxml.h"
+#include "../include/pnd_pxml.h"
+
+extern "C" {
+
+void pnd_pxml_load(const char* pFilename, pnd_pxml_t *app)
+{
+
+       TiXmlDocument doc(pFilename);
+       if (!doc.LoadFile()) return;
+
+       TiXmlHandle hDoc(&doc);
+       TiXmlElement* pElem;
+       TiXmlHandle hRoot(0);
+
+       pElem=hDoc.FirstChildElement().Element();
+       if (!pElem) return;
+       hRoot=TiXmlHandle(pElem);
+
+       pElem = hRoot.FirstChild( "title" ).FirstChildElement("en").Element();
+       if ( pElem )
+       {
+               app->title_en = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "title" ).FirstChildElement("de").Element();
+       if ( pElem )
+       {
+               app->title_de = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "title" ).FirstChildElement("it").Element();
+       if ( pElem )
+       {
+               app->title_it = strdup(pElem->GetText());
+       }
+       
+       pElem = hRoot.FirstChild( "title" ).FirstChildElement("fr").Element();
+       if ( pElem )
+       {
+               app->title_fr = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("unique_id").Element();
+       if (pElem)
+       {       
+               app->unique_id = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("standalone").Element();
+       if (pElem)
+       {       
+               app->standalone = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("icon").Element();
+       if (pElem)
+       {       
+               char anotherbuffer [ FILENAME_MAX ];
+               strcpy ( anotherbuffer, pFilename );
+               char *s = strstr ( anotherbuffer, PXML_FILENAME );
+               strcpy ( s, strdup(pElem->GetText()));
+               app->icon = strdup(anotherbuffer);
+       }
+
+       pElem = hRoot.FirstChild( "description" ).FirstChildElement("en").Element();
+       if ( pElem )
+       {
+               app->description_en = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "description" ).FirstChildElement("de").Element();
+       if ( pElem )
+       {
+               app->description_de = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "description" ).FirstChildElement("it").Element();
+       if ( pElem )
+       {
+               app->description_it = strdup(pElem->GetText());
+       }
+       
+       pElem = hRoot.FirstChild( "description" ).FirstChildElement("fr").Element();
+       if ( pElem )
+       {
+               app->description_fr = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "previewpic" ).FirstChildElement("pic1").Element();
+       if ( pElem )
+       {
+               app->previewpic1 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "previewpic" ).FirstChildElement("pic2").Element();
+       if ( pElem )
+       {
+               app->previewpic2 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "author" ).FirstChildElement("name").Element();
+       if ( pElem )
+       {
+               app->author_name = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "author" ).FirstChildElement("website").Element();
+       if ( pElem )
+       {
+               app->author_website = strdup(pElem->GetText());;
+       }
+
+       pElem = hRoot.FirstChild( "version" ).FirstChildElement("major").Element();
+       if ( pElem )
+       {
+               app->version_major = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "version" ).FirstChildElement("minor").Element();
+       if ( pElem )
+       {
+               app->version_minor = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "version" ).FirstChildElement("release").Element();
+       if ( pElem )
+       {
+               app->version_release = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "version" ).FirstChildElement("build").Element();
+       if ( pElem )
+       {
+               app->version_build = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("exec").Element();
+       if (pElem)
+       {       
+               char anotherbuffer [ FILENAME_MAX ];
+               strcpy ( anotherbuffer, pFilename );
+               char *s = strstr ( anotherbuffer, PXML_FILENAME );
+               strcpy ( s, strdup(pElem->GetText()));
+               app->exec = strdup(anotherbuffer);
+       }       
+
+       pElem = hRoot.FirstChild( "category" ).FirstChildElement("main").Element();
+       if ( pElem )
+       {
+               app->main_category = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "category" ).FirstChildElement("subcategory1").Element();
+       if ( pElem )
+       {
+               app->subcategory1 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "category" ).FirstChildElement("subcategory2").Element();
+       if ( pElem )
+       {
+               app->subcategory2 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("main").Element();
+       if ( pElem )
+       {
+               app->altcategory = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("subcategory1").Element();
+       if ( pElem )
+       {
+               app->altsubcategory1 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("subcategory2").Element();
+       if ( pElem )
+       {
+               app->altsubcategory2 = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("major").Element();
+       if ( pElem )
+       {
+               app->osversion_major = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("minor").Element();
+       if ( pElem )
+       {
+               app->osversion_minor = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("release").Element();
+       if ( pElem )
+       {
+               app->osversion_release = strdup(pElem->GetText());
+       }       
+
+       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("build").Element();
+       if ( pElem )
+       {
+               app->osversion_build = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("name").Element();
+       if ( pElem )
+       {
+               app->associationitem1_name = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("filetype").Element();
+       if ( pElem )
+       {
+               app->associationitem1_filetype = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("parameter").Element();
+       if ( pElem )
+       {
+               app->associationitem1_parameter = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("name").Element();
+       if ( pElem )
+       {
+               app->associationitem2_name = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("filetype").Element();
+       if ( pElem )
+       {
+               app->associationitem2_filetype = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("parameter").Element();
+       if ( pElem )
+       {
+               app->associationitem2_parameter = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("name").Element();
+       if ( pElem )
+       {
+               app->associationitem3_name = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("filetype").Element();
+       if ( pElem )
+       {
+               app->associationitem3_filetype = strdup(pElem->GetText());
+       }
+
+       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("parameter").Element();
+       if ( pElem )
+       {
+               app->associationitem3_parameter = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("clockspeed").Element();
+       if (pElem)
+       {       
+               app->clockspeed = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("background").Element();
+       if (pElem)
+       {       
+               app->background = strdup(pElem->GetText());
+       }
+
+       pElem=hRoot.FirstChild("startdir").Element();
+       if (pElem)
+       {       
+               app->startdir = strdup(pElem->GetText());
+       }
+}
+
+}
+
diff --git a/lib/tinyxml/tinystr.cpp b/lib/tinyxml/tinystr.cpp
new file mode 100644 (file)
index 0000000..6812507
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005.
+ */
+
+
+#ifndef TIXML_USE_STL
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
+
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+       if (cap > capacity())
+       {
+               TiXmlString tmp;
+               tmp.init(length(), cap);
+               memcpy(tmp.start(), data(), length());
+               swap(tmp);
+       }
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+       size_type cap = capacity();
+       if (len > cap || cap > 3*(len + 8))
+       {
+               TiXmlString tmp;
+               tmp.init(len);
+               memcpy(tmp.start(), str, len);
+               swap(tmp);
+       }
+       else
+       {
+               memmove(start(), str, len);
+               set_size(len);
+       }
+       return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+       size_type newsize = length() + len;
+       if (newsize > capacity())
+       {
+               reserve (newsize + capacity());
+       }
+       memmove(finish(), str, len);
+       set_size(newsize);
+       return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+       TiXmlString tmp;
+       tmp.reserve(a.length() + b.length());
+       tmp += a;
+       tmp += b;
+       return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+       TiXmlString tmp;
+       TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
+       tmp.reserve(a.length() + b_len);
+       tmp += a;
+       tmp.append(b, b_len);
+       return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+       TiXmlString tmp;
+       TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
+       tmp.reserve(a_len + b.length());
+       tmp.append(a, a_len);
+       tmp += b;
+       return tmp;
+}
+
+
+#endif // TIXML_USE_STL
diff --git a/lib/tinyxml/tinystr.h b/lib/tinyxml/tinystr.h
new file mode 100644 (file)
index 0000000..3c2aa9d
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#include <assert.h>
+#include <string.h>
+
+/*     The support for explicit isn't that universal, and it isn't really
+       required - it is used to check that the TiXmlString class isn't incorrectly
+       used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
+       // Microsoft visual studio, version 6 and higher.
+       #define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3 )
+       // GCC version 3 and higher.s
+       #define TIXML_EXPLICIT explicit
+#else
+       #define TIXML_EXPLICIT
+#endif
+
+
+/*
+   TiXmlString is an emulation of a subset of the std::string template.
+   Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+   Only the member functions relevant to the TinyXML project have been implemented.
+   The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+   a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+  public :
+       // The size type used
+       typedef size_t size_type;
+
+       // Error value for find primitive
+       static const size_type npos; // = -1;
+
+
+       // TiXmlString empty constructor
+       TiXmlString () : rep_(&nullrep_)
+       {
+       }
+
+       // TiXmlString copy constructor
+       TiXmlString ( const TiXmlString & copy) : rep_(0)
+       {
+               init(copy.length());
+               memcpy(start(), copy.data(), length());
+       }
+
+       // TiXmlString constructor, based on a string
+       TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
+       {
+               init( static_cast<size_type>( strlen(copy) ));
+               memcpy(start(), copy, length());
+       }
+
+       // TiXmlString constructor, based on a string
+       TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
+       {
+               init(len);
+               memcpy(start(), str, len);
+       }
+
+       // TiXmlString destructor
+       ~TiXmlString ()
+       {
+               quit();
+       }
+
+       // = operator
+       TiXmlString& operator = (const char * copy)
+       {
+               return assign( copy, (size_type)strlen(copy));
+       }
+
+       // = operator
+       TiXmlString& operator = (const TiXmlString & copy)
+       {
+               return assign(copy.start(), copy.length());
+       }
+
+
+       // += operator. Maps to append
+       TiXmlString& operator += (const char * suffix)
+       {
+               return append(suffix, static_cast<size_type>( strlen(suffix) ));
+       }
+
+       // += operator. Maps to append
+       TiXmlString& operator += (char single)
+       {
+               return append(&single, 1);
+       }
+
+       // += operator. Maps to append
+       TiXmlString& operator += (const TiXmlString & suffix)
+       {
+               return append(suffix.data(), suffix.length());
+       }
+
+
+       // Convert a TiXmlString into a null-terminated char *
+       const char * c_str () const { return rep_->str; }
+
+       // Convert a TiXmlString into a char * (need not be null terminated).
+       const char * data () const { return rep_->str; }
+
+       // Return the length of a TiXmlString
+       size_type length () const { return rep_->size; }
+
+       // Alias for length()
+       size_type size () const { return rep_->size; }
+
+       // Checks if a TiXmlString is empty
+       bool empty () const { return rep_->size == 0; }
+
+       // Return capacity of string
+       size_type capacity () const { return rep_->capacity; }
+
+
+       // single char extraction
+       const char& at (size_type index) const
+       {
+               assert( index < length() );
+               return rep_->str[ index ];
+       }
+
+       // [] operator
+       char& operator [] (size_type index) const
+       {
+               assert( index < length() );
+               return rep_->str[ index ];
+       }
+
+       // find a char in a string. Return TiXmlString::npos if not found
+       size_type find (char lookup) const
+       {
+               return find(lookup, 0);
+       }
+
+       // find a char in a string from an offset. Return TiXmlString::npos if not found
+       size_type find (char tofind, size_type offset) const
+       {
+               if (offset >= length()) return npos;
+
+               for (const char* p = c_str() + offset; *p != '\0'; ++p)
+               {
+                  if (*p == tofind) return static_cast< size_type >( p - c_str() );
+               }
+               return npos;
+       }
+
+       void clear ()
+       {
+               //Lee:
+               //The original was just too strange, though correct:
+               //      TiXmlString().swap(*this);
+               //Instead use the quit & re-init:
+               quit();
+               init(0,0);
+       }
+
+       /*      Function to reserve a big amount of data when we know we'll need it. Be aware that this
+               function DOES NOT clear the content of the TiXmlString if any exists.
+       */
+       void reserve (size_type cap);
+
+       TiXmlString& assign (const char* str, size_type len);
+
+       TiXmlString& append (const char* str, size_type len);
+
+       void swap (TiXmlString& other)
+       {
+               Rep* r = rep_;
+               rep_ = other.rep_;
+               other.rep_ = r;
+       }
+
+  private:
+
+       void init(size_type sz) { init(sz, sz); }
+       void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+       char* start() const { return rep_->str; }
+       char* finish() const { return rep_->str + rep_->size; }
+
+       struct Rep
+       {
+               size_type size, capacity;
+               char str[1];
+       };
+
+       void init(size_type sz, size_type cap)
+       {
+               if (cap)
+               {
+                       // Lee: the original form:
+                       //      rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+                       // doesn't work in some cases of new being overloaded. Switching
+                       // to the normal allocation, although use an 'int' for systems
+                       // that are overly picky about structure alignment.
+                       const size_type bytesNeeded = sizeof(Rep) + cap;
+                       const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); 
+                       rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
+
+                       rep_->str[ rep_->size = sz ] = '\0';
+                       rep_->capacity = cap;
+               }
+               else
+               {
+                       rep_ = &nullrep_;
+               }
+       }
+
+       void quit()
+       {
+               if (rep_ != &nullrep_)
+               {
+                       // The rep_ is really an array of ints. (see the allocator, above).
+                       // Cast it back before delete, so the compiler won't incorrectly call destructors.
+                       delete [] ( reinterpret_cast<int*>( rep_ ) );
+               }
+       }
+
+       Rep * rep_;
+       static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+       return    ( a.length() == b.length() )                          // optimization on some platforms
+              && ( strcmp(a.c_str(), b.c_str()) == 0 );        // actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+       return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator >  (const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+   TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+   Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+       // TiXmlOutStream << operator.
+       TiXmlOutStream & operator << (const TiXmlString & in)
+       {
+               *this += in;
+               return *this;
+       }
+
+       // TiXmlOutStream << operator.
+       TiXmlOutStream & operator << (const char * in)
+       {
+               *this += in;
+               return *this;
+       }
+
+} ;
+
+#endif // TIXML_STRING_INCLUDED
+#endif // TIXML_USE_STL
diff --git a/lib/tinyxml/tinyxml.cpp b/lib/tinyxml/tinyxml.cpp
new file mode 100644 (file)
index 0000000..5de21f6
--- /dev/null
@@ -0,0 +1,1888 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#include "tinyxml.h"
+
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+// Microsoft compiler security
+FILE* TiXmlFOpen( const char* filename, const char* mode )
+{
+       #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+               FILE* fp = 0;
+               errno_t err = fopen_s( &fp, filename, mode );
+               if ( !err && fp )
+                       return fp;
+               return 0;
+       #else
+               return fopen( filename, mode );
+       #endif
+}
+
+void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
+{
+       int i=0;
+
+       while( i<(int)str.length() )
+       {
+               unsigned char c = (unsigned char) str[i];
+
+               if (    c == '&' 
+                    && i < ( (int)str.length() - 2 )
+                        && str[i+1] == '#'
+                        && str[i+2] == 'x' )
+               {
+                       // Hexadecimal character reference.
+                       // Pass through unchanged.
+                       // &#xA9;       -- copyright symbol, for example.
+                       //
+                       // The -1 is a bug fix from Rob Laveaux. It keeps
+                       // an overflow from happening if there is no ';'.
+                       // There are actually 2 ways to exit this loop -
+                       // while fails (error case) and break (semicolon found).
+                       // However, there is no mechanism (currently) for
+                       // this function to return an error.
+                       while ( i<(int)str.length()-1 )
+                       {
+                               outString->append( str.c_str() + i, 1 );
+                               ++i;
+                               if ( str[i] == ';' )
+                                       break;
+                       }
+               }
+               else if ( c == '&' )
+               {
+                       outString->append( entity[0].str, entity[0].strLength );
+                       ++i;
+               }
+               else if ( c == '<' )
+               {
+                       outString->append( entity[1].str, entity[1].strLength );
+                       ++i;
+               }
+               else if ( c == '>' )
+               {
+                       outString->append( entity[2].str, entity[2].strLength );
+                       ++i;
+               }
+               else if ( c == '\"' )
+               {
+                       outString->append( entity[3].str, entity[3].strLength );
+                       ++i;
+               }
+               else if ( c == '\'' )
+               {
+                       outString->append( entity[4].str, entity[4].strLength );
+                       ++i;
+               }
+               else if ( c < 32 )
+               {
+                       // Easy pass at non-alpha/numeric/symbol
+                       // Below 32 is symbolic.
+                       char buf[ 32 ];
+                       
+                       #if defined(TIXML_SNPRINTF)             
+                               TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
+                       #else
+                               sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
+                       #endif          
+
+                       //*ME:  warning C4267: convert 'size_t' to 'int'
+                       //*ME:  Int-Cast to make compiler happy ...
+                       outString->append( buf, (int)strlen( buf ) );
+                       ++i;
+               }
+               else
+               {
+                       //char realc = (char) c;
+                       //outString->append( &realc, 1 );
+                       *outString += (char) c; // somewhat more efficient function call.
+                       ++i;
+               }
+       }
+}
+
+
+TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
+{
+       parent = 0;
+       type = _type;
+       firstChild = 0;
+       lastChild = 0;
+       prev = 0;
+       next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+       TiXmlNode* node = firstChild;
+       TiXmlNode* temp = 0;
+
+       while ( node )
+       {
+               temp = node;
+               node = node->next;
+               delete temp;
+       }       
+}
+
+
+void TiXmlNode::CopyTo( TiXmlNode* target ) const
+{
+       target->SetValue (value.c_str() );
+       target->userData = userData; 
+}
+
+
+void TiXmlNode::Clear()
+{
+       TiXmlNode* node = firstChild;
+       TiXmlNode* temp = 0;
+
+       while ( node )
+       {
+               temp = node;
+               node = node->next;
+               delete temp;
+       }       
+
+       firstChild = 0;
+       lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
+{
+       assert( node->parent == 0 || node->parent == this );
+       assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
+
+       if ( node->Type() == TiXmlNode::DOCUMENT )
+       {
+               delete node;
+               if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+
+       node->parent = this;
+
+       node->prev = lastChild;
+       node->next = 0;
+
+       if ( lastChild )
+               lastChild->next = node;
+       else
+               firstChild = node;                      // it was an empty list.
+
+       lastChild = node;
+       return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
+{
+       if ( addThis.Type() == TiXmlNode::DOCUMENT )
+       {
+               if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+       TiXmlNode* node = addThis.Clone();
+       if ( !node )
+               return 0;
+
+       return LinkEndChild( node );
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
+{      
+       if ( !beforeThis || beforeThis->parent != this ) {
+               return 0;
+       }
+       if ( addThis.Type() == TiXmlNode::DOCUMENT )
+       {
+               if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+
+       TiXmlNode* node = addThis.Clone();
+       if ( !node )
+               return 0;
+       node->parent = this;
+
+       node->next = beforeThis;
+       node->prev = beforeThis->prev;
+       if ( beforeThis->prev )
+       {
+               beforeThis->prev->next = node;
+       }
+       else
+       {
+               assert( firstChild == beforeThis );
+               firstChild = node;
+       }
+       beforeThis->prev = node;
+       return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
+{
+       if ( !afterThis || afterThis->parent != this ) {
+               return 0;
+       }
+       if ( addThis.Type() == TiXmlNode::DOCUMENT )
+       {
+               if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+
+       TiXmlNode* node = addThis.Clone();
+       if ( !node )
+               return 0;
+       node->parent = this;
+
+       node->prev = afterThis;
+       node->next = afterThis->next;
+       if ( afterThis->next )
+       {
+               afterThis->next->prev = node;
+       }
+       else
+       {
+               assert( lastChild == afterThis );
+               lastChild = node;
+       }
+       afterThis->next = node;
+       return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
+{
+       if ( replaceThis->parent != this )
+               return 0;
+
+       TiXmlNode* node = withThis.Clone();
+       if ( !node )
+               return 0;
+
+       node->next = replaceThis->next;
+       node->prev = replaceThis->prev;
+
+       if ( replaceThis->next )
+               replaceThis->next->prev = node;
+       else
+               lastChild = node;
+
+       if ( replaceThis->prev )
+               replaceThis->prev->next = node;
+       else
+               firstChild = node;
+
+       delete replaceThis;
+       node->parent = this;
+       return node;
+}
+
+
+bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
+{
+       if ( removeThis->parent != this )
+       {       
+               assert( 0 );
+               return false;
+       }
+
+       if ( removeThis->next )
+               removeThis->next->prev = removeThis->prev;
+       else
+               lastChild = removeThis->prev;
+
+       if ( removeThis->prev )
+               removeThis->prev->next = removeThis->next;
+       else
+               firstChild = removeThis->next;
+
+       delete removeThis;
+       return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
+{
+       const TiXmlNode* node;
+       for ( node = firstChild; node; node = node->next )
+       {
+               if ( strcmp( node->Value(), _value ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
+{
+       const TiXmlNode* node;
+       for ( node = lastChild; node; node = node->prev )
+       {
+               if ( strcmp( node->Value(), _value ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
+{
+       if ( !previous )
+       {
+               return FirstChild();
+       }
+       else
+       {
+               assert( previous->parent == this );
+               return previous->NextSibling();
+       }
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
+{
+       if ( !previous )
+       {
+               return FirstChild( val );
+       }
+       else
+       {
+               assert( previous->parent == this );
+               return previous->NextSibling( val );
+       }
+}
+
+
+const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const 
+{
+       const TiXmlNode* node;
+       for ( node = next; node; node = node->next )
+       {
+               if ( strcmp( node->Value(), _value ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
+{
+       const TiXmlNode* node;
+       for ( node = prev; node; node = node->prev )
+       {
+               if ( strcmp( node->Value(), _value ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+
+
+void TiXmlElement::RemoveAttribute( const char * name )
+{
+    #ifdef TIXML_USE_STL
+       TIXML_STRING str( name );
+       TiXmlAttribute* node = attributeSet.Find( str );
+       #else
+       TiXmlAttribute* node = attributeSet.Find( name );
+       #endif
+       if ( node )
+       {
+               attributeSet.Remove( node );
+               delete node;
+       }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+       const TiXmlNode* node;
+
+       for (   node = FirstChild();
+                       node;
+                       node = node->NextSibling() )
+       {
+               if ( node->ToElement() )
+                       return node->ToElement();
+       }
+       return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
+{
+       const TiXmlNode* node;
+
+       for (   node = FirstChild( _value );
+                       node;
+                       node = node->NextSibling( _value ) )
+       {
+               if ( node->ToElement() )
+                       return node->ToElement();
+       }
+       return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+       const TiXmlNode* node;
+
+       for (   node = NextSibling();
+                       node;
+                       node = node->NextSibling() )
+       {
+               if ( node->ToElement() )
+                       return node->ToElement();
+       }
+       return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
+{
+       const TiXmlNode* node;
+
+       for (   node = NextSibling( _value );
+                       node;
+                       node = node->NextSibling( _value ) )
+       {
+               if ( node->ToElement() )
+                       return node->ToElement();
+       }
+       return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+       const TiXmlNode* node;
+
+       for( node = this; node; node = node->parent )
+       {
+               if ( node->ToDocument() )
+                       return node->ToDocument();
+       }
+       return 0;
+}
+
+
+TiXmlElement::TiXmlElement (const char * _value)
+       : TiXmlNode( TiXmlNode::ELEMENT )
+{
+       firstChild = lastChild = 0;
+       value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement( const std::string& _value ) 
+       : TiXmlNode( TiXmlNode::ELEMENT )
+{
+       firstChild = lastChild = 0;
+       value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement( const TiXmlElement& copy)
+       : TiXmlNode( TiXmlNode::ELEMENT )
+{
+       firstChild = lastChild = 0;
+       copy.CopyTo( this );    
+}
+
+
+void TiXmlElement::operator=( const TiXmlElement& base )
+{
+       ClearThis();
+       base.CopyTo( this );
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+       ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+       Clear();
+       while( attributeSet.First() )
+       {
+               TiXmlAttribute* node = attributeSet.First();
+               attributeSet.Remove( node );
+               delete node;
+       }
+}
+
+
+const char* TiXmlElement::Attribute( const char* name ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( node )
+               return node->Value();
+       return 0;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( node )
+               return &node->ValueStr();
+       return 0;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, int* i ) const
+{
+       const char* s = Attribute( name );
+       if ( i )
+       {
+               if ( s ) {
+                       *i = atoi( s );
+               }
+               else {
+                       *i = 0;
+               }
+       }
+       return s;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
+{
+       const std::string* s = Attribute( name );
+       if ( i )
+       {
+               if ( s ) {
+                       *i = atoi( s->c_str() );
+               }
+               else {
+                       *i = 0;
+               }
+       }
+       return s;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, double* d ) const
+{
+       const char* s = Attribute( name );
+       if ( d )
+       {
+               if ( s ) {
+                       *d = atof( s );
+               }
+               else {
+                       *d = 0;
+               }
+       }
+       return s;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
+{
+       const std::string* s = Attribute( name );
+       if ( d )
+       {
+               if ( s ) {
+                       *d = atof( s->c_str() );
+               }
+               else {
+                       *d = 0;
+               }
+       }
+       return s;
+}
+#endif
+
+
+int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( !node )
+               return TIXML_NO_ATTRIBUTE;
+       return node->QueryIntValue( ival );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( !node )
+               return TIXML_NO_ATTRIBUTE;
+       return node->QueryIntValue( ival );
+}
+#endif
+
+
+int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( !node )
+               return TIXML_NO_ATTRIBUTE;
+       return node->QueryDoubleValue( dval );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
+{
+       const TiXmlAttribute* node = attributeSet.Find( name );
+       if ( !node )
+               return TIXML_NO_ATTRIBUTE;
+       return node->QueryDoubleValue( dval );
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * name, int val )
+{      
+       char buf[64];
+       #if defined(TIXML_SNPRINTF)             
+               TIXML_SNPRINTF( buf, sizeof(buf), "%d", val );
+       #else
+               sprintf( buf, "%d", val );
+       #endif
+       SetAttribute( name, buf );
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, int val )
+{      
+   std::ostringstream oss;
+   oss << val;
+   SetAttribute( name, oss.str() );
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute( const char * name, double val )
+{      
+       char buf[256];
+       #if defined(TIXML_SNPRINTF)             
+               TIXML_SNPRINTF( buf, sizeof(buf), "%f", val );
+       #else
+               sprintf( buf, "%f", val );
+       #endif
+       SetAttribute( name, buf );
+}
+
+
+void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
+{
+    #ifdef TIXML_USE_STL
+       TIXML_STRING _name( cname );
+       TIXML_STRING _value( cvalue );
+       #else
+       const char* _name = cname;
+       const char* _value = cvalue;
+       #endif
+
+       TiXmlAttribute* node = attributeSet.Find( _name );
+       if ( node )
+       {
+               node->SetValue( _value );
+               return;
+       }
+
+       TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue );
+       if ( attrib )
+       {
+               attributeSet.Add( attrib );
+       }
+       else
+       {
+               TiXmlDocument* document = GetDocument();
+               if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
+       }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value )
+{
+       TiXmlAttribute* node = attributeSet.Find( name );
+       if ( node )
+       {
+               node->SetValue( _value );
+               return;
+       }
+
+       TiXmlAttribute* attrib = new TiXmlAttribute( name, _value );
+       if ( attrib )
+       {
+               attributeSet.Add( attrib );
+       }
+       else
+       {
+               TiXmlDocument* document = GetDocument();
+               if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
+       }
+}
+#endif
+
+
+void TiXmlElement::Print( FILE* cfile, int depth ) const
+{
+       int i;
+       assert( cfile );
+       for ( i=0; i<depth; i++ ) {
+               fprintf( cfile, "    " );
+       }
+
+       fprintf( cfile, "<%s", value.c_str() );
+
+       const TiXmlAttribute* attrib;
+       for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
+       {
+               fprintf( cfile, " " );
+               attrib->Print( cfile, depth );
+       }
+
+       // There are 3 different formatting approaches:
+       // 1) An element without children is printed as a <foo /> node
+       // 2) An element with only a text child is printed as <foo> text </foo>
+       // 3) An element with children is printed on multiple lines.
+       TiXmlNode* node;
+       if ( !firstChild )
+       {
+               fprintf( cfile, " />" );
+       }
+       else if ( firstChild == lastChild && firstChild->ToText() )
+       {
+               fprintf( cfile, ">" );
+               firstChild->Print( cfile, depth + 1 );
+               fprintf( cfile, "</%s>", value.c_str() );
+       }
+       else
+       {
+               fprintf( cfile, ">" );
+
+               for ( node = firstChild; node; node=node->NextSibling() )
+               {
+                       if ( !node->ToText() )
+                       {
+                               fprintf( cfile, "\n" );
+                       }
+                       node->Print( cfile, depth+1 );
+               }
+               fprintf( cfile, "\n" );
+               for( i=0; i<depth; ++i ) {
+                       fprintf( cfile, "    " );
+               }
+               fprintf( cfile, "</%s>", value.c_str() );
+       }
+}
+
+
+void TiXmlElement::CopyTo( TiXmlElement* target ) const
+{
+       // superclass:
+       TiXmlNode::CopyTo( target );
+
+       // Element class: 
+       // Clone the attributes, then clone the children.
+       const TiXmlAttribute* attribute = 0;
+       for(    attribute = attributeSet.First();
+       attribute;
+       attribute = attribute->Next() )
+       {
+               target->SetAttribute( attribute->Name(), attribute->Value() );
+       }
+
+       TiXmlNode* node = 0;
+       for ( node = firstChild; node; node = node->NextSibling() )
+       {
+               target->LinkEndChild( node->Clone() );
+       }
+}
+
+bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
+{
+       if ( visitor->VisitEnter( *this, attributeSet.First() ) ) 
+       {
+               for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+               {
+                       if ( !node->Accept( visitor ) )
+                               break;
+               }
+       }
+       return visitor->VisitExit( *this );
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+       TiXmlElement* clone = new TiXmlElement( Value() );
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+       const TiXmlNode* child = this->FirstChild();
+       if ( child ) {
+               const TiXmlText* childText = child->ToText();
+               if ( childText ) {
+                       return childText->Value();
+               }
+       }
+       return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )
+{
+       tabsize = 4;
+       useMicrosoftBOM = false;
+       ClearError();
+}
+
+TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
+{
+       tabsize = 4;
+       useMicrosoftBOM = false;
+       value = documentName;
+       ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
+{
+       tabsize = 4;
+       useMicrosoftBOM = false;
+    value = documentName;
+       ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )
+{
+       copy.CopyTo( this );
+}
+
+
+void TiXmlDocument::operator=( const TiXmlDocument& copy )
+{
+       Clear();
+       copy.CopyTo( this );
+}
+
+
+bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
+{
+       // See STL_STRING_BUG below.
+       //StringToBuffer buf( value );
+
+       return LoadFile( Value(), encoding );
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+       // See STL_STRING_BUG below.
+//     StringToBuffer buf( value );
+//
+//     if ( buf.buffer && SaveFile( buf.buffer ) )
+//             return true;
+//
+//     return false;
+       return SaveFile( Value() );
+}
+
+bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
+{
+       // There was a really terrifying little bug here. The code:
+       //              value = filename
+       // in the STL case, cause the assignment method of the std::string to
+       // be called. What is strange, is that the std::string had the same
+       // address as it's c_str() method, and so bad things happen. Looks
+       // like a bug in the Microsoft STL implementation.
+       // Add an extra string to avoid the crash.
+       TIXML_STRING filename( _filename );
+       value = filename;
+
+       // reading in binary mode so that tinyxml can normalize the EOL
+       FILE* file = TiXmlFOpen( value.c_str (), "rb" );        
+
+       if ( file )
+       {
+               bool result = LoadFile( file, encoding );
+               fclose( file );
+               return result;
+       }
+       else
+       {
+               SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return false;
+       }
+}
+
+bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
+{
+       if ( !file ) 
+       {
+               SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return false;
+       }
+
+       // Delete the existing data:
+       Clear();
+       location.Clear();
+
+       // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+       long length = 0;
+       fseek( file, 0, SEEK_END );
+       length = ftell( file );
+       fseek( file, 0, SEEK_SET );
+
+       // Strange case, but good to handle up front.
+       if ( length <= 0 )
+       {
+               SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return false;
+       }
+
+       // If we have a file, assume it is all one big XML file, and read it in.
+       // The document parser may decide the document ends sooner than the entire file, however.
+       TIXML_STRING data;
+       data.reserve( length );
+
+       // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+       // 2.11 End-of-Line Handling
+       // <snip>
+       // <quote>
+       // ...the XML processor MUST behave as if it normalized all line breaks in external 
+       // parsed entities (including the document entity) on input, before parsing, by translating 
+       // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to 
+       // a single #xA character.
+       // </quote>
+       //
+       // It is not clear fgets does that, and certainly isn't clear it works cross platform. 
+       // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+       // convention, and not work generally.
+
+       /*
+       while( fgets( buf, sizeof(buf), file ) )
+       {
+               data += buf;
+       }
+       */
+
+       char* buf = new char[ length+1 ];
+       buf[0] = 0;
+
+       if ( fread( buf, length, 1, file ) != 1 ) {
+               delete [] buf;
+               SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return false;
+       }
+
+       const char* lastPos = buf;
+       const char* p = buf;
+
+       buf[length] = 0;
+       while( *p ) {
+               assert( p < (buf+length) );
+               if ( *p == 0xa ) {
+                       // Newline character. No special rules for this. Append all the characters
+                       // since the last string, and include the newline.
+                       data.append( lastPos, (p-lastPos+1) );  // append, include the newline
+                       ++p;                                                                    // move past the newline
+                       lastPos = p;                                                    // and point to the new buffer (may be 0)
+                       assert( p <= (buf+length) );
+               }
+               else if ( *p == 0xd ) {
+                       // Carriage return. Append what we have so far, then
+                       // handle moving forward in the buffer.
+                       if ( (p-lastPos) > 0 ) {
+                               data.append( lastPos, p-lastPos );      // do not add the CR
+                       }
+                       data += (char)0xa;                                              // a proper newline
+
+                       if ( *(p+1) == 0xa ) {
+                               // Carriage return - new line sequence
+                               p += 2;
+                               lastPos = p;
+                               assert( p <= (buf+length) );
+                       }
+                       else {
+                               // it was followed by something else...that is presumably characters again.
+                               ++p;
+                               lastPos = p;
+                               assert( p <= (buf+length) );
+                       }
+               }
+               else {
+                       ++p;
+               }
+       }
+       // Handle any left over characters.
+       if ( p-lastPos ) {
+               data.append( lastPos, p-lastPos );
+       }               
+       delete [] buf;
+       buf = 0;
+
+       Parse( data.c_str(), 0, encoding );
+
+       if (  Error() )
+        return false;
+    else
+               return true;
+}
+
+
+bool TiXmlDocument::SaveFile( const char * filename ) const
+{
+       // The old c stuff lives on...
+       FILE* fp = TiXmlFOpen( filename, "w" );
+       if ( fp )
+       {
+               bool result = SaveFile( fp );
+               fclose( fp );
+               return result;
+       }
+       return false;
+}
+
+
+bool TiXmlDocument::SaveFile( FILE* fp ) const
+{
+       if ( useMicrosoftBOM ) 
+       {
+               const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+               const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+               const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+               fputc( TIXML_UTF_LEAD_0, fp );
+               fputc( TIXML_UTF_LEAD_1, fp );
+               fputc( TIXML_UTF_LEAD_2, fp );
+       }
+       Print( fp, 0 );
+       return (ferror(fp) == 0);
+}
+
+
+void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
+{
+       TiXmlNode::CopyTo( target );
+
+       target->error = error;
+       target->errorId = errorId;
+       target->errorDesc = errorDesc;
+       target->tabsize = tabsize;
+       target->errorLocation = errorLocation;
+       target->useMicrosoftBOM = useMicrosoftBOM;
+
+       TiXmlNode* node = 0;
+       for ( node = firstChild; node; node = node->NextSibling() )
+       {
+               target->LinkEndChild( node->Clone() );
+       }       
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+       TiXmlDocument* clone = new TiXmlDocument();
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+void TiXmlDocument::Print( FILE* cfile, int depth ) const
+{
+       assert( cfile );
+       for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+       {
+               node->Print( cfile, depth );
+               fprintf( cfile, "\n" );
+       }
+}
+
+
+bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
+{
+       if ( visitor->VisitEnter( *this ) )
+       {
+               for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+               {
+                       if ( !node->Accept( visitor ) )
+                               break;
+               }
+       }
+       return visitor->VisitExit( *this );
+}
+
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+       // We are using knowledge of the sentinel. The sentinel
+       // have a value or name.
+       if ( next->value.empty() && next->name.empty() )
+               return 0;
+       return next;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+       // We are using knowledge of the sentinel. The sentinel
+       // have a value or name.
+       if ( next->value.empty() && next->name.empty() )
+               return 0;
+       return next;
+}
+*/
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+       // We are using knowledge of the sentinel. The sentinel
+       // have a value or name.
+       if ( prev->value.empty() && prev->name.empty() )
+               return 0;
+       return prev;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+       // We are using knowledge of the sentinel. The sentinel
+       // have a value or name.
+       if ( prev->value.empty() && prev->name.empty() )
+               return 0;
+       return prev;
+}
+*/
+
+void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+       TIXML_STRING n, v;
+
+       EncodeString( name, &n );
+       EncodeString( value, &v );
+
+       if (value.find ('\"') == TIXML_STRING::npos) {
+               if ( cfile ) {
+               fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
+               }
+               if ( str ) {
+                       (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
+               }
+       }
+       else {
+               if ( cfile ) {
+               fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
+               }
+               if ( str ) {
+                       (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
+               }
+       }
+}
+
+
+int TiXmlAttribute::QueryIntValue( int* ival ) const
+{
+       if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
+               return TIXML_SUCCESS;
+       return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue( double* dval ) const
+{
+       if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
+               return TIXML_SUCCESS;
+       return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue( int _value )
+{
+       char buf [64];
+       #if defined(TIXML_SNPRINTF)             
+               TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+       #else
+               sprintf (buf, "%d", _value);
+       #endif
+       SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue( double _value )
+{
+       char buf [256];
+       #if defined(TIXML_SNPRINTF)             
+               TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value);
+       #else
+               sprintf (buf, "%lf", _value);
+       #endif
+       SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+       return atoi (value.c_str ());
+}
+
+double  TiXmlAttribute::DoubleValue() const
+{
+       return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT )
+{
+       copy.CopyTo( this );
+}
+
+
+void TiXmlComment::operator=( const TiXmlComment& base )
+{
+       Clear();
+       base.CopyTo( this );
+}
+
+
+void TiXmlComment::Print( FILE* cfile, int depth ) const
+{
+       assert( cfile );
+       for ( int i=0; i<depth; i++ )
+       {
+               fprintf( cfile,  "    " );
+       }
+       fprintf( cfile, "<!--%s-->", value.c_str() );
+}
+
+
+void TiXmlComment::CopyTo( TiXmlComment* target ) const
+{
+       TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
+{
+       return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+       TiXmlComment* clone = new TiXmlComment();
+
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+void TiXmlText::Print( FILE* cfile, int depth ) const
+{
+       assert( cfile );
+       if ( cdata )
+       {
+               int i;
+               fprintf( cfile, "\n" );
+               for ( i=0; i<depth; i++ ) {
+                       fprintf( cfile, "    " );
+               }
+               fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() );    // unformatted output
+       }
+       else
+       {
+               TIXML_STRING buffer;
+               EncodeString( value, &buffer );
+               fprintf( cfile, "%s", buffer.c_str() );
+       }
+}
+
+
+void TiXmlText::CopyTo( TiXmlText* target ) const
+{
+       TiXmlNode::CopyTo( target );
+       target->cdata = cdata;
+}
+
+
+bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
+{
+       return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{      
+       TiXmlText* clone = 0;
+       clone = new TiXmlText( "" );
+
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration( const char * _version,
+                                                                       const char * _encoding,
+                                                                       const char * _standalone )
+       : TiXmlNode( TiXmlNode::DECLARATION )
+{
+       version = _version;
+       encoding = _encoding;
+       standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration(    const std::string& _version,
+                                                                       const std::string& _encoding,
+                                                                       const std::string& _standalone )
+       : TiXmlNode( TiXmlNode::DECLARATION )
+{
+       version = _version;
+       encoding = _encoding;
+       standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
+       : TiXmlNode( TiXmlNode::DECLARATION )
+{
+       copy.CopyTo( this );    
+}
+
+
+void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
+{
+       Clear();
+       copy.CopyTo( this );
+}
+
+
+void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+       if ( cfile ) fprintf( cfile, "<?xml " );
+       if ( str )       (*str) += "<?xml ";
+
+       if ( !version.empty() ) {
+               if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ());
+               if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
+       }
+       if ( !encoding.empty() ) {
+               if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+               if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
+       }
+       if ( !standalone.empty() ) {
+               if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+               if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
+       }
+       if ( cfile ) fprintf( cfile, "?>" );
+       if ( str )       (*str) += "?>";
+}
+
+
+void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
+{
+       TiXmlNode::CopyTo( target );
+
+       target->version = version;
+       target->encoding = encoding;
+       target->standalone = standalone;
+}
+
+
+bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
+{
+       return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{      
+       TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+void TiXmlUnknown::Print( FILE* cfile, int depth ) const
+{
+       for ( int i=0; i<depth; i++ )
+               fprintf( cfile, "    " );
+       fprintf( cfile, "<%s>", value.c_str() );
+}
+
+
+void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
+{
+       TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
+{
+       return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+       TiXmlUnknown* clone = new TiXmlUnknown();
+
+       if ( !clone )
+               return 0;
+
+       CopyTo( clone );
+       return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+       sentinel.next = &sentinel;
+       sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+       assert( sentinel.next == &sentinel );
+       assert( sentinel.prev == &sentinel );
+}
+
+
+void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
+{
+    #ifdef TIXML_USE_STL
+       assert( !Find( TIXML_STRING( addMe->Name() ) ) );       // Shouldn't be multiply adding to the set.
+       #else
+       assert( !Find( addMe->Name() ) );       // Shouldn't be multiply adding to the set.
+       #endif
+
+       addMe->next = &sentinel;
+       addMe->prev = sentinel.prev;
+
+       sentinel.prev->next = addMe;
+       sentinel.prev      = addMe;
+}
+
+void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
+{
+       TiXmlAttribute* node;
+
+       for( node = sentinel.next; node != &sentinel; node = node->next )
+       {
+               if ( node == removeMe )
+               {
+                       node->prev->next = node->next;
+                       node->next->prev = node->prev;
+                       node->next = 0;
+                       node->prev = 0;
+                       return;
+               }
+       }
+       assert( 0 );            // we tried to remove a non-linked attribute.
+}
+
+
+#ifdef TIXML_USE_STL
+const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
+{
+       for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+       {
+               if ( node->name == name )
+                       return node;
+       }
+       return 0;
+}
+
+/*
+TiXmlAttribute*        TiXmlAttributeSet::Find( const std::string& name )
+{
+       for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+       {
+               if ( node->name == name )
+                       return node;
+       }
+       return 0;
+}
+*/
+#endif
+
+
+const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
+{
+       for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+       {
+               if ( strcmp( node->name.c_str(), name ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+
+/*
+TiXmlAttribute*        TiXmlAttributeSet::Find( const char* name )
+{
+       for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+       {
+               if ( strcmp( node->name.c_str(), name ) == 0 )
+                       return node;
+       }
+       return 0;
+}
+*/
+
+#ifdef TIXML_USE_STL   
+std::istream& operator>> (std::istream & in, TiXmlNode & base)
+{
+       TIXML_STRING tag;
+       tag.reserve( 8 * 1000 );
+       base.StreamIn( &in, &tag );
+
+       base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
+       return in;
+}
+#endif
+
+
+#ifdef TIXML_USE_STL   
+std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
+{
+       TiXmlPrinter printer;
+       printer.SetStreamPrinting();
+       base.Accept( &printer );
+       out << printer.Str();
+
+       return out;
+}
+
+
+std::string& operator<< (std::string& out, const TiXmlNode& base )
+{
+       TiXmlPrinter printer;
+       printer.SetStreamPrinting();
+       base.Accept( &printer );
+       out.append( printer.Str() );
+
+       return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+       if ( node )
+       {
+               TiXmlNode* child = node->FirstChild();
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
+{
+       if ( node )
+       {
+               TiXmlNode* child = node->FirstChild( value );
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+       if ( node )
+       {
+               TiXmlElement* child = node->FirstChildElement();
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
+{
+       if ( node )
+       {
+               TiXmlElement* child = node->FirstChildElement( value );
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( int count ) const
+{
+       if ( node )
+       {
+               int i;
+               TiXmlNode* child = node->FirstChild();
+               for (   i=0;
+                               child && i<count;
+                               child = child->NextSibling(), ++i )
+               {
+                       // nothing
+               }
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
+{
+       if ( node )
+       {
+               int i;
+               TiXmlNode* child = node->FirstChild( value );
+               for (   i=0;
+                               child && i<count;
+                               child = child->NextSibling( value ), ++i )
+               {
+                       // nothing
+               }
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( int count ) const
+{
+       if ( node )
+       {
+               int i;
+               TiXmlElement* child = node->FirstChildElement();
+               for (   i=0;
+                               child && i<count;
+                               child = child->NextSiblingElement(), ++i )
+               {
+                       // nothing
+               }
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
+{
+       if ( node )
+       {
+               int i;
+               TiXmlElement* child = node->FirstChildElement( value );
+               for (   i=0;
+                               child && i<count;
+                               child = child->NextSiblingElement( value ), ++i )
+               {
+                       // nothing
+               }
+               if ( child )
+                       return TiXmlHandle( child );
+       }
+       return TiXmlHandle( 0 );
+}
+
+
+bool TiXmlPrinter::VisitEnter( const TiXmlDocument& )
+{
+       return true;
+}
+
+bool TiXmlPrinter::VisitExit( const TiXmlDocument& )
+{
+       return true;
+}
+
+bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
+{
+       DoIndent();
+       buffer += "<";
+       buffer += element.Value();
+
+       for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )
+       {
+               buffer += " ";
+               attrib->Print( 0, 0, &buffer );
+       }
+
+       if ( !element.FirstChild() ) 
+       {
+               buffer += " />";
+               DoLineBreak();
+       }
+       else 
+       {
+               buffer += ">";
+               if (    element.FirstChild()->ToText()
+                         && element.LastChild() == element.FirstChild()
+                         && element.FirstChild()->ToText()->CDATA() == false )
+               {
+                       simpleTextPrint = true;
+                       // no DoLineBreak()!
+               }
+               else
+               {
+                       DoLineBreak();
+               }
+       }
+       ++depth;        
+       return true;
+}
+
+
+bool TiXmlPrinter::VisitExit( const TiXmlElement& element )
+{
+       --depth;
+       if ( !element.FirstChild() ) 
+       {
+               // nothing.
+       }
+       else 
+       {
+               if ( simpleTextPrint )
+               {
+                       simpleTextPrint = false;
+               }
+               else
+               {
+                       DoIndent();
+               }
+               buffer += "</";
+               buffer += element.Value();
+               buffer += ">";
+               DoLineBreak();
+       }
+       return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlText& text )
+{
+       if ( text.CDATA() )
+       {
+               DoIndent();
+               buffer += "<![CDATA[";
+               buffer += text.Value();
+               buffer += "]]>";
+               DoLineBreak();
+       }
+       else if ( simpleTextPrint )
+       {
+               TIXML_STRING str;
+               TiXmlBase::EncodeString( text.ValueTStr(), &str );
+               buffer += str;
+       }
+       else
+       {
+               DoIndent();
+               TIXML_STRING str;
+               TiXmlBase::EncodeString( text.ValueTStr(), &str );
+               buffer += str;
+               DoLineBreak();
+       }
+       return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )
+{
+       DoIndent();
+       declaration.Print( 0, 0, &buffer );
+       DoLineBreak();
+       return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlComment& comment )
+{
+       DoIndent();
+       buffer += "<!--";
+       buffer += comment.Value();
+       buffer += "-->";
+       DoLineBreak();
+       return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )
+{
+       DoIndent();
+       buffer += "<";
+       buffer += unknown.Value();
+       buffer += ">";
+       DoLineBreak();
+       return true;
+}
+
diff --git a/lib/tinyxml/tinyxml.h b/lib/tinyxml/tinyxml.h
new file mode 100644 (file)
index 0000000..c6f40cc
--- /dev/null
@@ -0,0 +1,1802 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4530 )
+#pragma warning( disable : 4786 )
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// Help out windows:
+#if defined( _DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_STL
+       #include <string>
+       #include <iostream>
+       #include <sstream>
+       #define TIXML_STRING            std::string
+#else
+       #include "tinystr.h"
+       #define TIXML_STRING            TiXmlString
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+#define TIXML_SAFE
+
+#ifdef TIXML_SAFE
+       #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+               // Microsoft visual studio, version 2005 and higher.
+               #define TIXML_SNPRINTF _snprintf_s
+               #define TIXML_SNSCANF  _snscanf_s
+               #define TIXML_SSCANF   sscanf_s
+       #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
+               // Microsoft visual studio, version 6 and higher.
+               //#pragma message( "Using _sn* functions." )
+               #define TIXML_SNPRINTF _snprintf
+               #define TIXML_SNSCANF  _snscanf
+               #define TIXML_SSCANF   sscanf
+       #elif defined(__GNUC__) && (__GNUC__ >= 3 )
+               // GCC version 3 and higher.s
+               //#warning( "Using sn* functions." )
+               #define TIXML_SNPRINTF snprintf
+               #define TIXML_SNSCANF  snscanf
+               #define TIXML_SSCANF   sscanf
+       #else
+               #define TIXML_SSCANF   sscanf
+       #endif
+#endif 
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 5;
+const int TIXML_PATCH_VERSION = 3;
+
+/*     Internal structure for tracking location of items 
+       in the XML file.
+*/
+struct TiXmlCursor
+{
+       TiXmlCursor()           { Clear(); }
+       void Clear()            { row = col = -1; }
+
+       int row;        // 0 based.
+       int col;        // 0 based.
+};
+
+
+/**
+       If you call the Accept() method, it requires being passed a TiXmlVisitor
+       class to handle callbacks. For nodes that contain other nodes (Document, Element)
+       you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
+       are simple called with Visit().
+
+       If you return 'true' from a Visit method, recursive parsing will continue. If you return
+       false, <b>no children of this node or its sibilings</b> will be Visited.
+
+       All flavors of Visit methods have a default implementation that returns 'true' (continue 
+       visiting). You need to only override methods that are interesting to you.
+
+       Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
+
+       You should never change the document from a callback.
+
+       @sa TiXmlNode::Accept()
+*/
+class TiXmlVisitor
+{
+public:
+       virtual ~TiXmlVisitor() {}
+
+       /// Visit a document.
+       virtual bool VisitEnter( const TiXmlDocument& /*doc*/ )                 { return true; }
+       /// Visit a document.
+       virtual bool VisitExit( const TiXmlDocument& /*doc*/ )                  { return true; }
+
+       /// Visit an element.
+       virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ )    { return true; }
+       /// Visit an element.
+       virtual bool VisitExit( const TiXmlElement& /*element*/ )               { return true; }
+
+       /// Visit a declaration
+       virtual bool Visit( const TiXmlDeclaration& /*declaration*/ )   { return true; }
+       /// Visit a text node
+       virtual bool Visit( const TiXmlText& /*text*/ )                                 { return true; }
+       /// Visit a comment node
+       virtual bool Visit( const TiXmlComment& /*comment*/ )                   { return true; }
+       /// Visit an unknow node
+       virtual bool Visit( const TiXmlUnknown& /*unknown*/ )                   { return true; }
+};
+
+// Only used by Attribute::Query functions
+enum 
+{ 
+       TIXML_SUCCESS,
+       TIXML_NO_ATTRIBUTE,
+       TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+       TIXML_ENCODING_UNKNOWN,
+       TIXML_ENCODING_UTF8,
+       TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+       It does little except to establish that TinyXml classes
+       can be printed and provide some utility functions.
+
+       In XML, the document and elements can contain
+       other elements and other types of nodes.
+
+       @verbatim
+       A Document can contain: Element (container or leaf)
+                                                       Comment (leaf)
+                                                       Unknown (leaf)
+                                                       Declaration( leaf )
+
+       An Element can contain: Element (container or leaf)
+                                                       Text    (leaf)
+                                                       Attributes (not on tree)
+                                                       Comment (leaf)
+                                                       Unknown (leaf)
+
+       A Decleration contains: Attributes (not on tree)
+       @endverbatim
+*/
+class TiXmlBase
+{
+       friend class TiXmlNode;
+       friend class TiXmlElement;
+       friend class TiXmlDocument;
+
+public:
+       TiXmlBase()     :       userData(0)             {}
+       virtual ~TiXmlBase()                    {}
+
+       /**     All TinyXml classes can print themselves to a filestream
+               or the string class (TiXmlString in non-STL mode, std::string
+               in STL mode.) Either or both cfile and str can be null.
+               
+               This is a formatted print, and will insert 
+               tabs and newlines.
+               
+               (For an unformatted stream, use the << operator.)
+       */
+       virtual void Print( FILE* cfile, int depth ) const = 0;
+
+       /**     The world does not agree on whether white space should be kept or
+               not. In order to make everyone happy, these global, static functions
+               are provided to set whether or not TinyXml will condense all white space
+               into a single space or not. The default is to condense. Note changing this
+               value is not thread safe.
+       */
+       static void SetCondenseWhiteSpace( bool condense )              { condenseWhiteSpace = condense; }
+
+       /// Return the current white space setting.
+       static bool IsWhiteSpaceCondensed()                                             { return condenseWhiteSpace; }
+
+       /** Return the position, in the original source file, of this node or attribute.
+               The row and column are 1-based. (That is the first row and first column is
+               1,1). If the returns values are 0 or less, then the parser does not have
+               a row and column value.
+
+               Generally, the row and column value will be set when the TiXmlDocument::Load(),
+               TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+               when the DOM was created from operator>>.
+
+               The values reflect the initial load. Once the DOM is modified programmatically
+               (by adding or changing nodes and attributes) the new values will NOT update to
+               reflect changes in the document.
+
+               There is a minor performance cost to computing the row and column. Computation
+               can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+               @sa TiXmlDocument::SetTabSize()
+       */
+       int Row() const                 { return location.row + 1; }
+       int Column() const              { return location.col + 1; }    ///< See Row()
+
+       void  SetUserData( void* user )                 { userData = user; }    ///< Set a pointer to arbitrary user data.
+       void* GetUserData()                                             { return userData; }    ///< Get a pointer to arbitrary user data.
+       const void* GetUserData() const                 { return userData; }    ///< Get a pointer to arbitrary user data.
+
+       // Table that returs, for a given lead byte, the total number of bytes
+       // in the UTF-8 sequence.
+       static const int utf8ByteTable[256];
+
+       virtual const char* Parse(      const char* p, 
+                                                               TiXmlParsingData* data, 
+                                                               TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
+
+       /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, 
+               or they will be transformed into entities!
+       */
+       static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
+
+       enum
+       {
+               TIXML_NO_ERROR = 0,
+               TIXML_ERROR,
+               TIXML_ERROR_OPENING_FILE,
+               TIXML_ERROR_OUT_OF_MEMORY,
+               TIXML_ERROR_PARSING_ELEMENT,
+               TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+               TIXML_ERROR_READING_ELEMENT_VALUE,
+               TIXML_ERROR_READING_ATTRIBUTES,
+               TIXML_ERROR_PARSING_EMPTY,
+               TIXML_ERROR_READING_END_TAG,
+               TIXML_ERROR_PARSING_UNKNOWN,
+               TIXML_ERROR_PARSING_COMMENT,
+               TIXML_ERROR_PARSING_DECLARATION,
+               TIXML_ERROR_DOCUMENT_EMPTY,
+               TIXML_ERROR_EMBEDDED_NULL,
+               TIXML_ERROR_PARSING_CDATA,
+               TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+               TIXML_ERROR_STRING_COUNT
+       };
+
+protected:
+
+       static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
+       inline static bool IsWhiteSpace( char c )               
+       { 
+               return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); 
+       }
+       inline static bool IsWhiteSpace( int c )
+       {
+               if ( c < 256 )
+                       return IsWhiteSpace( (char) c );
+               return false;   // Again, only truly correct for English/Latin...but usually works.
+       }
+
+       #ifdef TIXML_USE_STL
+       static bool     StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
+       static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
+       #endif
+
+       /*      Reads an XML name into the string provided. Returns
+               a pointer just past the last character of the name,
+               or 0 if the function has an error.
+       */
+       static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
+
+       /*      Reads text. Returns a pointer past the given end tag.
+               Wickedly complex options, but it keeps the (sensitive) code in one place.
+       */
+       static const char* ReadText(    const char* in,                         // where to start
+                                                                       TIXML_STRING* text,                     // the string read
+                                                                       bool ignoreWhiteSpace,          // whether to keep the white space
+                                                                       const char* endTag,                     // what ends this text
+                                                                       bool ignoreCase,                        // whether to ignore case in the end tag
+                                                                       TiXmlEncoding encoding );       // the current encoding
+
+       // If an entity has been found, transform it into a character.
+       static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
+
+       // Get a character, while interpreting entities.
+       // The length can be from 0 to 4 bytes.
+       inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
+       {
+               assert( p );
+               if ( encoding == TIXML_ENCODING_UTF8 )
+               {
+                       *length = utf8ByteTable[ *((const unsigned char*)p) ];
+                       assert( *length >= 0 && *length < 5 );
+               }
+               else
+               {
+                       *length = 1;
+               }
+
+               if ( *length == 1 )
+               {
+                       if ( *p == '&' )
+                               return GetEntity( p, _value, length, encoding );
+                       *_value = *p;
+                       return p+1;
+               }
+               else if ( *length )
+               {
+                       //strncpy( _value, p, *length );        // lots of compilers don't like this function (unsafe),
+                                                                                               // and the null terminator isn't needed
+                       for( int i=0; p[i] && i<*length; ++i ) {
+                               _value[i] = p[i];
+                       }
+                       return p + (*length);
+               }
+               else
+               {
+                       // Not valid text.
+                       return 0;
+               }
+       }
+
+       // Return true if the next characters in the stream are any of the endTag sequences.
+       // Ignore case only works for english, and should only be relied on when comparing
+       // to English words: StringEqual( p, "version", true ) is fine.
+       static bool StringEqual(        const char* p,
+                                                               const char* endTag,
+                                                               bool ignoreCase,
+                                                               TiXmlEncoding encoding );
+
+       static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+       TiXmlCursor location;
+
+    /// Field containing a generic user pointer
+       void*                   userData;
+       
+       // None of these methods are reliable for any language except English.
+       // Good for approximation, not great for accuracy.
+       static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
+       static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
+       inline static int ToLower( int v, TiXmlEncoding encoding )
+       {
+               if ( encoding == TIXML_ENCODING_UTF8 )
+               {
+                       if ( v < 128 ) return tolower( v );
+                       return v;
+               }
+               else
+               {
+                       return tolower( v );
+               }
+       }
+       static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+private:
+       TiXmlBase( const TiXmlBase& );                          // not implemented.
+       void operator=( const TiXmlBase& base );        // not allowed.
+
+       struct Entity
+       {
+               const char*     str;
+               unsigned int    strLength;
+               char                chr;
+       };
+       enum
+       {
+               NUM_ENTITY = 5,
+               MAX_ENTITY_LENGTH = 6
+
+       };
+       static Entity entity[ NUM_ENTITY ];
+       static bool condenseWhiteSpace;
+};
+
+
+/** The parent class for everything in the Document Object Model.
+       (Except for attributes).
+       Nodes have siblings, a parent, and children. A node can be
+       in a document, or stand on its own. The type of a TiXmlNode
+       can be queried, and it can be cast to its more defined type.
+*/
+class TiXmlNode : public TiXmlBase
+{
+       friend class TiXmlDocument;
+       friend class TiXmlElement;
+
+public:
+       #ifdef TIXML_USE_STL    
+
+           /** An input stream operator, for every class. Tolerant of newlines and
+                   formatting, but doesn't expect them.
+           */
+           friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+           /** An output stream operator, for every class. Note that this outputs
+                   without any newlines or formatting, as opposed to Print(), which
+                   includes tabs and new lines.
+
+                   The operator<< and operator>> are not completely symmetric. Writing
+                   a node to a stream is very well defined. You'll get a nice stream
+                   of output, without any extra whitespace or newlines.
+                   
+                   But reading is not as well defined. (As it always is.) If you create
+                   a TiXmlElement (for example) and read that from an input stream,
+                   the text needs to define an element or junk will result. This is
+                   true of all input streams, but it's worth keeping in mind.
+
+                   A TiXmlDocument will read nodes until it reads a root element, and
+                       all the children of that root element.
+           */  
+           friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+               /// Appends the XML node or attribute to a std::string.
+               friend std::string& operator<< (std::string& out, const TiXmlNode& base );
+
+       #endif
+
+       /** The types of XML nodes supported by TinyXml. (All the
+                       unsupported types are picked up by UNKNOWN.)
+       */
+       enum NodeType
+       {
+               DOCUMENT,
+               ELEMENT,
+               COMMENT,
+               UNKNOWN,
+               TEXT,
+               DECLARATION,
+               TYPECOUNT
+       };
+
+       virtual ~TiXmlNode();
+
+       /** The meaning of 'value' changes for the specific type of
+               TiXmlNode.
+               @verbatim
+               Document:       filename of the xml file
+               Element:        name of the element
+               Comment:        the comment text
+               Unknown:        the tag contents
+               Text:           the text string
+               @endverbatim
+
+               The subclasses will wrap this function.
+       */
+       const char *Value() const { return value.c_str (); }
+
+    #ifdef TIXML_USE_STL
+       /** Return Value() as a std::string. If you only use STL,
+           this is more efficient than calling Value().
+               Only available in STL mode.
+       */
+       const std::string& ValueStr() const { return value; }
+       #endif
+
+       const TIXML_STRING& ValueTStr() const { return value; }
+
+       /** Changes the value of the node. Defined as:
+               @verbatim
+               Document:       filename of the xml file
+               Element:        name of the element
+               Comment:        the comment text
+               Unknown:        the tag contents
+               Text:           the text string
+               @endverbatim
+       */
+       void SetValue(const char * _value) { value = _value;}
+
+    #ifdef TIXML_USE_STL
+       /// STL std::string form.
+       void SetValue( const std::string& _value )      { value = _value; }
+       #endif
+
+       /// Delete all the children of this node. Does not affect 'this'.
+       void Clear();
+
+       /// One step up the DOM.
+       TiXmlNode* Parent()                                                     { return parent; }
+       const TiXmlNode* Parent() const                         { return parent; }
+
+       const TiXmlNode* FirstChild()   const           { return firstChild; }  ///< The first child of this node. Will be null if there are no children.
+       TiXmlNode* FirstChild()                                         { return firstChild; }
+       const TiXmlNode* FirstChild( const char * value ) const;                        ///< The first child of this node with the matching 'value'. Will be null if none found.
+       /// The first child of this node with the matching 'value'. Will be null if none found.
+       TiXmlNode* FirstChild( const char * _value ) {
+               // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
+               // call the method, cast the return back to non-const.
+               return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
+       }
+       const TiXmlNode* LastChild() const      { return lastChild; }           /// The last child of this node. Will be null if there are no children.
+       TiXmlNode* LastChild()  { return lastChild; }
+       
+       const TiXmlNode* LastChild( const char * value ) const;                 /// The last child of this node matching 'value'. Will be null if there are no children.
+       TiXmlNode* LastChild( const char * _value ) {
+               return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
+       }
+
+    #ifdef TIXML_USE_STL
+       const TiXmlNode* FirstChild( const std::string& _value ) const  {       return FirstChild (_value.c_str ());    }       ///< STL std::string form.
+       TiXmlNode* FirstChild( const std::string& _value )                              {       return FirstChild (_value.c_str ());    }       ///< STL std::string form.
+       const TiXmlNode* LastChild( const std::string& _value ) const   {       return LastChild (_value.c_str ());     }       ///< STL std::string form.
+       TiXmlNode* LastChild( const std::string& _value )                               {       return LastChild (_value.c_str ());     }       ///< STL std::string form.
+       #endif
+
+       /** An alternate way to walk the children of a node.
+               One way to iterate over nodes is:
+               @verbatim
+                       for( child = parent->FirstChild(); child; child = child->NextSibling() )
+               @endverbatim
+
+               IterateChildren does the same thing with the syntax:
+               @verbatim
+                       child = 0;
+                       while( child = parent->IterateChildren( child ) )
+               @endverbatim
+
+               IterateChildren takes the previous child as input and finds
+               the next one. If the previous child is null, it returns the
+               first. IterateChildren will return null when done.
+       */
+       const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
+       TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
+               return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
+       }
+
+       /// This flavor of IterateChildren searches for children with a particular 'value'
+       const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
+       TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
+               return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
+       }
+
+    #ifdef TIXML_USE_STL
+       const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const  {       return IterateChildren (_value.c_str (), previous);     }       ///< STL std::string form.
+       TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) {    return IterateChildren (_value.c_str (), previous);     }       ///< STL std::string form.
+       #endif
+
+       /** Add a new node related to this. Adds a child past the LastChild.
+               Returns a pointer to the new object or NULL if an error occured.
+       */
+       TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
+
+
+       /** Add a new node related to this. Adds a child past the LastChild.
+
+               NOTE: the node to be added is passed by pointer, and will be
+               henceforth owned (and deleted) by tinyXml. This method is efficient
+               and avoids an extra copy, but should be used with care as it
+               uses a different memory model than the other insert functions.
+
+               @sa InsertEndChild
+       */
+       TiXmlNode* LinkEndChild( TiXmlNode* addThis );
+
+       /** Add a new node related to this. Adds a child before the specified child.
+               Returns a pointer to the new object or NULL if an error occured.
+       */
+       TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
+
+       /** Add a new node related to this. Adds a child after the specified child.
+               Returns a pointer to the new object or NULL if an error occured.
+       */
+       TiXmlNode* InsertAfterChild(  TiXmlNode* afterThis, const TiXmlNode& addThis );
+
+       /** Replace a child of this node.
+               Returns a pointer to the new object or NULL if an error occured.
+       */
+       TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
+
+       /// Delete a child of this node.
+       bool RemoveChild( TiXmlNode* removeThis );
+
+       /// Navigate to a sibling node.
+       const TiXmlNode* PreviousSibling() const                        { return prev; }
+       TiXmlNode* PreviousSibling()                                            { return prev; }
+
+       /// Navigate to a sibling node.
+       const TiXmlNode* PreviousSibling( const char * ) const;
+       TiXmlNode* PreviousSibling( const char *_prev ) {
+               return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
+       }
+
+    #ifdef TIXML_USE_STL
+       const TiXmlNode* PreviousSibling( const std::string& _value ) const     {       return PreviousSibling (_value.c_str ());       }       ///< STL std::string form.
+       TiXmlNode* PreviousSibling( const std::string& _value )                         {       return PreviousSibling (_value.c_str ());       }       ///< STL std::string form.
+       const TiXmlNode* NextSibling( const std::string& _value) const          {       return NextSibling (_value.c_str ());   }       ///< STL std::string form.
+       TiXmlNode* NextSibling( const std::string& _value)                                      {       return NextSibling (_value.c_str ());   }       ///< STL std::string form.
+       #endif
+
+       /// Navigate to a sibling node.
+       const TiXmlNode* NextSibling() const                            { return next; }
+       TiXmlNode* NextSibling()                                                        { return next; }
+
+       /// Navigate to a sibling node with the given 'value'.
+       const TiXmlNode* NextSibling( const char * ) const;
+       TiXmlNode* NextSibling( const char* _next ) {
+               return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
+       }
+
+       /** Convenience function to get through elements.
+               Calls NextSibling and ToElement. Will skip all non-Element
+               nodes. Returns 0 if there is not another element.
+       */
+       const TiXmlElement* NextSiblingElement() const;
+       TiXmlElement* NextSiblingElement() {
+               return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
+       }
+
+       /** Convenience function to get through elements.
+               Calls NextSibling and ToElement. Will skip all non-Element
+               nodes. Returns 0 if there is not another element.
+       */
+       const TiXmlElement* NextSiblingElement( const char * ) const;
+       TiXmlElement* NextSiblingElement( const char *_next ) {
+               return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
+       }
+
+    #ifdef TIXML_USE_STL
+       const TiXmlElement* NextSiblingElement( const std::string& _value) const        {       return NextSiblingElement (_value.c_str ());    }       ///< STL std::string form.
+       TiXmlElement* NextSiblingElement( const std::string& _value)                            {       return NextSiblingElement (_value.c_str ());    }       ///< STL std::string form.
+       #endif
+
+       /// Convenience function to get through elements.
+       const TiXmlElement* FirstChildElement() const;
+       TiXmlElement* FirstChildElement() {
+               return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
+       }
+
+       /// Convenience function to get through elements.
+       const TiXmlElement* FirstChildElement( const char * _value ) const;
+       TiXmlElement* FirstChildElement( const char * _value ) {
+               return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
+       }
+
+    #ifdef TIXML_USE_STL
+       const TiXmlElement* FirstChildElement( const std::string& _value ) const        {       return FirstChildElement (_value.c_str ());     }       ///< STL std::string form.
+       TiXmlElement* FirstChildElement( const std::string& _value )                            {       return FirstChildElement (_value.c_str ());     }       ///< STL std::string form.
+       #endif
+
+       /** Query the type (as an enumerated value, above) of this node.
+               The possible types are: DOCUMENT, ELEMENT, COMMENT,
+                                                               UNKNOWN, TEXT, and DECLARATION.
+       */
+       int Type() const        { return type; }
+
+       /** Return a pointer to the Document this node lives in.
+               Returns null if not in a document.
+       */
+       const TiXmlDocument* GetDocument() const;
+       TiXmlDocument* GetDocument() {
+               return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
+       }
+
+       /// Returns true if this node has no children.
+       bool NoChildren() const                                         { return !firstChild; }
+
+       virtual const TiXmlDocument*    ToDocument()    const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual const TiXmlElement*     ToElement()     const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual const TiXmlComment*     ToComment()     const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual const TiXmlUnknown*     ToUnknown()     const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual const TiXmlText*        ToText()        const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+       virtual TiXmlDocument*          ToDocument()    { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual TiXmlElement*           ToElement()         { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual TiXmlComment*           ToComment()     { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual TiXmlUnknown*           ToUnknown()         { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual TiXmlText*                  ToText()        { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+       virtual TiXmlDeclaration*       ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+       /** Create an exact duplicate of this node and return it. The memory must be deleted
+               by the caller. 
+       */
+       virtual TiXmlNode* Clone() const = 0;
+
+       /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the 
+               XML tree will be conditionally visited and the host will be called back
+               via the TiXmlVisitor interface.
+
+               This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+               the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+               interface versus any other.)
+
+               The interface has been based on ideas from:
+
+               - http://www.saxproject.org/
+               - http://c2.com/cgi/wiki?HierarchicalVisitorPattern 
+
+               Which are both good references for "visiting".
+
+               An example of using Accept():
+               @verbatim
+               TiXmlPrinter printer;
+               tinyxmlDoc.Accept( &printer );
+               const char* xmlcstr = printer.CStr();
+               @endverbatim
+       */
+       virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
+
+protected:
+       TiXmlNode( NodeType _type );
+
+       // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+       // and the assignment operator.
+       void CopyTo( TiXmlNode* target ) const;
+
+       #ifdef TIXML_USE_STL
+           // The real work of the input operator.
+       virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
+       #endif
+
+       // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+       TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
+
+       TiXmlNode*              parent;
+       NodeType                type;
+
+       TiXmlNode*              firstChild;
+       TiXmlNode*              lastChild;
+
+       TIXML_STRING    value;
+
+       TiXmlNode*              prev;
+       TiXmlNode*              next;
+
+private:
+       TiXmlNode( const TiXmlNode& );                          // not implemented.
+       void operator=( const TiXmlNode& base );        // not allowed.
+};
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+       number of attributes, each with a unique name.
+
+       @note The attributes are not TiXmlNodes, since they are not
+                 part of the tinyXML document object model. There are other
+                 suggested ways to look at this problem.
+*/
+class TiXmlAttribute : public TiXmlBase
+{
+       friend class TiXmlAttributeSet;
+
+public:
+       /// Construct an empty attribute.
+       TiXmlAttribute() : TiXmlBase()
+       {
+               document = 0;
+               prev = next = 0;
+       }
+
+       #ifdef TIXML_USE_STL
+       /// std::string constructor.
+       TiXmlAttribute( const std::string& _name, const std::string& _value )
+       {
+               name = _name;
+               value = _value;
+               document = 0;
+               prev = next = 0;
+       }
+       #endif
+
+       /// Construct an attribute with a name and value.
+       TiXmlAttribute( const char * _name, const char * _value )
+       {
+               name = _name;
+               value = _value;
+               document = 0;
+               prev = next = 0;
+       }
+
+       const char*             Name()  const           { return name.c_str(); }                ///< Return the name of this attribute.
+       const char*             Value() const           { return value.c_str(); }               ///< Return the value of this attribute.
+       #ifdef TIXML_USE_STL
+       const std::string& ValueStr() const     { return value; }                               ///< Return the value of this attribute.
+       #endif
+       int                             IntValue() const;                                                                       ///< Return the value of this attribute, converted to an integer.
+       double                  DoubleValue() const;                                                            ///< Return the value of this attribute, converted to a double.
+
+       // Get the tinyxml string representation
+       const TIXML_STRING& NameTStr() const { return name; }
+
+       /** QueryIntValue examines the value string. It is an alternative to the
+               IntValue() method with richer error checking.
+               If the value is an integer, it is stored in 'value' and 
+               the call returns TIXML_SUCCESS. If it is not
+               an integer, it returns TIXML_WRONG_TYPE.
+
+               A specialized but useful call. Note that for success it returns 0,
+               which is the opposite of almost all other TinyXml calls.
+       */
+       int QueryIntValue( int* _value ) const;
+       /// QueryDoubleValue examines the value string. See QueryIntValue().
+       int QueryDoubleValue( double* _value ) const;
+
+       void SetName( const char* _name )       { name = _name; }                               ///< Set the name of this attribute.
+       void SetValue( const char* _value )     { value = _value; }                             ///< Set the value.
+
+       void SetIntValue( int _value );                                                                         ///< Set the value from an integer.
+       void SetDoubleValue( double _value );                                                           ///< Set the value from a double.
+
+    #ifdef TIXML_USE_STL
+       /// STL std::string form.
+       void SetName( const std::string& _name )        { name = _name; }       
+       /// STL std::string form.       
+       void SetValue( const std::string& _value )      { value = _value; }
+       #endif
+
+       /// Get the next sibling attribute in the DOM. Returns null at end.
+       const TiXmlAttribute* Next() const;
+       TiXmlAttribute* Next() {
+               return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); 
+       }
+
+       /// Get the previous sibling attribute in the DOM. Returns null at beginning.
+       const TiXmlAttribute* Previous() const;
+       TiXmlAttribute* Previous() {
+               return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); 
+       }
+
+       bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
+       bool operator<( const TiXmlAttribute& rhs )      const { return name < rhs.name; }
+       bool operator>( const TiXmlAttribute& rhs )  const { return name > rhs.name; }
+
+       /*      Attribute parsing starts: first letter of the name
+                                                returns: the next char after the value end quote
+       */
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       // Prints this Attribute to a FILE stream.
+       virtual void Print( FILE* cfile, int depth ) const {
+               Print( cfile, depth, 0 );
+       }
+       void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+
+       // [internal use]
+       // Set the document pointer so the attribute can report errors.
+       void SetDocument( TiXmlDocument* doc )  { document = doc; }
+
+private:
+       TiXmlAttribute( const TiXmlAttribute& );                                // not implemented.
+       void operator=( const TiXmlAttribute& base );   // not allowed.
+
+       TiXmlDocument*  document;       // A pointer back to a document, for error reporting.
+       TIXML_STRING name;
+       TIXML_STRING value;
+       TiXmlAttribute* prev;
+       TiXmlAttribute* next;
+};
+
+
+/*     A class used to manage a group of attributes.
+       It is only used internally, both by the ELEMENT and the DECLARATION.
+       
+       The set can be changed transparent to the Element and Declaration
+       classes that use it, but NOT transparent to the Attribute
+       which has to implement a next() and previous() method. Which makes
+       it a bit problematic and prevents the use of STL.
+
+       This version is implemented with circular lists because:
+               - I like circular lists
+               - it demonstrates some independence from the (typical) doubly linked list.
+*/
+class TiXmlAttributeSet
+{
+public:
+       TiXmlAttributeSet();
+       ~TiXmlAttributeSet();
+
+       void Add( TiXmlAttribute* attribute );
+       void Remove( TiXmlAttribute* attribute );
+
+       const TiXmlAttribute* First()   const   { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+       TiXmlAttribute* First()                                 { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+       const TiXmlAttribute* Last() const              { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+       TiXmlAttribute* Last()                                  { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+
+       const TiXmlAttribute*   Find( const char* _name ) const;
+       TiXmlAttribute* Find( const char* _name ) {
+               return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
+       }
+       #ifdef TIXML_USE_STL
+       const TiXmlAttribute*   Find( const std::string& _name ) const;
+       TiXmlAttribute* Find( const std::string& _name ) {
+               return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
+       }
+
+       #endif
+
+private:
+       //*ME:  Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+       //*ME:  this class must be also use a hidden/disabled copy-constructor !!!
+       TiXmlAttributeSet( const TiXmlAttributeSet& );  // not allowed
+       void operator=( const TiXmlAttributeSet& );     // not allowed (as TiXmlAttribute)
+
+       TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+       and can contain other elements, text, comments, and unknowns.
+       Elements also contain an arbitrary number of attributes.
+*/
+class TiXmlElement : public TiXmlNode
+{
+public:
+       /// Construct an element.
+       TiXmlElement (const char * in_value);
+
+       #ifdef TIXML_USE_STL
+       /// std::string constructor.
+       TiXmlElement( const std::string& _value );
+       #endif
+
+       TiXmlElement( const TiXmlElement& );
+
+       void operator=( const TiXmlElement& base );
+
+       virtual ~TiXmlElement();
+
+       /** Given an attribute name, Attribute() returns the value
+               for the attribute of that name, or null if none exists.
+       */
+       const char* Attribute( const char* name ) const;
+
+       /** Given an attribute name, Attribute() returns the value
+               for the attribute of that name, or null if none exists.
+               If the attribute exists and can be converted to an integer,
+               the integer value will be put in the return 'i', if 'i'
+               is non-null.
+       */
+       const char* Attribute( const char* name, int* i ) const;
+
+       /** Given an attribute name, Attribute() returns the value
+               for the attribute of that name, or null if none exists.
+               If the attribute exists and can be converted to an double,
+               the double value will be put in the return 'd', if 'd'
+               is non-null.
+       */
+       const char* Attribute( const char* name, double* d ) const;
+
+       /** QueryIntAttribute examines the attribute - it is an alternative to the
+               Attribute() method with richer error checking.
+               If the attribute is an integer, it is stored in 'value' and 
+               the call returns TIXML_SUCCESS. If it is not
+               an integer, it returns TIXML_WRONG_TYPE. If the attribute
+               does not exist, then TIXML_NO_ATTRIBUTE is returned.
+       */      
+       int QueryIntAttribute( const char* name, int* _value ) const;
+       /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+       int QueryDoubleAttribute( const char* name, double* _value ) const;
+       /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+       int QueryFloatAttribute( const char* name, float* _value ) const {
+               double d;
+               int result = QueryDoubleAttribute( name, &d );
+               if ( result == TIXML_SUCCESS ) {
+                       *_value = (float)d;
+               }
+               return result;
+       }
+
+    #ifdef TIXML_USE_STL
+       /** Template form of the attribute query which will try to read the
+               attribute into the specified type. Very easy, very powerful, but
+               be careful to make sure to call this with the correct type.
+               
+               NOTE: This method doesn't work correctly for 'string' types.
+
+               @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+       */
+       template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
+       {
+               const TiXmlAttribute* node = attributeSet.Find( name );
+               if ( !node )
+                       return TIXML_NO_ATTRIBUTE;
+
+               std::stringstream sstream( node->ValueStr() );
+               sstream >> *outValue;
+               if ( !sstream.fail() )
+                       return TIXML_SUCCESS;
+               return TIXML_WRONG_TYPE;
+       }
+       /*
+        This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string"
+        but template specialization is hard to get working cross-compiler. Leaving the bug for now.
+        
+       // The above will fail for std::string because the space character is used as a seperator.
+       // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string
+       template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const
+       {
+               const TiXmlAttribute* node = attributeSet.Find( name );
+               if ( !node )
+                       return TIXML_NO_ATTRIBUTE;
+               *outValue = node->ValueStr();
+               return TIXML_SUCCESS;
+       }
+       */
+       #endif
+
+       /** Sets an attribute of name to a given value. The attribute
+               will be created if it does not exist, or changed if it does.
+       */
+       void SetAttribute( const char* name, const char * _value );
+
+    #ifdef TIXML_USE_STL
+       const std::string* Attribute( const std::string& name ) const;
+       const std::string* Attribute( const std::string& name, int* i ) const;
+       const std::string* Attribute( const std::string& name, double* d ) const;
+       int QueryIntAttribute( const std::string& name, int* _value ) const;
+       int QueryDoubleAttribute( const std::string& name, double* _value ) const;
+
+       /// STL std::string form.
+       void SetAttribute( const std::string& name, const std::string& _value );
+       ///< STL std::string form.
+       void SetAttribute( const std::string& name, int _value );
+       #endif
+
+       /** Sets an attribute of name to a given value. The attribute
+               will be created if it does not exist, or changed if it does.
+       */
+       void SetAttribute( const char * name, int value );
+
+       /** Sets an attribute of name to a given value. The attribute
+               will be created if it does not exist, or changed if it does.
+       */
+       void SetDoubleAttribute( const char * name, double value );
+
+       /** Deletes an attribute with the given name.
+       */
+       void RemoveAttribute( const char * name );
+    #ifdef TIXML_USE_STL
+       void RemoveAttribute( const std::string& name ) {       RemoveAttribute (name.c_str ());        }       ///< STL std::string form.
+       #endif
+
+       const TiXmlAttribute* FirstAttribute() const    { return attributeSet.First(); }                ///< Access the first attribute in this element.
+       TiXmlAttribute* FirstAttribute()                                { return attributeSet.First(); }
+       const TiXmlAttribute* LastAttribute()   const   { return attributeSet.Last(); }         ///< Access the last attribute in this element.
+       TiXmlAttribute* LastAttribute()                                 { return attributeSet.Last(); }
+
+       /** Convenience function for easy access to the text inside an element. Although easy
+               and concise, GetText() is limited compared to getting the TiXmlText child
+               and accessing it directly.
+       
+               If the first child of 'this' is a TiXmlText, the GetText()
+               returns the character string of the Text node, else null is returned.
+
+               This is a convenient method for getting the text of simple contained text:
+               @verbatim
+               <foo>This is text</foo>
+               const char* str = fooElement->GetText();
+               @endverbatim
+
+               'str' will be a pointer to "This is text". 
+               
+               Note that this function can be misleading. If the element foo was created from
+               this XML:
+               @verbatim
+               <foo><b>This is text</b></foo> 
+               @endverbatim
+
+               then the value of str would be null. The first child node isn't a text node, it is
+               another element. From this XML:
+               @verbatim
+               <foo>This is <b>text</b></foo> 
+               @endverbatim
+               GetText() will return "This is ".
+
+               WARNING: GetText() accesses a child node - don't become confused with the 
+                                similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are 
+                                safe type casts on the referenced node.
+       */
+       const char* GetText() const;
+
+       /// Creates a new Element and returns it - the returned element is a copy.
+       virtual TiXmlNode* Clone() const;
+       // Print the Element to a FILE stream.
+       virtual void Print( FILE* cfile, int depth ) const;
+
+       /*      Attribtue parsing starts: next char past '<'
+                                                returns: next char past '>'
+       */
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       virtual const TiXmlElement*     ToElement()     const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlElement*           ToElement()               { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+
+       void CopyTo( TiXmlElement* target ) const;
+       void ClearThis();       // like clear, but initializes 'this' object as well
+
+       // Used to be public [internal use]
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+       /*      [internal use]
+               Reads the "value" of the element -- another element, or text.
+               This should terminate with the current end tag.
+       */
+       const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+private:
+
+       TiXmlAttributeSet attributeSet;
+};
+
+
+/**    An XML comment.
+*/
+class TiXmlComment : public TiXmlNode
+{
+public:
+       /// Constructs an empty comment.
+       TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
+       /// Construct a comment from text.
+       TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) {
+               SetValue( _value );
+       }
+       TiXmlComment( const TiXmlComment& );
+       void operator=( const TiXmlComment& base );
+
+       virtual ~TiXmlComment() {}
+
+       /// Returns a copy of this Comment.
+       virtual TiXmlNode* Clone() const;
+       // Write this Comment to a FILE stream.
+       virtual void Print( FILE* cfile, int depth ) const;
+
+       /*      Attribtue parsing starts: at the ! of the !--
+                                                returns: next char past '>'
+       */
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       virtual const TiXmlComment*  ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlComment*  ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+       void CopyTo( TiXmlComment* target ) const;
+
+       // used to be public
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+//     virtual void StreamOut( TIXML_OSTREAM * out ) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output 
+       and CDATA. It will default to the mode it was parsed from the XML file and
+       you generally want to leave it alone, but you can change the output mode with 
+       SetCDATA() and query it with CDATA().
+*/
+class TiXmlText : public TiXmlNode
+{
+       friend class TiXmlElement;
+public:
+       /** Constructor for text element. By default, it is treated as 
+               normal, encoded text. If you want it be output as a CDATA text
+               element, set the parameter _cdata to 'true'
+       */
+       TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT)
+       {
+               SetValue( initValue );
+               cdata = false;
+       }
+       virtual ~TiXmlText() {}
+
+       #ifdef TIXML_USE_STL
+       /// Constructor.
+       TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
+       {
+               SetValue( initValue );
+               cdata = false;
+       }
+       #endif
+
+       TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT )       { copy.CopyTo( this ); }
+       void operator=( const TiXmlText& base )                                                         { base.CopyTo( this ); }
+
+       // Write this text object to a FILE stream.
+       virtual void Print( FILE* cfile, int depth ) const;
+
+       /// Queries whether this represents text using a CDATA section.
+       bool CDATA() const                              { return cdata; }
+       /// Turns on or off a CDATA representation of text.
+       void SetCDATA( bool _cdata )    { cdata = _cdata; }
+
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlText*       ToText()       { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+       ///  [internal use] Creates a new Element and returns it.
+       virtual TiXmlNode* Clone() const;
+       void CopyTo( TiXmlText* target ) const;
+
+       bool Blank() const;     // returns true if all white space and new lines
+       // [internal use]
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+
+private:
+       bool cdata;                     // true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+       @verbatim
+               <?xml version="1.0" standalone="yes"?>
+       @endverbatim
+
+       TinyXml will happily read or write files without a declaration,
+       however. There are 3 possible attributes to the declaration:
+       version, encoding, and standalone.
+
+       Note: In this version of the code, the attributes are
+       handled as special cases, not generic attributes, simply
+       because there can only be at most 3 and they are always the same.
+*/
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+       /// Construct an empty declaration.
+       TiXmlDeclaration()   : TiXmlNode( TiXmlNode::DECLARATION ) {}
+
+#ifdef TIXML_USE_STL
+       /// Constructor.
+       TiXmlDeclaration(       const std::string& _version,
+                                               const std::string& _encoding,
+                                               const std::string& _standalone );
+#endif
+
+       /// Construct.
+       TiXmlDeclaration(       const char* _version,
+                                               const char* _encoding,
+                                               const char* _standalone );
+
+       TiXmlDeclaration( const TiXmlDeclaration& copy );
+       void operator=( const TiXmlDeclaration& copy );
+
+       virtual ~TiXmlDeclaration()     {}
+
+       /// Version. Will return an empty string if none was found.
+       const char *Version() const                     { return version.c_str (); }
+       /// Encoding. Will return an empty string if none was found.
+       const char *Encoding() const            { return encoding.c_str (); }
+       /// Is this a standalone document?
+       const char *Standalone() const          { return standalone.c_str (); }
+
+       /// Creates a copy of this Declaration and returns it.
+       virtual TiXmlNode* Clone() const;
+       // Print this declaration to a FILE stream.
+       virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+       virtual void Print( FILE* cfile, int depth ) const {
+               Print( cfile, depth, 0 );
+       }
+
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlDeclaration*       ToDeclaration()       { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+       void CopyTo( TiXmlDeclaration* target ) const;
+       // used to be public
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+
+private:
+
+       TIXML_STRING version;
+       TIXML_STRING encoding;
+       TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+       unknown. It is a tag of text, but should not be modified.
+       It will be written back to the XML, unchanged, when the file
+       is saved.
+
+       DTD tags get thrown into TiXmlUnknowns.
+*/
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+       TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN )        {}
+       virtual ~TiXmlUnknown() {}
+
+       TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN )              { copy.CopyTo( this ); }
+       void operator=( const TiXmlUnknown& copy )                                                                              { copy.CopyTo( this ); }
+
+       /// Creates a copy of this Unknown and returns it.
+       virtual TiXmlNode* Clone() const;
+       // Print this Unknown to a FILE stream.
+       virtual void Print( FILE* cfile, int depth ) const;
+
+       virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+       virtual const TiXmlUnknown*     ToUnknown()     const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlUnknown*           ToUnknown()         { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected:
+       void CopyTo( TiXmlUnknown* target ) const;
+
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+       XML pieces. It can be saved, loaded, and printed to the screen.
+       The 'value' of a document node is the xml file name.
+*/
+class TiXmlDocument : public TiXmlNode
+{
+public:
+       /// Create an empty document, that has no name.
+       TiXmlDocument();
+       /// Create a document with a name. The name of the document is also the filename of the xml.
+       TiXmlDocument( const char * documentName );
+
+       #ifdef TIXML_USE_STL
+       /// Constructor.
+       TiXmlDocument( const std::string& documentName );
+       #endif
+
+       TiXmlDocument( const TiXmlDocument& copy );
+       void operator=( const TiXmlDocument& copy );
+
+       virtual ~TiXmlDocument() {}
+
+       /** Load a file using the current document value.
+               Returns true if successful. Will delete any existing
+               document data before loading.
+       */
+       bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+       /// Save a file using the current document value. Returns true if successful.
+       bool SaveFile() const;
+       /// Load a file using the given filename. Returns true if successful.
+       bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+       /// Save a file using the given filename. Returns true if successful.
+       bool SaveFile( const char * filename ) const;
+       /** Load a file using the given FILE*. Returns true if successful. Note that this method
+               doesn't stream - the entire object pointed at by the FILE*
+               will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+               file location. Streaming may be added in the future.
+       */
+       bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+       /// Save a file using the given FILE*. Returns true if successful.
+       bool SaveFile( FILE* ) const;
+
+       #ifdef TIXML_USE_STL
+       bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING )                   ///< STL std::string version.
+       {
+//             StringToBuffer f( filename );
+//             return ( f.buffer && LoadFile( f.buffer, encoding ));
+               return LoadFile( filename.c_str(), encoding );
+       }
+       bool SaveFile( const std::string& filename ) const              ///< STL std::string version.
+       {
+//             StringToBuffer f( filename );
+//             return ( f.buffer && SaveFile( f.buffer ));
+               return SaveFile( filename.c_str() );
+       }
+       #endif
+
+       /** Parse the given null terminated block of xml data. Passing in an encoding to this
+               method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+               to use that encoding, regardless of what TinyXml might otherwise try to detect.
+       */
+       virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+
+       /** Get the root element -- the only top level element -- of the document.
+               In well formed XML, there should only be one. TinyXml is tolerant of
+               multiple elements at the document level.
+       */
+       const TiXmlElement* RootElement() const         { return FirstChildElement(); }
+       TiXmlElement* RootElement()                                     { return FirstChildElement(); }
+
+       /** If an error occurs, Error will be set to true. Also,
+               - The ErrorId() will contain the integer identifier of the error (not generally useful)
+               - The ErrorDesc() method will return the name of the error. (very useful)
+               - The ErrorRow() and ErrorCol() will return the location of the error (if known)
+       */      
+       bool Error() const                                              { return error; }
+
+       /// Contains a textual (english) description of the error if one occurs.
+       const char * ErrorDesc() const  { return errorDesc.c_str (); }
+
+       /** Generally, you probably want the error string ( ErrorDesc() ). But if you
+               prefer the ErrorId, this function will fetch it.
+       */
+       int ErrorId()   const                           { return errorId; }
+
+       /** Returns the location (if known) of the error. The first column is column 1, 
+               and the first row is row 1. A value of 0 means the row and column wasn't applicable
+               (memory errors, for example, have no row/column) or the parser lost the error. (An
+               error in the error reporting, in that case.)
+
+               @sa SetTabSize, Row, Column
+       */
+       int ErrorRow() const    { return errorLocation.row+1; }
+       int ErrorCol() const    { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
+
+       /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+               to report the correct values for row and column. It does not change the output
+               or input in any way.
+               
+               By calling this method, with a tab size
+               greater than 0, the row and column of each node and attribute is stored
+               when the file is loaded. Very useful for tracking the DOM back in to
+               the source file.
+
+               The tab size is required for calculating the location of nodes. If not
+               set, the default of 4 is used. The tabsize is set per document. Setting
+               the tabsize to 0 disables row/column tracking.
+
+               Note that row and column tracking is not supported when using operator>>.
+
+               The tab size needs to be enabled before the parse or load. Correct usage:
+               @verbatim
+               TiXmlDocument doc;
+               doc.SetTabSize( 8 );
+               doc.Load( "myfile.xml" );
+               @endverbatim
+
+               @sa Row, Column
+       */
+       void SetTabSize( int _tabsize )         { tabsize = _tabsize; }
+
+       int TabSize() const     { return tabsize; }
+
+       /** If you have handled the error, it can be reset with this call. The error
+               state is automatically cleared if you Parse a new XML block.
+       */
+       void ClearError()                                               {       error = false; 
+                                                                                               errorId = 0; 
+                                                                                               errorDesc = ""; 
+                                                                                               errorLocation.row = errorLocation.col = 0; 
+                                                                                               //errorLocation.last = 0; 
+                                                                                       }
+
+       /** Write the document to standard out using formatted printing ("pretty print"). */
+       void Print() const                                              { Print( stdout, 0 ); }
+
+       /* Write the document to a string using formatted printing ("pretty print"). This
+               will allocate a character array (new char[]) and return it as a pointer. The
+               calling code pust call delete[] on the return char* to avoid a memory leak.
+       */
+       //char* PrintToMemory() const; 
+
+       /// Print this Document to a FILE stream.
+       virtual void Print( FILE* cfile, int depth = 0 ) const;
+       // [internal use]
+       void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+       virtual const TiXmlDocument*    ToDocument()    const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+       virtual TiXmlDocument*          ToDocument()          { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+       /** Walk the XML tree visiting this node and all of its children. 
+       */
+       virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+       // [internal use]
+       virtual TiXmlNode* Clone() const;
+       #ifdef TIXML_USE_STL
+       virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+       #endif
+
+private:
+       void CopyTo( TiXmlDocument* target ) const;
+
+       bool error;
+       int  errorId;
+       TIXML_STRING errorDesc;
+       int tabsize;
+       TiXmlCursor errorLocation;
+       bool useMicrosoftBOM;           // the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+       A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+       an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+       DOM structure. It is a separate utility class.
+
+       Take an example:
+       @verbatim
+       <Document>
+               <Element attributeA = "valueA">
+                       <Child attributeB = "value1" />
+                       <Child attributeB = "value2" />
+               </Element>
+       <Document>
+       @endverbatim
+
+       Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very 
+       easy to write a *lot* of code that looks like:
+
+       @verbatim
+       TiXmlElement* root = document.FirstChildElement( "Document" );
+       if ( root )
+       {
+               TiXmlElement* element = root->FirstChildElement( "Element" );
+               if ( element )
+               {
+                       TiXmlElement* child = element->FirstChildElement( "Child" );
+                       if ( child )
+                       {
+                               TiXmlElement* child2 = child->NextSiblingElement( "Child" );
+                               if ( child2 )
+                               {
+                                       // Finally do something useful.
+       @endverbatim
+
+       And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+       of such code. A TiXmlHandle checks for null     pointers so it is perfectly safe 
+       and correct to use:
+
+       @verbatim
+       TiXmlHandle docHandle( &document );
+       TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
+       if ( child2 )
+       {
+               // do something useful
+       @endverbatim
+
+       Which is MUCH more concise and useful.
+
+       It is also safe to copy handles - internally they are nothing more than node pointers.
+       @verbatim
+       TiXmlHandle handleCopy = handle;
+       @endverbatim
+
+       What they should not be used for is iteration:
+
+       @verbatim
+       int i=0; 
+       while ( true )
+       {
+               TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
+               if ( !child )
+                       break;
+               // do something
+               ++i;
+       }
+       @endverbatim
+
+       It seems reasonable, but it is in fact two embedded while loops. The Child method is 
+       a linear walk to find the element, so this code would iterate much more than it needs 
+       to. Instead, prefer:
+
+       @verbatim
+       TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
+
+       for( child; child; child=child->NextSiblingElement() )
+       {
+               // do something
+       }
+       @endverbatim
+*/
+class TiXmlHandle
+{
+public:
+       /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+       TiXmlHandle( TiXmlNode* _node )                                 { this->node = _node; }
+       /// Copy constructor
+       TiXmlHandle( const TiXmlHandle& ref )                   { this->node = ref.node; }
+       TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }
+
+       /// Return a handle to the first child node.
+       TiXmlHandle FirstChild() const;
+       /// Return a handle to the first child node with the given name.
+       TiXmlHandle FirstChild( const char * value ) const;
+       /// Return a handle to the first child element.
+       TiXmlHandle FirstChildElement() const;
+       /// Return a handle to the first child element with the given name.
+       TiXmlHandle FirstChildElement( const char * value ) const;
+
+       /** Return a handle to the "index" child with the given name. 
+               The first child is 0, the second 1, etc.
+       */
+       TiXmlHandle Child( const char* value, int index ) const;
+       /** Return a handle to the "index" child. 
+               The first child is 0, the second 1, etc.
+       */
+       TiXmlHandle Child( int index ) const;
+       /** Return a handle to the "index" child element with the given name. 
+               The first child element is 0, the second 1, etc. Note that only TiXmlElements
+               are indexed: other types are not counted.
+       */
+       TiXmlHandle ChildElement( const char* value, int index ) const;
+       /** Return a handle to the "index" child element. 
+               The first child element is 0, the second 1, etc. Note that only TiXmlElements
+               are indexed: other types are not counted.
+       */
+       TiXmlHandle ChildElement( int index ) const;
+
+       #ifdef TIXML_USE_STL
+       TiXmlHandle FirstChild( const std::string& _value ) const                               { return FirstChild( _value.c_str() ); }
+       TiXmlHandle FirstChildElement( const std::string& _value ) const                { return FirstChildElement( _value.c_str() ); }
+
+       TiXmlHandle Child( const std::string& _value, int index ) const                 { return Child( _value.c_str(), index ); }
+       TiXmlHandle ChildElement( const std::string& _value, int index ) const  { return ChildElement( _value.c_str(), index ); }
+       #endif
+
+       /** Return the handle as a TiXmlNode. This may return null.
+       */
+       TiXmlNode* ToNode() const                       { return node; } 
+       /** Return the handle as a TiXmlElement. This may return null.
+       */
+       TiXmlElement* ToElement() const         { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
+       /**     Return the handle as a TiXmlText. This may return null.
+       */
+       TiXmlText* ToText() const                       { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
+       /** Return the handle as a TiXmlUnknown. This may return null.
+       */
+       TiXmlUnknown* ToUnknown() const         { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
+
+       /** @deprecated use ToNode. 
+               Return the handle as a TiXmlNode. This may return null.
+       */
+       TiXmlNode* Node() const                 { return ToNode(); } 
+       /** @deprecated use ToElement. 
+               Return the handle as a TiXmlElement. This may return null.
+       */
+       TiXmlElement* Element() const   { return ToElement(); }
+       /**     @deprecated use ToText()
+               Return the handle as a TiXmlText. This may return null.
+       */
+       TiXmlText* Text() const                 { return ToText(); }
+       /** @deprecated use ToUnknown()
+               Return the handle as a TiXmlUnknown. This may return null.
+       */
+       TiXmlUnknown* Unknown() const   { return ToUnknown(); }
+
+private:
+       TiXmlNode* node;
+};
+
+
+/** Print to memory functionality. The TiXmlPrinter is useful when you need to:
+
+       -# Print to memory (especially in non-STL mode)
+       -# Control formatting (line endings, etc.)
+
+       When constructed, the TiXmlPrinter is in its default "pretty printing" mode.
+       Before calling Accept() you can call methods to control the printing
+       of the XML document. After TiXmlNode::Accept() is called, the printed document can
+       be accessed via the CStr(), Str(), and Size() methods.
+
+       TiXmlPrinter uses the Visitor API.
+       @verbatim
+       TiXmlPrinter printer;
+       printer.SetIndent( "\t" );
+
+       doc.Accept( &printer );
+       fprintf( stdout, "%s", printer.CStr() );
+       @endverbatim
+*/
+class TiXmlPrinter : public TiXmlVisitor
+{
+public:
+       TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
+                                        buffer(), indent( "    " ), lineBreak( "\n" ) {}
+
+       virtual bool VisitEnter( const TiXmlDocument& doc );
+       virtual bool VisitExit( const TiXmlDocument& doc );
+
+       virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
+       virtual bool VisitExit( const TiXmlElement& element );
+
+       virtual bool Visit( const TiXmlDeclaration& declaration );
+       virtual bool Visit( const TiXmlText& text );
+       virtual bool Visit( const TiXmlComment& comment );
+       virtual bool Visit( const TiXmlUnknown& unknown );
+
+       /** Set the indent characters for printing. By default 4 spaces
+               but tab (\t) is also useful, or null/empty string for no indentation.
+       */
+       void SetIndent( const char* _indent )                   { indent = _indent ? _indent : "" ; }
+       /// Query the indention string.
+       const char* Indent()                                                    { return indent.c_str(); }
+       /** Set the line breaking string. By default set to newline (\n). 
+               Some operating systems prefer other characters, or can be
+               set to the null/empty string for no indenation.
+       */
+       void SetLineBreak( const char* _lineBreak )             { lineBreak = _lineBreak ? _lineBreak : ""; }
+       /// Query the current line breaking string.
+       const char* LineBreak()                                                 { return lineBreak.c_str(); }
+
+       /** Switch over to "stream printing" which is the most dense formatting without 
+               linebreaks. Common when the XML is needed for network transmission.
+       */
+       void SetStreamPrinting()                                                { indent = "";
+                                                                                                         lineBreak = "";
+                                                                                                       }       
+       /// Return the result.
+       const char* CStr()                                                              { return buffer.c_str(); }
+       /// Return the length of the result string.
+       size_t Size()                                                                   { return buffer.size(); }
+
+       #ifdef TIXML_USE_STL
+       /// Return the result.
+       const std::string& Str()                                                { return buffer; }
+       #endif
+
+private:
+       void DoIndent() {
+               for( int i=0; i<depth; ++i )
+                       buffer += indent;
+       }
+       void DoLineBreak() {
+               buffer += lineBreak;
+       }
+
+       int depth;
+       bool simpleTextPrint;
+       TIXML_STRING buffer;
+       TIXML_STRING indent;
+       TIXML_STRING lineBreak;
+};
+
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+#endif
+
diff --git a/lib/tinyxml/tinyxmlerror.cpp b/lib/tinyxml/tinyxmlerror.cpp
new file mode 100644 (file)
index 0000000..d24f63b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied 
+warranty. In no event will the authors be held liable for any 
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml.h"
+
+// The goal of the seperate error file is to make the first
+// step towards localization. tinyxml (currently) only supports
+// english error messages, but the could now be translated.
+//
+// It also cleans up the code a bit.
+//
+
+const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
+{
+       "No error",
+       "Error",
+       "Failed to open file",
+       "Memory allocation failed.",
+       "Error parsing Element.",
+       "Failed to read Element name",
+       "Error reading Element value.",
+       "Error reading Attributes.",
+       "Error: empty tag.",
+       "Error reading end tag.",
+       "Error parsing Unknown.",
+       "Error parsing Comment.",
+       "Error parsing Declaration.",
+       "Error document empty.",
+       "Error null (0) or unexpected EOF found in input stream.",
+       "Error parsing CDATA.",
+       "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
+};
diff --git a/lib/tinyxml/tinyxmlparser.cpp b/lib/tinyxml/tinyxmlparser.cpp
new file mode 100644 (file)
index 0000000..9c91b4f
--- /dev/null
@@ -0,0 +1,1638 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied 
+warranty. In no event will the authors be held liable for any 
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must 
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+distribution.
+*/
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include "tinyxml.h"
+
+//#define DEBUG_PARSER
+#if defined( DEBUG_PARSER )
+#      if defined( DEBUG ) && defined( _MSC_VER )
+#              include <windows.h>
+#              define TIXML_LOG OutputDebugString
+#      else
+#              define TIXML_LOG printf
+#      endif
+#endif
+
+// Note tha "PutString" hardcodes the same list. This
+// is less flexible than it appears. Changing the entries
+// or order will break putstring.      
+TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = 
+{
+       { "&amp;",  5, '&' },
+       { "&lt;",   4, '<' },
+       { "&gt;",   4, '>' },
+       { "&quot;", 6, '\"' },
+       { "&apos;", 6, '\'' }
+};
+
+// Bunch of unicode info at:
+//             http://www.unicode.org/faq/utf_bom.html
+// Including the basic of this table, which determines the #bytes in the
+// sequence from the lead byte. 1 placed for invalid sequences --
+// although the result will be junk, pass it through as much as possible.
+// Beware of the non-characters in UTF-8:      
+//                             ef bb bf (Microsoft "lead bytes")
+//                             ef bf be
+//                             ef bf bf 
+
+const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+const int TiXmlBase::utf8ByteTable[256] = 
+{
+       //      0       1       2       3       4       5       6       7       8       9       a       b       c       d       e       f
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x00
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x10
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x20
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x30
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x40
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x50
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x60
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x70 End of ASCII range
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x80 0x80 to 0xc1 invalid
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x90 
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0xa0 
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0xb0 
+               1,      1,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      // 0xc0 0xc2 to 0xdf 2 byte
+               2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      // 0xd0
+               3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      // 0xe0 0xe0 to 0xef 3 byte
+               4,      4,      4,      4,      4,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1       // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
+};
+
+
+void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+       const unsigned long BYTE_MASK = 0xBF;
+       const unsigned long BYTE_MARK = 0x80;
+       const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+       if (input < 0x80) 
+               *length = 1;
+       else if ( input < 0x800 )
+               *length = 2;
+       else if ( input < 0x10000 )
+               *length = 3;
+       else if ( input < 0x200000 )
+               *length = 4;
+       else
+               { *length = 0; return; }        // This code won't covert this correctly anyway.
+
+       output += *length;
+
+       // Scary scary fall throughs.
+       switch (*length) 
+       {
+               case 4:
+                       --output; 
+                       *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+                       input >>= 6;
+               case 3:
+                       --output; 
+                       *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+                       input >>= 6;
+               case 2:
+                       --output; 
+                       *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+                       input >>= 6;
+               case 1:
+                       --output; 
+                       *output = (char)(input | FIRST_BYTE_MARK[*length]);
+       }
+}
+
+
+/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+       // This will only work for low-ascii, everything else is assumed to be a valid
+       // letter. I'm not sure this is the best approach, but it is quite tricky trying
+       // to figure out alhabetical vs. not across encoding. So take a very 
+       // conservative approach.
+
+//     if ( encoding == TIXML_ENCODING_UTF8 )
+//     {
+               if ( anyByte < 127 )
+                       return isalpha( anyByte );
+               else
+                       return 1;       // What else to do? The unicode set is huge...get the english ones right.
+//     }
+//     else
+//     {
+//             return isalpha( anyByte );
+//     }
+}
+
+
+/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+       // This will only work for low-ascii, everything else is assumed to be a valid
+       // letter. I'm not sure this is the best approach, but it is quite tricky trying
+       // to figure out alhabetical vs. not across encoding. So take a very 
+       // conservative approach.
+
+//     if ( encoding == TIXML_ENCODING_UTF8 )
+//     {
+               if ( anyByte < 127 )
+                       return isalnum( anyByte );
+               else
+                       return 1;       // What else to do? The unicode set is huge...get the english ones right.
+//     }
+//     else
+//     {
+//             return isalnum( anyByte );
+//     }
+}
+
+
+class TiXmlParsingData
+{
+       friend class TiXmlDocument;
+  public:
+       void Stamp( const char* now, TiXmlEncoding encoding );
+
+       const TiXmlCursor& Cursor()     { return cursor; }
+
+  private:
+       // Only used by the document!
+       TiXmlParsingData( const char* start, int _tabsize, int row, int col )
+       {
+               assert( start );
+               stamp = start;
+               tabsize = _tabsize;
+               cursor.row = row;
+               cursor.col = col;
+       }
+
+       TiXmlCursor             cursor;
+       const char*             stamp;
+       int                             tabsize;
+};
+
+
+void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
+{
+       assert( now );
+
+       // Do nothing if the tabsize is 0.
+       if ( tabsize < 1 )
+       {
+               return;
+       }
+
+       // Get the current row, column.
+       int row = cursor.row;
+       int col = cursor.col;
+       const char* p = stamp;
+       assert( p );
+
+       while ( p < now )
+       {
+               // Treat p as unsigned, so we have a happy compiler.
+               const unsigned char* pU = (const unsigned char*)p;
+
+               // Code contributed by Fletcher Dunn: (modified by lee)
+               switch (*pU) {
+                       case 0:
+                               // We *should* never get here, but in case we do, don't
+                               // advance past the terminating null character, ever
+                               return;
+
+                       case '\r':
+                               // bump down to the next line
+                               ++row;
+                               col = 0;                                
+                               // Eat the character
+                               ++p;
+
+                               // Check for \r\n sequence, and treat this as a single character
+                               if (*p == '\n') {
+                                       ++p;
+                               }
+                               break;
+
+                       case '\n':
+                               // bump down to the next line
+                               ++row;
+                               col = 0;
+
+                               // Eat the character
+                               ++p;
+
+                               // Check for \n\r sequence, and treat this as a single
+                               // character.  (Yes, this bizarre thing does occur still
+                               // on some arcane platforms...)
+                               if (*p == '\r') {
+                                       ++p;
+                               }
+                               break;
+
+                       case '\t':
+                               // Eat the character
+                               ++p;
+
+                               // Skip to next tab stop
+                               col = (col / tabsize + 1) * tabsize;
+                               break;
+
+                       case TIXML_UTF_LEAD_0:
+                               if ( encoding == TIXML_ENCODING_UTF8 )
+                               {
+                                       if ( *(p+1) && *(p+2) )
+                                       {
+                                               // In these cases, don't advance the column. These are
+                                               // 0-width spaces.
+                                               if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )
+                                                       p += 3; 
+                                               else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )
+                                                       p += 3; 
+                                               else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )
+                                                       p += 3; 
+                                               else
+                                                       { p +=3; ++col; }       // A normal character.
+                                       }
+                               }
+                               else
+                               {
+                                       ++p;
+                                       ++col;
+                               }
+                               break;
+
+                       default:
+                               if ( encoding == TIXML_ENCODING_UTF8 )
+                               {
+                                       // Eat the 1 to 4 byte utf8 character.
+                                       int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)];
+                                       if ( step == 0 )
+                                               step = 1;               // Error case from bad encoding, but handle gracefully.
+                                       p += step;
+
+                                       // Just advance one column, of course.
+                                       ++col;
+                               }
+                               else
+                               {
+                                       ++p;
+                                       ++col;
+                               }
+                               break;
+               }
+       }
+       cursor.row = row;
+       cursor.col = col;
+       assert( cursor.row >= -1 );
+       assert( cursor.col >= -1 );
+       stamp = p;
+       assert( stamp );
+}
+
+
+const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
+{
+       if ( !p || !*p )
+       {
+               return 0;
+       }
+       if ( encoding == TIXML_ENCODING_UTF8 )
+       {
+               while ( *p )
+               {
+                       const unsigned char* pU = (const unsigned char*)p;
+                       
+                       // Skip the stupid Microsoft UTF-8 Byte order marks
+                       if (    *(pU+0)==TIXML_UTF_LEAD_0
+                                && *(pU+1)==TIXML_UTF_LEAD_1 
+                                && *(pU+2)==TIXML_UTF_LEAD_2 )
+                       {
+                               p += 3;
+                               continue;
+                       }
+                       else if(*(pU+0)==TIXML_UTF_LEAD_0
+                                && *(pU+1)==0xbfU
+                                && *(pU+2)==0xbeU )
+                       {
+                               p += 3;
+                               continue;
+                       }
+                       else if(*(pU+0)==TIXML_UTF_LEAD_0
+                                && *(pU+1)==0xbfU
+                                && *(pU+2)==0xbfU )
+                       {
+                               p += 3;
+                               continue;
+                       }
+
+                       if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )            // Still using old rules for white space.
+                               ++p;
+                       else
+                               break;
+               }
+       }
+       else
+       {
+               while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )
+                       ++p;
+       }
+
+       return p;
+}
+
+#ifdef TIXML_USE_STL
+/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag )
+{
+       for( ;; )
+       {
+               if ( !in->good() ) return false;
+
+               int c = in->peek();
+               // At this scope, we can't get to a document. So fail silently.
+               if ( !IsWhiteSpace( c ) || c <= 0 )
+                       return true;
+
+               *tag += (char) in->get();
+       }
+}
+
+/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag )
+{
+       //assert( character > 0 && character < 128 );   // else it won't work in utf-8
+       while ( in->good() )
+       {
+               int c = in->peek();
+               if ( c == character )
+                       return true;
+               if ( c <= 0 )           // Silent failure: can't get document at this scope
+                       return false;
+
+               in->get();
+               *tag += (char) c;
+       }
+       return false;
+}
+#endif
+
+// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The
+// "assign" optimization removes over 10% of the execution time.
+//
+const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
+{
+       // Oddly, not supported on some comilers,
+       //name->clear();
+       // So use this:
+       *name = "";
+       assert( p );
+
+       // Names start with letters or underscores.
+       // Of course, in unicode, tinyxml has no idea what a letter *is*. The
+       // algorithm is generous.
+       //
+       // After that, they can be letters, underscores, numbers,
+       // hyphens, or colons. (Colons are valid ony for namespaces,
+       // but tinyxml can't tell namespaces from names.)
+       if (    p && *p 
+                && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
+       {
+               const char* start = p;
+               while(          p && *p
+                               &&      (               IsAlphaNum( (unsigned char ) *p, encoding ) 
+                                                || *p == '_'
+                                                || *p == '-'
+                                                || *p == '.'
+                                                || *p == ':' ) )
+               {
+                       //(*name) += *p; // expensive
+                       ++p;
+               }
+               if ( p-start > 0 ) {
+                       name->assign( start, p-start );
+               }
+               return p;
+       }
+       return 0;
+}
+
+const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
+{
+       // Presume an entity, and pull it out.
+    TIXML_STRING ent;
+       int i;
+       *length = 0;
+
+       if ( *(p+1) && *(p+1) == '#' && *(p+2) )
+       {
+               unsigned long ucs = 0;
+               ptrdiff_t delta = 0;
+               unsigned mult = 1;
+
+               if ( *(p+2) == 'x' )
+               {
+                       // Hexadecimal.
+                       if ( !*(p+3) ) return 0;
+
+                       const char* q = p+3;
+                       q = strchr( q, ';' );
+
+                       if ( !q || !*q ) return 0;
+
+                       delta = q-p;
+                       --q;
+
+                       while ( *q != 'x' )
+                       {
+                               if ( *q >= '0' && *q <= '9' )
+                                       ucs += mult * (*q - '0');
+                               else if ( *q >= 'a' && *q <= 'f' )
+                                       ucs += mult * (*q - 'a' + 10);
+                               else if ( *q >= 'A' && *q <= 'F' )
+                                       ucs += mult * (*q - 'A' + 10 );
+                               else 
+                                       return 0;
+                               mult *= 16;
+                               --q;
+                       }
+               }
+               else
+               {
+                       // Decimal.
+                       if ( !*(p+2) ) return 0;
+
+                       const char* q = p+2;
+                       q = strchr( q, ';' );
+
+                       if ( !q || !*q ) return 0;
+
+                       delta = q-p;
+                       --q;
+
+                       while ( *q != '#' )
+                       {
+                               if ( *q >= '0' && *q <= '9' )
+                                       ucs += mult * (*q - '0');
+                               else 
+                                       return 0;
+                               mult *= 10;
+                               --q;
+                       }
+               }
+               if ( encoding == TIXML_ENCODING_UTF8 )
+               {
+                       // convert the UCS to UTF-8
+                       ConvertUTF32ToUTF8( ucs, value, length );
+               }
+               else
+               {
+                       *value = (char)ucs;
+                       *length = 1;
+               }
+               return p + delta + 1;
+       }
+
+       // Now try to match it.
+       for( i=0; i<NUM_ENTITY; ++i )
+       {
+               if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
+               {
+                       assert( strlen( entity[i].str ) == entity[i].strLength );
+                       *value = entity[i].chr;
+                       *length = 1;
+                       return ( p + entity[i].strLength );
+               }
+       }
+
+       // So it wasn't an entity, its unrecognized, or something like that.
+       *value = *p;    // Don't put back the last one, since we return it!
+       //*length = 1;  // Leave unrecognized entities - this doesn't really work.
+                                       // Just writes strange XML.
+       return p+1;
+}
+
+
+bool TiXmlBase::StringEqual( const char* p,
+                                                        const char* tag,
+                                                        bool ignoreCase,
+                                                        TiXmlEncoding encoding )
+{
+       assert( p );
+       assert( tag );
+       if ( !p || !*p )
+       {
+               assert( 0 );
+               return false;
+       }
+
+       const char* q = p;
+
+       if ( ignoreCase )
+       {
+               while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
+               {
+                       ++q;
+                       ++tag;
+               }
+
+               if ( *tag == 0 )
+                       return true;
+       }
+       else
+       {
+               while ( *q && *tag && *q == *tag )
+               {
+                       ++q;
+                       ++tag;
+               }
+
+               if ( *tag == 0 )                // Have we found the end of the tag, and everything equal?
+                       return true;
+       }
+       return false;
+}
+
+const char* TiXmlBase::ReadText(       const char* p, 
+                                                                       TIXML_STRING * text, 
+                                                                       bool trimWhiteSpace, 
+                                                                       const char* endTag, 
+                                                                       bool caseInsensitive,
+                                                                       TiXmlEncoding encoding )
+{
+    *text = "";
+       if (    !trimWhiteSpace                 // certain tags always keep whitespace
+                || !condenseWhiteSpace )       // if true, whitespace is always kept
+       {
+               // Keep all the white space.
+               while (    p && *p
+                               && !StringEqual( p, endTag, caseInsensitive, encoding )
+                         )
+               {
+                       int len;
+                       char cArr[4] = { 0, 0, 0, 0 };
+                       p = GetChar( p, cArr, &len, encoding );
+                       text->append( cArr, len );
+               }
+       }
+       else
+       {
+               bool whitespace = false;
+
+               // Remove leading white space:
+               p = SkipWhiteSpace( p, encoding );
+               while (    p && *p
+                               && !StringEqual( p, endTag, caseInsensitive, encoding ) )
+               {
+                       if ( *p == '\r' || *p == '\n' )
+                       {
+                               whitespace = true;
+                               ++p;
+                       }
+                       else if ( IsWhiteSpace( *p ) )
+                       {
+                               whitespace = true;
+                               ++p;
+                       }
+                       else
+                       {
+                               // If we've found whitespace, add it before the
+                               // new character. Any whitespace just becomes a space.
+                               if ( whitespace )
+                               {
+                                       (*text) += ' ';
+                                       whitespace = false;
+                               }
+                               int len;
+                               char cArr[4] = { 0, 0, 0, 0 };
+                               p = GetChar( p, cArr, &len, encoding );
+                               if ( len == 1 )
+                                       (*text) += cArr[0];     // more efficient
+                               else
+                                       text->append( cArr, len );
+                       }
+               }
+       }
+       if ( p ) 
+               p += strlen( endTag );
+       return p;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+       // The basic issue with a document is that we don't know what we're
+       // streaming. Read something presumed to be a tag (and hope), then
+       // identify it, and call the appropriate stream method on the tag.
+       //
+       // This "pre-streaming" will never read the closing ">" so the
+       // sub-tag can orient itself.
+
+       if ( !StreamTo( in, '<', tag ) ) 
+       {
+               SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return;
+       }
+
+       while ( in->good() )
+       {
+               int tagIndex = (int) tag->length();
+               while ( in->good() && in->peek() != '>' )
+               {
+                       int c = in->get();
+                       if ( c <= 0 )
+                       {
+                               SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                               break;
+                       }
+                       (*tag) += (char) c;
+               }
+
+               if ( in->good() )
+               {
+                       // We now have something we presume to be a node of 
+                       // some sort. Identify it, and call the node to
+                       // continue streaming.
+                       TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
+
+                       if ( node )
+                       {
+                               node->StreamIn( in, tag );
+                               bool isElement = node->ToElement() != 0;
+                               delete node;
+                               node = 0;
+
+                               // If this is the root element, we're done. Parsing will be
+                               // done by the >> operator.
+                               if ( isElement )
+                               {
+                                       return;
+                               }
+                       }
+                       else
+                       {
+                               SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+                               return;
+                       }
+               }
+       }
+       // We should have returned sooner.
+       SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+}
+
+#endif
+
+const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
+{
+       ClearError();
+
+       // Parse away, at the document level. Since a document
+       // contains nothing but other tags, most of what happens
+       // here is skipping white space.
+       if ( !p || !*p )
+       {
+               SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+
+       // Note that, for a document, this needs to come
+       // before the while space skip, so that parsing
+       // starts from the pointer we are given.
+       location.Clear();
+       if ( prevData )
+       {
+               location.row = prevData->cursor.row;
+               location.col = prevData->cursor.col;
+       }
+       else
+       {
+               location.row = 0;
+               location.col = 0;
+       }
+       TiXmlParsingData data( p, TabSize(), location.row, location.col );
+       location = data.Cursor();
+
+       if ( encoding == TIXML_ENCODING_UNKNOWN )
+       {
+               // Check for the Microsoft UTF-8 lead bytes.
+               const unsigned char* pU = (const unsigned char*)p;
+               if (    *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
+                        && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
+                        && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
+               {
+                       encoding = TIXML_ENCODING_UTF8;
+                       useMicrosoftBOM = true;
+               }
+       }
+
+    p = SkipWhiteSpace( p, encoding );
+       if ( !p )
+       {
+               SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+               return 0;
+       }
+
+       while ( p && *p )
+       {
+               TiXmlNode* node = Identify( p, encoding );
+               if ( node )
+               {
+                       p = node->Parse( p, &data, encoding );
+                       LinkEndChild( node );
+               }
+               else
+               {
+                       break;
+               }
+
+               // Did we get encoding info?
+               if (    encoding == TIXML_ENCODING_UNKNOWN
+                        && node->ToDeclaration() )
+               {
+                       TiXmlDeclaration* dec = node->ToDeclaration();
+                       const char* enc = dec->Encoding();
+                       assert( enc );
+
+                       if ( *enc == 0 )
+                               encoding = TIXML_ENCODING_UTF8;
+                       else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
+                               encoding = TIXML_ENCODING_UTF8;
+                       else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
+                               encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
+                       else 
+                               encoding = TIXML_ENCODING_LEGACY;
+               }
+
+               p = SkipWhiteSpace( p, encoding );
+       }
+
+       // Was this empty?
+       if ( !firstChild ) {
+               SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
+               return 0;
+       }
+
+       // All is well.
+       return p;
+}
+
+void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
+{      
+       // The first error in a chain is more accurate - don't set again!
+       if ( error )
+               return;
+
+       assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
+       error   = true;
+       errorId = err;
+       errorDesc = errorString[ errorId ];
+
+       errorLocation.Clear();
+       if ( pError && data )
+       {
+               data->Stamp( pError, encoding );
+               errorLocation = data->Cursor();
+       }
+}
+
+
+TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
+{
+       TiXmlNode* returnNode = 0;
+
+       p = SkipWhiteSpace( p, encoding );
+       if( !p || !*p || *p != '<' )
+       {
+               return 0;
+       }
+
+       TiXmlDocument* doc = GetDocument();
+       p = SkipWhiteSpace( p, encoding );
+
+       if ( !p || !*p )
+       {
+               return 0;
+       }
+
+       // What is this thing? 
+       // - Elements start with a letter or underscore, but xml is reserved.
+       // - Comments: <!--
+       // - Decleration: <?xml
+       // - Everthing else is unknown to tinyxml.
+       //
+
+       const char* xmlHeader = { "<?xml" };
+       const char* commentHeader = { "<!--" };
+       const char* dtdHeader = { "<!" };
+       const char* cdataHeader = { "<![CDATA[" };
+
+       if ( StringEqual( p, xmlHeader, true, encoding ) )
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing Declaration\n" );
+               #endif
+               returnNode = new TiXmlDeclaration();
+       }
+       else if ( StringEqual( p, commentHeader, false, encoding ) )
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing Comment\n" );
+               #endif
+               returnNode = new TiXmlComment();
+       }
+       else if ( StringEqual( p, cdataHeader, false, encoding ) )
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing CDATA\n" );
+               #endif
+               TiXmlText* text = new TiXmlText( "" );
+               text->SetCDATA( true );
+               returnNode = text;
+       }
+       else if ( StringEqual( p, dtdHeader, false, encoding ) )
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing Unknown(1)\n" );
+               #endif
+               returnNode = new TiXmlUnknown();
+       }
+       else if (    IsAlpha( *(p+1), encoding )
+                         || *(p+1) == '_' )
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing Element\n" );
+               #endif
+               returnNode = new TiXmlElement( "" );
+       }
+       else
+       {
+               #ifdef DEBUG_PARSER
+                       TIXML_LOG( "XML parsing Unknown(2)\n" );
+               #endif
+               returnNode = new TiXmlUnknown();
+       }
+
+       if ( returnNode )
+       {
+               // Set the parent, so it can report errors
+               returnNode->parent = this;
+       }
+       else
+       {
+               if ( doc )
+                       doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
+       }
+       return returnNode;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag)
+{
+       // We're called with some amount of pre-parsing. That is, some of "this"
+       // element is in "tag". Go ahead and stream to the closing ">"
+       while( in->good() )
+       {
+               int c = in->get();
+               if ( c <= 0 )
+               {
+                       TiXmlDocument* document = GetDocument();
+                       if ( document )
+                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                       return;
+               }
+               (*tag) += (char) c ;
+               
+               if ( c == '>' )
+                       break;
+       }
+
+       if ( tag->length() < 3 ) return;
+
+       // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
+       // If not, identify and stream.
+
+       if (    tag->at( tag->length() - 1 ) == '>' 
+                && tag->at( tag->length() - 2 ) == '/' )
+       {
+               // All good!
+               return;
+       }
+       else if ( tag->at( tag->length() - 1 ) == '>' )
+       {
+               // There is more. Could be:
+               //              text
+               //              cdata text (which looks like another node)
+               //              closing tag
+               //              another node.
+               for ( ;; )
+               {
+                       StreamWhiteSpace( in, tag );
+
+                       // Do we have text?
+                       if ( in->good() && in->peek() != '<' ) 
+                       {
+                               // Yep, text.
+                               TiXmlText text( "" );
+                               text.StreamIn( in, tag );
+
+                               // What follows text is a closing tag or another node.
+                               // Go around again and figure it out.
+                               continue;
+                       }
+
+                       // We now have either a closing tag...or another node.
+                       // We should be at a "<", regardless.
+                       if ( !in->good() ) return;
+                       assert( in->peek() == '<' );
+                       int tagIndex = (int) tag->length();
+
+                       bool closingTag = false;
+                       bool firstCharFound = false;
+
+                       for( ;; )
+                       {
+                               if ( !in->good() )
+                                       return;
+
+                               int c = in->peek();
+                               if ( c <= 0 )
+                               {
+                                       TiXmlDocument* document = GetDocument();
+                                       if ( document )
+                                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                                       return;
+                               }
+                               
+                               if ( c == '>' )
+                                       break;
+
+                               *tag += (char) c;
+                               in->get();
+
+                               // Early out if we find the CDATA id.
+                               if ( c == '[' && tag->size() >= 9 )
+                               {
+                                       size_t len = tag->size();
+                                       const char* start = tag->c_str() + len - 9;
+                                       if ( strcmp( start, "<![CDATA[" ) == 0 ) {
+                                               assert( !closingTag );
+                                               break;
+                                       }
+                               }
+
+                               if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
+                               {
+                                       firstCharFound = true;
+                                       if ( c == '/' )
+                                               closingTag = true;
+                               }
+                       }
+                       // If it was a closing tag, then read in the closing '>' to clean up the input stream.
+                       // If it was not, the streaming will be done by the tag.
+                       if ( closingTag )
+                       {
+                               if ( !in->good() )
+                                       return;
+
+                               int c = in->get();
+                               if ( c <= 0 )
+                               {
+                                       TiXmlDocument* document = GetDocument();
+                                       if ( document )
+                                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                                       return;
+                               }
+                               assert( c == '>' );
+                               *tag += (char) c;
+
+                               // We are done, once we've found our closing tag.
+                               return;
+                       }
+                       else
+                       {
+                               // If not a closing tag, id it, and stream.
+                               const char* tagloc = tag->c_str() + tagIndex;
+                               TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
+                               if ( !node )
+                                       return;
+                               node->StreamIn( in, tag );
+                               delete node;
+                               node = 0;
+
+                               // No return: go around from the beginning: text, closing tag, or node.
+                       }
+               }
+       }
+}
+#endif
+
+const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       p = SkipWhiteSpace( p, encoding );
+       TiXmlDocument* document = GetDocument();
+
+       if ( !p || !*p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
+               return 0;
+       }
+
+       if ( data )
+       {
+               data->Stamp( p, encoding );
+               location = data->Cursor();
+       }
+
+       if ( *p != '<' )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
+               return 0;
+       }
+
+       p = SkipWhiteSpace( p+1, encoding );
+
+       // Read the name.
+       const char* pErr = p;
+
+    p = ReadName( p, &value, encoding );
+       if ( !p || !*p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
+               return 0;
+       }
+
+    TIXML_STRING endTag ("</");
+       endTag += value;
+       endTag += ">";
+
+       // Check for and read attributes. Also look for an empty
+       // tag or an end tag.
+       while ( p && *p )
+       {
+               pErr = p;
+               p = SkipWhiteSpace( p, encoding );
+               if ( !p || !*p )
+               {
+                       if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+                       return 0;
+               }
+               if ( *p == '/' )
+               {
+                       ++p;
+                       // Empty tag.
+                       if ( *p  != '>' )
+                       {
+                               if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );             
+                               return 0;
+                       }
+                       return (p+1);
+               }
+               else if ( *p == '>' )
+               {
+                       // Done with attributes (if there were any.)
+                       // Read the value -- which can include other
+                       // elements -- read the end tag, and return.
+                       ++p;
+                       p = ReadValue( p, data, encoding );             // Note this is an Element method, and will set the error if one happens.
+                       if ( !p || !*p ) {
+                               // We were looking for the end tag, but found nothing.
+                               // Fix for [ 1663758 ] Failure to report error on bad XML
+                               if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+                               return 0;
+                       }
+
+                       // We should find the end tag now
+                       if ( StringEqual( p, endTag.c_str(), false, encoding ) )
+                       {
+                               p += endTag.length();
+                               return p;
+                       }
+                       else
+                       {
+                               if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+                               return 0;
+                       }
+               }
+               else
+               {
+                       // Try to read an attribute:
+                       TiXmlAttribute* attrib = new TiXmlAttribute();
+                       if ( !attrib )
+                       {
+                               if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );
+                               return 0;
+                       }
+
+                       attrib->SetDocument( document );
+                       pErr = p;
+                       p = attrib->Parse( p, data, encoding );
+
+                       if ( !p || !*p )
+                       {
+                               if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
+                               delete attrib;
+                               return 0;
+                       }
+
+                       // Handle the strange case of double attributes:
+                       #ifdef TIXML_USE_STL
+                       TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
+                       #else
+                       TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
+                       #endif
+                       if ( node )
+                       {
+                               node->SetValue( attrib->Value() );
+                               delete attrib;
+                               return 0;
+                       }
+
+                       attributeSet.Add( attrib );
+               }
+       }
+       return p;
+}
+
+
+const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       TiXmlDocument* document = GetDocument();
+
+       // Read in text and elements in any order.
+       const char* pWithWhiteSpace = p;
+       p = SkipWhiteSpace( p, encoding );
+
+       while ( p && *p )
+       {
+               if ( *p != '<' )
+               {
+                       // Take what we have, make a text element.
+                       TiXmlText* textNode = new TiXmlText( "" );
+
+                       if ( !textNode )
+                       {
+                               if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding );
+                                   return 0;
+                       }
+
+                       if ( TiXmlBase::IsWhiteSpaceCondensed() )
+                       {
+                               p = textNode->Parse( p, data, encoding );
+                       }
+                       else
+                       {
+                               // Special case: we want to keep the white space
+                               // so that leading spaces aren't removed.
+                               p = textNode->Parse( pWithWhiteSpace, data, encoding );
+                       }
+
+                       if ( !textNode->Blank() )
+                               LinkEndChild( textNode );
+                       else
+                               delete textNode;
+               } 
+               else 
+               {
+                       // We hit a '<'
+                       // Have we hit a new element or an end tag? This could also be
+                       // a TiXmlText in the "CDATA" style.
+                       if ( StringEqual( p, "</", false, encoding ) )
+                       {
+                               return p;
+                       }
+                       else
+                       {
+                               TiXmlNode* node = Identify( p, encoding );
+                               if ( node )
+                               {
+                                       p = node->Parse( p, data, encoding );
+                                       LinkEndChild( node );
+                               }                               
+                               else
+                               {
+                                       return 0;
+                               }
+                       }
+               }
+               pWithWhiteSpace = p;
+               p = SkipWhiteSpace( p, encoding );
+       }
+
+       if ( !p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
+       }       
+       return p;
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+       while ( in->good() )
+       {
+               int c = in->get();      
+               if ( c <= 0 )
+               {
+                       TiXmlDocument* document = GetDocument();
+                       if ( document )
+                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                       return;
+               }
+               (*tag) += (char) c;
+
+               if ( c == '>' )
+               {
+                       // All is well.
+                       return;         
+               }
+       }
+}
+#endif
+
+
+const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       TiXmlDocument* document = GetDocument();
+       p = SkipWhiteSpace( p, encoding );
+
+       if ( data )
+       {
+               data->Stamp( p, encoding );
+               location = data->Cursor();
+       }
+       if ( !p || !*p || *p != '<' )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
+               return 0;
+       }
+       ++p;
+    value = "";
+
+       while ( p && *p && *p != '>' )
+       {
+               value += *p;
+               ++p;
+       }
+
+       if ( !p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
+       }
+       if ( *p == '>' )
+               return p+1;
+       return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+       while ( in->good() )
+       {
+               int c = in->get();      
+               if ( c <= 0 )
+               {
+                       TiXmlDocument* document = GetDocument();
+                       if ( document )
+                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                       return;
+               }
+
+               (*tag) += (char) c;
+
+               if ( c == '>' 
+                        && tag->at( tag->length() - 2 ) == '-'
+                        && tag->at( tag->length() - 3 ) == '-' )
+               {
+                       // All is well.
+                       return;         
+               }
+       }
+}
+#endif
+
+
+const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       TiXmlDocument* document = GetDocument();
+       value = "";
+
+       p = SkipWhiteSpace( p, encoding );
+
+       if ( data )
+       {
+               data->Stamp( p, encoding );
+               location = data->Cursor();
+       }
+       const char* startTag = "<!--";
+       const char* endTag   = "-->";
+
+       if ( !StringEqual( p, startTag, false, encoding ) )
+       {
+               document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
+               return 0;
+       }
+       p += strlen( startTag );
+
+       // [ 1475201 ] TinyXML parses entities in comments
+       // Oops - ReadText doesn't work, because we don't want to parse the entities.
+       // p = ReadText( p, &value, false, endTag, false, encoding );
+       //
+       // from the XML spec:
+       /*
+        [Definition: Comments may appear anywhere in a document outside other markup; in addition, 
+                     they may appear within the document type declaration at places allowed by the grammar. 
+                                 They are not part of the document's character data; an XML processor MAY, but need not, 
+                                 make it possible for an application to retrieve the text of comments. For compatibility, 
+                                 the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity 
+                                 references MUST NOT be recognized within comments.
+
+                                 An example of a comment:
+
+                                 <!-- declarations for <head> & <body> -->
+       */
+
+    value = "";
+       // Keep all the white space.
+       while ( p && *p && !StringEqual( p, endTag, false, encoding ) )
+       {
+               value.append( p, 1 );
+               ++p;
+       }
+       if ( p ) 
+               p += strlen( endTag );
+
+       return p;
+}
+
+
+const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       p = SkipWhiteSpace( p, encoding );
+       if ( !p || !*p ) return 0;
+
+//     int tabsize = 4;
+//     if ( document )
+//             tabsize = document->TabSize();
+
+       if ( data )
+       {
+               data->Stamp( p, encoding );
+               location = data->Cursor();
+       }
+       // Read the name, the '=' and the value.
+       const char* pErr = p;
+       p = ReadName( p, &name, encoding );
+       if ( !p || !*p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+               return 0;
+       }
+       p = SkipWhiteSpace( p, encoding );
+       if ( !p || !*p || *p != '=' )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+               return 0;
+       }
+
+       ++p;    // skip '='
+       p = SkipWhiteSpace( p, encoding );
+       if ( !p || !*p )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+               return 0;
+       }
+       
+       const char* end;
+       const char SINGLE_QUOTE = '\'';
+       const char DOUBLE_QUOTE = '\"';
+
+       if ( *p == SINGLE_QUOTE )
+       {
+               ++p;
+               end = "\'";             // single quote in string
+               p = ReadText( p, &value, false, end, false, encoding );
+       }
+       else if ( *p == DOUBLE_QUOTE )
+       {
+               ++p;
+               end = "\"";             // double quote in string
+               p = ReadText( p, &value, false, end, false, encoding );
+       }
+       else
+       {
+               // All attribute values should be in single or double quotes.
+               // But this is such a common error that the parser will try
+               // its best, even without them.
+               value = "";
+               while (    p && *p                                                                                      // existence
+                               && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r'      // whitespace
+                               && *p != '/' && *p != '>' )                                                     // tag end
+               {
+                       if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {
+                               // [ 1451649 ] Attribute values with trailing quotes not handled correctly
+                               // We did not have an opening quote but seem to have a 
+                               // closing one. Give up and throw an error.
+                               if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+                               return 0;
+                       }
+                       value += *p;
+                       ++p;
+               }
+       }
+       return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+       while ( in->good() )
+       {
+               int c = in->peek();     
+               if ( !cdata && (c == '<' ) ) 
+               {
+                       return;
+               }
+               if ( c <= 0 )
+               {
+                       TiXmlDocument* document = GetDocument();
+                       if ( document )
+                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                       return;
+               }
+
+               (*tag) += (char) c;
+               in->get();      // "commits" the peek made above
+
+               if ( cdata && c == '>' && tag->size() >= 3 ) {
+                       size_t len = tag->size();
+                       if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) {
+                               // terminator of cdata.
+                               return;
+                       }
+               }    
+       }
+}
+#endif
+
+const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+       value = "";
+       TiXmlDocument* document = GetDocument();
+
+       if ( data )
+       {
+               data->Stamp( p, encoding );
+               location = data->Cursor();
+       }
+
+       const char* const startTag = "<![CDATA[";
+       const char* const endTag   = "]]>";
+
+       if ( cdata || StringEqual( p, startTag, false, encoding ) )
+       {
+               cdata = true;
+
+               if ( !StringEqual( p, startTag, false, encoding ) )
+               {
+                       document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );
+                       return 0;
+               }
+               p += strlen( startTag );
+
+               // Keep all the white space, ignore the encoding, etc.
+               while (    p && *p
+                               && !StringEqual( p, endTag, false, encoding )
+                         )
+               {
+                       value += *p;
+                       ++p;
+               }
+
+               TIXML_STRING dummy; 
+               p = ReadText( p, &dummy, false, endTag, false, encoding );
+               return p;
+       }
+       else
+       {
+               bool ignoreWhite = true;
+
+               const char* end = "<";
+               p = ReadText( p, &value, ignoreWhite, end, false, encoding );
+               if ( p )
+                       return p-1;     // don't truncate the '<'
+               return 0;
+       }
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+       while ( in->good() )
+       {
+               int c = in->get();
+               if ( c <= 0 )
+               {
+                       TiXmlDocument* document = GetDocument();
+                       if ( document )
+                               document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+                       return;
+               }
+               (*tag) += (char) c;
+
+               if ( c == '>' )
+               {
+                       // All is well.
+                       return;
+               }
+       }
+}
+#endif
+
+const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
+{
+       p = SkipWhiteSpace( p, _encoding );
+       // Find the beginning, find the end, and look for
+       // the stuff in-between.
+       TiXmlDocument* document = GetDocument();
+       if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
+       {
+               if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
+               return 0;
+       }
+       if ( data )
+       {
+               data->Stamp( p, _encoding );
+               location = data->Cursor();
+       }
+       p += 5;
+
+       version = "";
+       encoding = "";
+       standalone = "";
+
+       while ( p && *p )
+       {
+               if ( *p == '>' )
+               {
+                       ++p;
+                       return p;
+               }
+
+               p = SkipWhiteSpace( p, _encoding );
+               if ( StringEqual( p, "version", true, _encoding ) )
+               {
+                       TiXmlAttribute attrib;
+                       p = attrib.Parse( p, data, _encoding );         
+                       version = attrib.Value();
+               }
+               else if ( StringEqual( p, "encoding", true, _encoding ) )
+               {
+                       TiXmlAttribute attrib;
+                       p = attrib.Parse( p, data, _encoding );         
+                       encoding = attrib.Value();
+               }
+               else if ( StringEqual( p, "standalone", true, _encoding ) )
+               {
+                       TiXmlAttribute attrib;
+                       p = attrib.Parse( p, data, _encoding );         
+                       standalone = attrib.Value();
+               }
+               else
+               {
+                       // Read over whatever it is.
+                       while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
+                               ++p;
+               }
+       }
+       return 0;
+}
+
+bool TiXmlText::Blank() const
+{
+       for ( unsigned i=0; i<value.length(); i++ )
+               if ( !IsWhiteSpace( value[i] ) )
+                       return false;
+       return true;
+}
+
diff --git a/libpnd.txt b/libpnd.txt
new file mode 100644 (file)
index 0000000..e5b2f25
--- /dev/null
@@ -0,0 +1,42 @@
+
+Overview
+--------
+
+libpnd is a basic collection of functions and tools to make working Pandora-specific
+operations easier; to wit, it is hoped multiple applications will make use of this
+library rather than re-implement similar functionality and lead to problems down the
+road. (Instead we can run into problems together and thus clobberize them.)
+
+The library is broken into parts:
+
+include            - include these files to make use of the lib
+lib        - the code that produces the lib
+test       - mini tools to test various pieces of the lib in isolation
+bin        - test tools
+testdata/   - for testing /etc/pandora; will contain 'conf' dir, for example
+
+Revisions
+---------
+
+The initial version of this code will be very limited and meant to kick-start further
+work, yet provide basic functionaliy.
+
+TODO
+----
+
+o We need an actual matchbox plugin that uses this lib to obtain app list
+
+o PND-file handling, so iso/cram/zip app bundles actually work when pnd_apps_exec()'d
+
+o PXML parser has to actually be an XML parser
+
+o UNICODE support? ie: paths and filenames and app-names..
+
+Nice to do:
+
+o Handle regexp's or globbing in searchpaths, so can do tricks like /mnt/*/app etc
+
+jeff
+
+PS: Yes, I know, the entire thing could be done in 20 lines of perl, but we wanted C to
+maximize availability.
diff --git a/test/conftest.c b/test/conftest.c
new file mode 100644 (file)
index 0000000..43aef1a
--- /dev/null
@@ -0,0 +1,52 @@
+
+#include <stdio.h> /* for printf, NULL */
+#include <stdlib.h> /* for free */
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+
+int main ( void ) {
+
+  // attempt to fetch a sensible default searchpath for configs
+  char *configpath = pnd_conf_query_searchpath();
+  printf ( "Config searchpath is: '%s'\n", configpath );
+
+  // attempt to fetch the 'apps' config
+  pnd_conf_handle apph;
+
+  apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath );
+
+  if ( ! apph ) {
+    printf ( "Couldn't locate apps config!\n" );
+    return ( -1 );
+  }
+
+  // dump the config file
+  printf ( "Config file name is: '%s'\n", pnd_box_get_name ( apph ) );
+  char *value = pnd_box_get_head ( apph );
+  printf ( "Config has key '%s'\n", pnd_box_get_key ( value ) );
+  printf ( "Config has value '%s'\n", value );
+  while ( ( value = pnd_box_get_next ( value ) ) ) {
+    printf ( "Config has key '%s'\n", pnd_box_get_key ( value ) );
+    printf ( "Config has value '%s'\n", value );
+  }
+
+  // lets query the apps config
+  char *binpath;
+
+  binpath = pnd_conf_get_as_char ( apph, PND_APPS_KEY );
+
+  if ( ! binpath ) {
+    printf ( "Couldn't locate the app auto-discovery searchpath!\n" );
+    return ( -2 );
+  }
+
+  printf ( "Located auto-discovery searchpath '%s'\n", binpath );
+
+  // exeunt with alarums
+  free ( configpath );
+  pnd_box_delete ( apph );
+
+  return ( 0 );
+}
diff --git a/test/discotest.c b/test/discotest.c
new file mode 100644 (file)
index 0000000..034afcf
--- /dev/null
@@ -0,0 +1,101 @@
+
+#include <stdio.h> /* for printf, NULL */
+#include <stdlib.h> /* for free */
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+#include "pnd_pxml.h"
+#include "pnd_discovery.h"
+
+int main ( void) {
+  char *configpath;
+  char *appspath;
+  char *overridespath;
+
+  /* attempt to sort out the config file madness
+   */
+
+  // attempt to fetch a sensible default searchpath for configs
+  configpath = pnd_conf_query_searchpath();
+
+  // attempt to fetch the apps config to pick up a searchpath
+  pnd_conf_handle apph;
+
+  apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath );
+
+  if ( apph ) {
+    appspath = pnd_conf_get_as_char ( apph, PND_APPS_KEY );
+
+    if ( ! appspath ) {
+      appspath = PND_APPS_SEARCHPATH;
+    }
+
+    overridespath = pnd_conf_get_as_char ( apph, PND_PXML_OVERRIDE_KEY );
+
+    if ( ! overridespath ) {
+      overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
+    }
+
+  } else {
+    // couldn't find a useful app search path so use the default
+    appspath = PND_APPS_SEARCHPATH;
+    overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
+  }
+
+  printf ( "Apps searchpath is '%s'\n", appspath );
+  printf ( "Apps overrides searchpath is '%s'\n", overridespath );
+
+  /* attempt to discover apps in the path
+   */
+  pnd_box_handle applist;
+
+  applist = pnd_disco_search ( appspath, overridespath );
+
+  // list the found apps (if any)
+
+  if ( applist ) {
+    pnd_disco_t *d = pnd_box_get_head ( applist );
+
+    while ( d ) {
+
+      // display the app 'as is'
+
+      printf ( "App: %s\n", pnd_box_get_key ( d ) );
+
+      if ( d -> title_en ) {
+       printf ( "  Name: %s\n", d -> title_en );
+      }
+      if ( d -> icon ) {
+       printf ( "  Icon: %s\n", d -> icon );
+      }
+      if ( d -> unique_id ) {
+       printf ( "  Unique ID: %s\n", d -> unique_id );
+      }
+      if ( d -> main_category ) {
+       printf ( "  Category: %s\n", d -> main_category );
+      }
+      if ( d -> exec ) {
+       printf ( "  Executable: %s\n", d -> exec );
+      }
+      if ( d -> clockspeed ) {
+       printf ( "  Clockspeed: %s\n", d -> clockspeed );
+      }
+
+      // next!
+      d = pnd_box_get_next ( d );
+
+    } // while applist
+
+  } else {
+    printf ( "No applications found in search path\n" );
+  }
+
+  // exeunt with alarums
+  free ( configpath );
+  if ( apph ) {
+    pnd_box_delete ( apph );
+  }
+
+  return ( 0 );
+}
diff --git a/test/locatetest.c b/test/locatetest.c
new file mode 100644 (file)
index 0000000..59ad3b0
--- /dev/null
@@ -0,0 +1,56 @@
+
+#include <stdio.h> // for stdio
+#include <unistd.h> // for exit()
+#include <stdlib.h> // for exit()
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+#include "pnd_locate.h"
+
+int main ( int argc, char *argv[] ) {
+  char *configpath;
+  char *pndpath;
+
+  /* attempt to sort out the config file madness
+   */
+
+  // attempt to fetch a sensible default searchpath for configs
+  configpath = pnd_conf_query_searchpath();
+
+  // attempt to fetch app config, or default the pnd_run.sh location
+  pnd_conf_handle apph;
+
+  apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath );
+
+  if ( apph ) {
+    pndpath = pnd_conf_get_as_char ( apph, PND_PNDRUN_SEARCHPATH );
+
+    printf ( "Found a path in apps config: '%s'\n", pndpath );
+
+    if ( ! pndpath ) {
+      pndpath = PND_PNDRUN_SEARCHPATH;
+    }
+
+  } else {
+    // couldn't find a useful app search path so use the default
+    pndpath = PND_PNDRUN_SEARCHPATH;
+  }
+
+  // given a searchpath (Default or configured), try to find pnd_run.sh; if not
+  // found, then just use the default
+  char *pndrun;
+
+  printf ( "Searching in path '%s'\n", pndpath );
+
+  pndrun = pnd_locate_filename ( pndpath, PND_PNDRUN_FILENAME );
+
+  if ( ! pndrun ) {
+    printf ( "Result is default, not a locate-find\n" );
+    pndrun = PND_PNDRUN_DEFAULT;
+  }
+
+  printf ( "Locate found pnd_run.sh in '%s'\n", pndrun );
+
+  return ( 0 );
+}
diff --git a/test/notifytest.c b/test/notifytest.c
new file mode 100644 (file)
index 0000000..dee3e37
--- /dev/null
@@ -0,0 +1,46 @@
+
+#include <stdio.h> // for stdio
+#include <unistd.h> // for exit()
+#include <stdlib.h> // for exit()
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+#include "pnd_notify.h"
+
+int main ( int argc, char *argv[] ) {
+
+  pnd_notify_handle nh;
+
+  nh = pnd_notify_init();
+
+  if ( ! nh ) {
+    printf ( "INOTIFY failed to init.\n" );
+    exit ( 0 );
+  }
+
+  printf ( "INOTIFY is up.\n" );
+
+  /* do it!
+   */
+
+  unsigned int countdown = 10;
+
+  while ( countdown ) {
+    printf ( "Countdown = %u\n", countdown );
+
+    if ( pnd_notify_rediscover_p ( nh ) ) {
+      printf ( "Must do a rediscover!\n" );
+      break;
+    }
+
+    countdown--;
+  }
+
+  /* do it!
+   */
+
+  pnd_notify_shutdown ( nh );
+
+  return ( 0 );
+}
diff --git a/testdata/apps-override/123shaboo.xml b/testdata/apps-override/123shaboo.xml
new file mode 100644 (file)
index 0000000..86d7717
--- /dev/null
@@ -0,0 +1,3 @@
+<PXML>
+  <title>Program Title Override</title>
+</PXML>
diff --git a/testdata/apps/sampleapp/PXML.xml b/testdata/apps/sampleapp/PXML.xml
new file mode 100644 (file)
index 0000000..776b9b7
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<PXML>
+<title>
+       <en>Program Title</en>
+       <de>Program Title in German Language</de>
+       <fr>Program Title in French Language</fr>
+       <it>Program Title in Italian Language</it>
+</title>
+
+<unique_id>123shaboo</unique_id>
+
+<standalone>Yes</standalone>
+
+<icon>program.png</icon>
+
+<description>
+       <en>This is the [b]English Description[/b] of the file.
+            Can use [i]multiple lines[/i] and BBCode.</en>
+       <de>The German Description</de>
+       <it>The Italian Description</it>
+       <fr>The French Description</fr>
+</description>
+
+<previewpic>
+       <pic1>./preview/pic1.jpg</pic1>
+       <pic2>./preview/pic2.jpg</pic2>
+</previewpic>
+
+<author>
+       <name>EvilDragon</name>
+       <website>http://www.openpandora.org</website>
+</author>
+
+<version>
+       <major>1</major>
+       <minor>1</minor>
+       <release>1</release>
+       <build>2</build>
+</version>
+
+<exec>program.exe</exec>
+
+<category>
+       <main>Main category</main>
+       <subcategory1>Subcategory 1</subcategory1>
+       <subcategory2>Subcategory 2</subcategory2>
+</category>
+
+<altcategory>
+       <main>Alternate category</main>
+       <subcategory1>Alternate Subcategory 1</subcategory1>
+       <subcategory2>Alternate Subcategory 2</subcategory2>
+</altcategory>
+
+<osversion> 
+       <major>1</major>
+       <minor>1</minor>
+       <release>1</release>
+       <build>2</build>
+</osversion>
+
+<associationitem1>
+       <name>View this Picture</name>
+       <filetype>jpg,bmp,gif</filetype>
+       <parameter>­view</parameter>
+</associationitem1>
+
+<associationitem2>
+       <name>Convert this Picture</name>
+       <filetype>jpg,bmp,gif</filetype>
+       <parameter>­convert</parameter>
+</associationitem2>
+
+<associationitem3>
+       <name>Watch This Movie</name>
+       <filetype>mpg,avi,wmv</filetype>
+       <parameter>­convert</parameter>
+</associationitem3>
+
+<clockspeed>600</clockspeed>
+
+<background>Yes</background>
+
+<startdir>../differentdir</startdir>
+
+</PXML>
diff --git a/testdata/apps/sampleapp/program.exe b/testdata/apps/sampleapp/program.exe
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/testdata/conf/apps b/testdata/conf/apps
new file mode 100644 (file)
index 0000000..63d0e18
--- /dev/null
@@ -0,0 +1,13 @@
+
+# Open Pandora
+# Application configuration
+
+[autodiscovery]
+searchpath     /mnt/sd1/pandora/apps:/mnt/sd2/pandora/apps:./testdata/apps     # path to depth-search for PXMLs
+
+[overrides]
+searchpath     ~/pxml-overrides:./testdata/apps-override
+
+[pnd]
+searchpath     /mnt/sd1/pandora/scripts:/mnt/sd2/pandora/scripts:./testdata/scripts
+default                pndrun.sh
diff --git a/testdata/scripts/pnd_run.sh b/testdata/scripts/pnd_run.sh
new file mode 100644 (file)
index 0000000..d3f9083
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/bash
+#input pnd_run.sh -p "/path/to/foobar.pnd" -e "exe" --a "arguments for exe"
+#output none
+########################### FS NAMES NEED ADJUSTMENT #################
+#todo
+# check if all vars are set to sensible values
+# parse arguments
+TEMP=`getopt -o p:e:a:: --long p-long,e-long:,a-long: -- "$@"`
+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+# Note the quotes around `$TEMP': they are essential!
+eval set -- "$TEMP"
+while true ; do
+        case "$1" in
+                -p|--p-long) echo "pnd set to \`$2'" ;PND=$2;shift 2;;
+                -e|--e-long) echo "exec set to \`$2'" ;EXENAME=$2;shift 2 ;;
+                -a|--a-long) 
+                        # a has an optional argument. As we are in quoted mode,
+                        # an empty parameter will be generated if its optional
+                        # argument is not found.
+                        case "$2" in
+                                "") echo "no arguments"; shift 2 ;;
+                                *)  echo "args set to \`$2'" ;ARGUMENTS=$2;shift 2 ;;
+                        esac ;;
+                --) shift ; break ;;
+                *) echo "Error while parsing arguments!" ; exit 1 ;;
+        esac
+done
+# add sanity check
+#vars
+DFS=$(file -b $PND | awk '{ print $1 }') # is -p a zip iso or folder?
+MOUNTPOINT=$(df $PND | grep -vE '^Filesystem' | awk '{ print $6  }') #find out which mountpoint the pnd/folder is on
+BASENAME=$(basename "$PND" | cut -d'.' -f1) #get basename (strip extension if file) for union mountpoints etc, maybe  this should be changed to something specified inside the xml
+oCWD=$(pwd)
+# add sanity check
+#detect fs
+if [ $DFS = ISO ]; then
+        mntline="mount -o loop $PND /mnt/pnd/$BASENAME"
+        echo "Filetype is $DFS"
+elif [ $DFS = Zip ]; then
+        mntline="fuse-zip $PND /mnt/pnd/$BASENAME -oro" #should be reight now
+        echo "Filetype is $DFS"
+elif [ $DFS = directory ]; then
+        mntline="mount --bind -o ro $PND /mnt/pnd/$BASENAME"
+#we bind the folder, now it can be treated in a unified way ATENTION: -o ro doesnt work for --bind at least on 25, on 26 its possible using remount, may have changed on 27
+        echo "Filetype is $DFS"
+else
+        echo "error"
+        exit 1;
+fi
+#create mountpoints
+#echo "
+#will run:
+# create mountpoints
+mkdir -p /mnt/pnd/$BASENAME
+mkdir -p $MOUNTPOINT/appdata/$BASENAME
+mkdir -p /mnt/utmp/$BASENAME
+#mount
+$mntline #mount the pnd/folder
+mount -t unionfs -o exec,dirs\=$MOUNTPOINT/appdata/$BASENAME=rw:/mnt/pnd/$BASENAME=ro unionfs /mnt/utmp/$BASENAME #union mount
+#start app
+cd /mnt/utmp/$BASENAME
+$EXENAME $ARGUMENTS 
+cd $oCWD
+#app exited
+#clean up
+umount /mnt/utmp/$BASENAME
+umount /mnt/pnd/$BASENAME
+rmdir /mnt/pnd/$BASENAME
+rmdir /mnt/utmp/$BASENAME
+#
\ No newline at end of file