summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING19
-rw-r--r--Makefile34
-rw-r--r--Makefile-riscos38
-rw-r--r--README39
-rw-r--r--build/Makefile.common39
-rw-r--r--docs/Todo14
-rw-r--r--include/dom/core/attr.h37
-rw-r--r--include/dom/core/document.h105
-rw-r--r--include/dom/core/exceptions.h34
-rw-r--r--include/dom/core/node.h170
-rw-r--r--include/dom/core/string.h40
-rw-r--r--include/dom/ctx.h26
-rw-r--r--src/Makefile72
-rw-r--r--src/core/Makefile53
-rw-r--r--src/core/attr.c173
-rw-r--r--src/core/document.c356
-rw-r--r--src/core/document.h19
-rw-r--r--src/core/node.c1069
-rw-r--r--src/core/node.h60
-rw-r--r--src/core/string.c222
-rw-r--r--src/utils/utils.h28
-rw-r--r--test/INDEX4
-rw-r--r--test/Makefile60
-rw-r--r--test/testrunner.pl147
24 files changed, 2858 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..7646f4c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,19 @@
+Copyright (C) 2007 J-M Bell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..db5a35b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+# Toolchain definitions for building on the destination platform
+export CC = gcc
+export AR = ar
+export LD = gcc
+
+export CP = cp
+export RM = rm
+export MKDIR = mkdir
+export MV = mv
+export ECHO = echo
+export MAKE = make
+export PERL = perl
+export PKGCONFIG = pkg-config
+
+# Toolchain flags
+WARNFLAGS = -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \
+ -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \
+ -Wmissing-declarations -Wnested-externs -Werror -pedantic
+export CFLAGS = -std=c99 -D_BSD_SOURCE -I$(TOP)/include/ $(WARNFLAGS)
+export ARFLAGS = -cru
+export LDFLAGS = -L$(TOP)/
+
+export CPFLAGS =
+export RMFLAGS =
+export MKDIRFLAGS = -p
+export MVFLAGS =
+export ECHOFLAGS =
+export MAKEFLAGS =
+export PKGCONFIGFLAGS =
+
+export EXEEXT =
+
+
+include build/Makefile.common
diff --git a/Makefile-riscos b/Makefile-riscos
new file mode 100644
index 0000000..f1d8cf0
--- /dev/null
+++ b/Makefile-riscos
@@ -0,0 +1,38 @@
+# Toolchain definitions for building for RISC OS using the GCCSDK cross-compiler
+GCCSDK_INSTALL_CROSSBIN ?= /home/riscos/cross/bin
+GCCSDK_INSTALL_ENV ?= /home/riscos/env
+
+export CC = $(GCCSDK_INSTALL_CROSSBIN)/gcc
+export AR = $(GCCSDK_INSTALL_CROSSBIN)/ar
+export LD = $(GCCSDK_INSTALL_CROSSBIN)/gcc
+
+export CP = cp
+export RM = rm
+export MKDIR = mkdir
+export MV = mv
+export ECHO = echo
+export MAKE = make
+export PERL = perl
+export PKGCONFIG = pkg-config
+
+# Toolchain flags
+WARNFLAGS = -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \
+ -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \
+ -Wmissing-declarations -Wnested-externs -Werror -pedantic
+export CFLAGS = -std=c99 -D_BSD_SOURCE -I$(TOP)/include/ $(WARNFLAGS) \
+ -mpoke-function-name
+export ARFLAGS = -cru
+export LDFLAGS = -L$(TOP)/
+
+export CPFLAGS =
+export RMFLAGS =
+export MKDIRFLAGS = -p
+export MVFLAGS =
+export ECHOFLAGS =
+export MAKEFLAGS =
+export PKGCONFIGFLAGS =
+
+export EXEEXT = ,ff8
+
+
+include build/Makefile.common
diff --git a/README b/README
new file mode 100644
index 0000000..beeaa91
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+libdom -- an implementation of the W3C DOM
+==========================================
+
+Overview
+--------
+
+ libdom is an implementation of the W3C DOM API in C.
+
+Requirements
+------------
+
+ libdom requires the following tools:
+
+ + A C99 capable C compiler
+ + GNU make or compatible
+ + Perl (for the testcases)
+
+Compilation
+-----------
+
+ If necessary, modify the toolchain settings in the Makefile.
+ Invoke make:
+ $ make
+
+Verification
+------------
+
+ To verify that the library is working, it is necessary to specify a
+ different makefile target than that used for normal compilation, thus:
+
+ $ make test
+
+API documentation
+-----------------
+
+ Currently, there is none. However, the code is well commented and the
+ public API may be found in the "include" directory. The testcase sources
+ may also be of use in working out how to use it.
+
diff --git a/build/Makefile.common b/build/Makefile.common
new file mode 100644
index 0000000..3b3f92c
--- /dev/null
+++ b/build/Makefile.common
@@ -0,0 +1,39 @@
+# Top-level Makefile fragment for DOM library
+
+# Name of component
+export COMPONENT = libdom
+
+# Environment
+export EXPORT = $(CURDIR)/dist
+export TOP = $(CURDIR)
+
+.PHONY: release debug test clean setup export distclean
+
+# Rules
+release: setup
+ @$(MAKE) $(MAKEFLAGS) -C src release
+
+debug: setup
+ @$(MAKE) $(MAKEFLAGS) -C src debug
+
+test: debug
+ @$(MAKE) $(MAKEFLAGS) -C test test
+
+clean:
+ @$(MAKE) $(MAKEFLAGS) -C src clean
+ @$(MAKE) $(MAKEFLAGS) -C test clean
+
+setup:
+ @$(MAKE) $(MAKEFLAGS) -C src setup
+ @$(MAKE) $(MAKEFLAGS) -C test setup
+
+export: release
+ @$(MKDIR) $(MKDIRFLAGS) $(TOP)/dist/lib
+ @$(CP) $(CPFLAGS) -r include $(EXPORT)/
+ @$(MAKE) $(MAKEFLAGS) -C src export
+ @$(MAKE) $(MAKEFLAGS) -C test export
+
+distclean: clean
+ -@$(RM) $(RMFLAGS) -r $(TOP)/dist
+ @$(MAKE) $(MAKEFLAGS) -C src distclean
+ @$(MAKE) $(MAKEFLAGS) -C test distclean
diff --git a/docs/Todo b/docs/Todo
new file mode 100644
index 0000000..507eee9
--- /dev/null
+++ b/docs/Todo
@@ -0,0 +1,14 @@
+TODO list
+=========
+
+ + Sort out the mess that is dom_ctx - I really don't like it; everything (except dom_strings)
+ should be allocated in the context of a document and, thus, the context is easily found
+ through foo->owner->blah. dom_strings are somewhat awkward here, as they aren't necessarily
+ allocated in the context of a document, but they still need access to an allocator. Perhaps
+ the simplest solution is for the string constructors to take an allocator and private word
+ as parameters, then store them within the string. This does, however, add an overhead of
+ 8 (or 16 on 64bit platforms) bytes per dom_string, which isn't exactly great.
+ + Fill out stub functions for DOM3 core
+ + Rest of DOM level 3
+ + DOM level 2
+ + DOM level 1
diff --git a/include/dom/core/attr.h b/include/dom/core/attr.h
new file mode 100644
index 0000000..782513e
--- /dev/null
+++ b/include/dom/core/attr.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_core_attr_h_
+#define dom_core_attr_h_
+
+#include <stdbool.h>
+
+#include <dom/core/exceptions.h>
+
+struct dom_ctx;
+struct dom_element;
+struct dom_typeinfo;
+struct dom_node;
+struct dom_attr;
+struct dom_string;
+
+dom_exception dom_attr_get_name(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string **result);
+dom_exception dom_attr_get_specified(struct dom_ctx *ctx,
+ struct dom_attr *attr, bool *result);
+dom_exception dom_attr_get_value(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string **result);
+dom_exception dom_attr_set_value(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string *value);
+dom_exception dom_attr_get_owner(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_element **result);
+dom_exception dom_attr_get_typeinfo(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_typeinfo **result);
+dom_exception dom_attr_is_id(struct dom_ctx *ctx,
+ struct dom_attr *attr, bool *result);
+
+#endif
diff --git a/include/dom/core/document.h b/include/dom/core/document.h
new file mode 100644
index 0000000..347fdc4
--- /dev/null
+++ b/include/dom/core/document.h
@@ -0,0 +1,105 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_core_document_h_
+#define dom_core_document_h_
+
+#include <stdbool.h>
+
+#include <dom/core/exceptions.h>
+
+struct dom_attr;
+struct dom_configuration;
+struct dom_ctx;
+struct dom_document;
+struct dom_document_type;
+struct dom_element;
+struct dom_implementation;
+struct dom_node;
+struct dom_nodelist;
+struct dom_string;
+struct dom_text;
+
+dom_exception dom_document_get_doctype(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_document_type **result);
+dom_exception dom_document_get_implementation(struct dom_ctx *ctx,
+ struct dom_document *doc,
+ struct dom_implementation **result);
+dom_exception dom_document_get_element(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_element **result);
+dom_exception dom_document_create_element(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *tag_name,
+ struct dom_element **result);
+dom_exception dom_document_create_document_fragment(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node **result);
+dom_exception dom_document_create_text_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *data,
+ struct dom_text **result);
+dom_exception dom_document_create_cdata_section(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *data,
+ struct dom_text **result);
+dom_exception dom_document_create_processing_instruction(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *target,
+ struct dom_string *data,
+ struct dom_node **result);
+dom_exception dom_document_create_attribute(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *name,
+ struct dom_attr **result);
+dom_exception dom_document_create_entity_reference(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *name,
+ struct dom_node **result);
+dom_exception dom_document_get_elements_by_tag_name(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *tagname,
+ struct dom_nodelist **result);
+dom_exception dom_document_import_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ bool deep, struct dom_node **result);
+dom_exception dom_document_create_element_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *qname, struct dom_element **result);
+dom_exception dom_document_create_attribute_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *qname, struct dom_attr **result);
+dom_exception dom_document_get_elements_by_tag_name_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_nodelist **result);
+dom_exception dom_document_get_element_by_id(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *id,
+ struct dom_element **result);
+dom_exception dom_document_get_input_encoding(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result);
+dom_exception dom_document_get_xml_encoding(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result);
+dom_exception dom_document_get_xml_standalone(struct dom_ctx *ctx,
+ struct dom_document *doc, bool *result);
+dom_exception dom_document_set_xml_standalone(struct dom_ctx *ctx,
+ struct dom_document *doc, bool standalone);
+dom_exception dom_document_get_xml_version(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result);
+dom_exception dom_document_set_xml_version(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *version);
+dom_exception dom_document_get_strict_error_checking(struct dom_ctx *ctx,
+ struct dom_document *doc, bool *result);
+dom_exception dom_document_set_strict_error_checking(struct dom_ctx *ctx,
+ struct dom_document *doc, bool strict);
+dom_exception dom_document_get_uri(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result);
+dom_exception dom_document_set_uri(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *uri);
+dom_exception dom_document_adopt_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ struct dom_node **result);
+dom_exception dom_document_get_dom_config(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_configuration **result);
+dom_exception dom_document_normalize(struct dom_ctx *ctx,
+ struct dom_document *doc);
+dom_exception dom_document_rename_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ struct dom_string *namespace, struct dom_string *qname,
+ struct dom_node **result);
+
+#endif
diff --git a/include/dom/core/exceptions.h b/include/dom/core/exceptions.h
new file mode 100644
index 0000000..d735013
--- /dev/null
+++ b/include/dom/core/exceptions.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_errors_h_
+#define dom_errors_h_
+
+/* The DOM spec says that this is actually an unsigned short */
+typedef enum {
+ DOM_NO_ERR = 0,
+ DOM_INDEX_SIZE_ERR = 1,
+ DOM_DOMSTRING_SIZE_ERR = 2,
+ DOM_HIERARCHY_REQUEST_ERR = 3,
+ DOM_WRONG_DOCUMENT_ERR = 4,
+ DOM_INVALID_CHARACTER_ERR = 5,
+ DOM_NO_DATA_ALLOWED_ERR = 6,
+ DOM_NO_MODIFICATION_ALLOWED_ERR = 7,
+ DOM_NOT_FOUND_ERR = 8,
+ DOM_NOT_SUPPORTED_ERR = 9,
+ DOM_INUSE_ATTRIBUTE_ERR = 10,
+ DOM_INVALID_STATE_ERR = 11,
+ DOM_SYNTAX_ERR = 12,
+ DOM_INVALID_MODIFICATION_ERR = 13,
+ DOM_NAMESPACE_ERR = 14,
+ DOM_INVALID_ACCESS_ERR = 15,
+ DOM_VALIDATION_ERR = 16,
+ DOM_TYPE_MISMATCH_ERR = 17,
+ DOM_NO_MEM_ERR = (1<<16) /* our own internal error */
+} dom_exception;
+
+#endif
diff --git a/include/dom/core/node.h b/include/dom/core/node.h
new file mode 100644
index 0000000..e37d99b
--- /dev/null
+++ b/include/dom/core/node.h
@@ -0,0 +1,170 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_core_node_h_
+#define dom_core_node_h_
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <dom/core/exceptions.h>
+
+struct dom_ctx;
+struct dom_document;
+struct dom_node;
+struct dom_node_list;
+struct dom_named_node_map;
+struct dom_string;
+
+/**
+ * Bits defining position of a node in a document relative to some other node
+ */
+typedef enum {
+ DOM_DOCUMENT_POSITION_DISCONNECTED = 0x01,
+ DOM_DOCUMENT_POSITION_PRECEDING = 0x02,
+ DOM_DOCUMENT_POSITION_FOLLOWING = 0x04,
+ DOM_DOCUMENT_POSITION_CONTAINS = 0x08,
+ DOM_DOCUMENT_POSITION_CONTAINED_BY = 0x10,
+ DOM_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20
+} dom_document_position;
+
+/**
+ * Type of node operation being notified to user_data_handler
+ */
+typedef enum {
+ DOM_NODE_CLONED = 1,
+ DOM_NODE_IMPORTED = 2,
+ DOM_NODE_DELETED = 3,
+ DOM_NODE_RENAMED = 4,
+ DOM_NODE_ADOPTED = 5
+} dom_node_operation;
+
+/**
+ * Type of handler function for user data registered on a DOM node
+ */
+typedef void (*dom_user_data_handler)(dom_node_operation operation,
+ struct dom_string *key, void *data, struct dom_node *src,
+ struct dom_node *dst);
+
+/**
+ * Type of a DOM node
+ */
+typedef enum {
+ DOM_ELEMENT_NODE = 1,
+ DOM_ATTRIBUTE_NODE = 2,
+ DOM_TEXT_NODE = 3,
+ DOM_CDATA_SECTION_NODE = 4,
+ DOM_ENTITY_REFERENCE_NODE = 5,
+ DOM_ENTITY_NODE = 6,
+ DOM_PROCESSING_INSTRUCTION_NODE = 7,
+ DOM_COMMENT_NODE = 8,
+ DOM_DOCUMENT_NODE = 9,
+ DOM_DOCUMENT_TYPE_NODE = 10,
+ DOM_DOCUMENT_FRAGMENT_NODE = 11,
+ DOM_NOTATION_NODE = 12
+} dom_node_type;
+
+
+void dom_node_ref(struct dom_ctx *ctx, struct dom_node *node);
+void dom_node_unref(struct dom_ctx *ctx, struct dom_node *node);
+
+dom_exception dom_node_get_name(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_get_value(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_set_value(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *value);
+dom_exception dom_node_get_type(struct dom_ctx *ctx,
+ struct dom_node *node, dom_node_type *result);
+dom_exception dom_node_get_parent(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result);
+dom_exception dom_node_get_children(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node_list **result);
+dom_exception dom_node_get_first_child(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result);
+dom_exception dom_node_get_last_child(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result);
+dom_exception dom_node_get_previous(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result);
+dom_exception dom_node_get_next(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result);
+dom_exception dom_node_get_attributes(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_named_node_map **result);
+dom_exception dom_node_get_owner(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_document **result);
+dom_exception dom_node_insert_before(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child, struct dom_node *ref_child,
+ struct dom_node **result);
+dom_exception dom_node_replace_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child, struct dom_node *old_child,
+ struct dom_node **result);
+dom_exception dom_node_remove_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *old_child,
+ struct dom_node **result);
+dom_exception dom_node_append_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child,
+ struct dom_node **result);
+dom_exception dom_node_has_children(struct dom_ctx *ctx,
+ struct dom_node *node, bool *result);
+dom_exception dom_node_clone(struct dom_ctx *ctx,
+ struct dom_node *node, bool deep,
+ struct dom_node **result);
+dom_exception dom_node_normalize(struct dom_ctx *ctx,
+ struct dom_node *node);
+dom_exception dom_node_is_supported(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *feature,
+ struct dom_node *version, bool *result);
+dom_exception dom_node_get_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_get_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_set_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *prefix);
+dom_exception dom_node_get_local_name(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_has_attributes(struct dom_ctx *ctx,
+ struct dom_node *node, bool *result);
+dom_exception dom_node_get_base(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_compare_document_position(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ uint16_t *result);
+dom_exception dom_node_get_text_content(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result);
+dom_exception dom_node_set_text_content(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *content);
+dom_exception dom_node_is_same(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ bool *result);
+dom_exception dom_node_lookup_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *namespace,
+ struct dom_string **result);
+dom_exception dom_node_is_default_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *namespace,
+ bool *result);
+dom_exception dom_node_lookup_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *prefix,
+ struct dom_string **result);
+dom_exception dom_node_is_equal(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ bool *result);
+dom_exception dom_node_get_feature(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *feature,
+ struct dom_string *version, void **result);
+dom_exception dom_node_set_user_data(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *key,
+ void *data, dom_user_data_handler handler,
+ void **result);
+dom_exception dom_node_get_user_data(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *key,
+ void **result);
+
+#endif
diff --git a/include/dom/core/string.h b/include/dom/core/string.h
new file mode 100644
index 0000000..f79eaa7
--- /dev/null
+++ b/include/dom/core/string.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_string_h_
+#define dom_string_h_
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include <dom/core/exceptions.h>
+
+struct dom_ctx;
+struct dom_document;
+struct dom_string;
+
+/* Claim a reference on a DOM string */
+void dom_string_ref(struct dom_ctx *ctx, struct dom_string *str);
+/* Release a reference on a DOM string */
+void dom_string_unref(struct dom_ctx *ctx, struct dom_string *str);
+
+/* Create a DOM string from an offset into the document buffer */
+dom_exception dom_string_create_from_off(struct dom_ctx *ctx,
+ struct dom_document *doc, uint32_t off, size_t len,
+ struct dom_string **str);
+/* Create a DOM string from a string of characters */
+dom_exception dom_string_create_from_ptr(struct dom_ctx *ctx,
+ const uint8_t *ptr, size_t len, struct dom_string **str);
+/* Create a DOM string from a constant string of characters */
+dom_exception dom_string_create_from_const_ptr(struct dom_ctx *ctx,
+ const uint8_t *ptr, size_t len, struct dom_string **str);
+
+/* Get a pointer to the string of characters within a DOM string */
+dom_exception dom_string_get_data(struct dom_ctx *ctx,
+ struct dom_string *str, const uint8_t **data, size_t *len);
+
+#endif
diff --git a/include/dom/ctx.h b/include/dom/ctx.h
new file mode 100644
index 0000000..f69ec65
--- /dev/null
+++ b/include/dom/ctx.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_ctx_h_
+#define dom_ctx_h_
+
+#include <stddef.h>
+
+/**
+ * Type of allocation function for DOM implementation
+ */
+typedef void *(*dom_alloc)(void *ptr, size_t size, void *pw);
+
+/**
+ * DOM allocation context
+ */
+struct dom_ctx {
+ dom_alloc alloc; /**< Memory (de)allocation function */
+ void *pw; /**< Pointer to client data */
+};
+
+#endif
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..ae0c9eb
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,72 @@
+# Makefile for libdom
+#
+# Toolchain is exported by top-level makefile
+#
+# Top-level makefile also exports the following variables:
+#
+# COMPONENT Name of component
+# EXPORT Absolute path of export directory
+# TOP Absolute path of source tree root
+#
+# The top-level makefile requires the following targets to exist:
+#
+# clean Clean source tree
+# debug Create a debug binary
+# distclean Fully clean source tree, back to pristine condition
+# export Export distributable components to ${EXPORT}
+# release Create a release binary
+# setup Perform any setup required prior to compilation
+# test Execute any test cases
+
+# Manipulate include paths
+CFLAGS += -I$(CURDIR)
+
+# Release output
+RELEASE = ${TOP}/${COMPONENT}.a
+
+# Debug output
+DEBUG = ${TOP}/${COMPONENT}-debug.a
+
+# Objects
+OBJS =
+
+.PHONY: clean debug distclean export release setup test
+
+# Targets
+release: $(addprefix Release/, $(addsuffix .o, $(OBJS)))
+ @${MAKE} -C core release
+ @${AR} ${ARFLAGS} $(RELEASE) Release/*
+
+debug: $(addprefix Debug/, $(addsuffix .o, $(OBJS)))
+ @${MAKE} -C core debug
+ @${AR} ${ARFLAGS} $(DEBUG) Debug/*
+
+clean:
+ @${MAKE} -C core clean
+ifneq (${OBJS}, )
+ -@${RM} ${RMFLAGS} $(addprefix Release/, $(addsuffix .o, ${OBJS}))
+ -@${RM} ${RMFLAGS} $(addprefix Debug/, $(addsuffix .o, ${OBJS}))
+endif
+ -@${RM} ${RMFLAGS} $(RELEASE) $(DEBUG)
+
+distclean:
+ -@${RM} ${RMFLAGS} -r Release
+ -@${RM} ${RMFLAGS} -r Debug
+
+setup:
+ @${MKDIR} ${MKDIRFLAGS} Release
+ @${MKDIR} ${MKDIRFLAGS} Debug
+
+export:
+ @${CP} ${CPFLAGS} $(RELEASE) ${EXPORT}/lib/
+
+test:
+
+# Pattern rules
+Release/%.o: %.c
+ @${ECHO} ${ECHOFLAGS} "==> $<"
+ @${CC} -c ${CFLAGS} -DNDEBUG -o $@ $<
+
+Debug/%.o: %.c
+ @${ECHO} ${ECHOFLAGS} "==> $<"
+ @${CC} -c -g ${CFLAGS} -o $@ $<
diff --git a/src/core/Makefile b/src/core/Makefile
new file mode 100644
index 0000000..3924ebb
--- /dev/null
+++ b/src/core/Makefile
@@ -0,0 +1,53 @@
+# Makefile for libdom
+#
+# Toolchain is exported by top-level makefile
+#
+# Top-level makefile also exports the following variables:
+#
+# COMPONENT Name of component
+# EXPORT Absolute path of export directory
+# TOP Absolute path of source tree root
+#
+# The top-level makefile requires the following targets to exist:
+#
+# clean Clean source tree
+# debug Create a debug binary
+# distclean Fully clean source tree, back to pristine condition
+# export Export distributable components to ${EXPORT}
+# release Create a release binary
+# setup Perform any setup required prior to compilation
+# test Execute any test cases
+
+# Manipulate include paths
+CFLAGS += -I$(CURDIR)
+
+# Objects
+OBJS = attr document node string
+
+.PHONY: clean debug distclean export release setup test
+
+# Targets
+release: $(addprefix ../Release/, $(addsuffix .o, $(OBJS)))
+
+debug: $(addprefix ../Debug/, $(addsuffix .o, $(OBJS)))
+
+clean:
+ -@${RM} ${RMFLAGS} $(addprefix ../Release/, $(addsuffix .o, ${OBJS}))
+ -@${RM} ${RMFLAGS} $(addprefix ../Debug/, $(addsuffix .o, ${OBJS}))
+
+distclean:
+
+setup:
+
+export:
+
+test:
+
+# Pattern rules
+../Release/%.o: %.c
+ @${ECHO} ${ECHOFLAGS} "==> $<"
+ @${CC} -c ${CFLAGS} -DNDEBUG -o $@ $<
+
+../Debug/%.o: %.c
+ @${ECHO} ${ECHOFLAGS} "==> $<"
+ @${CC} -c -g ${CFLAGS} -o $@ $<
diff --git a/src/core/attr.c b/src/core/attr.c
new file mode 100644
index 0000000..32fa66f
--- /dev/null
+++ b/src/core/attr.c
@@ -0,0 +1,173 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#include <dom/core/attr.h>
+
+#include "core/node.h"
+#include "utils/utils.h"
+
+struct dom_element;
+struct dom_typeinfo;
+
+/**
+ * DOM node attribute
+ */
+struct dom_attr {
+ struct dom_node base; /**< Base node */
+
+ bool specified; /**< Whether attribute was specified
+ * or defaulted */
+
+ struct dom_element *owner; /**< Owning element */
+
+ struct dom_typeinfo *schema_type_info; /**< Type information */
+
+ bool is_id; /**< Attribute is of type ID */
+};
+
+/**
+ * Retrieve an attribute's name
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr Attribute to retrieve name from
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_attr_get_name(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(attr);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Determine if attribute was specified or defaulted
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr Attribute to inspect
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_attr_get_specified(struct dom_ctx *ctx,
+ struct dom_attr *attr, bool *result)
+{
+ UNUSED(ctx);
+
+ *result = attr->specified;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve an attribute's value
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr Attribute to retrieve value from
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_attr_get_value(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(attr);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Set an attribute's value
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr Attribute to retrieve value from
+ * \param value New value for attribute
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if attribute is readonly.
+ */
+dom_exception dom_attr_set_value(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_string *value)
+{
+ UNUSED(ctx);
+ UNUSED(attr);
+ UNUSED(value);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the owning element of an attribute
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr The attribute to extract owning element from
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. The caller
+ * should unref it once it has finished with it.
+ */
+dom_exception dom_attr_get_owner(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_element **result)
+{
+ /* If there is an owning element, increase its reference count */
+ if (attr->owner != NULL)
+ dom_node_ref(ctx, (struct dom_node *) attr->owner);
+
+ *result = attr->owner;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve an attribute's type information
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr The attribute to extract type information from
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned typeinfo will have its reference count increased. The caller
+ * should unref it once it has finished with it.
+ */
+dom_exception dom_attr_get_typeinfo(struct dom_ctx *ctx,
+ struct dom_attr *attr, struct dom_typeinfo **result)
+{
+ UNUSED(ctx);
+ UNUSED(attr);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Determine if an attribute if of type ID
+ *
+ * \param ctx The context in which the attribute resides
+ * \param attr The attribute to inspect
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_attr_is_id(struct dom_ctx *ctx,
+ struct dom_attr *attr, bool *result)
+{
+ UNUSED(ctx);
+
+ *result = attr->is_id;
+
+ return DOM_NO_ERR;
+}
diff --git a/src/core/document.c b/src/core/document.c
new file mode 100644
index 0000000..1d54dfd
--- /dev/null
+++ b/src/core/document.c
@@ -0,0 +1,356 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#include <dom/core/document.h>
+
+#include "core/node.h"
+#include "utils/utils.h"
+
+/**
+ * DOM document
+ */
+struct dom_document {
+ struct dom_node base; /**< Base node */
+};
+
+
+dom_exception dom_document_get_doctype(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_document_type **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_implementation(struct dom_ctx *ctx,
+ struct dom_document *doc,
+ struct dom_implementation **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_element(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_element **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_element(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *tag_name,
+ struct dom_element **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(tag_name);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_document_fragment(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_text_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *data,
+ struct dom_text **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(data);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_cdata_section(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *data,
+ struct dom_text **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(data);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_processing_instruction(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *target,
+ struct dom_string *data,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(target);
+ UNUSED(data);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_attribute(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *name,
+ struct dom_attr **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(name);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_entity_reference(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *name,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(name);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_elements_by_tag_name(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *tagname,
+ struct dom_nodelist **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(tagname);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_import_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ bool deep, struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(node);
+ UNUSED(deep);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_element_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *qname, struct dom_element **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(namespace);
+ UNUSED(qname);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_create_attribute_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *qname, struct dom_attr **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(namespace);
+ UNUSED(qname);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_elements_by_tag_name_ns(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_nodelist **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(namespace);
+ UNUSED(localname);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_element_by_id(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *id,
+ struct dom_element **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(id);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_input_encoding(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_xml_encoding(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_xml_standalone(struct dom_ctx *ctx,
+ struct dom_document *doc, bool *result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_set_xml_standalone(struct dom_ctx *ctx,
+ struct dom_document *doc, bool standalone)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(standalone);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_xml_version(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_set_xml_version(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *version)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(version);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_strict_error_checking(struct dom_ctx *ctx,
+ struct dom_document *doc, bool *result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_set_strict_error_checking(struct dom_ctx *ctx,
+ struct dom_document *doc, bool strict)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(strict);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_uri(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_set_uri(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_string *uri)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(uri);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_adopt_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_get_dom_config(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_configuration **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_normalize(struct dom_ctx *ctx,
+ struct dom_document *doc)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception dom_document_rename_node(struct dom_ctx *ctx,
+ struct dom_document *doc, struct dom_node *node,
+ struct dom_string *namespace, struct dom_string *qname,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(doc);
+ UNUSED(node);
+ UNUSED(namespace);
+ UNUSED(qname);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
diff --git a/src/core/document.h b/src/core/document.h
new file mode 100644
index 0000000..399b2c8
--- /dev/null
+++ b/src/core/document.h
@@ -0,0 +1,19 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_internal_document_h_
+#define dom_internal_document_h_
+
+#include <inttypes.h>
+
+struct dom_ctx;
+struct dom_document;
+
+const uint8_t *dom_document_get_base(struct dom_ctx *ctx,
+ struct dom_document *doc);
+
+#endif
diff --git a/src/core/node.c b/src/core/node.c
new file mode 100644
index 0000000..4c2af88
--- /dev/null
+++ b/src/core/node.c
@@ -0,0 +1,1069 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#include <dom/ctx.h>
+
+#include "core/node.h"
+#include "utils/utils.h"
+
+/**
+ * Create a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param doc The document which owns the node
+ * \param type The node type required
+ * \param name The node name, or NULL
+ * \param value The node value, or NULL
+ * \param node Pointer to location to receive created node
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * The returned node will be referenced, so there is no need for the caller
+ * to explicitly reference it.
+ */
+dom_exception dom_node_create(struct dom_ctx *ctx,
+ struct dom_document *doc, dom_node_type type,
+ struct dom_string *name, struct dom_string *value,
+ struct dom_node **node)
+{
+ struct dom_node *n;
+
+ n = ctx->alloc(NULL, sizeof(struct dom_node), ctx->pw);
+ if (n == NULL)
+ return DOM_NO_MEM_ERR;
+
+ if (name != NULL)
+ dom_string_ref(ctx, name);
+ n->name = name;
+
+ if (value != NULL)
+ dom_string_ref(ctx, value);
+ n->value = value;
+
+ n->type = type;
+
+ n->parent = NULL;
+ n->first_child = NULL;
+ n->last_child = NULL;
+ n->previous = NULL;
+ n->next = NULL;
+ n->attributes = NULL;
+
+ dom_node_ref(ctx, (struct dom_node *) doc);
+ n->owner = doc;
+
+ /** \todo Namespace handling */
+ n->namespace = NULL;
+ n->prefix = NULL;
+ n->localname = NULL;
+
+ n->user_data = NULL;
+
+ n->refcnt = 1;
+
+ *node = n;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Claim a reference on a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to claim a reference on
+ */
+void dom_node_ref(struct dom_ctx *ctx, struct dom_node *node)
+{
+ UNUSED(ctx);
+
+ node->refcnt++;
+}
+
+/**
+ * Release a reference on a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to release the reference from
+ *
+ * If the reference count reaches zero, any memory claimed by the
+ * node will be released
+ */
+void dom_node_unref(struct dom_ctx *ctx, struct dom_node *node)
+{
+ UNUSED(ctx);
+
+ if (--node->refcnt == 0) {
+ /** \todo implement */
+ }
+}
+
+/**
+ * Retrieve the name of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the name of
+ * \param result Pointer to location to receive node name
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_name(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the value of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the value of
+ * \param result Pointer to location to receive node value
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ *
+ * DOM3Core states that this can raise DOMSTRING_SIZE_ERR. It will not in
+ * this implementation; dom_strings are unbounded.
+ */
+dom_exception dom_node_get_value(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Set the value of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node Node to set the value of
+ * \param value New value for node
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if the node is readonly and the
+ * value is not defined to be null.
+ *
+ * The new value will have its reference count increased, so the caller
+ * should unref it after the call (as the caller should have already claimed
+ * a reference on the string). The node's existing value will be unrefed.
+ */
+dom_exception dom_node_set_value(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *value)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(value);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the type of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the type of
+ * \param result Pointer to location to receive node type
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_get_type(struct dom_ctx *ctx,
+ struct dom_node *node, dom_node_type *result)
+{
+ UNUSED(ctx);
+
+ *result = node->type;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the parent of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the parent of
+ * \param result Pointer to location to receive node parent
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_parent(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result)
+{
+ /* If there is a parent node, then increase its reference count */
+ if (node->parent != NULL)
+ dom_node_ref(ctx, node->parent);
+
+ *result = node->parent;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve a list of children of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the children of
+ * \param result Pointer to location to receive child list
+ * \return DOM_NO_ERR.
+ *
+ * \todo Work out reference counting semantics of dom_node_list
+ */
+dom_exception dom_node_get_children(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node_list **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the first child of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the first child of
+ * \param result Pointer to location to receive node's first child
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_first_child(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result)
+{
+ /* If there is a first child, increase its reference count */
+ if (node->first_child != NULL)
+ dom_node_ref(ctx, node->first_child);
+
+ *result = node->first_child;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the last child of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the last child of
+ * \param result Pointer to location to receive node's last child
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_last_child(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result)
+{
+ /* If there is a last child, increase its reference count */
+ if (node->last_child != NULL)
+ dom_node_ref(ctx, node->last_child);
+
+ *result = node->last_child;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the previous sibling of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the previous sibling of
+ * \param result Pointer to location to receive node's previous sibling
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_previous(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result)
+{
+ /* If there is a previous sibling, increase its reference count */
+ if (node->previous != NULL)
+ dom_node_ref(ctx, node->previous);
+
+ *result = node->previous;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the subsequent sibling of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the subsequent sibling of
+ * \param result Pointer to location to receive node's subsequent sibling
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_next(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node **result)
+{
+ /* If there is a subsequent sibling, increase its reference count */
+ if (node->next != NULL)
+ dom_node_ref(ctx, node->next);
+
+ *result = node->next;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve a map of attributes associated with a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the attributes of
+ * \param result Pointer to location to receive attribute map
+ * \return DOM_NO_ERR.
+ *
+ * \todo Work out reference counting semantics of dom_named_node_map
+ */
+dom_exception dom_node_get_attributes(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_named_node_map **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the owning document of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the owner of
+ * \param result Pointer to location to receive node's owner
+ * \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_owner(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_document **result)
+{
+ /* If there is an owner, increase its reference count */
+ if (node->owner != NULL)
+ dom_node_ref(ctx, (struct dom_node *) node->owner);
+
+ *result = node->owner;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Insert a child into a node
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node Node to insert into
+ * \param new_child Node to insert
+ * \param ref_child Node to insert before, or NULL to insert as last child
+ * \param result Pointer to location to receive node being inserted
+ * \return DOM_NO_ERR on success,
+ * DOM_HIERARCHY_REQUEST_ERR if ::new_child's type is not
+ * permitted as a child of ::node,
+ * or ::new_child is an ancestor of
+ * ::node (or is ::node itself), or
+ * ::node is of type Document and a
+ * second DocumentType or Element is
+ * being inserted,
+ * DOM_WRONG_DOCUMENT_ERR if ::new_child was created from a
+ * different document than ::node,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly, or
+ * ::new_child's parent is readonly,
+ * DOM_NOT_FOUND_ERR if ::ref_child is not a child of
+ * ::node,
+ * DOM_NOT_SUPPORTED_ERR if ::node is of type Document and
+ * ::new_child is of type
+ * DocumentType or Element.
+ *
+ * If ::new_child is a DocumentFragment, all of its children are inserted.
+ * If ::new_child is already in the tree, it is first removed.
+ *
+ * ::new_child's reference count will be increased. The caller should unref
+ * it (as they should already have held a reference on the node)
+ */
+dom_exception dom_node_insert_before(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child, struct dom_node *ref_child,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(new_child);
+ UNUSED(ref_child);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Replace a node's child with a new one
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node Node whose child to replace
+ * \param new_child Replacement node
+ * \param old_child Child to replace
+ * \param result Pointer to location to receive replaced node
+ * \return DOM_NO_ERR on success,
+ * DOM_HIERARCHY_REQUEST_ERR if ::new_child's type is not
+ * permitted as a child of ::node,
+ * or ::new_child is an ancestor of
+ * ::node (or is ::node itself), or
+ * ::node is of type Document and a
+ * second DocumentType or Element is
+ * being inserted,
+ * DOM_WRONG_DOCUMENT_ERR if ::new_child was created from a
+ * different document than ::node,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly, or
+ * ::new_child's parent is readonly,
+ * DOM_NOT_FOUND_ERR if ::old_child is not a child of
+ * ::node,
+ * DOM_NOT_SUPPORTED_ERR if ::node is of type Document and
+ * ::new_child is of type
+ * DocumentType or Element.
+ *
+ * If ::new_child is a DocumentFragment, ::old_child is replaced by all of
+ * ::new_child's children.
+ * If ::new_child is already in the tree, it is first removed.
+ *
+ * ::new_child's reference count will be increased. The caller should unref
+ * it (as they should already have held a reference on the node)
+ *
+ * ::old_child's reference count remains unmodified (::node's reference is
+ * transferred to the caller). The caller should unref ::old_child once it
+ * is finished with it.
+ */
+dom_exception dom_node_replace_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child, struct dom_node *old_child,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(new_child);
+ UNUSED(old_child);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Remove a child from a node
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node Node whose child to replace
+ * \param old_child Child to remove
+ * \param result Pointer to location to receive removed node
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly
+ * DOM_NOT_FOUND_ERR if ::old_child is not a child of
+ * ::node,
+ * DOM_NOT_SUPPORTED_ERR if ::node is of type Document and
+ * ::new_child is of type
+ * DocumentType or Element.
+ *
+ * ::old_child's reference count remains unmodified (::node's reference is
+ * transferred to the caller). The caller should unref ::old_child once it
+ * is finished with it.
+ */
+dom_exception dom_node_remove_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *old_child,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(old_child);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Append a child to the end of a node's child list
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node Node to insert into
+ * \param new_child Node to append
+ * \param result Pointer to location to receive node being inserted
+ * \return DOM_NO_ERR on success,
+ * DOM_HIERARCHY_REQUEST_ERR if ::new_child's type is not
+ * permitted as a child of ::node,
+ * or ::new_child is an ancestor of
+ * ::node (or is ::node itself), or
+ * ::node is of type Document and a
+ * second DocumentType or Element is
+ * being inserted,
+ * DOM_WRONG_DOCUMENT_ERR if ::new_child was created from a
+ * different document than ::node,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly, or
+ * ::new_child's parent is readonly,
+ * DOM_NOT_SUPPORTED_ERR if ::node is of type Document and
+ * ::new_child is of type
+ * DocumentType or Element.
+ *
+ * If ::new_child is a DocumentFragment, all of its children are inserted.
+ * If ::new_child is already in the tree, it is first removed.
+ *
+ * ::new_child's reference count will be increased. The caller should unref
+ * it (as they should already have held a reference on the node)
+ */
+dom_exception dom_node_append_child(struct dom_ctx *ctx,
+ struct dom_node *node,
+ struct dom_node *new_child,
+ struct dom_node **result)
+{
+ /* This is just a veneer over insert_before */
+ return dom_node_insert_before(ctx, node, new_child, NULL, result);
+}
+
+/**
+ * Determine if a node has any children
+ *
+ * \param ctx The context in which the node resides
+ * \param node Node to inspect
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_has_children(struct dom_ctx *ctx,
+ struct dom_node *node, bool *result)
+{
+ UNUSED(ctx);
+
+ *result = node->first_child != NULL;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Clone a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to clone
+ * \param deep True to deep-clone the node's sub-tree
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MEMORY_ERR on memory exhaustion.
+ *
+ * The returned node will already be referenced.
+ *
+ * The duplicate node will have no parent and no user data.
+ *
+ * If ::node has registered user_data_handlers, then they will be called.
+ *
+ * Cloning an Element copies all attributes & their values (including those
+ * generated by the XML processor to represent defaulted attributes). It
+ * does not copy any child nodes unless it is a deep copy (this includes
+ * text contained within the Element, as the text is contained in a child
+ * Text node).
+ *
+ * Cloning an Attr directly, as opposed to cloning as part of an Element,
+ * returns a specified attribute. Cloning an Attr always clones its children,
+ * since they represent its value, no matter whether this is a deep clone or
+ * not.
+ *
+ * Cloning an EntityReference automatically constructs its subtree if a
+ * corresponding Entity is available, no matter whether this is a deep clone
+ * or not.
+ *
+ * Cloning any other type of node simply returns a copy.
+ *
+ * Note that cloning an immutable subtree results in a mutable copy, but
+ * the children of an EntityReference clone are readonly. In addition, clones
+ * of unspecified Attr nodes are specified.
+ *
+ * \todo work out what happens when cloning Document, DocumentType, Entity
+ * and Notation nodes.
+ */
+dom_exception dom_node_clone(struct dom_ctx *ctx,
+ struct dom_node *node, bool deep,
+ struct dom_node **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(deep);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Normalize a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to normalize
+ * \return DOM_NO_ERR.
+ *
+ * Puts all Text nodes in the full depth of the sub-tree beneath ::node,
+ * including Attr nodes into "normal" form, where only structure separates
+ * Text nodes.
+ */
+dom_exception dom_node_normalize(struct dom_ctx *ctx,
+ struct dom_node *node)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Test whether the DOM implementation implements a specific feature and
+ * that feature is supported by the node.
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to test
+ * \param feature The name of the feature to test
+ * \param version The version number of the feature to test
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_is_supported(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *feature,
+ struct dom_node *version, bool *result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(feature);
+ UNUSED(version);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the namespace of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the namespace of
+ * \param result Pointer to location to receive node's namespace
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ /* If there is a namespace, increase its reference count */
+ if (node->namespace != NULL)
+ dom_string_ref(ctx, node->namespace);
+
+ *result = node->namespace;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the prefix of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the prefix of
+ * \param result Pointer to location to receive node's prefix
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ /* If there is a prefix, increase its reference count */
+ if (node->prefix != NULL)
+ dom_string_ref(ctx, node->prefix);
+
+ *result = node->prefix;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Set the prefix of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to set the prefix of
+ * \param prefix Pointer to prefix string
+ * \return DOM_NO_ERR on success,
+ * DOM_INVALID_CHARACTER_ERR if the specified prefix contains
+ * an illegal character,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly,
+ * DOM_NAMESPACE_ERR if the specified prefix is
+ * malformed, if the namespaceURI of
+ * ::node is null, if the specified
+ * prefix is "xml" and the
+ * namespaceURI is different from
+ * "http://www.w3.org/XML/1998/namespace",
+ * if ::node is an attribute and the
+ * specified prefix is "xmlns" and
+ * the namespaceURI is different from
+ * "http://www.w3.org/2000/xmlns",
+ * or if this node is an attribute
+ * and the qualifiedName of ::node
+ * is "xmlns".
+ */
+dom_exception dom_node_set_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *prefix)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(prefix);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the local part of a node's qualified name
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the local name of
+ * \param result Pointer to location to receive local name
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_local_name(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ /* If there is a local name, increase its reference count */
+ if (node->localname != NULL)
+ dom_string_ref(ctx, node->localname);
+
+ *result = node->localname;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Determine if a node has any attributes
+ *
+ * \param ctx The context in which the node resides
+ * \param node Node to inspect
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_has_attributes(struct dom_ctx *ctx,
+ struct dom_node *node, bool *result)
+{
+ UNUSED(ctx);
+
+ *result = node->attributes != NULL;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve the base URI of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the base URI of
+ * \param result Pointer to location to receive base URI
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_get_base(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Compare the positions of two nodes in a DOM tree
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node The reference node
+ * \param other The node to compare
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR on success,
+ * DOM_NOT_SUPPORTED_ERR when the nodes are from different DOM
+ * implementations.
+ *
+ * The result is a bitfield of dom_document_position values.
+ */
+dom_exception dom_node_compare_document_position(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ uint16_t *result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(other);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve the text content of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve the text content of
+ * \param result Pointer to location to receive text content
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ *
+ * DOM3Core states that this can raise DOMSTRING_SIZE_ERR. It will not in
+ * this implementation; dom_strings are unbounded.
+ */
+dom_exception dom_node_get_text_content(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Set the text content of a DOM node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to set the text content of
+ * \param content New text content for node
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly.
+ *
+ * Any child nodes ::node may have are removed and replaced with a single
+ * Text node containing the new content.
+ */
+dom_exception dom_node_set_text_content(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *content)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(content);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Determine if two DOM nodes are the same
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node The node to compare
+ * \param other The node to compare against
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * This tests if the two nodes reference the same object.
+ */
+dom_exception dom_node_is_same(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ bool *result)
+{
+ UNUSED(ctx);
+
+ *result = (node == other);
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Lookup the prefix associated with the given namespace URI
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to start prefix search from
+ * \param namespace The namespace URI
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_lookup_prefix(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *namespace,
+ struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(namespace);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Determine if the specified namespace is the default namespace
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to query
+ * \param namespace The namespace URI to test
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_is_default_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *namespace,
+ bool *result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(namespace);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Lookup the namespace URI associated with the given prefix
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to start namespace search from
+ * \param prefix The prefix to look for, or NULL to find default.
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * The returned string will have its reference count increased. It is
+ * the responsibility of the caller to unref the string once it has
+ * finished with it.
+ */
+dom_exception dom_node_lookup_namespace(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *prefix,
+ struct dom_string **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(prefix);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Determine if two DOM nodes are equal
+ *
+ * \param ctx The context in which the nodes reside
+ * \param node The node to compare
+ * \param other The node to compare against
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ *
+ * Two nodes are equal iff:
+ * + They are of the same type
+ * + nodeName, localName, namespaceURI, prefix, nodeValue are equal
+ * + The node attributes are equal
+ * + The child nodes are equal
+ *
+ * Two DocumentType nodes are equal iff:
+ * + publicId, systemId, internalSubset are equal
+ * + The node entities are equal
+ * + The node notations are equal
+ */
+dom_exception dom_node_is_equal(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_node *other,
+ bool *result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(other);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve an object which implements the specialized APIs of the specified
+ * feature and version.
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to query
+ * \param feature The requested feature
+ * \param version The version number of the feature
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_get_feature(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *feature,
+ struct dom_string *version, void **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(feature);
+ UNUSED(version);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Associate an object to a key on this node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to insert object into
+ * \param key The key associated with the object
+ * \param data The object to associate with key, or NULL to remove
+ * \param handler User handler function, or NULL if none
+ * \param result Pointer to location to receive previously associated object
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_set_user_data(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *key,
+ void *data, dom_user_data_handler handler,
+ void **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(key);
+ UNUSED(data);
+ UNUSED(handler);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieves the object associated to a key on this node
+ *
+ * \param ctx The context in which the node resides
+ * \param node The node to retrieve object from
+ * \param key The key to search for
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_node_get_user_data(struct dom_ctx *ctx,
+ struct dom_node *node, struct dom_string *key,
+ void **result)
+{
+ UNUSED(ctx);
+ UNUSED(node);
+ UNUSED(key);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
diff --git a/src/core/node.h b/src/core/node.h
new file mode 100644
index 0000000..b39ac40
--- /dev/null
+++ b/src/core/node.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_internal_core_node_h_
+#define dom_internal_core_node_h_
+
+#include <dom/core/node.h>
+#include <dom/core/string.h>
+
+struct dom_attr;
+
+/**
+ * User data context attached to a DOM node
+ */
+struct dom_user_data {
+ struct dom_string *key; /**< Key for data */
+ void *data; /**< Client-specific data */
+ dom_user_data_handler handler; /**< Callback function */
+
+ struct dom_user_data *next; /**< Next in list */
+ struct dom_user_data *prev; /**< Previous in list */
+};
+
+/**
+ * DOM node object
+ *
+ * DOM nodes are reference counted
+ */
+struct dom_node {
+ struct dom_string *name; /**< Node name */
+ struct dom_string *value; /**< Node value */
+ dom_node_type type; /**< Node type */
+ struct dom_node *parent; /**< Parent node */
+ struct dom_node *first_child; /**< First child node */
+ struct dom_node *last_child; /**< Last child node */
+ struct dom_node *previous; /**< Previous sibling */
+ struct dom_node *next; /**< Next sibling */
+ struct dom_attr *attributes; /**< Node attributes */
+
+ struct dom_document *owner; /**< Owning document */
+
+ struct dom_string *namespace; /**< Namespace URI */
+ struct dom_string *prefix; /**< Namespace prefix */
+ struct dom_string *localname; /**< Local part of qualified name */
+
+ struct dom_user_data *user_data; /**< User data list */
+
+ uint32_t refcnt; /**< Reference count */
+};
+
+dom_exception dom_node_create(struct dom_ctx *ctx,
+ struct dom_document *doc, dom_node_type type,
+ struct dom_string *name, struct dom_string *value,
+ struct dom_node **node);
+
+#endif
diff --git a/src/core/string.c b/src/core/string.c
new file mode 100644
index 0000000..070af75
--- /dev/null
+++ b/src/core/string.c
@@ -0,0 +1,222 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <dom/ctx.h>
+#include <dom/core/string.h>
+
+#include "core/document.h"
+#include "utils/utils.h"
+
+/**
+ * A DOM string
+ *
+ * DOM strings store either a pointer to allocated data, a pointer
+ * to constant data or an offset into a document buffer.
+ *
+ * They are reference counted so freeing is performed correctly.
+ */
+struct dom_string {
+ enum { DOM_STRING_PTR,
+ DOM_STRING_CONST_PTR,
+ DOM_STRING_OFFSET
+ } type;
+ union {
+ uint8_t *ptr;
+ const uint8_t *cptr;
+ struct {
+ struct dom_document *doc;
+ uint32_t off;
+ } offset;
+ } data;
+
+ size_t len;
+
+ uint32_t refcnt;
+};
+
+/**
+ * Claim a reference on a DOM string
+ *
+ * \param ctx The context in which the string resides
+ * \param str The string to claim a reference on
+ */
+void dom_string_ref(struct dom_ctx *ctx, struct dom_string *str)
+{
+ UNUSED(ctx);
+
+ str->refcnt++;
+}
+
+/**
+ * Release a reference on a DOM string
+ *
+ * \param ctx The context in which the string resides
+ * \param str The string to release the reference from
+ *
+ * If the reference count reaches zero, any memory claimed by the
+ * string will be released
+ */
+void dom_string_unref(struct dom_ctx *ctx, struct dom_string *str)
+{
+ if (--str->refcnt == 0) {
+ if (str->type == DOM_STRING_PTR)
+ ctx->alloc(str->data.ptr, 0, ctx->pw);
+
+ ctx->alloc(str, 0, ctx->pw);
+ }
+}
+
+/**
+ * Create a DOM string from an offset into the document buffer
+ *
+ * \param ctx The context in which the string resides
+ * \param doc The document in which the string resides
+ * \param off Offset from start of document buffer
+ * \param len Length, in bytes, of string
+ * \param str Pointer to location to receive pointer to new string
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * The returned string will already be referenced, so there is no need
+ * to explicitly reference it.
+ */
+dom_exception dom_string_create_from_off(struct dom_ctx *ctx,
+ struct dom_document *doc, uint32_t off, size_t len,
+ struct dom_string **str)
+{
+ struct dom_string *ret;
+
+ ret = ctx->alloc(NULL, sizeof(struct dom_string), ctx->pw);
+ if (ret == NULL)
+ return DOM_NO_MEM_ERR;
+
+ ret->type = DOM_STRING_OFFSET;
+ ret->data.offset.doc = doc;
+ ret->data.offset.off = off;
+ ret->len = len;
+
+ ret->refcnt = 1;
+
+ *str = ret;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Create a DOM string from a string of characters
+ *
+ * \param ctx The context in which the string resides
+ * \param ptr Pointer to string of characters
+ * \param len Length, in bytes, of string of characters
+ * \param str Pointer to location to receive pointer to new string
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * The returned string will already be referenced, so there is no need
+ * to explicitly reference it.
+ *
+ * The string of characters passed in will be copied for use by the
+ * returned DOM string.
+ */
+dom_exception dom_string_create_from_ptr(struct dom_ctx *ctx,
+ const uint8_t *ptr, size_t len, struct dom_string **str)
+{
+ struct dom_string *ret;
+
+ ret = ctx->alloc(NULL, sizeof(struct dom_string), ctx->pw);
+ if (ret == NULL)
+ return DOM_NO_MEM_ERR;
+
+ ret->data.ptr = ctx->alloc(NULL, len, ctx->pw);
+ if (ret->data.ptr == NULL) {
+ ctx->alloc(ret, 0, ctx->pw);
+ return DOM_NO_MEM_ERR;
+ }
+
+ ret->type = DOM_STRING_PTR;
+
+ memcpy(ret->data.ptr, ptr, len);
+
+ ret->len = len;
+
+ ret->refcnt = 1;
+
+ *str = ret;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Create a DOM string from a constant string of characters
+ *
+ * \param ctx The context in which the string resides
+ * \param ptr Pointer to string of characters
+ * \param len Length, in bytes, of string of characters
+ * \param str Pointer to location to receive pointer to new string
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * The returned string will already be referenced, so there is no need
+ * to explicitly reference it.
+ *
+ * The string of characters passed in will _not_ be copied for use by the
+ * returned DOM string.
+ */
+dom_exception dom_string_create_from_const_ptr(struct dom_ctx *ctx,
+ const uint8_t *ptr, size_t len, struct dom_string **str)
+{
+ struct dom_string *ret;
+
+ ret = ctx->alloc(NULL, sizeof(struct dom_string), ctx->pw);
+ if (ret == NULL)
+ return DOM_NO_MEM_ERR;
+
+ ret->data.cptr = ptr;
+
+ ret->type = DOM_STRING_CONST_PTR;
+
+ ret->len = len;
+
+ ret->refcnt = 1;
+
+ *str = ret;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Get a pointer to the string of characters within a DOM string
+ *
+ * \param ctx The context in which the string resides
+ * \param str Pointer to DOM string to retrieve pointer from
+ * \param data Pointer to location to receive data
+ * \param len Pointer to location to receive byte length of data
+ * \return DOM_NO_ERR on success
+ *
+ * The caller must have previously claimed a reference on the DOM string.
+ * The returned pointer must not be freed.
+ */
+dom_exception dom_string_get_data(struct dom_ctx *ctx,
+ struct dom_string *str, const uint8_t **data, size_t *len)
+{
+ switch (str->type) {
+ case DOM_STRING_PTR:
+ *data = str->data.ptr;
+ break;
+ case DOM_STRING_CONST_PTR:
+ *data = str->data.cptr;
+ break;
+ case DOM_STRING_OFFSET:
+ *data = dom_document_get_base(ctx, str->data.offset.doc) +
+ str->data.offset.off;
+ break;
+ }
+
+ *len = str->len;
+
+ return DOM_NO_ERR;
+}
diff --git a/src/utils/utils.h b/src/utils/utils.h
new file mode 100644
index 0000000..ae861c7
--- /dev/null
+++ b/src/utils/utils.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+#ifndef dom_utils_h_
+#define dom_utils_h_
+
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef SLEN
+/* Calculate length of a string constant */
+#define SLEN(s) (sizeof((s)) - 1) /* -1 for '\0' */
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x) ((x)=(x))
+#endif
+
+#endif
diff --git a/test/INDEX b/test/INDEX
new file mode 100644
index 0000000..a1b7297
--- /dev/null
+++ b/test/INDEX
@@ -0,0 +1,4 @@
+# Index for libdom testcases
+#
+# Test Description DataDir
+
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..d9bb137
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,60 @@
+# Makefile for Hubbub testcases
+#
+# Toolchain is exported by top-level makefile
+#
+# Top-level makefile also exports the following variables:
+#
+# COMPONENT Name of component
+# EXPORT Absolute path of export directory
+# TOP Absolute path of source tree root
+#
+# The top-level makefile requires the following targets to exist:
+#
+# clean Clean source tree
+# debug Create a debug binary
+# distclean Fully clean source tree, back to pristine condition
+# export Export distributable components to ${EXPORT}
+# release Create a release binary
+# setup Perform any setup required prior to compilation
+# test Execute any test cases
+
+# Extend toolchain settings
+CFLAGS += -I${TOP}/src/ -I$(CURDIR)
+
+# Release output
+RELEASE =
+
+# Debug output
+DEBUG =
+
+# Objects
+OBJS =
+
+.PHONY: clean debug export release setup test
+
+# Targets
+release:
+
+debug:
+
+clean:
+ifneq (${OBJS}, )
+ -@${RM} ${RMFLAGS} $(addsuffix ${EXEEXT}, $(OBJS))
+endif
+
+distclean:
+ -@${RM} ${RMFLAGS} log
+
+setup:
+
+export:
+
+test: $(OBJS)
+ @${PERL} testrunner.pl ${EXEEXT}
+
+# Pattern rules
+%: %.c
+ @${ECHO} ${ECHOFLAGS} "==> $<"
+ @${CC} -c -g ${CFLAGS} -o $@.o $<
+ @${LD} -g -o $@ $@.o ${LDFLAGS} -ldom-debug
+ @${RM} ${RMFLAGS} $@.o
diff --git a/test/testrunner.pl b/test/testrunner.pl
new file mode 100644
index 0000000..00c54e7
--- /dev/null
+++ b/test/testrunner.pl
@@ -0,0 +1,147 @@
+#!/bin/perl
+#
+# Testcase runner for libhubbub
+#
+# Usage: testrunner <executable extension>
+#
+# Operates upon INDEX files described in the README.
+# Locates and executes testcases, feeding data files to programs
+# as appropriate.
+# Logs testcase output to file.
+# Aborts test sequence on detection of error.
+#
+
+use warnings;
+use strict;
+use File::Spec;
+use IPC::Open3;
+
+# Get EXE extension (if any)
+my $exeext = "";
+$exeext = shift @ARGV if (@ARGV > 0);
+
+# Open log file and /dev/null
+open(LOG, ">log") or die "Failed opening test log";
+open(NULL, "+<", File::Spec->devnull) or die "Failed opening /dev/null";
+
+# Open testcase index
+open(TINDEX, "<INDEX") or die "Failed opening test INDEX";
+
+# Parse testcase index, looking for testcases
+while (my $line = <TINDEX>) {
+ next if ($line =~ /^(#.*)?$/);
+
+ # Found one; decompose
+ (my $test, my $desc, my $data) = split /\t+/, $line;
+
+ # Strip whitespace
+ $test =~ s/^\s+|\s+$//g;
+ $desc =~ s/^\s+|\s+$//g;
+ $data =~ s/^\s+|\s+$//g if ($data);
+
+ # Append EXE extension to binary name
+ $test = $test . $exeext;
+
+ print "Test: $desc\n";
+
+ my $pid;
+
+ if ($data) {
+ # Testcase has external data files
+
+ # Open datafile index
+ open(DINDEX, "<./data/$data/INDEX") or
+ die "Failed opening ./data/$data/INDEX";
+
+ # Parse datafile index, looking for datafiles
+ while (my $dentry = <DINDEX>) {
+ next if ($dentry =~ /^(#.*)?$/);
+
+ # Found one; decompose
+ (my $dtest, my $ddesc) = split /\t+/, $dentry;
+
+ # Strip whitespace
+ $dtest =~ s/^\s+|\s+$//g;
+ $ddesc =~ s/^\s+|\s+$//g;
+
+ print LOG "Running ./$test ./data/Aliases " .
+ "./data/$data/$dtest\n";
+
+ # Make message fit on an 80 column terminal
+ my $msg = " ==> $test [$data/$dtest]";
+ $msg = $msg . "." x (80 - length($msg) - 8);
+
+ print $msg;
+
+ # Run testcase
+ $pid = open3("&<NULL", \*OUT, ">&NULL",
+ "./$test", "./data/Aliases",
+ "./data/$data/$dtest");
+
+ my $last;
+
+ # Marshal testcase output to log file
+ while (my $output = <OUT>) {
+ print LOG " $output";
+ $last = $output;
+ }
+
+ # Wait for child to finish
+ waitpid($pid, 0);
+
+ print substr($last, 0, 4) . "\n";
+
+ # Bail, noisily, on failure
+ if (substr($last, 0, 4) eq "FAIL") {
+ print "\n\nFailure detected: " .
+ "consult log file\n\n\n";
+
+ exit(1);
+ }
+ }
+
+ close(DINDEX);
+ } else {
+ # Testcase has no external data files
+ print LOG "Running ./$test ./data/Aliases\n";
+
+ # Make message fit on an 80 column terminal
+ my $msg = " ==> $test";
+ $msg = $msg . "." x (80 - length($msg) - 8);
+
+ print $msg;
+
+ # Run testcase
+ $pid = open3("&<NULL", \*OUT, "&>NULL",
+ "./$test", "./data/Aliases");
+
+ my $last;
+
+ # Marshal testcase output to log file
+ while (my $output = <OUT>) {
+ print LOG " $output";
+ $last = $output;
+ }
+
+ # Wait for child to finish
+ waitpid($pid, 0);
+
+ print substr($last, 0, 4) . "\n";
+
+ # Bail, noisily, on failure
+ if (substr($last, 0, 4) eq "FAIL") {
+ print "\n\nFailure detected: " .
+ "consult log file\n\n\n";
+
+ exit(1);
+ }
+ }
+
+ print "\n";
+}
+
+# Clean up
+close(TINDEX);
+
+close(NULL);
+close(LOG);