diff -Nru clearsilver-0.10.4/Makefile clearsilver-0.10.5/Makefile
--- clearsilver-0.10.4/Makefile	2006-11-14 22:54:13.000000000 -0800
+++ clearsilver-0.10.5/Makefile	2007-07-11 19:43:28.000000000 -0700
@@ -102,14 +102,15 @@
 		rm -rf $$mdir/*; \
 	done
 	rm -f config.cache config.log config.status rules.mk cs_config.h
+	rm -rf autom4te.cache
 
 output_dir:
 	@for mdir in $(OUTDIRS); do \
 		mkdir -p $$mdir; \
 	done
 
-CS_DISTDIR = clearsilver-0.10.4
-CS_LABEL = CLEARSILVER-0_10_4
+CS_DISTDIR = clearsilver-0.10.5
+CS_LABEL = CLEARSILVER-0_10_5
 CS_FILES = README README.python INSTALL LICENSE CS_LICENSE rules.mk.in Makefile acconfig.h autogen.sh config.guess config.sub configure.in cs_config.h.in mkinstalldirs install-sh ClearSilver.h
 CS_DIRS = util cs cgi python scripts mod_ecs imd java-jni perl ruby dso csharp ports contrib m4
 
diff -Nru clearsilver-0.10.4/cgi/cgi.c clearsilver-0.10.5/cgi/cgi.c
--- clearsilver-0.10.4/cgi/cgi.c	2006-11-06 15:32:16.000000000 -0800
+++ clearsilver-0.10.5/cgi/cgi.c	2007-07-11 19:38:03.000000000 -0700
@@ -280,6 +280,11 @@
   return nerr_pass(neos_url_escape(in, esc, other));
 }
 
+NEOERR *cgi_url_validate (const char *buf, char **esc)
+{
+  return nerr_pass(neos_url_validate(buf, esc));
+}
+
 static NEOERR *_parse_query (CGI *cgi, char *query)
 {
   NEOERR *err = STATUS_OK;
@@ -384,7 +389,7 @@
   query = (char *) malloc (sizeof(char) * (len + 1));
   if (query == NULL)
     return nerr_raise (NERR_NOMEM,
-	"Unable to allocate memory to read POST input of length %d", l);
+	"Unable to allocate memory to read POST input of length %d", len);
 
 
   o = 0;
@@ -885,7 +890,7 @@
   stream.next_out = (Bytef*)obuf;
   stream.avail_out = (uInt)*olen;
   if ((uLong)stream.avail_out != *olen)
-    return nerr_raise(NERR_NOMEM, "Destination too big: %ld", *olen);
+    return nerr_raise(NERR_NOMEM, "Destination too big: %d", *olen);
 
   stream.zalloc = (alloc_func)0;
   stream.zfree = (free_func)0;
@@ -1285,6 +1290,8 @@
   if (err != STATUS_OK) return nerr_pass(err);
   err = cs_register_strfunc(cs, "html_strip", cgi_html_strip_strfunc);
   if (err != STATUS_OK) return nerr_pass(err);
+  err = cs_register_esc_strfunc(cs, "url_validate", cgi_url_validate);
+  if (err != STATUS_OK) return nerr_pass(err);
   return STATUS_OK;
 }
 
@@ -1382,7 +1389,7 @@
 void cgi_debug_init (int argc, char **argv)
 {
   FILE *fp;
-  char line[256];
+  char line[4096];
   char *v, *k;
 
   Argv0 = argv[0];
diff -Nru clearsilver-0.10.4/cgi/cgi.h clearsilver-0.10.5/cgi/cgi.h
--- clearsilver-0.10.4/cgi/cgi.h	2005-11-30 19:57:32.000000000 -0800
+++ clearsilver-0.10.5/cgi/cgi.h	2007-07-11 19:35:55.000000000 -0700
@@ -284,7 +284,8 @@
  * Output: None
  * Return: None
  */
-void cgi_error (CGI *cgi, const char *fmt, ...);
+void cgi_error (CGI *cgi, const char *fmt, ...)
+                ATTRIBUTE_PRINTF(2,3);
 
 /*
  * Function: cgi_debug_init - initialize standalone debugging
@@ -328,6 +329,20 @@
 NEOERR *cgi_url_escape_more (const char *buf, char **esc, const char *other);
 
 /*
+ * Function: cgi_url_validate - validate that url is of an allowed format
+ * Description: cgi_url_validate will check that a URL starts with 
+ *              one of the accepted safe schemes. 
+ *              If not, it returns "#" as a safe substitute.
+ *              Currently accepted schemes are http, https, ftp and mailto.
+ *              It then html escapes the entire URL so that it is safe to
+ *              insert in an href attribute.
+ * Input: buf - a 0 terminated string
+ * Output: esc - a newly allocated string 
+ * Return: NERR_NOMEM - no memory available to allocate the escaped string
+ */
+NEOERR *cgi_url_validate (const char *buf, char **esc);
+
+/*
  * Function: cgi_url_unescape - unescape an url encoded string
  * Description: cgi_url_unescape will do URL unescaping on the passed in
  *              string.  This function modifies the string in place
@@ -351,7 +366,8 @@
  * Output: None
  * Return: None
  */
-void cgi_redirect (CGI *cgi, const char *fmt, ...);
+void cgi_redirect (CGI *cgi, const char *fmt, ...)
+                   ATTRIBUTE_PRINTF(2,3);
 
 /*
  * Function: cgi_redirect_uri - send an HTTP 302 redirect response
@@ -367,7 +383,8 @@
  * Output: None
  * Return: None
  */
-void cgi_redirect_uri (CGI *cgi, const char *fmt, ...);
+void cgi_redirect_uri (CGI *cgi, const char *fmt, ...)
+                       ATTRIBUTE_PRINTF(2,3);
 
 /*
  * Function: cgi_vredirect - send an HTTP 302 redirect response
diff -Nru clearsilver-0.10.4/cgi/cgiwrap.c clearsilver-0.10.5/cgi/cgiwrap.c
--- clearsilver-0.10.4/cgi/cgiwrap.c	2006-03-28 16:12:37.000000000 -0800
+++ clearsilver-0.10.5/cgi/cgiwrap.c	2006-12-18 20:36:20.000000000 -0800
@@ -11,7 +11,9 @@
 
 #include "cs_config.h"
 
+#if HAVE_FEATURES_H
 #include <features.h>
+#endif
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
diff -Nru clearsilver-0.10.4/cgi/cgiwrap.h clearsilver-0.10.5/cgi/cgiwrap.h
--- clearsilver-0.10.4/cgi/cgiwrap.h	2005-06-30 18:22:12.000000000 -0700
+++ clearsilver-0.10.5/cgi/cgiwrap.h	2007-07-11 19:36:04.000000000 -0700
@@ -124,7 +124,8 @@
  * Output: None
  * Returns: NERR_SYSTEM
  */
-NEOERR *cgiwrap_writef (const char *fmt, ...);
+NEOERR *cgiwrap_writef (const char *fmt, ...)
+                        ATTRIBUTE_PRINTF(1,2);
 
 /* 
  * Function: cgiwrap_writevf - a wrapper for vprintf
diff -Nru clearsilver-0.10.4/cgi/fcgi_hello.c clearsilver-0.10.5/cgi/fcgi_hello.c
--- clearsilver-0.10.4/cgi/fcgi_hello.c	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/cgi/fcgi_hello.c	2007-07-05 16:14:46.000000000 -0700
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2006 Mike Tsao. All rights reserved.
+ *
+ * Hello World using FastCGI and ClearSilver.
+ */
+
+#include "ClearSilver.h"
+#include <string>
+#include <fcgi_stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+static bool quit = false;
+
+static int cs_printf(void *ctx, const char *s, va_list args) {
+  return printf(s, args);
+}
+
+static int cs_write(void *ctx, const char *s, int n) {
+  return fwrite(const_cast<char *>(s), n, 1, FCGI_stdout);
+}
+
+int main(int argc, char **argv, char **envp) {
+  openlog(argv[0], 0, LOG_USER);
+  syslog(LOG_INFO, "%s started.", argv[0]); 
+
+  int hits = 0;
+  while (FCGI_Accept() >= 0) {
+    HDF *hdf = NULL;
+    CGI *cgi = NULL;
+
+    /* Note that we aren't doing any error handling here, we really should. */
+    hdf_init(&hdf);
+
+    // Takes ownership of HDF.
+    cgi_init(&cgi, hdf);
+
+    hits++;
+
+    /* Initialize the standard cgiwrap environment.  FastCGI already wraps some
+     * of the standard calls that cgiwrap wraps.  */
+    cgiwrap_init_std(argc, argv, environ);
+
+    /* Then, we install our own wrappers for some cgiwrap calls that aren't
+     * already wrapped in the standard wrappers. */
+    cgiwrap_init_emu(NULL, NULL, cs_printf, cs_write, NULL, NULL, NULL);
+
+    hdf_read_file(cgi->hdf, "common.hdf");
+    hdf_read_file(cgi->hdf, "hello_world.hdf");
+
+    cgi_display(cgi, "hello_world.cs");
+
+    // This destroys HDF.
+    cgi_destroy(&cgi);
+  }
+  syslog(LOG_INFO, "%s ending.", argv[0]); 
+  return 0;
+}
diff -Nru clearsilver-0.10.4/configure.in clearsilver-0.10.5/configure.in
--- clearsilver-0.10.4/configure.in	2006-03-12 16:05:26.000000000 -0800
+++ clearsilver-0.10.5/configure.in	2007-07-12 12:35:37.000000000 -0700
@@ -26,7 +26,7 @@
 AC_HEADER_DIRENT
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(fcntl.h stdarg.h varargs.h limits.h strings.h sys/ioctl.h sys/time.h unistd.h)
+AC_CHECK_HEADERS(fcntl.h stdarg.h varargs.h limits.h strings.h sys/ioctl.h sys/time.h unistd.h features.h)
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -400,7 +400,7 @@
   if test $cs_cv_java_path != "no" -a -d $cs_cv_java_path; then
     java_path=$cs_cv_java_path
   else
-    java_search_path="/neo/opt /usr/local /usr"
+    java_search_path="/neo/opt /usr/local /usr /usr/lib"
     for path in $java_search_path; do
       if test -d $path/java/j2sdk; then
 	java_path=$path/java/j2sdk
diff -Nru clearsilver-0.10.4/cs/Makefile clearsilver-0.10.5/cs/Makefile
--- clearsilver-0.10.4/cs/Makefile	2006-11-09 14:33:41.000000000 -0800
+++ clearsilver-0.10.5/cs/Makefile	2007-05-02 17:21:03.000000000 -0700
@@ -33,7 +33,7 @@
 	   test_each_array.cs test_name.cs test_with.cs test_numbers.cs \
 	   test_splice.cs test_joo.cs test_first_last.cs test_abs_max_min.cs \
 	   test_comma.cs test_macro_set.cs test_func.cs test_escape.cs \
-	   test_uvar.cs
+	   test_uvar.cs test_crc.cs
 
 
 all: $(TARGETS)
diff -Nru clearsilver-0.10.4/cs/cs.h clearsilver-0.10.5/cs/cs.h
--- clearsilver-0.10.4/cs/cs.h	2006-08-07 13:01:51.000000000 -0700
+++ clearsilver-0.10.5/cs/cs.h	2007-06-26 17:21:21.000000000 -0700
@@ -103,10 +103,13 @@
 typedef struct _parse CSPARSE;
 typedef struct _funct CS_FUNCTION;
 typedef struct _escape_context CS_ECONTEXT;
+typedef struct _position CS_POSITION;
+typedef struct _error CS_ERROR;
 
 typedef struct _arg
 {
   CSTOKEN_TYPE op_type;
+  char *argexpr;
   char *s;
   long int n;
   int alloc;
@@ -129,6 +132,10 @@
   CSARG arg2;
   CSARG *vargs;
 
+  char *fname;
+  int linenum;
+  int colnum;
+
   struct _tree *case_0;
   struct _tree *case_1;
   struct _tree *next;
@@ -212,8 +219,8 @@
  */
 struct _escape_context
 {
-  NEOS_ESCAPE global;     /* Contains global default escaping mode:
-                           none,html,js,url */
+  NEOS_ESCAPE global_ctx; /* Contains global default escaping mode:
+			     none,html,js,url */
   NEOS_ESCAPE current;    /* Used to pass around parse and evaluation specific
                              data from subfunctions upward. */
   NEOS_ESCAPE next_stack; /* This is a big fat workaround. Since STACK_ENTRYs
@@ -229,11 +236,31 @@
                              to get call's parsing context at eval time. */
 };
 
+/* This structure is used to track current location within the CS file being
+ * parsed. This information is used to find the filename and line number for
+ * each node.
+ */
+struct _position {
+  int line;        /* Line number for current position */
+  int col;         /* Column number for current position */
+  int cur_offset;  /* The current position - commence reading from here */
+};
+
+struct _error {
+  NEOERR *err;
+  struct _error *next;
+};
+  
 struct _parse
 {
   const char *context;   /* A string identifying where the parser is parsing */
   int in_file;           /* Indicates if current context is a file */
   int offset;
+
+  int audit_mode;        /* If in audit_mode, gather some extra information */
+  CS_POSITION pos;       /* Container for current position in CS file */
+  CS_ERROR *err_list;    /* List of non-fatal errors encountered */
+  
   char *context_string;
   CS_ECONTEXT escaping; /* Context container for escape data */
 
diff -Nru clearsilver-0.10.4/cs/csparse.c clearsilver-0.10.5/cs/csparse.c
--- clearsilver-0.10.4/cs/csparse.c	2006-10-19 16:26:11.000000000 -0700
+++ clearsilver-0.10.5/cs/csparse.c	2007-07-11 19:37:34.000000000 -0700
@@ -201,7 +201,61 @@
 
 static int NodeNumber = 0;
 
-static NEOERR *alloc_node (CSTREE **node)
+static void init_node_pos(CSTREE *node, CSPARSE *parse) 
+{  
+  CS_POSITION *pos = &parse->pos;
+  char *data;
+
+  if (parse->offset < pos->cur_offset) {
+    /* Oops, we went backwards in file, is this an error? */
+    node->linenum = -1;
+    node->colnum = parse->offset;
+    return;
+  }
+
+  /* Start counting from 1 not 0 */
+  if (pos->line == 0) pos->line = 1;
+  if (pos->col == 0) pos->col = 1;
+
+  if (parse->context == NULL) {
+    /* Not in a file */
+    node->fname = NULL;
+  }
+  else {
+    node->fname = strdup(parse->context);
+    if (node->fname == NULL) {
+      /* malloc error, cannot proceed */
+      node->linenum = -1;
+      return;
+    }
+  }
+
+  data = parse->context_string;
+  if (data == NULL) {
+    node->linenum = -1;
+    return;
+  }
+  
+  while (pos->cur_offset < parse->offset) {
+    if (data[pos->cur_offset] == '\n') {
+      pos->line++;
+      pos->col = 1;
+    }
+    else {
+      pos->col++;
+    }
+
+    pos->cur_offset++;
+  }
+  
+  node->linenum = pos->line;
+  node->colnum = pos->col;
+  
+  return;
+
+}
+
+static NEOERR *alloc_node (CSTREE **node, CSPARSE *parse)
 {
   CSTREE *my_node;
 
@@ -214,6 +268,10 @@
   my_node->node_num = NodeNumber++;
 
   *node = my_node;
+  
+  if (parse->audit_mode) {
+    init_node_pos(my_node, parse);
+  }
   return STATUS_OK;
 }
 
@@ -227,6 +285,9 @@
   if (p->expr1) dealloc_arg (&(p->expr1));
   if (p->expr2) dealloc_arg (&(p->expr2));
   if (p->next) dealloc_arg (&(p->next));
+
+  if (p->argexpr) free(p->argexpr);
+
   free(p);
   *arg = NULL;
 }
@@ -248,6 +309,10 @@
   if (my_node->arg2.expr2) dealloc_arg (&(my_node->arg2.expr2));
   if (my_node->arg2.next) dealloc_arg (&(my_node->arg2.next));
 
+  if (my_node->arg1.argexpr) free(my_node->arg1.argexpr);
+  if (my_node->arg2.argexpr) free(my_node->arg2.argexpr);
+  if (my_node->fname) free(my_node->fname);
+
   free(my_node);
   *node = NULL;
 }
@@ -302,6 +367,35 @@
   return -1;
 }
 
+static NEOERR *_store_error (CSPARSE *parse, NEOERR *err) 
+{
+  CS_ERROR *ptr;
+  CS_ERROR *node;
+
+  node = (CS_ERROR *) calloc(1, sizeof(CS_ERROR));
+  if (node == NULL) 
+  {
+    return nerr_raise (NERR_NOMEM,
+        "Unable to allocate memory for error entry");
+  }
+
+  node->err = err;
+
+  if (parse->err_list == NULL)
+  {
+    parse->err_list = node;
+    return STATUS_OK;
+  }
+
+  ptr = parse->err_list;
+  while (ptr->next != NULL) 
+    ptr = ptr->next;
+
+  ptr->next = node;
+  return STATUS_OK;
+      
+}
+
 NEOERR *cs_parse_file (CSPARSE *parse, const char *path)
 {
   NEOERR *err;
@@ -309,6 +403,7 @@
   const char *save_context;
   int save_infile;
   char fpath[_POSIX_PATH_MAX];
+  CS_POSITION pos;
 
   if (path == NULL)
     return nerr_raise (NERR_ASSERT, "path is NULL");
@@ -336,7 +431,22 @@
   parse->context = path;
   save_infile = parse->in_file;
   parse->in_file = 1;
+
+  if (parse->audit_mode) {
+    /* Save previous position before parsing the new file */
+    memcpy(&pos, &parse->pos, sizeof(CS_POSITION));
+    
+    parse->pos.line = 0;
+    parse->pos.col = 0;
+    parse->pos.cur_offset = 0;
+  }
+
   err = cs_parse_string(parse, ibuf, strlen(ibuf));
+
+  if (parse->audit_mode) {
+    memcpy(&parse->pos, &pos, sizeof(CS_POSITION));
+  }
+
   parse->in_file = save_infile;
   parse->context = save_context;
 
@@ -556,7 +666,7 @@
 		 * we parse "escape", the new stack has not yet been established.
 		 */
 		entry->escape = parse->escaping.next_stack;
-		parse->escaping.next_stack = parse->escaping.global;
+		parse->escaping.next_stack = parse->escaping.global_ctx;
 		err = uListAppend(parse->stack, entry);
 		if (err != STATUS_OK) {
 		  free (entry);
@@ -1339,6 +1449,12 @@
   memset(tokens, 0, sizeof(CSTOKEN) * MAX_TOKENS);
   err = parse_tokens (parse, arg, tokens, &ntokens);
   if (err) return nerr_pass(err);
+
+  if (parse->audit_mode) {
+    /* Save the complete expression string for future reference */
+    expr->argexpr = strdup(arg);
+  }
+
   err = parse_expr2 (parse, tokens, ntokens, lvalue, expr);
   if (err) return nerr_pass(err);
   return STATUS_OK;
@@ -1350,7 +1466,7 @@
   CSTREE *node;
 
   /* ne_warn ("literal: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   node->arg1.op_type = CS_TYPE_STRING;
@@ -1380,7 +1496,7 @@
   char tmp[256];
 
   /* ne_warn ("name: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -1415,7 +1531,7 @@
   CSTREE *node;
 
   /* ne_warn ("escape: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   /* Since this throws an error always if there's a problem
@@ -1494,7 +1610,7 @@
   if (err != STATUS_OK) return nerr_pass(err);
 
   /* ne_warn ("var: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
 
@@ -1532,7 +1648,7 @@
   CSTREE *node;
 
   /* ne_warn ("lvar: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -1559,7 +1675,7 @@
   CSTREE *node;
 
   /* ne_warn ("linclude: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -1586,7 +1702,7 @@
   CSTREE *node;
 
   /* ne_warn ("var: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -1617,7 +1733,7 @@
   char tmp[256];
 
   /* ne_warn ("evar: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -1670,7 +1786,7 @@
   CSTREE *node;
 
   /* ne_warn ("if: %s", arg); */
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err != STATUS_OK) return nerr_pass(err);
   node->cmd = cmd;
   arg++;
@@ -2131,9 +2247,9 @@
         if (arg2.op_type & (CS_TYPE_VAR_NUM | CS_TYPE_NUM))
         {
           long int n2 = arg_eval_num (parse, &arg2);
-          result->s = sprintf_alloc("%s.%d", arg1.s, n2);
+          result->s = sprintf_alloc("%s.%ld", arg1.s, n2);
           if (result->s == NULL)
-            return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %d", arg1.s, n2);
+            return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2);
         }
         else
         {
@@ -2169,9 +2285,9 @@
           if (arg2.op_type & CS_TYPE_NUM)
           {
             long int n2 = arg_eval_num (parse, &arg2);
-            result->s = sprintf_alloc("%s.%d", arg1.s, n2);
+            result->s = sprintf_alloc("%s.%ld", arg1.s, n2);
             if (result->s == NULL)
-              return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %d", arg1.s, n2);
+              return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2);
           }
           else
           {
@@ -2504,7 +2620,7 @@
   char *p;
   char tmp[256];
 
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -2722,7 +2838,7 @@
    */
   parse->escaping.next_stack = NEOS_ESCAPE_UNDEF;
 
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   arg++;
@@ -2896,7 +3012,7 @@
   err = uListGet (parse->stack, -1, (void *)&entry);
   if (err != STATUS_OK) return nerr_pass(err);
 
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   node->escape = entry->escape;
@@ -2927,9 +3043,15 @@
   if (macro == NULL)
   {
     dealloc_node(&node);
-    return nerr_raise (NERR_PARSE,
-	"%s Undefined macro called: %s",
-	find_context(parse, -1, tmp, sizeof(tmp)), arg);
+    err = nerr_raise (NERR_PARSE, "%s Undefined macro called: %s",
+          find_context(parse, -1, tmp, sizeof(tmp)), arg);
+    if (parse->audit_mode) {
+      /* Ignore macros that cannot be found */
+      return _store_error(parse, err);
+    }
+    else {
+      return err;
+    }
   }
   node->arg1.op_type = CS_TYPE_MACRO;
   node->arg1.macro = macro;
@@ -3112,7 +3234,7 @@
   char *s;
   char tmp[256];
 
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   arg++;
@@ -3217,7 +3339,7 @@
   char tmp[256];
   int x;
 
-  err = alloc_node (&node);
+  err = alloc_node (&node, parse);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
   if (arg[0] == '!')
@@ -3578,6 +3700,29 @@
   return STATUS_OK;
 }
 
+static NEOERR * _builtin_str_crc(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
+                                 CSARG *result)
+{
+  NEOERR *err;
+  CSARG val;
+
+  memset(&val, 0, sizeof(val));
+  err = eval_expr(parse, args, &val);
+  if (err) return nerr_pass(err);
+
+  /* non var/string objects have 0 length */
+  result->op_type = CS_TYPE_NUM;
+  result->n = 0;
+
+  if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING))
+  {
+    char *s = arg_eval(parse, &val);
+    if (s) result->n = ne_crc((unsigned char *)s, strlen(s));
+  }
+  if (val.alloc) free(val.s);
+  return STATUS_OK;
+}
+
 
 static NEOERR * _builtin_str_find(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
 {
@@ -3921,7 +4066,7 @@
     free(my_parse);
     return nerr_pass(err);
   }
-  err = alloc_node (&(my_parse->tree));
+  err = alloc_node (&(my_parse->tree), my_parse);
   if (err != STATUS_OK)
   {
     cs_destroy (&my_parse);
@@ -3952,7 +4097,7 @@
   my_parse->hdf = hdf;
 
   /* Let's set the default escape data */
-  my_parse->escaping.global = NEOS_ESCAPE_NONE;
+  my_parse->escaping.global_ctx = NEOS_ESCAPE_NONE;
   my_parse->escaping.next_stack = NEOS_ESCAPE_NONE;
   my_parse->escaping.when_undef = NEOS_ESCAPE_NONE;
 
@@ -3964,7 +4109,7 @@
        esc_cursor++)
     if (!strcmp(esc_value, esc_cursor->mode))
     {
-      my_parse->escaping.global = esc_cursor->context;
+      my_parse->escaping.global_ctx = esc_cursor->context;
       my_parse->escaping.next_stack = esc_cursor->context;
       entry->escape = esc_cursor->context;
       break;
@@ -3977,6 +4122,11 @@
       esc_value);
   }
 
+  /* Read configuration value to determine whether to enable audit mode */
+  my_parse->audit_mode = hdf_get_int_value(hdf, "Config.EnableAuditMode", 0);
+
+  my_parse->err_list = NULL;
+
   if (parent == NULL)
   {
     static struct _builtin_functions {
@@ -3995,6 +4145,7 @@
       { "string.find", 2, _builtin_str_find },
       { "string.slice", 3, _builtin_str_slice },
       { "string.length", 1, _builtin_str_length },
+      { "string.crc", 1, _builtin_str_crc},
 #ifdef ENABLE_GETTEXT
       { "_", 1, _builtin_gettext },
 #endif
@@ -4028,7 +4179,15 @@
     my_parse->global_hdf = parent->global_hdf;
     my_parse->fileload = parent->fileload;
     my_parse->fileload_ctx = parent->fileload_ctx;
+    // This should be safe since locals handling is done entirely local to the
+    // eval functions, not globally by the parse handling.  This should
+    // pass the locals down to the new parse context to make locals work with
+    // lvar
+    my_parse->locals = parent->locals;
     my_parse->parent = parent;
+
+    /* Copy the audit flag from parent */
+    my_parse->audit_mode = parent->audit_mode;
   }
 
   *parse = my_parse;
@@ -4058,12 +4217,23 @@
     dealloc_function(&(my_parse->functions));
   }
 
+  /* Free list of errors */
+  if (my_parse->err_list != NULL) {
+    CS_ERROR *ptr;
+
+    while (my_parse->err_list) {
+      ptr = my_parse->err_list->next;
+      free(my_parse->err_list->err);
+      free(my_parse->err_list);
+      my_parse->err_list = ptr;
+    }
+  }
+
   free(my_parse);
   *parse = NULL;
 }
 
 /* **** CS Debug Dumps ******************************************** */
-
 static NEOERR *dump_node (CSPARSE *parse, CSTREE *node, int depth, void *ctx,
     CSOUTFUNC cb, char *buf, int blen)
 {
diff -Nru clearsilver-0.10.4/cs/test16.cs clearsilver-0.10.5/cs/test16.cs
--- clearsilver-0.10.4/cs/test16.cs	2005-06-30 11:51:51.000000000 -0700
+++ clearsilver-0.10.5/cs/test16.cs	2006-12-19 17:12:40.000000000 -0800
@@ -1,3 +1,7 @@
 <?cs lvar:C ?>
 <?cs lvar:C ?>
 <?cs lvar:C ?>
+
+<?cs each:A = Days ?>
+  <?cs lvar:C ?>
+<?cs /each ?>
diff -Nru clearsilver-0.10.4/cs/test16.cs.gold clearsilver-0.10.5/cs/test16.cs.gold
--- clearsilver-0.10.4/cs/test16.cs.gold	2006-10-09 17:53:19.000000000 -0700
+++ clearsilver-0.10.5/cs/test16.cs.gold	2007-05-02 17:21:17.000000000 -0700
@@ -2,3 +2,19 @@
 HELLO/WORLD
 HELLO/WORLD
 HELLO/WORLD
+
+
+  0/WORLD
+
+  1/WORLD
+
+  2/WORLD
+
+  3/WORLD
+
+  4/WORLD
+
+  5/WORLD
+
+  6/WORLD
+
diff -Nru clearsilver-0.10.4/cs/test_crc.cs clearsilver-0.10.5/cs/test_crc.cs
--- clearsilver-0.10.4/cs/test_crc.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/cs/test_crc.cs	2007-05-02 17:18:24.000000000 -0700
@@ -0,0 +1,3 @@
+<?cs var:string.crc("my string") ?>
+<?cs var:string.crc("my other string") ?>
+<?cs var:string.crc("my first string") ?>
diff -Nru clearsilver-0.10.4/cs/test_crc.cs.gold clearsilver-0.10.5/cs/test_crc.cs.gold
--- clearsilver-0.10.4/cs/test_crc.cs.gold	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/cs/test_crc.cs.gold	2007-05-02 17:21:17.000000000 -0700
@@ -0,0 +1,4 @@
+Parsing test_crc.cs
+419156592
+1357503972
+-2128917020
diff -Nru clearsilver-0.10.4/cs/test_url_validate.cs clearsilver-0.10.5/cs/test_url_validate.cs
--- clearsilver-0.10.4/cs/test_url_validate.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/cs/test_url_validate.cs	2007-07-11 18:23:59.000000000 -0700
@@ -0,0 +1,23 @@
+The following urls should be allowed unmodified:
+<?cs var:BlahUrl ?> = <?cs var:url_validate(BlahUrl) ?>
+<?cs var:SslUrl ?> = <?cs var:url_validate(SslUrl) ?>
+<?cs var:FtpUrl ?> = <?cs var:url_validate(FtpUrl) ?>
+<?cs var:MailUrl ?> = <?cs var:url_validate(MailUrl) ?>
+
+<?cs var:AbsUrl ?> = <?cs var:url_validate(AbsUrl) ?>
+<?cs var:AbsUrl2 ?> = <?cs var:url_validate(AbsUrl2) ?>
+<?cs var:RelUrl ?> = <?cs var:url_validate(RelUrl) ?>
+
+<?cs var:ColonUrl ?> = <?cs var:url_validate(ColonUrl) ?>
+<?cs var:ColonUrlTwo ?> = <?cs var:url_validate(ColonUrlTwo) ?>
+
+This URL should be html escaped:
+<?cs var:HtmlUrl ?> = <?cs var:url_validate(HtmlUrl) ?>
+
+
+These URLs should be changed to #
+<?cs var:JsUrl ?> = <?cs var:url_validate(JsUrl) ?>
+<?cs var:DataUrl ?> = <?cs var:url_validate(DataUrl) ?>
+<?cs var:InvalidUrl ?> = <?cs var:url_validate(InvalidUrl) ?>
+<?cs var:InvalidUrl2 ?> = <?cs var:url_validate(InvalidUrl2) ?>
+<?cs var:ShortUrl ?> = <?cs var:url_validate(ShortUrl) ?>
diff -Nru clearsilver-0.10.4/cs/test_url_validate.hdf clearsilver-0.10.5/cs/test_url_validate.hdf
--- clearsilver-0.10.4/cs/test_url_validate.hdf	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/cs/test_url_validate.hdf	2007-07-11 18:23:59.000000000 -0700
@@ -0,0 +1,18 @@
+BlahUrl = http://www.google.com
+SslUrl = https://www.google.com
+FtpUrl = ftp://www.google.com/a
+MailUrl = mailto:tester@google.com
+
+JsUrl = javascript:www.google.com
+DataUrl = data:text/html;base64,PHNjcmlwdD5hbGVydCgncmZjMjM5NyBpcyBzY2FyeScpPC9zY3JpcHQ+
+
+AbsUrl = /mail/
+AbsUrl2 = /
+RelUrl = mail/google.x
+HtmlUrl = http://www.google.com/<script>alert(ha)</script>/good.x
+
+ColonUrl = relative/path:name
+ColonUrlTwo = /mail/path:name
+InvalidUrl = mail:80
+InvalidUrl2 = www.google.com:80
+ShortUrl = m:x
diff -Nru clearsilver-0.10.4/cs_config.h.in clearsilver-0.10.5/cs_config.h.in
--- clearsilver-0.10.4/cs_config.h.in	2005-06-30 18:49:22.000000000 -0700
+++ clearsilver-0.10.5/cs_config.h.in	2006-12-14 19:11:09.000000000 -0800
@@ -74,6 +74,9 @@
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
+/* Define to 1 if you have the <features.h> header file. */
+#undef HAVE_FEATURES_H
+
 /* Define to 1 if you have the `gettimeofday' function. */
 #undef HAVE_GETTIMEOFDAY
 
diff -Nru clearsilver-0.10.4/csharp/CS.cs clearsilver-0.10.5/csharp/CS.cs
--- clearsilver-0.10.4/csharp/CS.cs	2005-06-30 11:51:53.000000000 -0700
+++ clearsilver-0.10.5/csharp/CS.cs	2007-07-12 12:57:57.000000000 -0700
@@ -53,12 +53,14 @@
     public HDF *hdf_root;
 
     public Hdf() {
-      NEOERR* err = hdf_init(&hdf_root);
+      fixed (HDF **hdf_ptr = &hdf_root) {
+	hdf_init(hdf_ptr);
+      }
       // Console.WriteLine((int)hdf_root);
     }
 
     public void setValue(string name,string value) {
-         NEOERR* err = hdf_set_value(hdf_root,name,value);
+         hdf_set_value(hdf_root,name,value);
          
     }
     public string getValue(string name,string defvalue) {
@@ -81,16 +83,18 @@
 unsafe struct CSPARSE {};
 
 public class CSTContext {
-   CSPARSE *csp;
+   unsafe CSPARSE *csp;
    unsafe public CSTContext(Hdf hdf) {
-     NEOERR *err = cs_init(&csp, hdf.hdf_root);
+     fixed (CSPARSE **csp_ptr = &csp) {
+       cs_init(csp_ptr, hdf.hdf_root);
+     }
    } 
 
    [DllImport("libneo")]
    extern static unsafe NEOERR *cs_init (CSPARSE **parse, HDF *hdf);
 
    public unsafe void parseFile(string filename) {
-      NEOERR* err = cs_parse_file(csp,filename);
+      cs_parse_file(csp,filename);
    }
 
    [DllImport("libneo")]
@@ -131,7 +135,7 @@
 
    public unsafe string render() {
      OutputBuilder ob = new OutputBuilder();
-     NEOERR* err = cs_render(csp,null,new CSOUTFUNC(ob.handleOutput));
+     cs_render(csp,null,new CSOUTFUNC(ob.handleOutput));
      return ob.result();
    }
 
@@ -142,4 +146,4 @@
 };
 
 
-} // namespace Clearsilver
\ No newline at end of file
+} // namespace Clearsilver
diff -Nru clearsilver-0.10.4/csharp/Makefile clearsilver-0.10.5/csharp/Makefile
--- clearsilver-0.10.4/csharp/Makefile	2005-12-02 02:34:30.000000000 -0800
+++ clearsilver-0.10.5/csharp/Makefile	2007-07-12 12:39:33.000000000 -0700
@@ -19,13 +19,13 @@
 all: $(TARGETS)
 
 clearsilver.dll: CS.cs
-	$(CSHARP_CC) -target:library --unsafe CS.cs -out:clearsilver.dll
+	$(CSHARP_CC) -target:library -unsafe CS.cs -out:clearsilver.dll
 
 cstest.exe: clearsilver.dll ../dso/libneo.so cstest.cs
-	$(CSHARP_CC) -r:clearsilver.dll --unsafe cstest.cs 
+	$(CSHARP_CC) -r:clearsilver.dll -unsafe cstest.cs 
 
 csperftest.exe: clearsilver.dll ../dso/libneo.so csperftest.cs
-	$(CSHARP_CC) -r:clearsilver.dll --unsafe csperftest.cs
+	$(CSHARP_CC) -r:clearsilver.dll -unsafe csperftest.cs
 
 perf: csperftest.exe
 	export LD_LIBRARY_PATH=../dso; \
diff -Nru clearsilver-0.10.4/java-jni/CGI.java clearsilver-0.10.5/java-jni/CGI.java
--- clearsilver-0.10.4/java-jni/CGI.java	2005-06-30 11:51:54.000000000 -0700
+++ clearsilver-0.10.5/java-jni/CGI.java	2007-02-01 17:06:17.000000000 -0800
@@ -6,12 +6,7 @@
     public int _cgiptr;
     
     static { 
-	try {
-	    System.loadLibrary("clearsilver-jni");
-	} catch ( UnsatisfiedLinkError e ) {
-	    System.out.println("Could not load neo_cgi.so");
-	    System.exit(1);
-	}
+        JNI.loadLibrary();
     }
     
     public CGI() {
diff -Nru clearsilver-0.10.4/java-jni/CS.java clearsilver-0.10.5/java-jni/CS.java
--- clearsilver-0.10.4/java-jni/CS.java	2005-10-11 11:41:32.000000000 -0700
+++ clearsilver-0.10.5/java-jni/CS.java	2007-02-01 17:06:17.000000000 -0800
@@ -5,20 +5,17 @@
 
 public class CS {
   public int csptr;
-  
+
   protected HDF globalHDF;
+  protected HDF localHDF;
 
-  static { 
-    try {
-      System.loadLibrary("clearsilver-jni");
-    } catch ( UnsatisfiedLinkError e ) {
-      System.out.println("Could not load library 'clearsilver-jni'");
-      System.exit(1);
-    }
+  static {
+    JNI.loadLibrary();
   }
-  
+
   public CS(HDF ho) {
     this.globalHDF = null;
+    this.localHDF = ho;
     csptr = _init(ho.hdfptr);
   }
 
@@ -48,7 +45,7 @@
       _dealloc(csptr);
       csptr = 0;
     }
-  }    
+  }
 
   public void finalize() {
     close();
@@ -58,7 +55,7 @@
     if (csptr == 0) {
       throw new NullPointerException("CS is closed.");
     }
-    _parseFile(csptr,filename);
+    _parseFile(csptr, filename, fileLoader != null);
   }
 
   public void parseStr(String content) {
@@ -74,10 +71,49 @@
     }
     return _render(csptr);
   }
-  
+
+
+  protected String fileLoad(String filename) throws IOException,
+            FileNotFoundException {
+    if (csptr == 0) {
+      throw new NullPointerException("CS is closed.");
+    }
+    CSFileLoader aFileLoader = fileLoader;
+    if (aFileLoader == null) {
+      throw new NullPointerException("No fileLoader specified.");
+    } else {
+      String result = aFileLoader.load(localHDF, filename);
+      if (result == null) {
+        throw new NullPointerException("CSFileLoader.load() returned null");
+      }
+      return result;
+    }
+  }
+
+  // The optional CS file loader to use to read in files
+  private CSFileLoader fileLoader = null;
+
+  /**
+   * Get the file loader in use, if any.
+   * @return the file loader in use.
+   */
+  public CSFileLoader getFileLoader() {
+    return fileLoader;
+  }
+
+  /**
+   * Set the CS file loader to use
+   * @param fileLoader the file loader that should be used.
+   */
+  public void setFileLoader(CSFileLoader fileLoader) {
+    this.fileLoader = fileLoader;
+  }
+
+
   private native int    _init(int ptr);
   private native void   _dealloc(int ptr);
-  private native void   _parseFile(int ptr,String filename);
+  private native void   _parseFile(int ptr, String filename,
+                                   boolean use_cb);
   private native void   _parseStr(int ptr, String content);
   private native String _render(int ptr);
   private native void   _setGlobalHdf(int csptr, int hdfptr);
diff -Nru clearsilver-0.10.4/java-jni/CSFileLoader.java clearsilver-0.10.5/java-jni/CSFileLoader.java
--- clearsilver-0.10.4/java-jni/CSFileLoader.java	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/java-jni/CSFileLoader.java	2007-01-05 14:22:52.000000000 -0800
@@ -0,0 +1,22 @@
+package org.clearsilver;
+
+import java.io.IOException;
+
+/**
+ * Interface for CS file hook
+ *
+ * @author smarti@google.com (Sergio Marti)
+ */
+public interface CSFileLoader {
+
+  /**
+   * Callback method that is expected to return the contents of the specified
+   * file as a string.
+   * @param hdf the HDF structure associated with HDF or CS object making the
+   * callback.
+   * @param filename the name of the file that should be loaded.
+   * @return a string containing the contents of the file.
+   */
+  public String load(HDF hdf, String filename) throws IOException;
+
+}
diff -Nru clearsilver-0.10.4/java-jni/CSTest.java clearsilver-0.10.5/java-jni/CSTest.java
--- clearsilver-0.10.4/java-jni/CSTest.java	2006-11-09 16:54:04.000000000 -0800
+++ clearsilver-0.10.5/java-jni/CSTest.java	2007-01-05 14:22:52.000000000 -0800
@@ -3,9 +3,16 @@
 import java.util.*;
 
 import org.clearsilver.CS;
+import org.clearsilver.CSFileLoader;
 import org.clearsilver.HDF;
 
-class CSTest {
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+
+public class CSTest {
 
     public static void main( String [] args ) throws IOException {
         org.clearsilver.HDF hdf = new HDF();
@@ -226,5 +233,39 @@
         System.out.println("----");
         cs.parseStr(tmplstr);
         System.out.println(cs.render());
+
+        System.out.println("Testing HDF.readFile() with callback\n");
+        file_hdf = new HDF();
+	file_hdf.setFileLoader(new CSTestLoader());
+        file_hdf.readFile("testdata/test1.hdf");
+        System.out.println(file_hdf.dump());
+
+        System.out.println("Testing CS.parseFile() with callback\n");
+        cs = new CS(file_hdf);
+	cs.setFileLoader(new CSTestLoader());
+	cs.parseFile("testdata/test.cs");
+	System.out.println(cs.render());
+    }
+};
+
+class CSTestLoader implements CSFileLoader {
+  public String load(HDF hdf, String filename) throws IOException {
+    System.out.println("CSTestLoader::Load " + filename + "\n");
+    String data = readFile(new File(filename));
+    System.out.println("---- file begin ----\n" + data +
+	"\n---- file end ----\n");
+    return data;
+  }
+
+  private String readFile(File file) throws IOException {
+    InputStreamReader fin = new InputStreamReader(new FileInputStream(file),
+        "UTF-8");
+    StringBuilder sb = new StringBuilder(1024);
+    char[] charbuf = new char[1024];
+    int len = 0;
+    while ((len = fin.read(charbuf)) != -1) {
+      sb.append(charbuf, 0, len);
     }
+    return sb.toString();
+  }
 };
diff -Nru clearsilver-0.10.4/java-jni/CSUtil.java clearsilver-0.10.5/java-jni/CSUtil.java
--- clearsilver-0.10.4/java-jni/CSUtil.java	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/java-jni/CSUtil.java	2006-12-18 20:26:12.000000000 -0800
@@ -0,0 +1,62 @@
+package org.clearsilver;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.io.File;
+
+/**
+ * Utility class containing helper methods
+ *
+ * @author smarti@google.com (Sergio Marti)
+ */
+public class CSUtil {
+
+  private CSUtil() { }
+
+  public static final String HDF_LOADPATHS = "hdf.loadpaths";
+
+  /**
+   * Helper function that returns a concatenation of the loadpaths in the
+   * provided HDF.
+   * @param hdf an HDF structure containing load paths.
+   * @return A list of loadpaths in order in which to search.
+   * @throws NullPointerException if no loadpaths are found.
+   */
+  public static List<String> getLoadPaths(HDF hdf) {
+    List<String> list = new LinkedList<String>();
+    HDF loadpathsHdf = hdf.getObj(HDF_LOADPATHS);
+    if (loadpathsHdf == null) {
+      throw new NullPointerException("No HDF loadpaths located in the specified"
+          + " HDF structure");
+    }
+    for (HDF lpHdf = loadpathsHdf.objChild(); lpHdf != null;
+        lpHdf = lpHdf.objNext()) {
+      list.add(lpHdf.objValue());
+    }
+    return list;
+  }
+
+  /**
+   * Given an ordered list of directories to look in, locate the specified file.
+   * Returns <code>null</code> if file not found.
+   * @param loadpaths the ordered list of paths to search.
+   * @param filename the name of the file.
+   * @return a File object corresponding to the file. <code>null</code> if
+   * file not found.
+   */
+  public static File locateFile(List<String> loadpaths, String filename) {
+    if (filename == null) {
+      throw new NullPointerException("No filename provided");
+    }
+    if (loadpaths == null) {
+      throw new NullPointerException("No loadpaths provided.");
+    }
+    for (String path : loadpaths) {
+      File file = new File(path, filename);
+      if (file.exists()) {
+        return file;
+      }
+    }
+    return null;
+  }
+}
diff -Nru clearsilver-0.10.4/java-jni/HDF.java clearsilver-0.10.5/java-jni/HDF.java
--- clearsilver-0.10.4/java-jni/HDF.java	2006-10-09 17:47:43.000000000 -0700
+++ clearsilver-0.10.5/java-jni/HDF.java	2007-02-01 17:06:17.000000000 -0800
@@ -17,12 +17,7 @@
                // to hold a reference on the root to prevent the root from
                // being GC-ed.
   static {
-    try {
-      System.loadLibrary("clearsilver-jni");
-    } catch ( UnsatisfiedLinkError e ) {
-      System.out.println("Could not load 'clearsilver-jni'");
-      System.exit(1);
-    }
+    JNI.loadLibrary();
   }
 
   /** Constructs an empty HDF dataset */
@@ -69,7 +64,43 @@
     if (hdfptr == 0) {
       throw new NullPointerException("HDF is closed.");
     }
-    return _readFile(hdfptr, filename);
+    return _readFile(hdfptr, filename, fileLoader != null);
+  }
+
+  protected String fileLoad(String filename) throws IOException,
+            FileNotFoundException {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+    CSFileLoader aFileLoader = fileLoader;
+    if (aFileLoader == null) {
+      throw new NullPointerException("No fileLoader specified.");
+    } else {
+      String result = aFileLoader.load(this, filename);
+      if (result == null) {
+        throw new NullPointerException("CSFileLoader.load() returned null");
+      }
+      return result;
+    }
+  }
+
+  // The optional CS file loader to use to read in files
+  private CSFileLoader fileLoader = null;
+
+  /**
+   * Get the file loader in use, if any.
+   * @return the file loader in use.
+   */
+  public CSFileLoader getFileLoader() {
+    return fileLoader;
+  }
+
+  /**
+   * Set the CS file loader to use
+   * @param fileLoader the file loader that should be used.
+   */
+  public void setFileLoader(CSFileLoader fileLoader) {
+    this.fileLoader = fileLoader;
   }
 
   /** Serializes HDF contents to a file (readable by readFile)
@@ -332,7 +363,7 @@
 
   private static native int _init();
   private static native void _dealloc(int ptr);
-  private static native boolean _readFile(int ptr, String filename);
+  private native boolean _readFile(int ptr, String filename, boolean use_cb);
   private static native boolean _writeFile(int ptr, String filename);
   private static native boolean _writeFileAtomic(int ptr, String filename);
   private static native boolean _readString(int ptr, String data);
diff -Nru clearsilver-0.10.4/java-jni/JNI.java clearsilver-0.10.5/java-jni/JNI.java
--- clearsilver-0.10.4/java-jni/JNI.java	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/java-jni/JNI.java	2007-02-01 17:06:17.000000000 -0800
@@ -0,0 +1,80 @@
+package org.clearsilver;
+
+/**
+ * Loads the ClearSilver JNI library.
+ *
+ * <p>By default, it attempts to load the library 'clearsilver-jni' from the
+ * path specified in the 'java.library.path' system property.</p>
+ *
+ * <p>If this fails, the JVM exits with a code of 1. However, this strategy
+ * can be changed using {@link #setFailureCallback(Runnable)}.</p>
+ */
+public class JNI {
+    
+  /**
+   * Failure callback strategy that writes a message to sysout, then calls
+   * System.exit(1). 
+   */
+  public static Runnable EXIT_JVM = new Runnable() {
+    public void run() {
+      System.out.println("Could not load '" + libraryName + "'");
+      System.out.println("java.library.path = " 
+          + System.getProperty("java.library.path"));
+      System.exit(1);
+    }
+  };
+
+  /**
+   * Failure callback strategy that throws an UnsatisfiedLinkError, which 
+   * should be caught be client code.
+   */
+  public static Runnable THROW_ERROR = new Runnable() {
+    public void run() {
+      throw new UnsatisfiedLinkError("Could not load '" + libraryName + "'");
+    }
+  };
+
+  private static Runnable failureCallback = EXIT_JVM;
+
+  private static Object callbackLock = new Object();
+  
+  private static String libraryName = "clearsilver-jni";
+        
+  /**
+   * Attempts to load the ClearSilver JNI library.
+   *
+   * @see #setFailureCallback(Runnable)
+   */
+  public static void loadLibrary() {
+    try {
+      System.loadLibrary(libraryName);
+    } catch (UnsatisfiedLinkError e) {
+      synchronized (callbackLock) {
+        if (failureCallback != null) {
+          failureCallback.run();
+        }
+      }
+    }    
+  }
+  
+  /**
+   * Sets a callback for what should happen if the JNI library cannot
+   * be loaded. The default is {@link #EXIT_JVM}.
+   *
+   * @see #EXIT_JVM
+   * @see #THROW_ERROR
+   */
+  public static void setFailureCallback(Runnable failureCallback) {
+    synchronized(callbackLock) {
+      JNI.failureCallback = failureCallback;
+    }
+  }
+
+  /**
+   * Set name of JNI library to load. Default is 'clearsilver-jni'.
+   */
+  public static void setLibraryName(String libraryName) {
+    JNI.libraryName = libraryName;
+  }
+
+}
\ No newline at end of file
diff -Nru clearsilver-0.10.4/java-jni/Makefile clearsilver-0.10.5/java-jni/Makefile
--- clearsilver-0.10.4/java-jni/Makefile	2006-08-07 13:05:13.000000000 -0700
+++ clearsilver-0.10.5/java-jni/Makefile	2007-02-01 17:06:17.000000000 -0800
@@ -8,27 +8,29 @@
 include $(NEOTONIC_ROOT)/rules.mk
 
 NEO_UTIL_SO = libclearsilver-jni.so
-NEO_UTIL_JAVA_SRC = HDF.java CS.java # CGI.java
-NEO_UTIL_JAVA_CLASS = $(NEO_UTIL_JAVA_SRC:%.java=%)
-NEO_UTIL_JAVA_CLASSFILES = $(NEO_UTIL_JAVA_SRC:%.java=%.class)
+ifeq ($(OSTYPE),Darwin)
+NEO_UTIL_SO = libclearsilver-jni.jnilib
+endif
+NEO_UTIL_JAVA_SRC = HDF.java CS.java CSUtil.java CSFileLoader.java JNI.java # CGI.java
+
 NEO_UTIL_JAVA_JAR = clearsilver.jar
 NEO_UTIL_SRC = j_neo_util.c j_neo_cs.c
 NEO_UTIL_OBJ = $(NEO_UTIL_SRC:%.c=%.o)
 
-
 CFLAGS += $(JAVA_INCLUDE_PATH)
 DLIBS += -lneo_cgi -lneo_cs -lneo_utl
 LIBS += $(DLIBS)
+ifneq ($(OSTYPE),Darwin)
 LDFLAGS += -Wl,-soname=$(NEO_UTIL_SO)
+endif
 
 TARGETS = org_clearsilver_HDF.h org_clearsilver_CS.h $(NEO_UTIL_SO)
 
 all: $(TARGETS) test
 
 $(NEO_UTIL_JAVA_JAR): $(NEO_UTIL_JAVA_SRC)
-	$(JAVAC) $(NEO_UTIL_JAVA_SRC)
 	$(MKDIR) org/clearsilver
-	cp $(NEO_UTIL_JAVA_CLASSFILES) org/clearsilver
+	$(JAVAC) -d . $(NEO_UTIL_JAVA_SRC)
 	$(JAR) cf $(NEO_UTIL_JAVA_JAR) org
 	$(RM) -r org
 
Binary files clearsilver-0.10.4/java-jni/clearsilver.jar and clearsilver-0.10.5/java-jni/clearsilver.jar differ
diff -Nru clearsilver-0.10.4/java-jni/j_neo_cs.c clearsilver-0.10.5/java-jni/j_neo_cs.c
--- clearsilver-0.10.4/java-jni/j_neo_cs.c	2005-10-11 11:41:32.000000000 -0700
+++ clearsilver-0.10.5/java-jni/j_neo_cs.c	2006-12-19 15:44:54.000000000 -0800
@@ -14,6 +14,7 @@
 #include "cgi/html.h"
 #include "cs/cs.h"
 
+#include "j_neo_util.h"
 
 jfieldID _csobjFldID = NULL;
 
@@ -55,19 +56,32 @@
 }
 
 
-JNIEXPORT void JNICALL Java_org_clearsilver_CS__1parseFile 
-    (JNIEnv *env, jclass objClass, jint cs_obj_ptr, 
-     jstring j_filename) {
-  
+JNIEXPORT void JNICALL Java_org_clearsilver_CS__1parseFile(JNIEnv *env,
+    jobject objCS, jint cs_obj_ptr, jstring j_filename, jboolean use_cb) {
   CSPARSE *cs = (CSPARSE *)cs_obj_ptr;
   NEOERR *err;
   const char *filename;
+  FILELOAD_INFO fl_info;
 
   if (!j_filename) { return; } // throw
+
+  if (use_cb == JNI_TRUE) {
+    jclass csClass;
+    csClass = (*env)->GetObjectClass(env, objCS); 
+    if (csClass == NULL) return;
+    fl_info.env = env;
+    fl_info.fl_obj = objCS;
+    fl_info.hdf = cs->hdf;
+    fl_info.fl_method = (*env)->GetMethodID(env, csClass,
+        "fileLoad", "(Ljava/lang/String;)Ljava/lang/String;");
+    if (fl_info.fl_method == NULL) return;
+    cs_register_fileload(cs, &fl_info, jni_fileload_cb);
+  }
   
   filename = (*env)->GetStringUTFChars(env,j_filename,0);
 
   err = cs_parse_file(cs,(char *)filename);
+  if (use_cb == JNI_TRUE) cs_register_fileload(cs, NULL, NULL);
   if (err != STATUS_OK) { jNeoErr(env,err); return; }
 
 
diff -Nru clearsilver-0.10.4/java-jni/j_neo_util.c clearsilver-0.10.5/java-jni/j_neo_util.c
--- clearsilver-0.10.4/java-jni/j_neo_util.c	2006-04-20 12:56:25.000000000 -0700
+++ clearsilver-0.10.5/java-jni/j_neo_util.c	2006-12-19 15:44:42.000000000 -0800
@@ -1,3 +1,4 @@
+#include <string.h>
 #include <jni.h>
 #include "org_clearsilver_HDF.h"
 
@@ -11,6 +12,8 @@
 #include "cgi/date.h"
 #include "cgi/html.h"
 
+#include "j_neo_util.h"
+
 void throwException(JNIEnv *env, const char* class_name, const char *message) {
   jclass ex_class = (*env)->FindClass(env, class_name);
   if (ex_class == NULL) {
@@ -241,16 +244,71 @@
   return retval;
 }
 
+NEOERR *jni_fileload_cb(void *ctx, HDF *hdf, const char *filename,
+    char **contents) {
+  FILELOAD_INFO *info = (FILELOAD_INFO *)ctx;
+  jstring filename_str;
+  jstring loaded_string;
+  const char *c_loaded_string;
+
+  // We assume that the hdf passed back is actually the hdf we already
+  // have...
+  if (hdf != info->hdf)
+    return nerr_raise(NERR_ASSERT,
+        "jni_fileload_cb: passed HDF pointer doesn't match hdf_obj_ptr");
+
+  filename_str = (*info->env)->NewStringUTF(info->env, filename);
+  (*info->env)->NewLocalRef(info->env, filename_str);
+
+  loaded_string = (*info->env)->CallObjectMethod(info->env, info->fl_obj,
+      info->fl_method, filename_str);
+  if ((*info->env)->ExceptionCheck(info->env)) {
+    (*info->env)->ExceptionDescribe(info->env);
+    (*info->env)->ExceptionClear(info->env);
+    return nerr_raise(NERR_ASSERT,
+        "jni_fileload_cb: HDF.fileLoad returned exception, see STDERR");
+  }
+  c_loaded_string = (*info->env)->GetStringUTFChars(info->env, loaded_string,
+      0);
+  if (c_loaded_string) {
+    *contents = strdup(c_loaded_string);
+  }
+  (*info->env)->ReleaseStringUTFChars(info->env, loaded_string,
+                                      c_loaded_string);
+
+  return STATUS_OK;
+}
+
 JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1readFile(
-    JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename) {
+    JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename,
+    jboolean use_cb) {
   HDF *hdf = (HDF *)hdf_obj_ptr;
   NEOERR *err;
   const char *filename;
   jboolean retval;
+  FILELOAD_INFO fl_info;
+
+  if (use_cb == JNI_TRUE) {
+    jclass hdfClass;
+
+    fl_info.env = env;
+    fl_info.fl_obj = objClass;
+    fl_info.hdf = hdf;
+
+    hdfClass = (*env)->GetObjectClass(env, objClass); 
+    if (hdfClass == NULL) return JNI_FALSE;
+
+    fl_info.fl_method = (*env)->GetMethodID(env, hdfClass,
+        "fileLoad", "(Ljava/lang/String;)Ljava/lang/String;");
+    if (fl_info.fl_method == NULL) return JNI_FALSE;
+
+    hdf_register_fileload(hdf, &fl_info, jni_fileload_cb);
+  }
 
   filename = (*env)->GetStringUTFChars(env, j_filename, 0);
   err = hdf_read_file(hdf, filename);
   (*env)->ReleaseStringUTFChars(env, j_filename, filename);
+  if (use_cb == JNI_TRUE) hdf_register_fileload(hdf, NULL, NULL);
   if (err != STATUS_OK) {
     // Throw an exception.  jNeoErr handles all types of errors other than
     // NOT_FOUND, since that can mean different things in different contexts.
diff -Nru clearsilver-0.10.4/java-jni/j_neo_util.h clearsilver-0.10.5/java-jni/j_neo_util.h
--- clearsilver-0.10.4/java-jni/j_neo_util.h	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/java-jni/j_neo_util.h	2006-12-18 19:36:09.000000000 -0800
@@ -0,0 +1,20 @@
+// Copyright 2006 Brandon Long 
+// All Rights Reserved.
+//
+// This code is made available under the terms of the ClearSilver License.
+// http://www.clearsilver.net/license.hdf
+
+#ifndef __J_NEO_UTIL_H_
+#define __J_NEO_UTIL_H_ 1
+
+typedef struct _fileload_info {
+  JNIEnv *env;
+  jobject fl_obj;
+  HDF *hdf;
+  jmethodID fl_method;
+} FILELOAD_INFO;
+
+NEOERR *jni_fileload_cb(void *ctx, HDF *hdf, const char *filename,
+    char **contents);
+
+#endif  // __J_NEO_UTIL_H_
diff -Nru clearsilver-0.10.4/java-jni/javatest.gold clearsilver-0.10.5/java-jni/javatest.gold
--- clearsilver-0.10.4/java-jni/javatest.gold	2006-11-09 16:54:04.000000000 -0800
+++ clearsilver-0.10.5/java-jni/javatest.gold	2006-12-18 20:20:05.000000000 -0800
@@ -163,3 +163,44 @@
 html escape block: 
   &lt;script src=&quot;some.js&quot;&gt;alert(&#39;123&#39;);&lt;/script&gt;
 
+Testing HDF.readFile() with callback
+
+CSTestLoader::Load testdata/test1.hdf
+
+---- file begin ----
+# Simple HDF file to test that HDF.readFile() works
+Foo.Bar = 10
+Foo.Baz = 20
+
+---- file end ----
+
+Foo.Bar = 10
+Foo.Baz = 20
+
+Testing CS.parseFile() with callback
+
+CSTestLoader::Load testdata/test.cs
+
+---- file begin ----
+Testing CS parse file...
+
+<?cs set:Foo.Frank = "Beans" ?>
+
+<?cs each:f = Foo ?>
+  <?cs var:f ?>
+<?cs /each ?>
+
+---- file end ----
+
+Testing CS parse file...
+
+
+
+
+  10
+
+  20
+
+  Beans
+
+
Binary files clearsilver-0.10.4/java-jni/libclearsilver-jni.so and clearsilver-0.10.5/java-jni/libclearsilver-jni.so differ
diff -Nru clearsilver-0.10.4/java-jni/testdata/test.cs clearsilver-0.10.5/java-jni/testdata/test.cs
--- clearsilver-0.10.4/java-jni/testdata/test.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/java-jni/testdata/test.cs	2006-12-18 20:17:07.000000000 -0800
@@ -0,0 +1,7 @@
+Testing CS parse file...
+
+<?cs set:Foo.Frank = "Beans" ?>
+
+<?cs each:f = Foo ?>
+  <?cs var:f ?>
+<?cs /each ?>
diff -Nru clearsilver-0.10.4/perl/ClearSilver.xs clearsilver-0.10.5/perl/ClearSilver.xs
--- clearsilver-0.10.4/perl/ClearSilver.xs	2005-12-02 03:08:39.000000000 -0800
+++ clearsilver-0.10.5/perl/ClearSilver.xs	2007-07-11 18:31:34.000000000 -0700
@@ -34,10 +34,9 @@
 
 static NEOERR *output (void *ctx, char *s)
 {
-  NEOERR* err;
-  STRING* str_p = (STRING*)ctx;
-  err = string_append(str_p, s);
-  return err;
+  sv_catpv((SV*)ctx, s);
+
+  return STATUS_OK;
 }
 
 static int sortFunction(const void* in_a, const void* in_b)
@@ -400,27 +399,18 @@
 char *
 perlcs_render(cs)
 	ClearSilver::CS cs
-    PREINIT:
-	STRING str;
     CODE:
-	string_init(&str);
-
-        cs->err = cs_render(cs->cs, &str, output);
-	do {
-	    if (cs->err != STATUS_OK) {
-	        RETVAL = NULL;
-                break;
-	    }
-	    RETVAL = (char*)malloc(str.len + 1);
-	    if (! RETVAL) {
-                break;
-	    }
-	    strncpy(RETVAL, str.buf, str.len);
-	    *(RETVAL + str.len) = '\0';
-	    string_clear (&str);
-	} while (0);
-    OUTPUT:
-	RETVAL
+    {
+	SV *str = newSV(0);
+	cs->err = cs_render(cs->cs, str, output);
+	if (cs->err == STATUS_OK) {
+	  ST(0) = sv_2mortal(str);
+	} else {
+	  SvREFCNT_dec(str);
+	  ST(0) = &PL_sv_undef;
+	}
+	XSRETURN(1);
+    }
 
 int
 perlcs_parseFile(cs, cs_file)
diff -Nru clearsilver-0.10.4/perl/test.out clearsilver-0.10.5/perl/test.out
--- clearsilver-0.10.4/perl/test.out	2005-06-30 11:51:55.000000000 -0700
+++ clearsilver-0.10.5/perl/test.out	1969-12-31 16:00:00.000000000 -0800
@@ -1,11 +0,0 @@
-value3
-value1
-
-
-value1
-
-value2
-
-value3
-
-
diff -Nru clearsilver-0.10.4/ports/rpm/clearsilver.spec clearsilver-0.10.5/ports/rpm/clearsilver.spec
--- clearsilver-0.10.4/ports/rpm/clearsilver.spec	2006-11-14 22:54:20.000000000 -0800
+++ clearsilver-0.10.5/ports/rpm/clearsilver.spec	2007-07-11 19:43:43.000000000 -0700
@@ -53,11 +53,11 @@
 
 Summary: Neotonic ClearSilver
 Name: clearsilver
-Version: 0.10.4
+Version: 0.10.5
 Release: 1
 Copyright: Open Source - Neotonic ClearSilver License (Apache 1.1 based)
 Group: Development/Libraries
-Source: http://www.clearsilver.net/downloads/clearsilver-0.10.4.tar.gz
+Source: http://www.clearsilver.net/downloads/clearsilver-0.10.5.tar.gz
 URL: http://www.clearsilver.net/
 Vendor: Neotonic Software Corporation, Inc.
 Packager: Brandon Long <blong@neotonic.com>
diff -Nru clearsilver-0.10.4/python/neo_cgi.c clearsilver-0.10.5/python/neo_cgi.c
--- clearsilver-0.10.4/python/neo_cgi.c	2006-11-09 14:34:08.000000000 -0800
+++ clearsilver-0.10.5/python/neo_cgi.c	2007-07-11 20:07:43.000000000 -0700
@@ -728,7 +728,7 @@
   v = PyTuple_GetItem (result, 1);
   if (k == NULL || v == NULL)
   {
-    ne_warn ("p_iterenv: Unable to get k,v %s,%s", k, v); 
+    ne_warn ("p_iterenv: Unable to get k,v %p,%p", k, v); 
     Py_DECREF(env_list); 
     PyErr_Clear();
     return -1;
diff -Nru clearsilver-0.10.4/python/setup.py clearsilver-0.10.5/python/setup.py
--- clearsilver-0.10.4/python/setup.py	2006-11-14 22:54:32.000000000 -0800
+++ clearsilver-0.10.5/python/setup.py	2007-07-11 19:43:33.000000000 -0700
@@ -13,7 +13,7 @@
 from distutils.core import Extension
 from distutils import sysconfig
 
-VERSION = "0.10.4"
+VERSION = "0.10.5"
 INC_DIRS = ["../"]
 LIBRARIES = ["neo_cgi", "neo_cs", "neo_utl"]
 LIB_DIRS = ["../libs"]
@@ -31,7 +31,7 @@
 if not os.path.exists("../rules.mk"):
   raise "You need to run configure first to generate the rules.mk file!"
 
-make_vars = {}
+make_vars = { 'NEOTONIC_ROOT' : '..' }
 rules = open("../rules.mk").read()
 for line in string.split(rules, "\n"):
   parts = string.split(line, '=', 1)
@@ -78,7 +78,11 @@
     if var[:2] == "$(" and var[-1] == ")":
       var = variables.get(var[2:-1], "")
     return var
-  return re.sub('(\$\([^\)]*\))', replace_var, var)
+  while 1:
+    new_var = re.sub('(\$\([^\)]*\))', replace_var, var)
+    if new_var == var: break
+    var = new_var
+  return var.strip()
 
 def expand_vars(vlist, vars):
   nlist = []
diff -Nru clearsilver-0.10.4/ruby/ext/hdf/neo_cs.c clearsilver-0.10.5/ruby/ext/hdf/neo_cs.c
--- clearsilver-0.10.4/ruby/ext/hdf/neo_cs.c	2005-06-30 11:51:57.000000000 -0700
+++ clearsilver-0.10.5/ruby/ext/hdf/neo_cs.c	2007-02-15 16:31:39.000000000 -0800
@@ -11,6 +11,7 @@
 
 #include <ruby.h>
 #include "ClearSilver.h"
+#include "neo_ruby.h"
 
 static VALUE cCs;
 extern VALUE mNeotonic;
@@ -33,14 +34,14 @@
 VALUE c_new (VALUE class, VALUE oHdf) {
   CSPARSE *cs = NULL;
   NEOERR *err;
-  HDF *hdf;
+  t_hdfh *hdfh;
   VALUE r_cs;
 
-  Data_Get_Struct(oHdf, HDF, hdf);
+  Data_Get_Struct(oHdf, t_hdfh, hdfh);
 
-  if (hdf == NULL) rb_raise(eHdfError, "must include an Hdf object");
+  if (hdfh == NULL) rb_raise(eHdfError, "must include an Hdf object");
 
-  err = cs_init (&cs, hdf);
+  err = cs_init (&cs, hdfh->hdf);
   if (err) Srb_raise(r_neo_error(err));
   err = cgi_register_strfuncs(cs);
   if (err) Srb_raise(r_neo_error(err));
@@ -69,7 +70,7 @@
   CSPARSE *cs = NULL;
   NEOERR *err;
   char *s, *ms;
-  int l;
+  long l;
 
   Data_Get_Struct(self, CSPARSE, cs);
   s = rb_str2cstr(oString, &l);
@@ -78,7 +79,7 @@
   ms = strdup(s);
   if (ms == NULL) rb_raise(rb_eNoMemError, "out of memory");
 
-  err = cs_parse_string (cs, ms, l);
+  err = cs_parse_string (cs, ms, (size_t)l);
 
   if (err) Srb_raise(r_neo_error(err));
 
diff -Nru clearsilver-0.10.4/ruby/ext/hdf/neo_ruby.h clearsilver-0.10.5/ruby/ext/hdf/neo_ruby.h
--- clearsilver-0.10.4/ruby/ext/hdf/neo_ruby.h	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.5/ruby/ext/hdf/neo_ruby.h	2007-02-15 16:31:39.000000000 -0800
@@ -0,0 +1,11 @@
+
+#ifndef __NEO_RUBY_H_
+#define __NEO_RUBY_H_
+
+typedef struct s_hdfh {
+  HDF *hdf;
+  struct s_hdfh *parent;
+  VALUE top;
+} t_hdfh;
+
+#endif
diff -Nru clearsilver-0.10.4/ruby/ext/hdf/neo_util.c clearsilver-0.10.5/ruby/ext/hdf/neo_util.c
--- clearsilver-0.10.4/ruby/ext/hdf/neo_util.c	2005-06-30 11:51:57.000000000 -0700
+++ clearsilver-0.10.5/ruby/ext/hdf/neo_util.c	2007-02-15 16:32:03.000000000 -0800
@@ -12,10 +12,12 @@
 #include <ruby.h>
 #include <version.h>
 #include "ClearSilver.h"
+#include "neo_ruby.h"
 
 VALUE mNeotonic;
 static VALUE cHdf;
 VALUE eHdfError;
+static ID id_to_s;
 
 #define Srb_raise(val) rb_raise(eHdfError, "%s/%d %s",__FILE__,__LINE__,RSTRING(val)->ptr)
 
@@ -37,10 +39,31 @@
   return errstr;
 }
 
-static void h_free(void *p) {
-  hdf_destroy(p);
+static void h_free2(t_hdfh *hdfh) {
+#ifdef DEBUG
+  fprintf(stderr,"freeing hdf 0x%x\n",hdfh);
+#endif
+  hdf_destroy(&(hdfh->hdf));
+  free(hdfh);
+}
+static void h_free(t_hdfh *hdfh) {
+#ifdef DEBUG
+  fprintf(stderr,"freeing hdf holder 0x%x of 0x%x\n",hdfh,hdfh->parent);
+#endif
+  free(hdfh);
+}
+static void h_mark(t_hdfh *hdfh) {
+  /* Only mark the array if this is the top node, only the original node should
+     set up the marker.
+   */
+#ifdef DEBUG
+  fprintf(stderr,"marking 0x%x\n",hdfh);
+#endif
+  if ( ! NIL_P(hdfh->top) )
+    rb_gc_mark(hdfh->top);
+  else
+    fprintf(stderr,"mark top 0x%x\n",hdfh);
 }
-
 
 static VALUE h_init (VALUE self)
 {
@@ -49,32 +72,35 @@
 
 VALUE h_new(VALUE class)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   NEOERR *err;
-  VALUE r_hdf;
+  VALUE obj;
 
-  err = hdf_init (&hdf);
+  obj=Data_Make_Struct(class,t_hdfh,0,h_free2,hdfh);
+  err = hdf_init (&(hdfh->hdf));
   if (err) Srb_raise(r_neo_error(err));
-
-  r_hdf = Data_Wrap_Struct(class, 0, h_free, hdf);
-  rb_obj_call_init(r_hdf, 0, NULL);
-  return r_hdf;
+#ifdef DEBUG
+  fprintf(stderr,"allocated 0x%x\n",(void *)hdfh);
+#endif
+  hdfh->top=Qnil;
+  rb_obj_call_init(obj, 0, NULL);
+  return obj;
 }
 
 static VALUE h_get_attr (VALUE self, VALUE oName)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *name;
   HDF_ATTR *attr;
   VALUE k,v;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   name = STR2CSTR(oName);
 
   rv = rb_hash_new();
 
-  attr = hdf_get_attr(hdf, name);
+  attr = hdf_get_attr(hdfh->hdf, name);
   while ( attr != NULL ) {
     k=rb_str_new2(attr->key);
     v=rb_str_new2(attr->value);
@@ -86,11 +112,11 @@
 
 static VALUE h_set_attr(VALUE self, VALUE oName, VALUE oKey, VALUE oValue)
 {
-  HDF *hdf= NULL;
+  t_hdfh *hdfh;
   char *name, *key, *value;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
   name = STR2CSTR(oName);
   key = STR2CSTR(oKey);
@@ -99,7 +125,7 @@
   else
     value = STR2CSTR(oValue);
 
-  err = hdf_set_attr(hdf, name, key, value);
+  err = hdf_set_attr(hdfh->hdf, name, key, value);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -107,16 +133,23 @@
 
 static VALUE h_set_value (VALUE self, VALUE oName, VALUE oValue)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *name, *value;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  name=STR2CSTR(oName);
-  value=STR2CSTR(oValue);
+  if ( TYPE(oName) == T_STRING )
+    name=STR2CSTR(oName);
+  else
+    name=STR2CSTR(rb_funcall(oName,id_to_s,0));
+
+  if ( TYPE(oValue) == T_STRING )
+    value=STR2CSTR(oValue);
+  else
+    value=STR2CSTR(rb_funcall(oValue,id_to_s,0));
 
-  err = hdf_set_value (hdf, name, value);
+  err = hdf_set_value (hdfh->hdf, name, value);
 
   if (err) Srb_raise(r_neo_error(err));
 
@@ -125,116 +158,178 @@
 
 static VALUE h_get_int_value (VALUE self, VALUE oName, VALUE oDefault)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *name;
   int r, d = 0;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
+
   name=STR2CSTR(oName);
   d=NUM2INT(oDefault);
 
-  r = hdf_get_int_value (hdf, name, d);
+  r = hdf_get_int_value (hdfh->hdf, name, d);
   rv = INT2NUM(r);
   return rv;
 }
 
 static VALUE h_get_value (VALUE self, VALUE oName, VALUE oDefault)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *name;
   char *r, *d = NULL;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   name=STR2CSTR(oName);
   d=STR2CSTR(oDefault);
 
-  r = hdf_get_value (hdf, name, d);
+  r = hdf_get_value (hdfh->hdf, name, d);
   rv = rb_str_new2(r);
   return rv;
 }
 
 static VALUE h_get_child (VALUE self, VALUE oName)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh,*hdfh_new;
+  HDF *r;
+  VALUE rv;
+  char *name;
+
+  Data_Get_Struct(self, t_hdfh, hdfh);
+  name=STR2CSTR(oName);
+
+  r = hdf_get_child (hdfh->hdf, name);
+  if (r == NULL) {
+    return Qnil;
+  }
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
+  return rv;
+}
+
+static VALUE h_get_obj (VALUE self, VALUE oName)
+{
+  t_hdfh *hdfh,*hdfh_new;
   HDF *r;
   VALUE rv;
   char *name;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   name=STR2CSTR(oName);
 
-  r = hdf_get_child (hdf, name);
+  r = hdf_get_obj (hdfh->hdf, name);
   if (r == NULL) {
     return Qnil;
   }
 
-  rv = Data_Wrap_Struct(cHdf, 0, h_free, r);
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
+  return rv;
+}
+
+static VALUE h_get_node (VALUE self, VALUE oName)
+{
+  t_hdfh *hdfh,*hdfh_new;
+  HDF *r;
+  VALUE rv;
+  char *name;
+  NEOERR *err;
+
+  Data_Get_Struct(self, t_hdfh, hdfh);
+  name=STR2CSTR(oName);
+
+  err = hdf_get_node (hdfh->hdf, name, &r);
+  if (err)
+    Srb_raise(r_neo_error(err));
+
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
   return rv;
 }
 
 
 static VALUE h_obj_child (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh,*hdfh_new;
   HDF *r = NULL;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   
-  r = hdf_obj_child (hdf);
+  r = hdf_obj_child (hdfh->hdf);
   if (r == NULL) {
     return Qnil;
   }
 
-  rv = Data_Wrap_Struct(cHdf, 0, h_free, r);
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
   return rv;
 }
 
 static VALUE h_obj_next (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh,*hdfh_new;
   HDF *r = NULL;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  r = hdf_obj_next (hdf);
+  r = hdf_obj_next (hdfh->hdf);
   if (r == NULL) {
     return Qnil;
   }
 
-  rv = Data_Wrap_Struct(cHdf, 0, h_free, r);
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
   return rv;
 }
 
 static VALUE h_obj_top (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh,*hdfh_new;
   HDF *r = NULL;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  r = hdf_obj_top (hdf);
+  r = hdf_obj_top (hdfh->hdf);
   if (r == NULL) {
     return Qnil;
   }
 
-  rv = Data_Wrap_Struct(cHdf, 0, h_free, r);
+  rv=Data_Make_Struct(cHdf,t_hdfh,h_mark,h_free,hdfh_new);
+  hdfh_new->top=self;
+  hdfh_new->hdf=r;
+  hdfh_new->parent=hdfh;
+
   return rv;
 }
 
 static VALUE h_obj_name (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   VALUE rv;
   char *r;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  r = hdf_obj_name (hdf);
+  r = hdf_obj_name (hdfh->hdf);
   if (r == NULL) {
     return Qnil;
   }
@@ -245,15 +340,15 @@
 
 static VALUE h_obj_attr (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   HDF_ATTR *attr;
   VALUE k,v;
   VALUE rv;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   rv = rb_hash_new();
   
-  attr = hdf_obj_attr(hdf);
+  attr = hdf_obj_attr(hdfh->hdf);
   while ( attr != NULL ) {
     k=rb_str_new2(attr->key);
     v=rb_str_new2(attr->value);
@@ -266,13 +361,13 @@
 
 static VALUE h_obj_value (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   VALUE rv;
   char *r;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  r = hdf_obj_value (hdf);
+  r = hdf_obj_value (hdfh->hdf);
   if (r == NULL) {
     return Qnil;
   }
@@ -283,15 +378,15 @@
 
 static VALUE h_read_file (VALUE self, VALUE oPath)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *path;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
   path=STR2CSTR(oPath);
 
-  err = hdf_read_file (hdf, path);
+  err = hdf_read_file (hdfh->hdf, path);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -299,15 +394,15 @@
 
 static VALUE h_write_file (VALUE self, VALUE oPath)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *path;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
   path=STR2CSTR(oPath);
 
-  err = hdf_write_file (hdf, path);
+  err = hdf_write_file (hdfh->hdf, path);
 
   if (err) Srb_raise(r_neo_error(err));
 
@@ -316,15 +411,15 @@
 
 static VALUE h_write_file_atomic (VALUE self, VALUE oPath)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *path;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
   path=STR2CSTR(oPath);
 
-  err = hdf_write_file_atomic (hdf, path);
+  err = hdf_write_file_atomic (hdfh->hdf, path);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -332,14 +427,14 @@
 
 static VALUE h_remove_tree (VALUE self, VALUE oName)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *name;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   name = STR2CSTR(oName);
 
-  err = hdf_remove_tree (hdf, name);
+  err = hdf_remove_tree (hdfh->hdf, name);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -347,18 +442,21 @@
 
 static VALUE h_dump (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   VALUE rv;
   NEOERR *err;
   STRING str;
 
   string_init (&str);
   
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  err = hdf_dump_str (hdf, NULL, 0, &str);
+  err = hdf_dump_str (hdfh->hdf, NULL, 0, &str);
   if (err) Srb_raise(r_neo_error(err));
 
+  if (str.len==0)
+    return Qnil;
+
   rv = rb_str_new2(str.buf);
   string_clear (&str);
   return rv;
@@ -366,14 +464,14 @@
 
 static VALUE h_write_string (VALUE self)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   VALUE rv;
   NEOERR *err;
   char *s = NULL;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
-  err = hdf_write_string (hdf, &s);
+  err = hdf_write_string (hdfh->hdf, &s);
 
   if (err) Srb_raise(r_neo_error(err));
 
@@ -384,17 +482,17 @@
 
 static VALUE h_read_string (VALUE self, VALUE oString, VALUE oIgnore)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   NEOERR *err;
   char *s = NULL;
   int ignore = 0;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
 
   s = STR2CSTR(oString);
   ignore = NUM2INT(oIgnore);
 
-  err = hdf_read_string_ignore (hdf, s, ignore);
+  err = hdf_read_string_ignore (hdfh->hdf, s, ignore);
 
   if (err) Srb_raise(r_neo_error(err));
 
@@ -403,19 +501,18 @@
 
 static VALUE h_copy (VALUE self, VALUE oName, VALUE oHdfSrc)
 {
-  HDF *hdf = NULL;
-  HDF *src = NULL;
+  t_hdfh *hdfh, *hdfh_src;
   char *name;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
-  Data_Get_Struct(oHdfSrc, HDF, src);
+  Data_Get_Struct(self, t_hdfh, hdfh);
+  Data_Get_Struct(oHdfSrc, t_hdfh, hdfh_src);
 
   name = STR2CSTR(oName);
 
-  if (src == NULL) rb_raise(eHdfError, "second argument must be an Hdf object");
+  if (hdfh_src == NULL) rb_raise(eHdfError, "second argument must be an Hdf object");
 
-  err = hdf_copy (hdf, name, src);
+  err = hdf_copy (hdfh->hdf, name, hdfh_src->hdf);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -423,16 +520,16 @@
 
 static VALUE h_set_symlink (VALUE self, VALUE oSrc, VALUE oDest)
 {
-  HDF *hdf = NULL;
+  t_hdfh *hdfh;
   char *src;
   char *dest;
   NEOERR *err;
 
-  Data_Get_Struct(self, HDF, hdf);
+  Data_Get_Struct(self, t_hdfh, hdfh);
   src = STR2CSTR(oSrc);
   dest = STR2CSTR(oDest);
 
-  err = hdf_set_symlink (hdf, src, dest);
+  err = hdf_set_symlink (hdfh->hdf, src, dest);
   if (err) Srb_raise(r_neo_error(err));
 
   return self;
@@ -452,7 +549,7 @@
   esc_char = STR2CSTR(oEsc_char);
   escape = STR2CSTR(oEsc);
 
-  err = neos_escape(s, buflen, esc_char[0], escape, &ret);
+  err = neos_escape((UINT8*)s, buflen, esc_char[0], escape, &ret);
 
   if (err) Srb_raise(r_neo_error(err));
 
@@ -476,7 +573,7 @@
   copy = strdup(s);
   if (copy == NULL) rb_raise(rb_eNoMemError, "out of memory");
 
-  neos_unescape(copy, buflen, esc_char[0]);
+  neos_unescape((UINT8*)copy, buflen, esc_char[0]);
 
   rv = rb_str_new2(copy);
   free(copy);
@@ -486,6 +583,9 @@
 void Init_cs();
 
 void Init_hdf() {
+
+  id_to_s=rb_intern("to_s");
+
   mNeotonic = rb_define_module("Neo");
   cHdf = rb_define_class_under(mNeotonic, "Hdf", rb_cObject);
 
@@ -494,9 +594,12 @@
   rb_define_method(cHdf, "get_attr", h_get_attr, 1);
   rb_define_method(cHdf, "set_attr", h_set_attr, 3);
   rb_define_method(cHdf, "set_value", h_set_value, 2);
+  rb_define_method(cHdf, "put", h_set_value, 2);
   rb_define_method(cHdf, "get_int_value", h_get_int_value, 2);
   rb_define_method(cHdf, "get_value", h_get_value, 2);
   rb_define_method(cHdf, "get_child", h_get_child, 1);
+  rb_define_method(cHdf, "get_obj", h_get_obj, 1);
+  rb_define_method(cHdf, "get_node", h_get_node, 1);
   rb_define_method(cHdf, "obj_child", h_obj_child, 0);
   rb_define_method(cHdf, "obj_next", h_obj_next, 0);
   rb_define_method(cHdf, "obj_top", h_obj_top, 0);
diff -Nru clearsilver-0.10.4/rules.mk.in clearsilver-0.10.5/rules.mk.in
--- clearsilver-0.10.4/rules.mk.in	2005-12-02 03:25:19.000000000 -0800
+++ clearsilver-0.10.5/rules.mk.in	2006-12-18 20:39:58.000000000 -0800
@@ -105,6 +105,10 @@
 ifeq ($(OSNAME),SunOS)
 LDSHARED   = ld -G -fPIC
 endif
+ifeq ($(OSTYPE),Darwin)
+LDSHARED   = $(CC) -bundle -flat_namespace -undefined suppress $(PICFLG)
+CPPLDSHARED   = $(CPP) -bundle -flat_namespace -undefined suppress $(PICFLG)
+endif
 
 ## --------------win32 options
 
diff -Nru clearsilver-0.10.4/scripts/document.py clearsilver-0.10.5/scripts/document.py
--- clearsilver-0.10.4/scripts/document.py	2005-07-27 17:53:10.000000000 -0700
+++ clearsilver-0.10.5/scripts/document.py	2007-07-12 13:07:05.000000000 -0700
@@ -1,4 +1,4 @@
-#!/bin/env python
+#!/usr/bin/env python
 """
   document.py -- Simple script to generate manpages from C header
   files.  Looks for the following formatted C comments in the C header files:
diff -Nru clearsilver-0.10.4/util/neo_err.h clearsilver-0.10.5/util/neo_err.h
--- clearsilver-0.10.4/util/neo_err.h	2005-12-15 14:17:36.000000000 -0800
+++ clearsilver-0.10.5/util/neo_err.h	2007-07-11 20:09:09.000000000 -0700
@@ -12,6 +12,8 @@
 #ifndef __NEO_ERR_H_
 #define __NEO_ERR_H_ 1
 
+#include "util/neo_misc.h"
+
 /* For compilers (well, cpp actually) which don't define __PRETTY_FUNCTION__ */
 #ifndef __GNUC__
 #define __PRETTY_FUNCTION__ "unknown_function"
@@ -58,6 +60,16 @@
   struct _neo_err *next;
 } NEOERR;
 
+/* Technically, we could do this in configure and detect what their compiler
+ * can handle, but for now... */
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define USE_C99_VARARG_MACROS 1
+#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 4) || defined (S_SPLINT_S)
+#define USE_GNUC_VARARG_MACROS 1
+#else
+#error The compiler is missing support for variable-argument macros.
+#endif
+
 
 /*
  * function: nerr_raise
@@ -65,34 +77,38 @@
  *              return up the call chain
  * arguments: using the macro, the function name, file, and lineno are
  *            automagically recorded for you.  You just provide the
- *            error (from those listed above) and the printf-style 
+ *            error (from those listed above) and the printf-style
  *            reason.  THIS IS A PRINTF STYLE FUNCTION, DO NOT PASS
  *            UNKNOWN STRING DATA AS THE FORMAT STRING.
  * returns: a pointer to a NEOERR, or INTERNAL_ERR if allocation of
  *          NEOERR fails
  */
-#ifdef __GNUC__
-#define nerr_raise(e,f,a...) \
-   nerr_raisef(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
-#else
+#if defined(USE_C99_VARARG_MACROS)
 #define nerr_raise(e,f,...) \
    nerr_raisef(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,__VA_ARGS__)
+#elif defined(USE_GNUC_VARARG_MACROS)
+#define nerr_raise(e,f,a...) \
+   nerr_raisef(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
 #endif
 
-NEOERR *nerr_raisef (const char *func, const char *file, int lineno, 
-                     NERR_TYPE error, const char *fmt, ...);
+NEOERR *nerr_raisef (const char *func, const char *file, int lineno,
+                     NERR_TYPE error, const char *fmt, ...)
+                     ATTRIBUTE_PRINTF(5,6);
+  
 
 
-#ifdef __GNUC__
-#define nerr_raise_errno(e,f,a...) \
-   nerr_raise_errnof(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
-#else
+#if defined(USE_C99_VARARG_MACROS)
 #define nerr_raise_errno(e,f,...) \
    nerr_raise_errnof(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,__VA_ARGS__)
+#elif defined(USE_GNUC_VARARG_MACROS)
+#define nerr_raise_errno(e,f,a...) \
+   nerr_raise_errnof(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
 #endif
 
-NEOERR *nerr_raise_errnof (const char *func, const char *file, int lineno, 
-                           int error, const char *fmt, ...);
+NEOERR *nerr_raise_errnof (const char *func, const char *file, int lineno,
+                           int error, const char *fmt, ...)
+                           ATTRIBUTE_PRINTF(5,6);
+
 /* function: nerr_pass
  * description: this function is used to pass an error up a level in the
  *              call chain (ie, if the error isn't handled at the
@@ -104,7 +120,8 @@
  */
 #define nerr_pass(e) \
    nerr_passf(__PRETTY_FUNCTION__,__FILE__,__LINE__,e)
-NEOERR *nerr_passf (const char *func, const char *file, int lineno, 
+
+NEOERR *nerr_passf (const char *func, const char *file, int lineno,
                     NEOERR *err);
 
 /* function: nerr_pass_ctx
@@ -115,20 +132,22 @@
  *              This version includes context information about lower
  *              errors
  * arguments: with the macro, the function name, file and lineno are
- *            automagically recorded.  Just pass the error and 
+ *            automagically recorded.  Just pass the error and
  *            a printf format string giving more information about where
  *            the error is occuring.
  * returns: a pointer to an error
  */
-#ifdef __GNUC__
-#define nerr_pass_ctx(e,f,a...) \
-   nerr_pass_ctxf(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
-#else
+#if defined(USE_C99_VARARG_MACROS)
 #define nerr_pass_ctx(e,f,...) \
    nerr_pass_ctxf(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,__VA_ARGS__)
+#elif defined(USE_GNUC_VARARG_MACROS)
+#define nerr_pass_ctx(e,f,a...) \
+   nerr_pass_ctxf(__PRETTY_FUNCTION__,__FILE__,__LINE__,e,f,##a)
 #endif
-NEOERR *nerr_pass_ctxf (const char *func, const char *file, int lineno, NEOERR *err, 
-                       const char *fmt, ...);
+
+NEOERR *nerr_pass_ctxf (const char *func, const char *file, int lineno,
+                        NEOERR *err, const char *fmt, ...)
+                        ATTRIBUTE_PRINTF(5,6);
 
 /* function: nerr_log_error
  * description: currently, this prints out the error to stderr, and
@@ -164,7 +183,7 @@
  * description: register an error type.  This will assign a numeric value
  *              to the type, and keep track of the "pretty name" for it.
  * arguments: err - pointer to a NERR_TYPE
- *            name - pretty name for the error type 
+ *            name - pretty name for the error type
  * returns: NERR_NOMEM on no memory
  */
 NEOERR *nerr_register (NERR_TYPE *err, const char *name);
@@ -186,7 +205,7 @@
  *              parlance, this would be the equivalent of "catch".
  *              Typically, you can just compare a NEOERR against STATUS_OK
  *              or just test for true if you are checking for any error.
- * arguments: err - the NEOERR that has an error. 
+ * arguments: err - the NEOERR that has an error.
  *            type - the NEOERR type, as registered with nerr_register
  * returns: true on match
  */
@@ -196,7 +215,7 @@
  * description: nerr_handle is a convenience function.  It is the equivalent
  *              of nerr_match, but it will also deallocate the error chain
  *              on a match.
- * arguments: err - pointer to a pointer NEOERR 
+ * arguments: err - pointer to a pointer NEOERR
  *            type - the NEOERR type, as registered with nerr_register
  * returns: true on match
  */
diff -Nru clearsilver-0.10.4/util/neo_files.c clearsilver-0.10.5/util/neo_files.c
--- clearsilver-0.10.4/util/neo_files.c	2005-06-30 17:41:29.000000000 -0700
+++ clearsilver-0.10.5/util/neo_files.c	2007-07-11 19:14:23.000000000 -0700
@@ -92,7 +92,7 @@
   {
     close(fd);
     return nerr_raise (NERR_NOMEM, 
-	"Unable to allocate memory (%d) to load file %s", s.st_size, path);
+	"Unable to allocate memory (%d) to load file %s", len + 1, path);
   }
   if ((bytes_read = read (fd, *str, len)) == -1)
   {
diff -Nru clearsilver-0.10.4/util/neo_hdf.c clearsilver-0.10.5/util/neo_hdf.c
--- clearsilver-0.10.4/util/neo_hdf.c	2006-08-11 16:47:01.000000000 -0700
+++ clearsilver-0.10.5/util/neo_hdf.c	2007-07-11 18:52:37.000000000 -0700
@@ -640,7 +640,24 @@
     return nerr_raise(NERR_ASSERT, "Unable to set Empty component %s", name);
   }
 
-  hn = hdf;
+  if (hdf->link)
+  {
+    char *new_name = (char *) malloc(strlen(hdf->value) + 1 + strlen(name) + 1);
+    if (new_name == NULL)
+    {
+      return nerr_raise(NERR_NOMEM, "Unable to allocate memory");
+    }
+    strcpy(new_name, hdf->value);
+    strcat(new_name, ".");
+    strcat(new_name, name);
+    err = _set_value (hdf->top, new_name, value, dup, wf, link, attr, set_node);
+    free(new_name);
+    return nerr_pass(err);
+  }
+  else
+  {
+    hn = hdf;
+  }
 
   while (1)
   {
@@ -779,7 +796,7 @@
       }
       strcpy(new_name, hp->value);
       strcat(new_name, s);
-      err = _set_value (hdf, new_name, value, dup, wf, link, attr, set_node);
+      err = _set_value (hdf->top, new_name, value, dup, wf, link, attr, set_node);
       free(new_name);
       return nerr_pass(err);
     }
diff -Nru clearsilver-0.10.4/util/neo_hdf.h clearsilver-0.10.5/util/neo_hdf.h
--- clearsilver-0.10.4/util/neo_hdf.h	2006-04-06 15:05:43.000000000 -0700
+++ clearsilver-0.10.5/util/neo_hdf.h	2007-07-11 19:32:52.000000000 -0700
@@ -155,13 +155,14 @@
  *              default value possible.
  * Input: hdf -> the dataset node to start from
  *        namefmt -> the printf-style format string
- *        ... -> arguments to fill out namefmt 
+ *        ... -> arguments to fill out namefmt
  * Output: None
  * Returns: A pointer to the string stored in the data set, or NULL.
  *          The data set maintains ownership of the string, if you want
  *          a copy you either have to call strdup yourself.
  */
-char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...);
+char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...)
+                      ATTRIBUTE_PRINTF(2,3);
 
 /*
  * Function: hdf_get_copy - Returns a copy of a string in the HDF data set
@@ -173,9 +174,9 @@
  *                  exist
  * Output: value -> the allocated string (if defval = NULL, then value
  *                  will be NULL if defval is used)
- * Returns: NERR_NOMEM if unable to allocate the new copy 
+ * Returns: NERR_NOMEM if unable to allocate the new copy
  */
-NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value, 
+NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value,
                       const char *defval);
 
 /*
@@ -231,7 +232,7 @@
  * Output:
  * Returns:
  */
-NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key, 
+NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key,
                       const char *value);
 
 /*
@@ -344,8 +345,9 @@
  * Output: None
  * Returns: NERR_NOMEM
  */
-NEOERR* hdf_set_valuef (HDF *hdf, const char *fmt, ...);
-NEOERR* hdf_set_valuevf (HDF *hdf, const char *fmt, va_list ap); 
+NEOERR* hdf_set_valuef (HDF *hdf, const char *fmt, ...)
+                        ATTRIBUTE_PRINTF(2,3);
+NEOERR* hdf_set_valuevf (HDF *hdf, const char *fmt, va_list ap);
 
 /*
  * Function: hdf_set_int_value - Set the value of a named node to a number
@@ -405,7 +407,7 @@
  *              structure directly will bypass the symlink.  Use this
  *              feature sparingly as its likely to surprise you.
  * Input: hdf -> the dataset node
- *        src -> the source node name 
+ *        src -> the source node name
  *        dest -> the destination node name (from the top of the
  *        dataset, not relative names)
  * Output: None
@@ -414,7 +416,7 @@
 NEOERR *hdf_set_symlink (HDF *hdf, const char *src, const char *dest);
 
 /*
- * Function: hdf_sort_obj - sort the children of an HDF node 
+ * Function: hdf_sort_obj - sort the children of an HDF node
  * Description: hdf_sort_obj will sort the children of an HDF node,
  *              based on the given comparison function.
  *              This function works by creating an array of the pointers
@@ -425,7 +427,7 @@
  *              a pointer to an HDF struct, so your comparison function
  *              should work on HDF ** pointers.
  * Input: h - HDF node
- *        compareFunc - function which returns 1,0,-1 depending on some 
+ *        compareFunc - function which returns 1,0,-1 depending on some
  *                      criteria.  The arguments to this sort function
  *                      are pointers to pointers to HDF elements.  For
  *                      example:
@@ -437,7 +439,7 @@
  *                      }
  *
  * Output: None (h children will be sorted)
- * Return: NERR_NOMEM 
+ * Return: NERR_NOMEM
  */
 NEOERR *hdf_sort_obj(HDF *h, int (*compareFunc)(const void *, const void *));
 
@@ -563,10 +565,10 @@
 
 /*
  * Function: hdf_register_fileload - register a fileload function
- * Description: hdf_register_fileload registers a fileload function that 
+ * Description: hdf_register_fileload registers a fileload function that
  *              overrides the built-in function.  The built-in function
  *              uses hdf_search_path and ne_file_load (based on stat/open/read)
- *              to find and load the file on every hdf_read_file (including 
+ *              to find and load the file on every hdf_read_file (including
  *              #include).  You can override this function if you wish to provide
  *              other file search functions, or load the hdf file
  *              from an in-memory cache, etc.
diff -Nru clearsilver-0.10.4/util/neo_misc.h clearsilver-0.10.5/util/neo_misc.h
--- clearsilver-0.10.4/util/neo_misc.h	2005-12-05 20:19:36.000000000 -0800
+++ clearsilver-0.10.5/util/neo_misc.h	2007-07-11 19:36:32.000000000 -0700
@@ -16,12 +16,12 @@
 #include <time.h>
 #include <limits.h>
 
-/* In case they didn't start from ClearSilver.h... */
+/* In case they didn't start from ClearSilver.h. */
 #ifndef __CS_CONFIG_H_
 #include "cs_config.h"
 #endif
 
-/* Fix Up for systems that don't define these standard things... */
+/* Fix Up for systems that don't define these standard things */
 #ifndef __BEGIN_DECLS
 #ifdef __cplusplus
 #define __BEGIN_DECLS extern "C" {
@@ -55,6 +55,15 @@
 #define S_IROTH S_IRUSR
 #endif
 
+/* Format string checking for compilers that support it (GCC style) */
+
+#if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ > 6
+#define ATTRIBUTE_PRINTF(a1,a2) __attribute__((__format__ (__printf__, a1, a2)))
+#else
+#define ATTRIBUTE_PRINTF(a1,a2)
+#endif
+
+
 __BEGIN_DECLS
 
 #ifndef HAVE_STRTOK_R
@@ -74,7 +83,8 @@
 #endif
 
 #ifndef HAVE_SNPRINTF
-int snprintf (char *str, size_t count, const char *fmt, ...);
+int snprintf (char *str, size_t count, const char *fmt, ...)
+              ATTRIBUTE_PRINTF(3,4);
 #endif
 
 #ifndef HAVE_VSNPRINTF
@@ -104,9 +114,11 @@
 #endif
 
 void ne_vwarn (const char *fmt, va_list ap);
-void ne_warn (const char *fmt, ...);
+void ne_warn (const char *fmt, ...)
+              ATTRIBUTE_PRINTF(1,2);
 void ne_set_log (int level);
-void ne_log (int level, const char *fmt, ...);
+void ne_log (int level, const char *fmt, ...)
+             ATTRIBUTE_PRINTF(2,3);
 UINT32 python_string_hash (const char *s);
 UINT8 *ne_stream4 (UINT8  *dest, UINT32 num);
 UINT8 *ne_unstream4 (UINT32 *pnum, UINT8 *src);
diff -Nru clearsilver-0.10.4/util/neo_str.c clearsilver-0.10.5/util/neo_str.c
--- clearsilver-0.10.4/util/neo_str.c	2006-08-07 13:01:53.000000000 -0700
+++ clearsilver-0.10.5/util/neo_str.c	2007-07-11 18:24:00.000000000 -0700
@@ -801,6 +801,69 @@
   return STATUS_OK;
 }
 
+char *URL_PROTOCOLS[] = {"http://", "https://", "ftp://", "mailto:"};
+
+NEOERR *neos_url_validate (const char *in, char **esc)
+{
+  NEOERR *err = STATUS_OK;
+  STRING out_s;
+  int valid = 0;
+  size_t i;
+  size_t inlen;
+  int num_protocols = sizeof(URL_PROTOCOLS) / sizeof(char*);
+  void* slashpos;
+  void* colonpos;
+
+  inlen = strlen(in);
+
+  /*
+   * <a href="//b:80"> or <a href="a/b:80"> are allowed by browsers
+   * and ":" is treated as part of the path, while
+   * <a href="www.google.com:80"> is an invalid url
+   * and ":" is treated as a scheme separator. 
+   *
+   * Hence allow for ":" in the path part of a url (after /) 
+   */
+  slashpos = memchr(in, '/', inlen);
+  if (slashpos == NULL) {
+    i = inlen;
+  }
+  else {
+    i = (size_t)((char*)slashpos - in);
+  }
+
+  colonpos = memchr(in, ':', i);
+
+  if (colonpos == NULL) {
+    // no scheme in 'in': so this is a relative url
+    valid = 1;
+  }
+  else {
+    for (i = 0; i < num_protocols; i++)
+    {
+      if ((inlen >= strlen(URL_PROTOCOLS[i])) && 
+          strncmp(in, URL_PROTOCOLS[i], strlen(URL_PROTOCOLS[i])) == 0) {
+        // 'in' starts with one of the allowed protocols
+        valid = 1;
+        break;
+      }
+
+    }
+  }  
+ 
+  if (valid)
+    return neos_html_escape(in, inlen, esc);
+
+  // 'in' contains an unsupported scheme, replace with '#' 
+  string_init(&out_s);
+  err = string_append (&out_s, "#");
+  if (err) return nerr_pass (err);
+  
+  *esc = out_s.buf;
+  return STATUS_OK;    
+
+}
+
 NEOERR *neos_var_escape (NEOS_ESCAPE context,
                          const char *in,
                          char **esc)
diff -Nru clearsilver-0.10.4/util/neo_str.h clearsilver-0.10.5/util/neo_str.h
--- clearsilver-0.10.4/util/neo_str.h	2006-08-07 13:01:53.000000000 -0700
+++ clearsilver-0.10.5/util/neo_str.h	2007-07-11 19:34:19.000000000 -0700
@@ -20,22 +20,22 @@
 
 /* This modifies the string its called with by replacing all the white
  * space on the end with \0, and returns a pointer to the first
- * non-white space character in the string 
+ * non-white space character in the string
  */
 char *neos_strip (char *s);
 
 void neos_lower (char *s);
 
-char *sprintf_alloc (const char *fmt, ...);
-char *nsprintf_alloc (int start_size, const char *fmt, ...);
+char *sprintf_alloc (const char *fmt, ...) ATTRIBUTE_PRINTF(1,2);
+char *nsprintf_alloc (int start_size, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3);
 char *vsprintf_alloc (const char *fmt, va_list ap);
 char *vnsprintf_alloc (int start_size, const char *fmt, va_list ap);
 
-/* Versions of the above which actually return a length, necessary if 
+/* Versions of the above which actually return a length, necessary if
  * you expect embedded NULLs */
 int vnisprintf_alloc (char **buf, int start_size, const char *fmt, va_list ap);
 int visprintf_alloc (char **buf, const char *fmt, va_list ap);
-int isprintf_alloc (char **buf, const char *fmt, ...);
+int isprintf_alloc (char **buf, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3);
 
 typedef struct _string
 {
@@ -59,7 +59,7 @@
 NEOERR *string_append (STRING *str, const char *buf);
 NEOERR *string_appendn (STRING *str, const char *buf, int l);
 NEOERR *string_append_char (STRING *str, char c);
-NEOERR *string_appendf (STRING *str, const char *fmt, ...);
+NEOERR *string_appendf (STRING *str, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3);
 NEOERR *string_appendvf (STRING *str, const char *fmt, va_list ap);
 NEOERR *string_readline (STRING *str, FILE *fp);
 void string_clear (STRING *str);
@@ -68,7 +68,7 @@
 #include "util/ulist.h"
 /* s is not const because we actually temporarily modify the string
  * during split */
-NEOERR *string_array_split (ULIST **list, char *s, const char *sep, 
+NEOERR *string_array_split (ULIST **list, char *s, const char *sep,
                             int max);
 
 BOOL reg_search (const char *re, const char *str);
@@ -85,7 +85,7 @@
   NEOS_ESCAPE_FUNCTION =  1<<4  /* Special case used to override the others */
 } NEOS_ESCAPE;
 
-NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, const char *escape, 
+NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, const char *escape,
                     char **esc);
 UINT8 *neos_unescape (UINT8 *s, int buflen, char esc_char);
 
@@ -107,6 +107,8 @@
 NEOERR *neos_html_escape (const char *src, int slen,
                           char **out);
 
+NEOERR *neos_url_validate (const char *in, char **esc);
+
 __END_DECLS
 
 #endif /* __NEO_STR_H_ */
