diff -Nrub clearsilver-0.10.3/cgi/cgi.c clearsilver-0.10.4/cgi/cgi.c
--- clearsilver-0.10.3/cgi/cgi.c	2005-12-02 01:34:04.000000000 -0800
+++ clearsilver-0.10.4/cgi/cgi.c	2006-11-06 15:32:16.000000000 -0800
@@ -266,161 +266,18 @@
 
 NEOERR *cgi_js_escape (const char *in, char **esc)
 {
-  int nl = 0;
-  int l = 0;
-  unsigned char *buf = (unsigned char *)in;
-  unsigned char *s;
-
-  while (buf[l])
-  {
-    if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
-	buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || buf[l] < 32)
-    {
-      nl += 3;
-    }
-    nl++;
-    l++;
-  }
-
-  s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
-  if (s == NULL)
-    return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
-	buf);
-
-  nl = 0; l = 0;
-  while (buf[l])
-  {
-    if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
-	buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || buf[l] < 32)
-    {
-      s[nl++] = '\\';
-      s[nl++] = 'x';
-      s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF];
-      s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF];
-      l++;
-    }
-    else
-    {
-      s[nl++] = buf[l++];
-    }
-  }
-  s[nl] = '\0';
-
-  *esc = (char *)s;
-  return STATUS_OK;
+  return nerr_pass(neos_js_escape(in, esc));
 }
 
-// List of all characters that must be escaped
-// List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
-static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`";
-
-// Check if a single character needs to be escaped
-static BOOL is_reserved_char(char c)
+NEOERR *cgi_url_escape (const char *buf, char **esc)
 {
-  int i = 0;
-
-  if (c < 32 || c > 122) {
-    return TRUE;
-  } else {
-    while (EscapedChars[i]) {
-      if (c == EscapedChars[i]) {
-        return TRUE;
-      }
-      ++i;
-    }
-  }
-  return FALSE;
+  return nerr_pass(neos_url_escape(buf, esc, NULL));
 }
 
 NEOERR *cgi_url_escape_more (const char *in, char **esc,
                              const char *other)
 {
-  int nl = 0;
-  int l = 0;
-  int x = 0;
-  unsigned char *buf = (unsigned char *)in;
-  unsigned char *uother = (unsigned char *)other;
-  unsigned char *s;
-  int match = 0;
-
-  while (buf[l])
-  {
-    if (is_reserved_char(buf[l]))
-    {
-      nl += 2;
-    }
-    else if (uother)
-    {
-      x = 0;
-      while (uother[x])
-      {
-	if (uother[x] == buf[l])
-	{
-	  nl +=2;
-	  break;
-	}
-	x++;
-      }
-    }
-    nl++;
-    l++;
-  }
-
-  s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
-  if (s == NULL)
-    return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
-	buf);
-
-  nl = 0; l = 0;
-  while (buf[l])
-  {
-    match = 0;
-    if (buf[l] == ' ')
-    {
-      s[nl++] = '+';
-      l++;
-    }
-    else
-    {
-      if (is_reserved_char(buf[l]))
-      {
-	match = 1;
-      }
-      else if (uother)
-      {
-	x = 0;
-	while (uother[x])
-	{
-	  if (uother[x] == buf[l])
-	  {
-	    match = 1;
-	    break;
-	  }
-	  x++;
-	}
-      }
-      if (match)
-      {
-	s[nl++] = '%';
-	s[nl++] = "0123456789ABCDEF"[buf[l] / 16];
-	s[nl++] = "0123456789ABCDEF"[buf[l] % 16];
-	l++;
-      }
-      else
-      {
-	s[nl++] = buf[l++];
-      }
-    }
-  }
-  s[nl] = '\0';
-
-  *esc = (char *)s;
-  return STATUS_OK;
-}
-
-NEOERR *cgi_url_escape (const char *buf, char **esc)
-{
-  return nerr_pass(cgi_url_escape_more(buf, esc, NULL));
+  return nerr_pass(neos_url_escape(in, esc, other));
 }
 
 static NEOERR *_parse_query (CGI *cgi, char *query)
@@ -520,7 +377,7 @@
   l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
   if (l == NULL) return STATUS_OK;
   len = atoi (l);
-  if (len == 0) return STATUS_OK;
+  if (len <= 0) return STATUS_OK;
 
   cgi->data_expected = len;
 
@@ -695,7 +552,8 @@
     char *d = hdf_get_value(cgi->hdf, "Query.debug_pause", NULL);
     char *d_p = hdf_get_value(cgi->hdf, "Config.DebugPassword", NULL);
 
-    if (d && d_p && !strcmp(d, d_p)) {
+    if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
+        d && d_p && !strcmp(d, d_p)) {
       sleep(20);
     }
   }
@@ -732,6 +590,10 @@
   my_pcb->ctype = strdup(ctype);
   if (my_pcb->method == NULL || my_pcb->ctype == NULL)
   {
+    if (my_pcb->method != NULL)
+      free(my_pcb->method);
+    if (my_pcb->ctype != NULL)
+      free(my_pcb->ctype);
     free(my_pcb);
     return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb");
   }
@@ -826,6 +688,7 @@
     l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
     if (l == NULL) return STATUS_OK;
     len = atoi (l);
+    if (len <= 0) return STATUS_OK;
 
     x = 0;
     while (x < len)
@@ -1199,7 +1062,8 @@
 
   s = hdf_get_value (cgi->hdf, "Query.debug", NULL);
   e = hdf_get_value (cgi->hdf, "Config.DebugPassword", NULL);
-  if (s && e && !strcmp(s, e)) do_debug = 1;
+  if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) && 
+      s && e && !strcmp(s, e)) do_debug = 1;
   do_timefooter = hdf_get_int_value (cgi->hdf, "Config.TimeFooter", 1);
   ws_strip_level = hdf_get_int_value (cgi->hdf, "Config.WhiteSpaceStrip", 1);
 
@@ -1411,13 +1275,13 @@
 {
   NEOERR *err;
 
-  err = cs_register_strfunc(cs, "url_escape", cgi_url_escape);
+  err = cs_register_esc_strfunc(cs, "url_escape", cgi_url_escape);
   if (err != STATUS_OK) return nerr_pass(err);
-  err = cs_register_strfunc(cs, "html_escape", cgi_html_escape_strfunc);
+  err = cs_register_esc_strfunc(cs, "html_escape", cgi_html_escape_strfunc);
   if (err != STATUS_OK) return nerr_pass(err);
   err = cs_register_strfunc(cs, "text_html", cgi_text_html_strfunc);
   if (err != STATUS_OK) return nerr_pass(err);
-  err = cs_register_strfunc(cs, "js_escape", cgi_js_escape);
+  err = cs_register_esc_strfunc(cs, "js_escape", cgi_js_escape);
   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);
@@ -1455,7 +1319,8 @@
 
   debug = hdf_get_value (cgi->hdf, "Query.debug", NULL);
   t = hdf_get_value (cgi->hdf, "Config.DumpPassword", NULL);
-  if (debug && t && !strcmp (debug, t)) do_dump = 1;
+  if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
+      debug && t && !strcmp (debug, t)) do_dump = 1;
 
   do
   {
@@ -1567,7 +1432,7 @@
 
     host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL);
     if (host == NULL)
-      host = hdf_get_value (cgi->hdf, "CGI.ServerName", NULL);
+      host = hdf_get_value (cgi->hdf, "CGI.ServerName", "localhost");
 
     cgiwrap_writef ("Location: %s://%s", https ? "https" : "http", host);
 
diff -Nrub clearsilver-0.10.3/cgi/cgiwrap.c clearsilver-0.10.4/cgi/cgiwrap.c
--- clearsilver-0.10.3/cgi/cgiwrap.c	2006-01-22 15:43:00.000000000 -0800
+++ clearsilver-0.10.4/cgi/cgiwrap.c	2006-03-28 16:12:37.000000000 -0800
@@ -11,6 +11,7 @@
 
 #include "cs_config.h"
 
+#include <features.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -221,6 +222,23 @@
   }
   else
   {
+#ifdef __UCLIBC__
+    /* According to 
+     * http://cvs.uclinux.org/cgi-bin/cvsweb.cgi/uClibc/libc/stdio/stdio.c#rev1.28
+     * Note: there is a difference in behavior between glibc and uClibc here
+     * regarding fread() on a tty stream.  glibc's fread() seems to return
+     * after reading all _available_ data even if not at end-of-file, while
+     * uClibc's fread() continues reading until all requested or eof or error.
+     * The latter behavior seems correct w.r.t. the standards.
+     *
+     * So, we use read on uClibc.  This may be required on other platforms as
+     * well.  Using raw and buffered i/o interchangeably can be problematic,
+     * but everyone should be going through the cgiwrap interfaces which only
+     * provide this one read function.
+     */
+     *read_len = read (fileno(stdin), buf, buf_len);
+#else
     *read_len = fread (buf, sizeof(char), buf_len, stdin);
+#endif
   }
 }
diff -Nrub clearsilver-0.10.3/cgi/date.c clearsilver-0.10.4/cgi/date.c
--- clearsilver-0.10.3/cgi/date.c	2005-07-27 14:07:16.000000000 -0700
+++ clearsilver-0.10.4/cgi/date.c	2006-11-03 08:56:22.000000000 -0800
@@ -148,7 +148,7 @@
 
   if(isalpha(*ip)) {
     /* ctime */
-    sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
+    sscanf(ip,"%25s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
   }
   else if(ip[2] == '-') {
     /* RFC 850 (normal HTTP) */
diff -Nrub clearsilver-0.10.3/cgi/html.c clearsilver-0.10.4/cgi/html.c
--- clearsilver-0.10.3/cgi/html.c	2005-11-30 20:03:09.000000000 -0800
+++ clearsilver-0.10.4/cgi/html.c	2006-10-19 16:26:35.000000000 -0700
@@ -316,9 +316,9 @@
 	      err = string_append (out, "&gt;");
 	    else if (src[x] == '\n')
 	      if (opts->newlines_convert) 
-		err = string_append (out, "<BR>\n");
+		err = string_append (out, "<br/>\n");
 	      else if (x && src[x-1] == '\n')
-		err = string_append (out, "<P>\n");
+		err = string_append (out, "<p/>\n");
 	      else 
 		err = string_append_char (out, '\n');
 	    else if (src[x] != '\r')
@@ -602,51 +602,7 @@
 NEOERR *html_escape_alloc (const char *src, int slen,
                            char **out)
 {
-  NEOERR *err = STATUS_OK;
-  STRING out_s;
-  int x;
-  char *ptr;
-
-  string_init(&out_s);
-  err = string_append (&out_s, "");
-  if (err) return nerr_pass (err);
-  *out = NULL;
-
-  x = 0;
-  while (x < slen)
-  {
-    ptr = strpbrk(src + x, "&<>\"\r");
-    if (ptr == NULL || (ptr-src >= slen))
-    {
-      err = string_appendn (&out_s, src + x, slen-x);
-      x = slen;
-    }
-    else 
-    {
-      err = string_appendn (&out_s, src + x, (ptr - src) - x);
-      if (err != STATUS_OK) break;
-      x = ptr - src;
-      if (src[x] == '&')
-	err = string_append (&out_s, "&amp;");
-      else if (src[x] == '<')
-	err = string_append (&out_s, "&lt;");
-      else if (src[x] == '>')
-	err = string_append (&out_s, "&gt;");
-      else if (src[x] == '"')
-	err = string_append (&out_s, "&quot;");
-      else if (src[x] != '\r')
-	err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]);
-      x++;
-    }
-    if (err != STATUS_OK) break;
-  }
-  if (err) 
-  {
-    string_clear (&out_s);
-    return nerr_pass (err);
-  }
-  *out = out_s.buf;
-  return STATUS_OK;
+  return nerr_pass(neos_html_escape(src, slen, out));
 }
 
 /* Replace ampersand with iso-8859-1 character code */
diff -Nrub clearsilver-0.10.3/cgi/Makefile clearsilver-0.10.4/cgi/Makefile
--- clearsilver-0.10.3/cgi/Makefile	2006-01-31 18:53:23.000000000 -0800
+++ clearsilver-0.10.4/cgi/Makefile	2006-11-09 14:35:37.000000000 -0800
@@ -10,7 +10,7 @@
 CGI_SRC = cgiwrap.c cgi.c html.c date.c rfc2388.c
 CGI_OBJ = $(CGI_SRC:%.c=%.o)
 
-STATIC_EXE = static.cgi
+STATIC_EXE = cs_static.cgi
 STATIC_SRC = static.c
 STATIC_OBJ = $(STATIC_SRC:%.c=%.o)
 STATIC_CSO = $(STATIC_EXE:%.cgi=%.cso)
@@ -30,13 +30,13 @@
 	$(RANLIB) $@
 
 $(STATIC_EXE): $(STATIC_OBJ) $(DEP_LIBS)
-	$(LD) $@ $(STATIC_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS)
+	$(LD) $@ $(STATIC_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS)
 
 $(STATIC_CSO): $(STATIC_OBJ) $(DEP_LIBS)
-	$(LDSHARED) -o $@ $(STATIC_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS)
+	$(LDSHARED) -o $@ $(STATIC_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS)
 
 $(CGICSTEST_EXE): $(CGICSTEST_OBJ) $(DEP_LIBS)
-	$(LD) $@ $(CGICSTEST_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS)
+	$(LD) $@ $(CGICSTEST_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS)
 
 install: all
 	$(NEOTONIC_ROOT)/mkinstalldirs $(DESTDIR)$(cs_includedir)/cgi
diff -Nrub clearsilver-0.10.3/cgi/rfc2388.c clearsilver-0.10.4/cgi/rfc2388.c
--- clearsilver-0.10.3/cgi/rfc2388.c	2005-06-30 18:21:06.000000000 -0700
+++ clearsilver-0.10.4/cgi/rfc2388.c	2006-08-29 01:44:50.000000000 -0700
@@ -126,6 +126,7 @@
 {
   int ofs = 0;
   char *p;
+  int to_read;
 
   if (cgi->buf == NULL)
   {
@@ -154,7 +155,20 @@
     ofs = cgi->readlen - cgi->nl;
     memmove(cgi->buf, cgi->buf + cgi->nl, ofs);
   }
-  cgiwrap_read (cgi->buf + ofs, cgi->buflen - ofs, &(cgi->readlen));
+  // Read either as much buffer space as we have left, or up to 
+  // the amount of data remaining according to Content-Length
+  // If there is no Content-Length, just use the buffer space, but recognize
+  // that it might not work on some servers or cgiwrap implementations.
+  // Some servers will close their end of the stdin pipe, so cgiwrap_read
+  // will return if we ask for too much.  Techically, not including
+  // Content-Length is against the HTTP spec, so we should consider failing
+  // earlier if we don't have a length.
+  to_read = cgi->buflen - ofs;
+  if (cgi->data_expected && (to_read > cgi->data_expected - cgi->data_read))
+  {
+    to_read = cgi->data_expected - cgi->data_read;
+  }
+  cgiwrap_read (cgi->buf + ofs, to_read, &(cgi->readlen));
   if (cgi->readlen < 0)
   {
     return nerr_raise_errno (NERR_IO, "POST Read Error");
diff -Nrub clearsilver-0.10.3/cs/cs.h clearsilver-0.10.4/cs/cs.h
--- clearsilver-0.10.3/cs/cs.h	2006-02-02 18:17:49.000000000 -0800
+++ clearsilver-0.10.4/cs/cs.h	2006-08-07 13:01:51.000000000 -0700
@@ -46,6 +46,7 @@
 #include "util/neo_err.h"
 #include "util/ulist.h"
 #include "util/neo_hdf.h"
+#include "util/neo_str.h"
 
 __BEGIN_DECLS
 
@@ -101,6 +102,7 @@
 
 typedef struct _parse CSPARSE;
 typedef struct _funct CS_FUNCTION;
+typedef struct _escape_context CS_ECONTEXT;
 
 typedef struct _arg
 {
@@ -122,6 +124,7 @@
   int node_num;
   int cmd;
   int flags;
+  NEOS_ESCAPE escape;
   CSARG arg1;
   CSARG arg2;
   CSARG *vargs;
@@ -131,10 +134,6 @@
   struct _tree *next;
 } CSTREE;
 
-/* Technically, the char * for this func should be const char *, but that
- * would break existing code */
-typedef NEOERR* (*CSOUTFUNC)(void *, char *);
-
 typedef struct _local_map
 {
   CSTOKEN_TYPE type;
@@ -162,15 +161,44 @@
   struct _macro *next;
 } CS_MACRO;
 
+
+/* CSOUTFUNC is a callback function for where cs_render will render the 
+ * template to.
+ * Technically, the char * for this func should be const char *, but that
+ * would break existing code. */
+typedef NEOERR* (*CSOUTFUNC)(void *, char *);
+
+/* CSFUNCTION is a callback function used for handling a function made
+ * available inside the template.  Used by cs_register_function.  Exposed
+ * here as part of the experimental extension framework, this may change
+ * in future versions. */
 typedef NEOERR* (*CSFUNCTION)(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
                               CSARG *result);
+
+/* CSSTRFUNC is a callback function for the more limited "string filter"
+ * function handling.  String filters only take a string and return a 
+ * string and have no "data" that is passed back, attempting to make them
+ * "safe" from extensions that might try to do silly things like SQL queries.
+ * */
 typedef NEOERR* (*CSSTRFUNC)(const char *str, char **ret);
 
+/* CSFILELOAD is a callback function to intercept file load requests and
+ * provide templates via another mechanism.  This way you can load templates
+ * that you compiled-into your binary, from in-memory caches, or from a
+ * zip file, etc.  The HDF is provided so you can choose to use the 
+ * hdf_search_path function to find the file.  contents should return
+ * a full malloc copy of the contents of the file, which the parser will modify
+ * and own and free.  Use cs_register_fileload to set this function for
+ * your CSPARSE context. */
+typedef NEOERR* (*CSFILELOAD)(void *ctx, HDF *hdf, const char *filename,
+                              char **contents);
+
 struct _funct
 {
   char *name;
   int name_len;
   int n_args;
+  NEOS_ESCAPE escape; /* States escaping exemptions. default: NONE. */
 
   CSFUNCTION function;
   CSSTRFUNC str_func;
@@ -178,12 +206,36 @@
   struct _funct *next;
 };
 
+/* This structure maintains the necessary running state to manage
+ * automatic escaping in tandem with the 'escape' directive. It is passed
+ * around inside _parse.
+ */
+struct _escape_context
+{
+  NEOS_ESCAPE global;     /* 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
+                             are only added to the stack after the
+                             command[].parse_handler() is called for the call
+                             it is being setup for, this is used to pass state
+                             forward.  E.g. This is used for 'def' to set UNDEF
+                             escaping state to delay escaping status to
+                             evaluation time. */
+  NEOS_ESCAPE when_undef; /* Contains the escaping context to be used when a
+                             UNDEF is being replaced at evaluation time.  E.g.
+                             this is set in call_eval to force the macro code
+                             to get call's parsing context at eval time. */
+};
+
 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;
   char *context_string;
+  CS_ECONTEXT escaping; /* Context container for escape data */
 
   char *tag;		/* Usually cs, but can be set via HDF Config.TagStart */
   int taglen;
@@ -209,6 +261,9 @@
   void *output_ctx;
   CSOUTFUNC output_cb;
 
+  void *fileload_ctx;
+  CSFILELOAD fileload;
+
   /* Global hdf struct */
   /* smarti:  Added for support for global hdf under local hdf */
   HDF *global_hdf;
@@ -328,6 +383,27 @@
 void cs_destroy (CSPARSE **parse);
 
 /*
+ * Function: cs_register_fileload - register a fileload function
+ * Description: cs_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 template render.
+ *              You can override this function if you wish to provide
+ *              other template search functions, or load the template
+ *              from an in-memory cache, etc.
+ *              This fileload function will be used by cs_parse, including
+ *              any include: commands in the template.
+ * Input: parse - a pointer to an initialized CSPARSE structure
+ *        ctx - pointer that is passed to the CSFILELOAD function when called
+ *        fileload - a CSFILELOAD function
+ * Output: None
+ * Return: None
+ *
+ */
+
+void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload);
+
+/*
  * Function: cs_register_strfunc - register a string handling function
  * Description: cs_register_strfunc will register a string function that
  *              can be called during CS render.  This not-callback is 
@@ -354,6 +430,23 @@
  */
 NEOERR *cs_register_strfunc(CSPARSE *parse, char *funcname, CSSTRFUNC str_func);
 
+/*
+ * Function: cs_register_esc_strfunc - cs_register_strfunc with escaping context
+ * Description: cs_register_esc_strfunc functions exactly as cs_register_strfunc
+ *              except that it changes the evaluation escaping context to disable
+ *              default escaping.
+ * Input: parse - a pointer to a CSPARSE structure initialized with cs_init()
+ *        funcname - the name for the CS function call
+ *                   Note that registering a duplicate funcname will
+ *                   raise a NERR_DUPLICATE error
+ *        str_func - a CSSTRFUNC not-callback
+ * Return: NERR_NOMEM - failure to allocate any memory for data structures
+ *         NERR_DUPLICATE - funcname already registered
+ *
+ */
+NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname,
+                                CSSTRFUNC str_func);
+
 /* Testing functions for future function api.  This api may change in the
  * future. */
 NEOERR *cs_arg_parse(CSPARSE *parse, CSARG *args, const char *fmt, ...);
diff -Nrub clearsilver-0.10.3/cs/csparse.c clearsilver-0.10.4/cs/csparse.c
--- clearsilver-0.10.3/cs/csparse.c	2006-02-02 18:17:41.000000000 -0800
+++ clearsilver-0.10.4/cs/csparse.c	2006-10-19 16:26:11.000000000 -0700
@@ -59,13 +59,15 @@
   ST_DEF = 1<<6,
   ST_LOOP =  1<<7,
   ST_ALT = 1<<8,
+  ST_ESCAPE = 1<<9,
 } CS_STATE;
 
-#define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT)
+#define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT | ST_ESCAPE)
 
 typedef struct _stack_entry 
 {
   CS_STATE state;
+  NEOS_ESCAPE escape;
   CSTREE *tree;
   CSTREE *next_tree;
   int num_local;
@@ -103,6 +105,8 @@
 static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
 static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg);
 static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
+static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg);
+static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
 
 static NEOERR *render_node (CSPARSE *parse, CSTREE *node);
 static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent);
@@ -126,6 +130,8 @@
     name_parse, name_eval,     1},
   {"var",     sizeof("var")-1,     ST_ANYWHERE,     ST_SAME, 
     var_parse, var_eval,     1},
+  {"uvar",     sizeof("uvar")-1,     ST_ANYWHERE,     ST_SAME,
+    var_parse, var_eval,     1},
   {"evar",    sizeof("evar")-1,    ST_ANYWHERE,     ST_SAME, 
     evar_parse, skip_eval,    1},
   {"lvar",    sizeof("lvar")-1,    ST_ANYWHERE,     ST_SAME, 
@@ -168,6 +174,25 @@
     alt_parse, alt_eval, 1},
   {"/alt",    sizeof("/alt")-1,    ST_ALT,     ST_POP,
     end_parse, skip_eval, 1},
+  {"escape",    sizeof("escape")-1,    ST_ANYWHERE,     ST_ESCAPE,
+    escape_parse, escape_eval, 1},
+  {"/escape",    sizeof("/escape")-1,    ST_ESCAPE,     ST_POP,
+    end_parse, skip_eval, 1},
+  {NULL},
+};
+
+/* Possible Config.VarEscapeMode values */
+typedef struct _escape_modes
+{
+  char *mode; /* Add space for NUL */
+  NEOS_ESCAPE context; /* Context of the name */
+} CS_ESCAPE_MODES;
+
+CS_ESCAPE_MODES EscapeModes[] = {
+  {"none", NEOS_ESCAPE_NONE},
+  {"html", NEOS_ESCAPE_HTML},
+  {"js",   NEOS_ESCAPE_SCRIPT},
+  {"url",  NEOS_ESCAPE_URL},
   {NULL},
 };
 
@@ -288,14 +313,23 @@
   if (path == NULL)
     return nerr_raise (NERR_ASSERT, "path is NULL");
 
+  if (parse->fileload)
+  {
+    err = parse->fileload(parse->fileload_ctx, parse->hdf, path, &ibuf);
+  }
+  else
+  {
   if (path[0] != '/')
   {
     err = hdf_search_path (parse->hdf, path, fpath);
+      if (parse->global_hdf && nerr_handle(&err, NERR_NOT_FOUND))
+        err = hdf_search_path(parse->global_hdf, path, fpath);
     if (err != STATUS_OK) return nerr_pass(err);
     path = fpath;
   }
 
   err = ne_load_file (path, &ibuf);
+  }
   if (err) return nerr_pass (err);
 
   save_context = parse->context;
@@ -397,6 +431,8 @@
     return "LOOP";
   else if (state & ST_ALT)
     return "ALT";
+  else if (state & ST_ESCAPE)
+    return "ESCAPE";
 
   snprintf(buf, sizeof(buf), "Unknown state %d", state);
   return buf;
@@ -405,7 +441,7 @@
 NEOERR *cs_parse_string (CSPARSE *parse, char *ibuf, size_t ibuf_len)
 {
   NEOERR *err = STATUS_OK;
-  STACK_ENTRY *entry;
+  STACK_ENTRY *entry, *current_entry;
   char *p;
   char *token;
   int done = 0;
@@ -509,6 +545,18 @@
 		entry->state = Commands[i].next_state;
 		entry->tree = parse->current;
 		entry->location = parse->offset;
+		/* Set the new stack escape context to the parent one */
+		err = uListGet (parse->stack, -1, (void *)&current_entry);
+		if (err != STATUS_OK) {
+		  free (entry);
+		  goto cs_parse_done;
+		}
+		entry->escape = current_entry->escape;
+		/* Get the future escape context from parse because when
+		 * we parse "escape", the new stack has not yet been established.
+		 */
+		entry->escape = parse->escaping.next_stack;
+		parse->escaping.next_stack = parse->escaping.global;
 		err = uListAppend(parse->stack, entry);
 		if (err != STATUS_OK) {
 		  free (entry);
@@ -538,17 +586,16 @@
   {
     err = uListPop(parse->stack, (void *)&entry);
     if (err != STATUS_OK) goto cs_parse_done;
-    if (entry->state & (ST_IF | ST_ELSE))
-      return nerr_raise (NERR_PARSE, "%s Non-terminted if clause",
-	  find_context(parse, entry->location, tmp, sizeof(tmp)));
-    if (entry->state & ST_EACH)
-      return nerr_raise (NERR_PARSE, "%s Non-terminted each clause",
-	  find_context(parse, entry->location, tmp, sizeof(tmp)));
+    if (entry->state & ~(ST_GLOBAL | ST_POP))
+      return nerr_raise (NERR_PARSE, "%s Non-terminted %s clause",
+	  find_context(parse, entry->location, tmp, sizeof(tmp)),
+          expand_state(entry->state));
   }
 
 cs_parse_done:
   parse->offset = initial_offset;
   parse->context_string = initial_context;
+  parse->escaping.current = NEOS_ESCAPE_NONE;
   return nerr_pass(err);
 }
 
@@ -1006,7 +1053,7 @@
 
 static NEOERR *parse_expr2 (CSPARSE *parse, CSTOKEN *tokens, int ntokens, int lvalue, CSARG *arg)
 {
-  NEOERR *err;
+  NEOERR *err = STATUS_OK;
   char tmp[256];
   char tmp2[256];
   int x, op;
@@ -1260,8 +1307,13 @@
       return nerr_raise (NERR_NOMEM, 
 	  "%s Unable to allocate memory for expression", 
 	  find_context(parse, -1, tmp, sizeof(tmp)));
+    if (ntokens-3 > 0) {
     err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1);
     if (err) return nerr_pass(err);
+    } else {
+      free(arg->expr1);
+      arg->expr1 = NULL;
+    }
     nargs = rearrange_for_call(&(arg->expr1));
     if (nargs != arg->function->n_args)
     {
@@ -1354,6 +1406,65 @@
   return STATUS_OK;
 }
 
+static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg)
+{
+  NEOERR *err;
+  char *a = NULL;
+  char tmp[256];
+  CS_ESCAPE_MODES *esc_cursor;
+  CSTREE *node;
+
+  /* ne_warn ("escape: %s", arg); */
+  err = alloc_node (&node);
+  if (err) return nerr_pass(err);
+  node->cmd = cmd;
+  /* Since this throws an error always if there's a problem
+   * this flag seems pointless, but following convention,
+   * here it is. */
+  if (arg[0] == '!')
+    node->flags |= CSF_REQUIRED;
+  arg++; /* ignore colon, space, etc */
+
+  /* Parse the arg - we're expecting a string */
+  err = parse_expr (parse, arg, 0, &(node->arg1));
+  if (err)
+  {
+    dealloc_node(&node);
+    return nerr_pass(err);
+  }
+  if (node->arg1.op_type != CS_TYPE_STRING)
+  {
+    dealloc_node(&node);
+    return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s",
+      find_context(parse, -1, tmp, sizeof(tmp)), arg);
+  }
+
+  a = neos_strip(node->arg1.s); /* Strip spaces for testing */
+
+  /* Ensure the mode specified is allowed */
+  for (esc_cursor = &EscapeModes[0];
+       esc_cursor->mode != NULL;
+       esc_cursor++)
+    if (!strncasecmp(a, esc_cursor->mode, strlen(esc_cursor->mode)))
+    {
+      if (err != STATUS_OK) return nerr_pass(err);
+      parse->escaping.next_stack = esc_cursor->context;
+      break;
+    }
+  /* Didn't find an acceptable value we were looking for */
+  if (esc_cursor->mode == NULL)
+  {
+    dealloc_node(&node);
+    return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s",
+      find_context(parse, -1, tmp, sizeof(tmp)), a);
+  }
+
+  *(parse->next) = node;
+  parse->next = &(node->case_0);
+  parse->current = node;
+  return STATUS_OK;
+}
+
 static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
 {
   NEOERR *err = STATUS_OK;
@@ -1377,11 +1488,26 @@
 {
   NEOERR *err;
   CSTREE *node;
+  STACK_ENTRY *entry;
+
+  err = uListGet (parse->stack, -1, (void *)&entry);
+  if (err != STATUS_OK) return nerr_pass(err);
 
   /* ne_warn ("var: %s", arg); */
   err = alloc_node (&node);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
+
+  /* Default escape the variable based on
+   * current stack's escape context except for
+   * uvar:
+   */
+  if (!strcmp(Commands[cmd].cmd, "uvar"))
+    node->escape = NEOS_ESCAPE_NONE;
+  else
+    node->escape = entry->escape;
+
+
   if (arg[0] == '!')
     node->flags |= CSF_REQUIRED;
   arg++;
@@ -1916,6 +2042,11 @@
      * argument1 */
     err = expr->function->function(parse, expr->function, expr->expr1, result);
     if (err) return nerr_pass(err);
+    /* Indicate whether or not an explicit escape call was made by
+     * setting the mode (usually NONE or FUNCTION). This is ORed to
+     * ensure that escaping calls within other functions do not get
+     * double-escaped. E.g. slice(html_escape(foo), 10, 20) */
+    parse->escaping.current |= expr->function->escape;
   }
   else 
   {
@@ -2093,6 +2224,7 @@
   NEOERR *err = STATUS_OK;
   CSARG val;
 
+  parse->escaping.current = NEOS_ESCAPE_NONE;
   err = eval_expr(parse, &(node->arg1), &val);
   if (err) return nerr_pass(err);
   if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
@@ -2107,11 +2239,31 @@
   else
   {
     char *s = arg_eval (parse, &val);
-    /* Do we set it to blank if s == NULL? */
-    if (s)
+    /* Determine if the node has been escaped by an explicit function. If not
+     * call to escape. node->escape should contain the default escaping from
+     * Config.VarEscapeMode and parse->escaping.current will have a non-zero
+     * value if an explicit escape call was made sooooo.
+     */
+    if (s && parse->escaping.current == NEOS_ESCAPE_NONE) /* no explicit escape */
+    {
+      char *escaped = NULL;
+      /* Use default escape if escape is UNDEF */
+      if (node->escape == NEOS_ESCAPE_UNDEF)
+        err = neos_var_escape(parse->escaping.when_undef, s, &escaped);
+      else
+        err = neos_var_escape(node->escape, s, &escaped);
+
+      if (escaped)
     {
+        err = parse->output_cb (parse->output_ctx, escaped);
+        free(escaped);
+      }
+    }
+    else if (s)
+    { /* already explicitly escaped */
       err = parse->output_cb (parse->output_ctx, s);
     }
+    /* Do we set it to blank if s == NULL? */
   }
   if (val.alloc) free(val.s);
 
@@ -2260,6 +2412,16 @@
   return nerr_pass(err);
 }
 
+/* just calls through to the child nodes */
+static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
+{
+  NEOERR *err = STATUS_OK;
+  /* TODO(wad): Should I set a eval-time value here? */
+  err = render_node (parse, node->case_0);
+  *next = node->next;
+  return nerr_pass(err);
+}
+
 
 static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
 {
@@ -2487,7 +2649,12 @@
       if (with_map.map_alloc) free(with_map.s);
       parse->locals = with_map.next;
     }
-  } /* else WARNING */
+  }
+  else
+  {
+    /* else WARNING */
+    ne_warn("Invalid op_type for with: %s", expand_token_type(val.op_type, 1));
+  }
   if (val.alloc) free(val.s);
 
   *next = node->next;
@@ -2550,6 +2717,11 @@
   int x = 0;
   BOOL last = FALSE;
 
+  /* Since def doesn't get a new stack entry until after this is run,
+   * setup a dumb var on the parse object to hold the future setting.
+   */
+  parse->escaping.next_stack = NEOS_ESCAPE_UNDEF;
+
   err = alloc_node (&node);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
@@ -2719,10 +2891,15 @@
   char name[256];
   int x = 0;
   int nargs = 0;
+  STACK_ENTRY *entry;
+
+  err = uListGet (parse->stack, -1, (void *)&entry);
+  if (err != STATUS_OK) return nerr_pass(err);
 
   err = alloc_node (&node);
   if (err) return nerr_pass(err);
   node->cmd = cmd;
+  node->escape = entry->escape;
   arg++;
   s = arg;
   while (x < sizeof(name) && *s && *s != ' ' && *s != '#' && *s != '(')
@@ -2821,6 +2998,15 @@
   HDF *var;
   int x;
 
+  /* Reset the value of when_undef for the coming call evaluation.
+   * This is only used here so it there's no need to reset its value after
+   * the call. If this call is nested (escape == NEOS_ESCAPE_UNDEF), then
+   * leave the when_undef variable alone. The parent call_eval should have
+   * already defined it.
+   */
+  if (node->escape != NEOS_ESCAPE_UNDEF)
+    parse->escaping.when_undef = node->escape;
+
   macro = node->arg1.macro;
   if (macro->n_args)
   {
@@ -3265,6 +3451,7 @@
   }
   csf->function = function;
   csf->n_args = n_args;
+  csf->escape = NEOS_ESCAPE_NONE;
   csf->next = parse->functions;
   parse->functions = csf;
 
@@ -3284,7 +3471,7 @@
   long int *i;
   CSARG val;
 
-  while (*fmt || args || err)
+  while (*fmt)
   {
     memset(&val, 0, sizeof(val));
     err = eval_expr(parse, args, &val);
@@ -3315,6 +3502,7 @@
       default:
 	break;
     }
+    if (err) return nerr_pass(err);
     fmt++;
     args = args->next;
     if (val.alloc) free(val.s);
@@ -3391,6 +3579,34 @@
 }
 
 
+static NEOERR * _builtin_str_find(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
+{
+  NEOERR *err;
+  char *s = NULL;
+  char *substr = NULL;
+  char *pstr = NULL;
+
+  result->op_type = CS_TYPE_NUM;
+  result->n = -1;
+
+  err = cs_arg_parse(parse, args, "ss", &s, &substr);
+  if (err) return nerr_pass(err);
+  /* If null arguments, return -1 index */
+  if (s == NULL || substr == NULL) {
+    if (s) free(s);
+    if (substr) free(substr);
+    return STATUS_OK;
+  }
+  pstr = strstr(s, substr);
+  if (pstr != NULL) {
+    result->n = (pstr - s) / sizeof(char);
+  }
+  free(s);
+  free(substr);
+  return STATUS_OK;
+}
+
+
 static NEOERR * _builtin_name(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
 {
   NEOERR *err;
@@ -3661,6 +3877,17 @@
   return STATUS_OK;
 }
 
+NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname,
+                                CSSTRFUNC str_func)
+{
+  NEOERR *err;
+
+  err = cs_register_strfunc(parse, funcname, str_func);
+  if (err) return nerr_pass(err);
+  parse->functions->escape = NEOS_ESCAPE_FUNCTION;
+
+  return STATUS_OK;
+}
 
 /* **** CS Initialize/Destroy ************************************ */
 NEOERR *cs_init (CSPARSE **parse, HDF *hdf) {
@@ -3672,6 +3899,8 @@
   NEOERR *err = STATUS_OK;
   CSPARSE *my_parse;
   STACK_ENTRY *entry;
+  char *esc_value;
+  CS_ESCAPE_MODES *esc_cursor;
 
   err = nerr_init();
   if (err != STATUS_OK) return nerr_pass (err);
@@ -3711,6 +3940,7 @@
   entry->state = ST_GLOBAL;
   entry->tree = my_parse->current;
   entry->location = 0;
+  entry->escape = NEOS_ESCAPE_NONE;
   err = uListAppend(my_parse->stack, entry);
   if (err != STATUS_OK) {
     free (entry);
@@ -3721,6 +3951,32 @@
   my_parse->taglen = strlen(my_parse->tag);
   my_parse->hdf = hdf;
 
+  /* Let's set the default escape data */
+  my_parse->escaping.global = NEOS_ESCAPE_NONE;
+  my_parse->escaping.next_stack = NEOS_ESCAPE_NONE;
+  my_parse->escaping.when_undef = NEOS_ESCAPE_NONE;
+
+  /* See CS_ESCAPE_MODES. 0 is "none" */
+  esc_value = hdf_get_value(hdf, "Config.VarEscapeMode", EscapeModes[0].mode);
+  /* Let's ensure the specified escape mode is valid and proceed */
+  for (esc_cursor = &EscapeModes[0];
+       esc_cursor->mode != NULL;
+       esc_cursor++)
+    if (!strcmp(esc_value, esc_cursor->mode))
+    {
+      my_parse->escaping.global = esc_cursor->context;
+      my_parse->escaping.next_stack = esc_cursor->context;
+      entry->escape = esc_cursor->context;
+      break;
+    }
+  /* Didn't find an acceptable value we were looking for */
+  if (esc_cursor->mode == NULL) {
+    cs_destroy (&my_parse);
+    return nerr_raise (NERR_OUTOFRANGE,
+      "Invalid HDF value for Config.VarEscapeMode (none,html,js,url): %s",
+      esc_value);
+  }
+
   if (parent == NULL)
   {
     static struct _builtin_functions {
@@ -3736,6 +3992,7 @@
       { "abs", 1, _builtin_abs },
       { "max", 2, _builtin_max },
       { "min", 2, _builtin_min },
+      { "string.find", 2, _builtin_str_find },
       { "string.slice", 3, _builtin_str_slice },
       { "string.length", 1, _builtin_str_length },
 #ifdef ENABLE_GETTEXT
@@ -3769,6 +4026,8 @@
      * is gone. */
     my_parse->functions = parent->functions;
     my_parse->global_hdf = parent->global_hdf;
+    my_parse->fileload = parent->fileload;
+    my_parse->fileload_ctx = parent->fileload_ctx;
     my_parse->parent = parent;
   }
 
@@ -3776,6 +4035,13 @@
   return STATUS_OK;
 }
 
+void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload) {
+  if (parse != NULL) {
+    parse->fileload_ctx = ctx;
+    parse->fileload = fileload;
+  }
+}
+
 void cs_destroy (CSPARSE **parse)
 {
   CSPARSE *my_parse = *parse;
diff -Nrub clearsilver-0.10.3/cs/Makefile clearsilver-0.10.4/cs/Makefile
--- clearsilver-0.10.3/cs/Makefile	2006-02-01 18:20:49.000000000 -0800
+++ clearsilver-0.10.4/cs/Makefile	2006-11-09 14:33:41.000000000 -0800
@@ -22,7 +22,7 @@
 CSDUMP_SRC = csdump.c
 CSDUMP_OBJ = $(CSDUMP_SRC:%.c=%.o)
 
-LIBS += -L$(LIB_DIR) -lneo_cs -lneo_utl #  -lefence
+LIBS += -lneo_cs -lneo_utl #  -lefence
 
 TARGETS = $(CS_LIB) $(CSTEST_EXE) $(CSR_EXE) test
 
@@ -32,7 +32,8 @@
 	   test_var.cs test_paren.cs test_chuck.cs test_trak1.cs test_iter.cs \
 	   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_comma.cs test_macro_set.cs test_func.cs test_escape.cs \
+	   test_uvar.cs
 
 
 all: $(TARGETS)
@@ -42,13 +43,13 @@
 	$(RANLIB) $@
 
 $(CSTEST_EXE): $(CSTEST_OBJ) $(CS_LIB)
-	$(LD) $@ $(CSTEST_OBJ) $(LIBS) # -lefence
+	$(LD) $@ $(CSTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence
 
 $(CSR_EXE): $(CSR_OBJ) $(CS_LIB)
-	$(LD) $@ $(CSR_OBJ) $(LIBS) # -lefence
+	$(LD) $@ $(CSR_OBJ) $(LDFLAGS) $(LIBS) # -lefence
 
 $(CSDUMP_EXE): $(CSDUMP_OBJ) $(CS_LIB)
-	$(LD) $@ $(CSDUMP_OBJ) $(LIBS)
+	$(LD) $@ $(CSDUMP_OBJ) $(LDFLAGS) $(LIBS)
 
 ## BE VERY CAREFUL WHEN REGENERATING THESE
 gold: $(CSTEST_EXE)
@@ -93,6 +94,7 @@
 	$(INSTALL) -m 644 cs.h $(DESTDIR)$(cs_includedir)/cs
 	$(INSTALL) -m 644 $(CS_LIB) $(DESTDIR)$(libdir)
 	$(INSTALL) $(CSTEST_EXE) $(DESTDIR)$(bindir)
+	$(INSTALL) $(CSR_EXE) $(DESTDIR)$(bindir)
 
 clean:
 	$(RM) core *.o
diff -Nrub clearsilver-0.10.3/cs/test13.cs clearsilver-0.10.4/cs/test13.cs
--- clearsilver-0.10.3/cs/test13.cs	2005-06-30 11:51:51.000000000 -0700
+++ clearsilver-0.10.4/cs/test13.cs	2006-10-09 17:53:09.000000000 -0700
@@ -3,4 +3,4 @@
   <?cs lvar:test ?>
 <?cs /each ?>
 
-<?cs lvar: CS_START + " alt:Foo " + CS_END ?>
+<?cs lvar: CS_START + " alt:Foo " + CS_END + "bar" + CS_START + " /alt " + CS_END ?>
diff -Nrub clearsilver-0.10.3/cs/test_cgi_escape.cs clearsilver-0.10.4/cs/test_cgi_escape.cs
--- clearsilver-0.10.3/cs/test_cgi_escape.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_cgi_escape.cs	2006-08-07 13:01:51.000000000 -0700
@@ -0,0 +1,68 @@
+
+
+
+<html>
+<head><title><?cs var:Title ?></title>
+<style>
+@import(http://www.clearsilver.net?<?cs var:UrlArg ?>)
+</style>
+<script>
+var default_escape  = "<?cs var:BlahJs ?>";
+var explicit_escape = "<?cs var:js_escape(BlahJs) ?>";
+document.writeln("<style>");
+var style_in_script = "<?cs var:BlahJs?>";
+document.writeln("</style>");
+</script>
+<img alt="default escape"  src="http://www.clearsilver.net?<?cs var:UrlArg ?>"/>
+<img alt="explicit escape" src="http://www.clearsilver.net?<?cs var:url_escape(UrlArg) ?>"/>
+implicit:         <?cs var:Title ?>
+explicit escape: <?cs var:html_escape(Title) ?>
+uvar:          <?cs uvar:Title ?>
+implicit_slice:             <?cs var:string.slice(Title, 7, 100) ?>
+slice_explicit_escape:             <?cs var:string.slice(html_escape(Title), 7, 100) ?>
+explicit_escape_slice:             <?cs var:html_escape(string.slice(Title, 7, 100)) ?>
+</head>
+</html>
+non-html output: <?cs var:Title ?>
+<script>Script outside HTML: <?cs var:BlahJs ?></script>
+<script><html>Script outside HTML with HTML inside: <?cs var:BlahJs ?></html></script>
+
+<script>Script outside HTML: <?cs var:BlahJs ?></script>
+
+
+<?cs def:get_var(var) ?>
+   get_var:  <?cs var:var ?>
+ <?cs escape: "url" ?>
+   get_var_inside_url_escape:                      <?cs var:var ?>
+   get_var_inside_url_escape_with_explicit_escape: <?cs var:url_escape(var) ?>
+ <?cs /escape ?>
+ <?cs escape: "none" ?>
+   get_var_inside_none_escape: <?cs var:var ?>
+   get_var_inside_none_escape_with_explicit_url_escape: <?cs var:url_escape(var) ?>
+ <?cs /escape ?>
+<?cs /def ?>
+
+<?cs if #1 == #1 ?>
+  <?cs if #2 == #2 ?>
+    nested ifs
+  <?cs /if ?>
+<?cs /if ?>
+
+<?cs escape: "html" ?>
+Calling get_var(UrlArg) from within 'escape: "html"' <br/>
+--&gt; <?cs call:get_var(UrlArg) ?>
+<?cs /escape ?>
+
+Including test_escape.cs: <br/>
+---
+<?cs escape: "html" ?>
+<?cs include! "test_escape.cs" ?>
+<?cs /escape ?>
+----
+
+<?cs escape: "html" ?>
+  escape level 1
+  <?cs escape: "js" ?>
+  escape level 2
+  <?cs /escape ?>
+<?cs /escape ?>
diff -Nrub clearsilver-0.10.3/cs/test.cs clearsilver-0.10.4/cs/test.cs
--- clearsilver-0.10.3/cs/test.cs	2005-06-30 11:51:49.000000000 -0700
+++ clearsilver-0.10.4/cs/test.cs	2006-08-07 13:01:51.000000000 -0700
@@ -30,6 +30,9 @@
 <?cs include!"test2.cs" ?>
 <?cs linclude!"test2.cs" ?>
 
+<?cs include!"test_escape.cs" ?>
+<?cs escape: "html" ?><?cs call:echo_all(Title+BlahJs:UrlArg) ?><?cs /escape ?>
+
 <?cs each: x=Foo.Bar.Baz ?>
   x = <?cs var:x ?>
   x.num = <?cs var:x.num ?>
diff -Nrub clearsilver-0.10.3/cs/test.cs.gold clearsilver-0.10.4/cs/test.cs.gold
--- clearsilver-0.10.3/cs/test.cs.gold	2006-02-01 16:23:33.000000000 -0800
+++ clearsilver-0.10.4/cs/test.cs.gold	2006-10-09 17:53:19.000000000 -0700
@@ -36,6 +36,100 @@
 wow2
 
 
+escape: not used
+UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?
+BlahJs: quote ' backslash \ semicolon ; end tag </script>
+Title:  </title><script>alert(1)</script>
+
+
+escape: none
+UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?
+BlahJs: quote ' backslash \ semicolon ; end tag </script>
+Title:  </title><script>alert(1)</script>
+
+
+
+escape: html
+UrlArg: Secret Password~!@#$%^&amp;*()+=-_|\[]{}:&quot;;&#39;&lt;&gt;,.?
+BlahJs: quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+Title:  &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+
+
+
+escape: js
+UrlArg: Secret Password~!@#$%^\x26*()+=-_|\x5C[]{}:\x22\x3B\x27\x3C\x3E,.?
+BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+Title:  \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E
+
+
+
+escape: url
+UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F
+BlahJs: quote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+Title:  %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E
+
+
+
+Nested escaping: html
+The internal calls should take precedence
+url  -> UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F
+js   -> BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html -> Title:  &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+
+
+Defining the macro echo_all inside of a "html" escape.
+
+
+Calling echo_all() macro:
+
+not used: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+Calling echo_all() macro from within "html":
+
+not used: &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+
+Calling echo_all() macro from within "js":
+
+not used: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+
+Calling echo_all() macro from within "url":
+
+not used: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+
+not used: &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+none:     </title><script>alert(1)</script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+
+
 
   x = zero
   x.num = #0
diff -Nrub clearsilver-0.10.3/cs/test_escape.cs clearsilver-0.10.4/cs/test_escape.cs
--- clearsilver-0.10.3/cs/test_escape.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_escape.cs	2006-08-07 13:01:51.000000000 -0700
@@ -0,0 +1,67 @@
+escape: not used
+UrlArg: <?cs var:UrlArg ?>
+BlahJs: <?cs var:BlahJs ?>
+Title:  <?cs var:Title ?>
+
+<?cs escape: "none" ?>
+escape: none
+UrlArg: <?cs var:UrlArg ?>
+BlahJs: <?cs var:BlahJs ?>
+Title:  <?cs var:Title ?>
+<?cs /escape ?>
+
+<?cs escape: "html" ?>
+escape: html
+UrlArg: <?cs var:UrlArg ?>
+BlahJs: <?cs var:BlahJs ?>
+Title:  <?cs var:Title ?>
+<?cs /escape ?>
+
+<?cs escape: "js" ?>
+escape: js
+UrlArg: <?cs var:UrlArg ?>
+BlahJs: <?cs var:BlahJs ?>
+Title:  <?cs var:Title ?>
+<?cs /escape ?>
+
+<?cs escape: "url" ?>
+escape: url
+UrlArg: <?cs var:UrlArg ?>
+BlahJs: <?cs var:BlahJs ?>
+Title:  <?cs var:Title ?>
+<?cs /escape ?>
+
+<?cs escape: "html" ?>
+Nested escaping: html
+The internal calls should take precedence
+<?cs escape: "url"  ?>url  -> UrlArg: <?cs var:UrlArg ?><?cs /escape ?>
+<?cs escape: "js"   ?>js   -> BlahJs: <?cs var:BlahJs ?><?cs /escape ?>
+<?cs escape: "html" ?>html -> Title:  <?cs var:Title ?><?cs /escape ?>
+<?cs /escape ?>
+
+Defining the macro echo_all inside of a "html" escape.
+<?cs escape: "html" ?><?cs def:echo_all(e) ?>
+not used: <?cs var:e ?>
+none:     <?cs escape: "none" ?><?cs var:e ?><?cs /escape ?>
+url:      <?cs escape: "url" ?><?cs var:e ?><?cs /escape ?>
+js:       <?cs escape: "js" ?><?cs  var:e ?><?cs /escape ?>
+html:     <?cs escape: "html" ?><?cs var:e ?><?cs /escape ?>
+<?cs /def ?><?cs /escape ?>
+
+Calling echo_all() macro:
+<?cs call:echo_all(Title + UrlArh + BlahJs) ?>
+
+<?cs escape: "html" ?>
+Calling echo_all() macro from within "html":
+<?cs call:echo_all(Title + UrlArh + BlahJs) ?>
+<?cs /escape ?>
+
+<?cs escape: "js" ?>
+Calling echo_all() macro from within "js":
+<?cs call:echo_all(Title + UrlArh + BlahJs) ?>
+<?cs /escape ?>
+
+<?cs escape: "url" ?>
+Calling echo_all() macro from within "url":
+<?cs call:echo_all(Title + UrlArh + BlahJs) ?>
+<?cs /escape ?>
diff -Nrub clearsilver-0.10.3/cs/test_escape.cs.gold clearsilver-0.10.4/cs/test_escape.cs.gold
--- clearsilver-0.10.3/cs/test_escape.cs.gold	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_escape.cs.gold	2006-10-09 17:53:20.000000000 -0700
@@ -0,0 +1,86 @@
+Parsing test_escape.cs
+escape: not used
+UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?
+BlahJs: quote ' backslash \ semicolon ; end tag </script>
+Title:  </title><script>alert(1)</script>
+
+
+escape: none
+UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?
+BlahJs: quote ' backslash \ semicolon ; end tag </script>
+Title:  </title><script>alert(1)</script>
+
+
+
+escape: html
+UrlArg: Secret Password~!@#$%^&amp;*()+=-_|\[]{}:&quot;;&#39;&lt;&gt;,.?
+BlahJs: quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+Title:  &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+
+
+
+escape: js
+UrlArg: Secret Password~!@#$%^\x26*()+=-_|\x5C[]{}:\x22\x3B\x27\x3C\x3E,.?
+BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+Title:  \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E
+
+
+
+escape: url
+UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F
+BlahJs: quote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+Title:  %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E
+
+
+
+Nested escaping: html
+The internal calls should take precedence
+url  -> UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F
+js   -> BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html -> Title:  &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;
+
+
+Defining the macro echo_all inside of a "html" escape.
+
+
+Calling echo_all() macro:
+
+not used: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+Calling echo_all() macro from within "html":
+
+not used: &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+
+Calling echo_all() macro from within "js":
+
+not used: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
+
+
+Calling echo_all() macro from within "url":
+
+not used: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+none:     </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script>
+url:      %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E
+js:       \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E
+html:     &lt;/title&gt;&lt;script&gt;alert(1)&lt;/script&gt;quote &#39; backslash \ semicolon ; end tag &lt;/script&gt;
+
+
diff -Nrub clearsilver-0.10.3/cs/test.hdf clearsilver-0.10.4/cs/test.hdf
--- clearsilver-0.10.3/cs/test.hdf	2005-06-30 11:51:50.000000000 -0700
+++ clearsilver-0.10.4/cs/test.hdf	2006-08-07 13:01:51.000000000 -0700
@@ -1,4 +1,5 @@
 
+Config.VarEscapeMode = none
 arg1 = 1
 var =
 
@@ -11,6 +12,9 @@
 }
 
 Blah = wow
+BlahJs = quote ' backslash \ semicolon ; end tag </script>
+Title = </title><script>alert(1)</script>
+UrlArg = Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?
 Foo = Worn Out
 
 Foo.Bar.Baz {
diff -Nrub clearsilver-0.10.3/cs/test_str_find.cs clearsilver-0.10.4/cs/test_str_find.cs
--- clearsilver-0.10.3/cs/test_str_find.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_str_find.cs	2006-10-18 16:57:23.000000000 -0700
@@ -0,0 +1,20 @@
+
+<?cs var:'string.find("String1", "ring")' ?>
+<?cs var:string.find("String1", "ring") ?>        
+
+<?cs var:'string.find("String1", "1")' ?>
+<?cs var:string.find("String1", "1") ?>        
+
+<?cs var:'string.find("String1", "S")' ?>
+<?cs var:string.find("String1", "S") ?>        
+
+<?cs var:'string.find("String1", "k")' ?>
+<?cs var:string.find("String1", "k") ?>        
+
+<?cs var:'Foo = "TheString2"' ?>
+<?cs set:Foo = "TheString2" ?>
+<?cs var:'string.find(Foo, "Str")' ?>
+<?cs var:string.find(Foo, "Str") ?>        
+
+<?cs var:'string.find(Foo, 1+1)' ?>
+<?cs var:string.find(Foo, 1+1) ?>        
diff -Nrub clearsilver-0.10.3/cs/test_str_find.cs.gold clearsilver-0.10.4/cs/test_str_find.cs.gold
--- clearsilver-0.10.3/cs/test_str_find.cs.gold	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_str_find.cs.gold	2006-10-18 16:57:23.000000000 -0700
@@ -0,0 +1,21 @@
+Parsing test_str_find.cs
+
+string.find("String1", "ring")
+2        
+
+string.find("String1", "1")
+6        
+
+string.find("String1", "S")
+0        
+
+string.find("String1", "k")
+-1        
+
+Foo = "TheString2"
+
+string.find(Foo, "Str")
+3        
+
+string.find(Foo, 1+1)
+9        
diff -Nrub clearsilver-0.10.3/cs/test_uvar.cs clearsilver-0.10.4/cs/test_uvar.cs
--- clearsilver-0.10.3/cs/test_uvar.cs	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_uvar.cs	2006-08-07 13:01:51.000000000 -0700
@@ -0,0 +1,6 @@
+<?cs uvar:BlahJs+UrlArg+Title ?>
+
+<?cs escape: "none" ?><?cs uvar:BlahJs+UrlArg+Title ?><?cs /escape ?>
+<?cs escape: "html" ?><?cs uvar:BlahJs+UrlArg+Title ?><?cs /escape ?>
+<?cs escape: "js" ?><?cs uvar:BlahJs+UrlArg+Title ?><?cs /escape ?>
+<?cs escape: "url" ?><?cs uvar:BlahJs+UrlArg+Title ?><?cs /escape ?>
diff -Nrub clearsilver-0.10.3/cs/test_uvar.cs.gold clearsilver-0.10.4/cs/test_uvar.cs.gold
--- clearsilver-0.10.3/cs/test_uvar.cs.gold	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/cs/test_uvar.cs.gold	2006-10-09 17:53:20.000000000 -0700
@@ -0,0 +1,7 @@
+Parsing test_uvar.cs
+quote ' backslash \ semicolon ; end tag </script>Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?</title><script>alert(1)</script>
+
+quote ' backslash \ semicolon ; end tag </script>Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?</title><script>alert(1)</script>
+quote ' backslash \ semicolon ; end tag </script>Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?</title><script>alert(1)</script>
+quote ' backslash \ semicolon ; end tag </script>Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?</title><script>alert(1)</script>
+quote ' backslash \ semicolon ; end tag </script>Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.?</title><script>alert(1)</script>
diff -Nrub clearsilver-0.10.3/dso/Makefile clearsilver-0.10.4/dso/Makefile
--- clearsilver-0.10.3/dso/Makefile	2005-12-02 02:35:00.000000000 -0800
+++ clearsilver-0.10.4/dso/Makefile	2006-07-12 23:49:45.000000000 -0700
@@ -9,7 +9,7 @@
 NEO_SO = libneo.so
 NEO_STATIC_LIBS =  
 
-LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB)
+LIBS += $(DLIBS) $(DB2_LIB)
 
 
 TARGETS = $(NEO_SO) dsotest
@@ -17,7 +17,7 @@
 all: $(TARGETS)
 
 $(NEO_SO): $(NEO_STATIC_LIBS) $(DEP_LIBS) Makefile 
-	 /usr/bin/ld  -rpath ../libs -shared -o libneo.so -whole-archive ../libs/libneo_cgi.a ../libs/libneo_cs.a ../libs/libneo_utl.a -no-whole-archive $(LIBS)
+	 /usr/bin/ld  -rpath ../libs -shared -o libneo.so -whole-archive ../libs/libneo_cgi.a ../libs/libneo_cs.a ../libs/libneo_utl.a -no-whole-archive $(LDFLAGS) $(LIBS)
 
 #	$(LDSHARED) -o $@ $(LDFLAGS) -Wl,-whole-archive $(DLIBS)
 
diff -Nrub clearsilver-0.10.3/imd/imd.c clearsilver-0.10.4/imd/imd.c
--- clearsilver-0.10.3/imd/imd.c	2005-06-30 11:51:53.000000000 -0700
+++ clearsilver-0.10.4/imd/imd.c	2006-11-03 08:56:08.000000000 -0800
@@ -56,7 +56,7 @@
 
   if(isalpha(*ip)) {
     /* ctime */
-    sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
+    sscanf(ip,"%25s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
   }
   else if(ip[2] == '-') {
     /* RFC 850 (normal HTTP) */
@@ -1089,8 +1089,8 @@
   err = cgi_init(&cgi, NULL);
   if (err != STATUS_OK)
   {
-    cgi_neo_error(cgi, err);
     nerr_log_error(err);
+    cgi_destroy(&cgi);
     return -1;
   }
   imd_file = hdf_get_value(cgi->hdf, "CGI.PathTranslated", NULL);
@@ -1100,6 +1100,7 @@
   {
     cgi_neo_error(cgi, err);
     nerr_log_error(err);
+    cgi_destroy(&cgi);
     return -1;
   }
 
@@ -1113,6 +1114,7 @@
     if (err)
     {
       nerr_log_error(err);
+      cgi_destroy(&cgi);
       return -1;
     }
   }
@@ -1136,6 +1138,7 @@
       {
 	cgi_neo_error(cgi, err);
 	nerr_log_error(err);
+        cgi_destroy(&cgi);
 	return -1;
       }
     }
@@ -1146,9 +1149,11 @@
       {
 	cgi_neo_error(cgi, err);
 	nerr_log_error(err);
+        cgi_destroy(&cgi);
 	return -1;
       }
     }
   }
+  cgi_destroy(&cgi);
   return 0;
 }
Binary files clearsilver-0.10.3/java-jni/clearsilver.jar and clearsilver-0.10.4/java-jni/clearsilver.jar differ
diff -Nrub clearsilver-0.10.3/java-jni/CSTest.java clearsilver-0.10.4/java-jni/CSTest.java
--- clearsilver-0.10.3/java-jni/CSTest.java	2005-11-16 19:17:07.000000000 -0800
+++ clearsilver-0.10.4/java-jni/CSTest.java	2006-11-09 16:54:04.000000000 -0800
@@ -100,6 +100,13 @@
           System.out.println("Caught exception of type " + e.getClass().getName() + "\n");
         }
 
+	System.out.println("Testing HDF.writeFile()\n");
+	file_hdf.writeFile("test1_out.hdf");
+	file_hdf.writeFileAtomic("test1_out2.hdf");
+
+	System.out.println("Testing HDF.writeString()\n");
+	System.out.println(file_hdf.writeString());
+
         System.out.println("Testing HDF.getObj()");
         HDF foo_hdf = file_hdf.getObj("Foo");
         System.out.println(foo_hdf.dump());
@@ -134,5 +141,90 @@
 	two.copy("entity.value", one);
 	System.out.println("name should be barneyb: " + 
 	    two.getValue("entity.value.name", "--undefined--") +"\n");
+
+	System.out.println("Testing HDF.exportDate()");
+        HDF date_hdf = new HDF();
+        date_hdf.exportDate("DatePST", "US/Pacific", 1142308218);
+        date_hdf.exportDate("DateEST", "US/Eastern", 1142308218);
+        System.out.println(date_hdf.dump());
+
+        // Test default escaping mode: html
+        HDF escape_hdf = new HDF();
+        System.out.println("Testing escape mode: html");
+        System.out.println("Config.VarEscapeMode = \"html\"");
+        escape_hdf.setValue("Config.VarEscapeMode", "html");
+        cs = new CS(escape_hdf);
+
+        System.out.println("Some.HTML = " +
+            "<script src=\"some.js\">alert('123');</script>");
+        escape_hdf.setValue("Some.HTML",
+            "<script src=\"some.js\">alert('123');</script>");
+        tmplstr = "Default HTML escaping: <?cs var:Some.HTML ?>\n";
+        System.out.println(tmplstr);
+        System.out.println("----");
+        cs.parseStr(tmplstr);
+        System.out.println(cs.render());
+
+        // Test default escaping mode: js
+        escape_hdf = new HDF();
+        System.out.println("Testing escape mode: js");
+        System.out.println("Config.VarEscapeMode = \"js\"");
+        escape_hdf.setValue("Config.VarEscapeMode", "js");
+        cs = new CS(escape_hdf);
+
+        System.out.println("Some.HTML = " +
+            "<script src=\"some.js\">alert('123');</script>");
+        escape_hdf.setValue("Some.HTML",
+            "<script src=\"some.js\">alert('123');</script>");
+        tmplstr = "Default JS escaping: <?cs var:Some.HTML ?>\n";
+        System.out.println(tmplstr);
+        System.out.println("----");
+        cs.parseStr(tmplstr);
+        System.out.println(cs.render());
+
+        // Test default escaping mode: url
+        escape_hdf = new HDF();
+        System.out.println("Testing escape mode: url");
+        System.out.println("Config.VarEscapeMode = \"url\"");
+        escape_hdf.setValue("Config.VarEscapeMode", "url");
+        cs = new CS(escape_hdf);
+
+        System.out.println("Some.HTML = " +
+            "<script src=\"some.js\">alert('123');</script>");
+        escape_hdf.setValue("Some.HTML",
+            "<script src=\"some.js\">alert('123');</script>");
+        tmplstr = "Default URL escaping: <?cs var:Some.HTML ?>\n";
+        System.out.println(tmplstr);
+        System.out.println("----");
+        cs.parseStr(tmplstr);
+        System.out.println(cs.render());
+
+        // Test escape blocks
+        escape_hdf = new HDF();
+        System.out.println("Testing escape blocks: none");
+        System.out.println("Config.VarEscapeMode = \"none\"");
+        escape_hdf.setValue("Config.VarEscapeMode", "none");
+        cs = new CS(escape_hdf);
+
+        System.out.println("Some.HTML = " +
+            "<script src=\"some.js\">alert('123');</script>");
+        escape_hdf.setValue("Some.HTML",
+            "<script src=\"some.js\">alert('123');</script>");
+        tmplstr = "url escape block: \n" +
+          "<?cs escape: \"url\"?>" +
+          "  <?cs var:Some.HTML ?>" +
+          "<?cs /escape ?>\n" +
+          "js escape block: \n" +
+          "<?cs escape: \"js\"?>" +
+          "  <?cs var:Some.HTML ?>" +
+          "<?cs /escape ?>\n" +
+          "html escape block: \n" +
+          "<?cs escape: \"html\"?>" +
+          "  <?cs var:Some.HTML ?>" +
+          "<?cs /escape ?>\n";
+        System.out.println(tmplstr);
+        System.out.println("----");
+        cs.parseStr(tmplstr);
+        System.out.println(cs.render());
     }
 };
diff -Nrub clearsilver-0.10.3/java-jni/HDF.java clearsilver-0.10.4/java-jni/HDF.java
--- clearsilver-0.10.3/java-jni/HDF.java	2005-11-16 19:10:06.000000000 -0800
+++ clearsilver-0.10.4/java-jni/HDF.java	2006-10-09 17:47:43.000000000 -0700
@@ -3,6 +3,10 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
 /** This class is a wrapper around the HDF C API.  Many features of the C API
  *  are not yet exposed through this wrapper.
  */
@@ -68,6 +72,45 @@
     return _readFile(hdfptr, filename);
   }
 
+  /** Serializes HDF contents to a file (readable by readFile)
+   */
+  public boolean writeFile(String filename) throws IOException {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+    return _writeFile(hdfptr, filename);
+  }
+
+  /** Serializes HDF contents to a file (readable by readFile), but
+   *  writes the file atomically by writing to a temp file then doing a
+   *  rename(2) on it.
+   */
+  public boolean writeFileAtomic(String filename) throws IOException {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+    return _writeFileAtomic(hdfptr, filename);
+  }
+
+  /** Parses/loads the contents of the given string as HDF into the current
+   *  HDF object.  The loaded contents are merged with the existing contents.
+   */
+  public boolean readString(String data) {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+    return _readString(hdfptr, data);
+  }
+
+  /** Serializes HDF contents to a string (readable by readString)
+   */
+  public String writeString() {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+    return _writeString(hdfptr);
+  }
+
   /** Retrieves the integer value at the specified path in this HDF node's
    *  subtree.  If the value does not exist, or cannot be converted to an
    *  integer, default_value will be returned. */
@@ -111,6 +154,66 @@
     _setSymLink(hdfptr,hdf_name_src,hdf_name_dest);
   }
 
+  /** Export a date to a clearsilver tree using a specified timezone */
+  public void exportDate(String hdfname, TimeZone timeZone, Date date) {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+
+    Calendar cal = Calendar.getInstance(timeZone);
+    cal.setTime(date);
+
+    String sec = Integer.toString(cal.get(Calendar.SECOND));
+    setValue(hdfname + ".sec", sec.length() == 1 ? "0" + sec : sec);
+
+    String min = Integer.toString(cal.get(Calendar.MINUTE));
+    setValue(hdfname + ".min", min.length() == 1 ? "0" + min : min);
+
+    setValue(hdfname + ".24hour",
+             Integer.toString(cal.get(Calendar.HOUR_OF_DAY)));
+    // java.util.Calendar uses represents 12 o'clock as 0
+    setValue(hdfname + ".hour",
+             Integer.toString(
+                 cal.get(Calendar.HOUR) == 0 ? 12 : cal.get(Calendar.HOUR)));
+    setValue(hdfname + ".am",
+             cal.get(Calendar.AM_PM) == Calendar.AM ? "1" : "0");
+    setValue(hdfname + ".mday",
+             Integer.toString(cal.get(Calendar.DAY_OF_MONTH)));
+    setValue(hdfname + ".mon",
+             Integer.toString(cal.get(Calendar.MONTH)+1));
+    setValue(hdfname + ".year",
+             Integer.toString(cal.get(Calendar.YEAR)));
+    setValue(hdfname + ".2yr",
+             Integer.toString(cal.get(Calendar.YEAR)).substring(2));
+    setValue(hdfname + ".wday",
+             Integer.toString(cal.get(Calendar.DAY_OF_WEEK)));
+
+    boolean tzNegative = timeZone.getRawOffset() < 0;
+    int tzAbsolute = java.lang.Math.abs(timeZone.getRawOffset()/1000);
+    String tzHour = Integer.toString(tzAbsolute/3600);
+    String tzMin = Integer.toString(tzAbsolute/60 - (tzAbsolute/3600)*60);
+    String tzString = (tzNegative ? "-" : "+")
+                      + (tzHour.length() == 1 ? "0" + tzHour : tzHour)
+                      + (tzMin.length() == 1 ? "0" + tzMin : tzMin);
+    setValue(hdfname + ".tzoffset", tzString);
+  }
+
+  /** Export a date to a clearsilver tree using a specified timezone */
+  public void exportDate(String hdfname, String tz, int tt) {
+    if (hdfptr == 0) {
+      throw new NullPointerException("HDF is closed.");
+    }
+
+    TimeZone timeZone = TimeZone.getTimeZone(tz);
+
+    if (timeZone == null) {
+      throw new RuntimeException("Unknown timezone: " + tz);
+    }
+
+    Date date = new Date((long)tt * 1000);
+
+    exportDate(hdfname, timeZone, date);
+  }
 
   /** Retrieves the HDF object that is the root of the subtree at hdfpath, or
    *  null if no object exists at that path. */
@@ -125,8 +228,9 @@
     return new HDF(obj_ptr, this);
   }
 
-  /** Retrieves the HDF for the first child of the root of the subtree at hdfpath, or
-   *  null if no child exists of that path or if the path doesn't exist. */
+  /** Retrieves the HDF for the first child of the root of the subtree
+   *  at hdfpath, or null if no child exists of that path or if the
+   *  path doesn't exist. */
   public HDF getChild(String hdfpath) {
     if (hdfptr == 0) {
       throw new NullPointerException("HDF is closed.");
@@ -138,6 +242,12 @@
     return new HDF(obj_ptr, this);
   }
 
+  /** Return the root of the tree where the current node lies.  If the
+   *  current node is the root, return this. */
+  public HDF getRootObj() {
+    return root != null ? root : this;
+  }
+
   /** Retrieves the HDF object that is the root of the subtree at
    *  hdfpath, create the subtree if it doesn't exist */
   public HDF getOrCreateObj(String hdfpath) {
@@ -223,6 +333,10 @@
   private static native int _init();
   private static native void _dealloc(int ptr);
   private static native boolean _readFile(int ptr, String filename);
+  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);
+  private static native String _writeString(int ptr);
   private static native int _getIntValue(int ptr, String hdfname,
                                          int default_value);
   private static native String _getValue(int ptr, String hdfname,
diff -Nrub clearsilver-0.10.3/java-jni/javatest.gold clearsilver-0.10.4/java-jni/javatest.gold
--- clearsilver-0.10.3/java-jni/javatest.gold	2005-11-16 19:17:29.000000000 -0800
+++ clearsilver-0.10.4/java-jni/javatest.gold	2006-11-09 16:54:04.000000000 -0800
@@ -65,6 +65,15 @@
 Testing HDF.readFile() for a file that doesn't exist
 Caught exception of type java.io.FileNotFoundException
 
+Testing HDF.writeFile()
+
+Testing HDF.writeString()
+
+Foo {
+Bar = 10
+Baz = 20
+}
+
 Testing HDF.getObj()
 Bar = 10
 Baz = 20
@@ -88,3 +97,69 @@
 Testing HDF.copy()
 name should be barneyb: barneyb
 
+Testing HDF.exportDate()
+DatePST.sec = 18
+DatePST.min = 50
+DatePST.24hour = 19
+DatePST.hour = 7
+DatePST.am = 0
+DatePST.mday = 13
+DatePST.mon = 3
+DatePST.year = 2006
+DatePST.2yr = 06
+DatePST.wday = 2
+DatePST.tzoffset = -0800
+DateEST.sec = 18
+DateEST.min = 50
+DateEST.24hour = 22
+DateEST.hour = 10
+DateEST.am = 0
+DateEST.mday = 13
+DateEST.mon = 3
+DateEST.year = 2006
+DateEST.2yr = 06
+DateEST.wday = 2
+DateEST.tzoffset = -0500
+
+Testing escape mode: html
+Config.VarEscapeMode = "html"
+Some.HTML = <script src="some.js">alert('123');</script>
+Default HTML escaping: <?cs var:Some.HTML ?>
+
+----
+Default HTML escaping: &lt;script src=&quot;some.js&quot;&gt;alert(&#39;123&#39;);&lt;/script&gt;
+
+Testing escape mode: js
+Config.VarEscapeMode = "js"
+Some.HTML = <script src="some.js">alert('123');</script>
+Default JS escaping: <?cs var:Some.HTML ?>
+
+----
+Default JS escaping: \x3Cscript src=\x22some.js\x22\x3Ealert(\x27123\x27)\x3B\x3C\x2Fscript\x3E
+
+Testing escape mode: url
+Config.VarEscapeMode = "url"
+Some.HTML = <script src="some.js">alert('123');</script>
+Default URL escaping: <?cs var:Some.HTML ?>
+
+----
+Default URL escaping: %3Cscript+src%3D%22some.js%22%3Ealert(%27123%27)%3B%3C%2Fscript%3E
+
+Testing escape blocks: none
+Config.VarEscapeMode = "none"
+Some.HTML = <script src="some.js">alert('123');</script>
+url escape block: 
+<?cs escape: "url"?>  <?cs var:Some.HTML ?><?cs /escape ?>
+js escape block: 
+<?cs escape: "js"?>  <?cs var:Some.HTML ?><?cs /escape ?>
+html escape block: 
+<?cs escape: "html"?>  <?cs var:Some.HTML ?><?cs /escape ?>
+
+----
+url escape block: 
+  %3Cscript+src%3D%22some.js%22%3Ealert(%27123%27)%3B%3C%2Fscript%3E
+js escape block: 
+  \x3Cscript src=\x22some.js\x22\x3Ealert(\x27123\x27)\x3B\x3C\x2Fscript\x3E
+html escape block: 
+  &lt;script src=&quot;some.js&quot;&gt;alert(&#39;123&#39;);&lt;/script&gt;
+
diff -Nrub clearsilver-0.10.3/java-jni/j_neo_util.c clearsilver-0.10.4/java-jni/j_neo_util.c
--- clearsilver-0.10.3/java-jni/j_neo_util.c	2005-11-16 20:08:32.000000000 -0800
+++ clearsilver-0.10.4/java-jni/j_neo_util.c	2006-04-20 12:56:25.000000000 -0700
@@ -249,7 +249,7 @@
   jboolean retval;
 
   filename = (*env)->GetStringUTFChars(env, j_filename, 0);
-  err = hdf_read_file(hdf, (char*)filename);
+  err = hdf_read_file(hdf, filename);
   (*env)->ReleaseStringUTFChars(env, j_filename, filename);
   if (err != STATUS_OK) {
     // Throw an exception.  jNeoErr handles all types of errors other than
@@ -269,6 +269,74 @@
   return retval;
 }
 
+JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1writeFile(
+    JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename) {
+  HDF *hdf = (HDF *)hdf_obj_ptr;
+  NEOERR *err;
+  const char *filename;
+  jboolean retval;
+
+  filename = (*env)->GetStringUTFChars(env, j_filename, 0);
+  err = hdf_write_file(hdf, filename);
+  (*env)->ReleaseStringUTFChars(env, j_filename, filename);
+  if (err != STATUS_OK) {
+    jNeoErr(env, err);
+  }
+  retval = (err == STATUS_OK);
+  return retval;
+}
+
+JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1writeFileAtomic(
+    JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename) {
+  HDF *hdf = (HDF *)hdf_obj_ptr;
+  NEOERR *err;
+  const char *filename;
+  jboolean retval;
+
+  filename = (*env)->GetStringUTFChars(env, j_filename, 0);
+  err = hdf_write_file_atomic(hdf, filename);
+  (*env)->ReleaseStringUTFChars(env, j_filename, filename);
+  if (err != STATUS_OK) {
+    jNeoErr(env, err);
+  }
+  retval = (err == STATUS_OK);
+  return retval;
+}
+
+JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1readString(
+    JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_data) {
+  HDF *hdf = (HDF *)hdf_obj_ptr;
+  NEOERR *err;
+  const char *data;
+  jboolean retval;
+
+  data = (*env)->GetStringUTFChars(env, j_data, 0);
+  err = hdf_read_string(hdf, data);
+  (*env)->ReleaseStringUTFChars(env, j_data, data);
+  if (err != STATUS_OK) {
+    jNeoErr(env, err);
+  }
+  retval = (err == STATUS_OK);
+  return retval;
+}
+
+JNIEXPORT jstring JNICALL Java_org_clearsilver_HDF__1writeString(
+    JNIEnv *env, jclass objClass, jint hdf_obj_ptr) {
+  HDF *hdf = (HDF *)hdf_obj_ptr;
+  NEOERR *err;
+  char *output = NULL;
+  jstring retval = NULL;
+
+  err = hdf_write_string(hdf, &output);
+  if (err != STATUS_OK) {
+    jNeoErr(env, err);
+  } else if (output) {
+    retval = (*env)->NewStringUTF(env, output);
+    free(output);
+  }
+  return retval;
+}
+
 JNIEXPORT jint JNICALL Java_org_clearsilver_HDF__1getObj(
     JNIEnv *env, jclass objClass, jint hdf_obj_ptr, jstring j_hdf_path) {
   HDF *hdf = (HDF *)hdf_obj_ptr;
Binary files clearsilver-0.10.3/java-jni/libclearsilver-jni.so and clearsilver-0.10.4/java-jni/libclearsilver-jni.so differ
diff -Nrub clearsilver-0.10.3/java-jni/Makefile clearsilver-0.10.4/java-jni/Makefile
--- clearsilver-0.10.3/java-jni/Makefile	2005-12-02 02:35:28.000000000 -0800
+++ clearsilver-0.10.4/java-jni/Makefile	2006-08-07 13:05:13.000000000 -0700
@@ -18,7 +18,8 @@
 
 CFLAGS += $(JAVA_INCLUDE_PATH)
 DLIBS += -lneo_cgi -lneo_cs -lneo_utl 
-LIBS += -L$(LIB_DIR) $(DLIBS)
+LIBS += $(DLIBS)
+LDFLAGS += -Wl,-soname=$(NEO_UTIL_SO)
 
 TARGETS = org_clearsilver_HDF.h org_clearsilver_CS.h $(NEO_UTIL_SO)
 
diff -Nrub clearsilver-0.10.3/Makefile clearsilver-0.10.4/Makefile
--- clearsilver-0.10.3/Makefile	2006-03-12 15:42:06.000000000 -0800
+++ clearsilver-0.10.4/Makefile	2006-11-14 22:54:13.000000000 -0800
@@ -29,7 +29,7 @@
 	@for mdir in $(SUBDIRS); do \
 	  if test -d $$mdir; then \
 	    if test -f $$mdir/Makefile.PL -a ! -f $$mdir/Makefile; then \
-	      cd $$mdir; $(PERL) Makefile.PL; cd ..; \
+	      cd $$mdir; $(PERL) Makefile.PL PREFIX=$(prefix); cd ..; \
 	    fi; \
 	    $(MAKE) -C $$mdir PREFIX=$(prefix); \
 	  fi; \
@@ -46,7 +46,7 @@
 	@for mdir in $(SUBDIRS); do \
 	  if test -d $$mdir; then \
 	    if test -f $$mdir/Makefile.PL -a ! -f $$mdir/Makefile; then \
-	      cd $$mdir; $(PERL) Makefile.PL; cd ..; \
+	      cd $$mdir; $(PERL) Makefile.PL PREFIX=$(prefix); cd ..; \
 	    fi; \
 	    $(MAKE) -C $$mdir PREFIX=$(prefix) install; \
 	  fi; \
@@ -90,15 +90,15 @@
 	
 
 clean:
-	@for mdir in $(SUBDIRS); do \
+	-@for mdir in $(SUBDIRS); do \
 	  $(MAKE) -C $$mdir clean; \
 	done
 
 distclean:
-	@for mdir in $(SUBDIRS); do \
+	-@for mdir in $(SUBDIRS); do \
 	  $(MAKE) -C $$mdir distclean; \
 	done
-	@for mdir in $(OUTDIRS); do \
+	-@for mdir in $(OUTDIRS); do \
 		rm -rf $$mdir/*; \
 	done
 	rm -f config.cache config.log config.status rules.mk cs_config.h
@@ -108,8 +108,8 @@
 		mkdir -p $$mdir; \
 	done
 
-CS_DISTDIR = clearsilver-0.10.3
-CS_LABEL = CLEARSILVER-0_10_3
+CS_DISTDIR = clearsilver-0.10.4
+CS_LABEL = CLEARSILVER-0_10_4
 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 -Nrub clearsilver-0.10.3/mod_ecs/Makefile clearsilver-0.10.4/mod_ecs/Makefile
--- clearsilver-0.10.3/mod_ecs/Makefile	2005-12-02 02:35:34.000000000 -0800
+++ clearsilver-0.10.4/mod_ecs/Makefile	2006-07-12 23:50:57.000000000 -0700
@@ -10,7 +10,7 @@
 MOD_ECS_SO = mod_ecs.so
 
 DLIBS += -lneo_cgi -lneo_cs -lneo_utl 
-LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB)
+LIBS += $(DLIBS) $(DB2_LIB)
 
 TARGETS = $(MOD_ECS_SO)
 
diff -Nrub clearsilver-0.10.3/ports/rpm/clearsilver.spec clearsilver-0.10.4/ports/rpm/clearsilver.spec
--- clearsilver-0.10.3/ports/rpm/clearsilver.spec	2006-03-12 15:43:17.000000000 -0800
+++ clearsilver-0.10.4/ports/rpm/clearsilver.spec	2006-11-14 22:54:20.000000000 -0800
@@ -53,11 +53,11 @@
 
 Summary: Neotonic ClearSilver
 Name: clearsilver
-Version: 0.10.3
+Version: 0.10.4
 Release: 1
 Copyright: Open Source - Neotonic ClearSilver License (Apache 1.1 based)
 Group: Development/Libraries
-Source: http://www.clearsilver.net/downloads/clearsilver-0.10.3.tar.gz
+Source: http://www.clearsilver.net/downloads/clearsilver-0.10.4.tar.gz
 URL: http://www.clearsilver.net/
 Vendor: Neotonic Software Corporation, Inc.
 Packager: Brandon Long <blong@neotonic.com>
diff -Nrub clearsilver-0.10.3/python/Makefile clearsilver-0.10.4/python/Makefile
--- clearsilver-0.10.3/python/Makefile	2005-12-02 02:35:46.000000000 -0800
+++ clearsilver-0.10.4/python/Makefile	2006-07-12 23:51:49.000000000 -0700
@@ -13,7 +13,7 @@
 
 CFLAGS += $(PYTHON_INC)
 DLIBS += -lneo_cgi -lneo_cs -lneo_utl 
-LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB)
+LIBS += $(DLIBS) $(DB2_LIB)
 
 ifeq ($(USE_MINGW32),1)
 TARGETS = $(NEO_UTIL_PYD)
@@ -38,7 +38,7 @@
 		--def neo_cgi.def -o neo_cgi.pyd \
 		$(NEO_UTIL_OBJ) -s --entry _DllMain@12 \
 		--target=i386-mingw32 \
-		$(PYTHON_LIB) $(LIBS)
+		$(LDFLAGS) $(PYTHON_LIB) $(LIBS)
 
 install: all
 	$(NEOTONIC_ROOT)/mkinstalldirs $(DESTDIR)$(PYTHON_SITE)
diff -Nrub clearsilver-0.10.3/python/neo_cgi.c clearsilver-0.10.4/python/neo_cgi.c
--- clearsilver-0.10.3/python/neo_cgi.c	2005-11-30 20:15:03.000000000 -0800
+++ clearsilver-0.10.4/python/neo_cgi.c	2006-11-09 14:34:08.000000000 -0800
@@ -57,7 +57,7 @@
   {
     cgi_destroy (&(ho->cgi));
   }
-  PyMem_DEL(ho);
+  PyObject_DEL(ho);
 }
 
 PyObject * p_cgi_to_object (CGI *data)
diff -Nrub clearsilver-0.10.3/python/neo_cs.c clearsilver-0.10.4/python/neo_cs.c
--- clearsilver-0.10.3/python/neo_cs.c	2005-06-30 11:51:56.000000000 -0700
+++ clearsilver-0.10.4/python/neo_cs.c	2006-11-09 14:34:34.000000000 -0800
@@ -54,7 +54,7 @@
   {
     cs_destroy (&(ho->data));
   }
-  PyMem_DEL(ho);
+  PyObject_DEL(ho);
 }
 
 PyObject * p_cs_to_object (CSPARSE *data)
diff -Nrub clearsilver-0.10.3/python/neo_util.c clearsilver-0.10.4/python/neo_util.c
--- clearsilver-0.10.3/python/neo_util.c	2005-06-30 11:51:56.000000000 -0700
+++ clearsilver-0.10.4/python/neo_util.c	2006-11-09 14:34:58.000000000 -0800
@@ -76,7 +76,7 @@
   {
     hdf_destroy (&(ho->data));
   }
-  PyMem_DEL(ho);
+  PyObject_DEL(ho);
 }
 
 PyObject * p_hdf_to_object (HDF *data, int dealloc)
diff -Nrub clearsilver-0.10.3/python/setup.py clearsilver-0.10.4/python/setup.py
--- clearsilver-0.10.3/python/setup.py	2006-03-12 16:15:48.000000000 -0800
+++ clearsilver-0.10.4/python/setup.py	2006-11-14 22:54:32.000000000 -0800
@@ -13,7 +13,7 @@
 from distutils.core import Extension
 from distutils import sysconfig
 
-VERSION = "0.10.3"
+VERSION = "0.10.4"
 INC_DIRS = ["../"]
 LIBRARIES = ["neo_cgi", "neo_cs", "neo_utl"]
 LIB_DIRS = ["../libs"]
diff -Nrub clearsilver-0.10.3/ruby/hdftest.out clearsilver-0.10.4/ruby/hdftest.out
--- clearsilver-0.10.3/ruby/hdftest.out	1969-12-31 16:00:00.000000000 -0800
+++ clearsilver-0.10.4/ruby/hdftest.out	2006-11-14 23:22:59.000000000 -0800
@@ -0,0 +1,27 @@
+1 = farming
+2 = sewing
+3 = bowling
+party.1 [Drool="True"]  = baloons
+party.2 [Pink]  = noise makers
+party.3 << EOM
+telling long
+stories
+EOM
+arf.1 = farming
+arf.2 = sewing
+arf.3 = bowling
+arf.party.1 [Drool="True"]  = baloons
+arf.party.2 [Pink]  = noise makers
+arf.party.3 << EOM
+telling long
+stories
+EOM
+party.2 attr (Pink=1)
+This is a funny test. farming.
+
+baloons
+
+noise makers
+
+telling long
+stories
diff -Nrub clearsilver-0.10.3/util/neo_hash.c clearsilver-0.10.4/util/neo_hash.c
--- clearsilver-0.10.3/util/neo_hash.c	2005-11-30 20:06:03.000000000 -0800
+++ clearsilver-0.10.4/util/neo_hash.c	2006-10-18 16:57:24.000000000 -0700
@@ -289,5 +289,5 @@
 
 UINT32 ne_hash_int_hash(const void *a)
 {
-  return (UINT32)(a);
+  return (UINT32)(long)(a);
 }
diff -Nrub clearsilver-0.10.3/util/neo_hdf.c clearsilver-0.10.4/util/neo_hdf.c
--- clearsilver-0.10.3/util/neo_hdf.c	2006-03-07 12:24:44.000000000 -0800
+++ clearsilver-0.10.4/util/neo_hdf.c	2006-08-11 16:47:01.000000000 -0700
@@ -25,6 +25,7 @@
 #include "neo_rand.h"
 #include "neo_hdf.h"
 #include "neo_str.h"
+#include "neo_files.h"
 #include "ulist.h"
 
 /* Ok, in order to use the hash, we have to support n-len strings
@@ -419,7 +420,7 @@
 	/* a set of NULL deletes the attr */
 	if (value == NULL)
 	{
-	  if (last == obj->attr)
+	  if (attr == obj->attr)
 	    obj->attr = attr->next;
 	  else
 	    last->next = attr->next;
@@ -996,16 +997,56 @@
   return STATUS_OK;
 }
 
+static NEOERR * _copy_attr (HDF_ATTR **dest, HDF_ATTR *src)
+{
+  HDF_ATTR *copy, *last = NULL;
+
+  *dest = NULL;
+  while (src != NULL)
+  {
+    copy = (HDF_ATTR *)malloc(sizeof(HDF_ATTR));
+    if (copy == NULL)
+    {
+      _dealloc_hdf_attr(dest);
+      return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR");
+    }
+    copy->key = strdup(src->key);
+    copy->value = strdup(src->value);
+    copy->next = NULL;
+    if ((copy->key == NULL) || (copy->value == NULL))
+    {
+      _dealloc_hdf_attr(dest);
+      return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR");
+    }
+    if (last) {
+      last->next = copy;
+    }
+    else
+    {
+      *dest = copy;
+    }
+    last = copy;
+    src = src->next;
+  }
+  return STATUS_OK;
+}
+
 static NEOERR * _copy_nodes (HDF *dest, HDF *src)
 {
   NEOERR *err = STATUS_OK;
   HDF *dt, *st;
+  HDF_ATTR *attr_copy;
 
   st = src->child;
   while (st != NULL)
   {
-    err = _set_value(dest, st->name, st->value, 1, 1, 0, st->attr, &dt);
+    err = _copy_attr(&attr_copy, st->attr);
     if (err) return nerr_pass(err);
+    err = _set_value(dest, st->name, st->value, 1, 1, 0, attr_copy, &dt);
+    if (err) {
+      _dealloc_hdf_attr(&attr_copy);
+      return nerr_pass(err);
+    }
     if (src->child)
     {
       err = _copy_nodes (dt, st);
@@ -1292,7 +1333,6 @@
 }
 
 
-/* HDF file looks like the following: */
 #define SKIPWS(s) while (*s && isspace(*s)) s++;
 
 static int _copy_line (const char **s, char *buf, size_t buf_len)
@@ -1311,33 +1351,32 @@
   return x;
 }
 
-static int _copy_line_alloc (const char **s, char **buf)
+/* Copy the characters in the file (up to the next newline) into line
+ * and advance s to the next line */
+static NEOERR *_copy_line_advance(const char **s, STRING *line)
 {
   NEOERR *err;
   int x = 0;
   const char *st = *s;
-  STRING str;
   const char *nl;
 
-  string_init(&str);
-
   nl = strchr(st, '\n');
   if (nl == NULL)
   {
     x = strlen(st);
-    err = string_append(&str, st);
+    err = string_appendn(line, st, x);
+    if (err) return nerr_pass(err);
     *s = st + x;
   }
   else
   {
     x = nl - st;
-    err = string_appendn(&str, st, x);
+    err = string_appendn(line, st, x);
+    if (err) return nerr_pass(err);
     *s = nl + 1;
   }
 
-  *buf = str.buf;
-
-  return x;
+  return STATUS_OK;
 }
 
 char *_strndup(const char *s, int len) {
@@ -1495,260 +1534,40 @@
   return STATUS_OK;
 }
 
-static NEOERR* _hdf_read_string (HDF *hdf, const char **str, int *line, 
-                                 int ignore)
+#define INCLUDE_ERROR 0
+#define INCLUDE_IGNORE 1
+#define INCLUDE_FILE 2
+
+static NEOERR* _hdf_read_string (HDF *hdf, const char **str, STRING *line,
+                                 const char *path, int *lineno, int include_handle)
 {
   NEOERR *err;
   HDF *lower;
-  char *buf = NULL;
   char *s;
   char *name, *value;
   HDF_ATTR *attr = NULL;
 
   while (**str != '\0')
   {
-    _copy_line_alloc(str, &buf);
+    /* Reset string length, but don't free the reserved buffer */
+    line->len = 0;
+    err = _copy_line_advance(str, line);
+    if (err) return nerr_pass(err);
     attr = NULL;
-    (*line)++;
-    s = buf;
+    (*lineno)++;
+    s = line->buf;
     SKIPWS(s);
     if (!strncmp(s, "#include ", 9))
     {
-      if (!ignore) 
+      if (include_handle == INCLUDE_ERROR)
       {
-        if (buf != NULL) free(buf);
 	return nerr_raise (NERR_PARSE, 
                            "[%d]: #include not supported in string parse",
-                           *line);
-      }
-    }
-    else if (s[0] == '#')
-    {
-      /* comment: pass */
-    }
-    else if (s[0] == '}') /* up */
-    {
-      s = neos_strip(s);
-      if (strcmp(s, "}"))
-      {
-        err = nerr_raise(NERR_PARSE, 
-	    "[%d] Trailing garbage on line following }: %s", *line,
-	    buf);
-        if (buf != NULL) free(buf);
-        return err;
-      }
-      if (buf != NULL) free(buf);
-      return STATUS_OK;
-    }
-    else if (s[0])
-    {
-      /* Valid hdf name is [0-9a-zA-Z_.]+ */
-      name = s;
-      while (*s && (isalnum(*s) || *s == '_' || *s == '.')) s++;
-      SKIPWS(s);
-
-      if (s[0] == '[') /* attributes */
-      {
-	*s = '\0';
-	name = neos_strip(name);
-	s++;
-	err = parse_attr(&s, &attr);
-	if (err)
-        {
-          if (buf != NULL) free(buf);
-          return nerr_pass_ctx(err, "In String %d", *line);
+                           *lineno);
         }
-	SKIPWS(s);
-      }
-      if (s[0] == '=') /* assignment */
+      else if (include_handle == INCLUDE_FILE)
       {
-	*s = '\0';
-	name = neos_strip(name);
-	s++;
-	value = neos_strip(s);
-	err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL);
-	if (err != STATUS_OK) 
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_pass_ctx(err, "In String %d", *line);
-        }
-      }
-      else if (s[0] == ':' && s[1] == '=') /* copy */
-      {
-	*s = '\0';
-	name = neos_strip(name);
-	s+=2;
-	value = neos_strip(s);
-	value = hdf_get_value(hdf->top, value, "");
-	err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL);
-	if (err != STATUS_OK)
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_pass_ctx(err, "In string %d", *line);
-        }
-      }
-      else if (s[0] == ':') /* link */
-      {
-	*s = '\0';
-	name = neos_strip(name);
-	s++;
-	value = neos_strip(s);
-	err = _set_value (hdf, name, value, 1, 1, 1, attr, NULL);
-	if (err != STATUS_OK)
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_pass_ctx(err, "In string %d", *line);
-        }
-      }
-      else if (s[0] == '{') /* deeper */
-      {
-	*s = '\0';
-	name = neos_strip(name);
-	lower = hdf_get_obj (hdf, name);
-	if (lower == NULL)
-	{
-	  err = _set_value (hdf, name, NULL, 1, 1, 0, attr, &lower);
-	}
-	else
-	{
-	  err = _set_value (lower, NULL, lower->value, 1, 1, 0, attr, NULL);
-	}
-	if (err != STATUS_OK) 
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_pass_ctx(err, "In string %d", *line);
-        }
-	err = _hdf_read_string (lower, str, line, ignore);
-	if (err != STATUS_OK) 
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_pass_ctx(err, "In string %d", *line);
-        }
-      }
-      else if (s[0] == '<' && s[1] == '<') /* multi-line assignment */
-      {
-	char *m;
-	int msize = 0;
-	int mmax = 128;
 	int l;
-
-	*s = '\0';
-	name = neos_strip(name);
-	s+=2;
-	value = neos_strip(s);
-	l = strlen(value);
-	if (l == 0)
-        {
-	  err = nerr_raise(NERR_PARSE, 
-	      "[%d] No multi-assignment terminator given: %s", *line, 
-	      buf);
-          if (buf != NULL) free(buf);
-          return err;
-        }
-	m = (char *) malloc (mmax * sizeof(char));
-	if (m == NULL)
-        {
-          if (buf != NULL) free(buf);
-	  return nerr_raise(NERR_NOMEM, 
-	    "[%d] Unable to allocate memory for multi-line assignment to %s",
-	    *line, name);
-        }
-	while (_copy_line (str, m+msize, mmax-msize) != 0)
-	{
-	  if (!strncmp(value, m+msize, l) && isspace(m[msize+l]))
-	  {
-	    m[msize] = '\0';
-	    break;
-	  }
-	  msize += strlen(m+msize);
-	  if (msize + l + 10 > mmax)
-	  {
-	    mmax += 128;
-	    m = (char *) realloc (m, mmax * sizeof(char));
-	    if (m == NULL)
-            {
-              if (buf != NULL) free(buf);
-	      return nerr_raise(NERR_NOMEM, 
-		  "[%d] Unable to allocate memory for multi-line assignment to %s: size=%d",
-		  *line, name, mmax);
-            }
-	  }
-	}
-	err = _set_value (hdf, name, m, 0, 1, 0, attr, NULL);
-	if (err != STATUS_OK)
-	{
-	  free (m);
-	  return nerr_pass_ctx(err, "In string %d", *line);
-	}
-
-      }
-      else
-      {
-	err = nerr_raise(NERR_PARSE, "[%d] Unable to parse line %s",
-	    *line, buf);
-        if (buf != NULL) free(buf);
-        return err;
-      }
-    }
-    if (buf != NULL) free(buf);
-    buf = NULL;
-  }
-  if (buf != NULL) free(buf);
-  return STATUS_OK;
-}
-
-NEOERR * hdf_read_string (HDF *hdf, const char *str)
-{
-  int line = 0;
-  return nerr_pass (_hdf_read_string (hdf, &str, &line, 0));
-}
-
-NEOERR * hdf_read_string_ignore (HDF *hdf, const char *str, int ignore)
-{
-  int line = 0;
-  return nerr_pass (_hdf_read_string (hdf, &str, &line, ignore));
-}
-
-static int count_newlines (const char *s)
-{
-  int i = 0;
-  const char *n = s;
-
-  n = strchr(s, '\n');
-  while (n != NULL)
-  {
-    i++;
-    n = strchr(n+1, '\n');
-  }
-  return i;
-}
-
-static NEOERR* hdf_read_file_fp (HDF *hdf, FILE *fp, const char *path, 
-                                 int *line)
-{
-  NEOERR *err;
-  STRING str;
-  HDF *lower;
-  HDF_ATTR *attr = NULL;
-  char *s;
-  char *name, *value;
-  int l;
-
-  string_init(&str);
-  err = string_readline(&str, fp);
-  if (err) 
-  {
-    string_clear(&str);
-    return nerr_pass(err);
-  }
-  while (str.len != 0)
-  {
-    attr = NULL;
-    (*line)++;
-    s = str.buf;
-    SKIPWS(s);
-    if (!strncmp(s, "#include ", 9))
-    {
       s += 9;
       name = neos_strip(s);
       l = strlen(name);
@@ -1760,8 +1579,8 @@
       err = hdf_read_file(hdf, name);
       if (err != STATUS_OK) 
       {
-	string_clear(&str);
-	return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
+        }
       }
     }
     else if (s[0] == '#')
@@ -1774,12 +1593,10 @@
       if (strcmp(s, "}"))
       {
 	err = nerr_raise(NERR_PARSE, 
-	    "[%s:%d] Trailing garbage on line following }: %s", path, *line,
-	    str.buf);
-	string_clear(&str);
+	    "[%s:%d] Trailing garbage on line following }: %s", path, *lineno,
+	    line->buf);
 	return err;
       }
-      string_clear(&str);
       return STATUS_OK;
     }
     else if (s[0])
@@ -1797,8 +1614,7 @@
 	err = parse_attr(&s, &attr);
 	if (err) 
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
 	SKIPWS(s);
       }
@@ -1811,8 +1627,7 @@
 	err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL);
 	if (err != STATUS_OK)
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
       }
       else if (s[0] == ':' && s[1] == '=') /* copy */
@@ -1825,8 +1640,7 @@
 	err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL);
 	if (err != STATUS_OK)
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
       }
       else if (s[0] == ':') /* link */
@@ -1838,8 +1652,7 @@
 	err = _set_value (hdf, name, value, 1, 1, 1, attr, NULL);
 	if (err != STATUS_OK)
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
       }
       else if (s[0] == '{') /* deeper */
@@ -1857,16 +1670,13 @@
 	}
 	if (err != STATUS_OK) 
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
-	err = hdf_read_file_fp(lower, fp, path, line);
+	err = _hdf_read_string (lower, str, line, path, lineno, include_handle);
 	if (err != STATUS_OK) 
 	{
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
-	if (feof(fp)) break;
       }
       else if (s[0] == '<' && s[1] == '<') /* multi-line assignment */
       {
@@ -1883,22 +1693,21 @@
 	if (l == 0)
 	{
 	  err = nerr_raise(NERR_PARSE, 
-	      "[%s:%d] No multi-assignment terminator given: %s", path, *line, 
-	      str.buf);
-	  string_clear(&str);
+	      "[%s:%d] No multi-assignment terminator given: %s", path, *lineno, 
+	      line->buf);
 	  return err;
 	}
 	m = (char *) malloc (mmax * sizeof(char));
 	if (m == NULL)
 	{
-	  string_clear(&str);
 	  return nerr_raise(NERR_NOMEM, 
 	    "[%s:%d] Unable to allocate memory for multi-line assignment to %s",
-	    path, *line, name);
+	    path, *lineno, name);
 	}
-	while (fgets(m+msize, mmax-msize, fp) != NULL)
+	while (_copy_line (str, m+msize, mmax-msize) != 0)
 	{
-	  if (!strncmp(value, m+msize, l) && (m[msize+l] == '\r' || m[msize+l] == '\n'))
+          (*lineno)++;
+	  if (!strncmp(value, m+msize, l) && isspace(m[msize+l]))
 	  {
 	    m[msize] = '\0';
 	    break;
@@ -1910,10 +1719,9 @@
 	    m = (char *) realloc (m, mmax * sizeof(char));
 	    if (m == NULL)
 	    {
-	      string_clear(&str);
 	      return nerr_raise(NERR_NOMEM, 
 		  "[%s:%d] Unable to allocate memory for multi-line assignment to %s: size=%d",
-		  path, *line, name, mmax);
+		  path, *lineno, name, mmax);
 	    }
 	  }
 	}
@@ -1921,33 +1729,44 @@
 	if (err != STATUS_OK)
 	{
 	  free (m);
-	  string_clear(&str);
-	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+          return nerr_pass_ctx(err, "In file %s:%d", path, *lineno);
 	}
-	/* count the newlines so we know what line we're on... +1 for EOM */
-	(*line) = (*line) + count_newlines(m) + 1;
+
       }
       else
       {
-	err = nerr_raise(NERR_PARSE, "[%s:%d] Unable to parse line %s",
-	    path, *line, str.buf);
-	string_clear(&str);
+	err = nerr_raise(NERR_PARSE, "[%s:%d] Unable to parse line %s", path,
+	    *lineno, line->buf);
 	return err;
       }
     }
-    str.len = 0;
-    err = string_readline(&str, fp);
-    /* ne_warn("string buf len is %d", str.len); */
-    if (err) 
-    {
-      string_clear(&str);
-      return nerr_pass(err);
-    }
   }
-  string_clear(&str);
   return STATUS_OK;
 }
 
+NEOERR * hdf_read_string (HDF *hdf, const char *str)
+{
+  NEOERR *err;
+  int lineno = 0;
+  STRING line;
+  string_init(&line);
+  err = _hdf_read_string(hdf, &str, &line, "<string>", &lineno, INCLUDE_ERROR);
+  string_clear(&line);
+  return nerr_pass(err);
+}
+
+NEOERR * hdf_read_string_ignore (HDF *hdf, const char *str, int ignore)
+{
+  NEOERR *err;
+  int lineno = 0;
+  STRING line;
+  string_init(&line);
+  err = _hdf_read_string(hdf, &str, &line, "<string>", &lineno,
+                         (ignore ? INCLUDE_IGNORE : INCLUDE_ERROR));
+  string_clear(&line);
+  return nerr_pass(err);
+}
+
 /* The search path is part of the HDF by convention */
 NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full)
 {
@@ -1985,12 +1804,24 @@
 NEOERR* hdf_read_file (HDF *hdf, const char *path)
 {
   NEOERR *err;
-  FILE *fp;
-  int line = 0;
+  int lineno = 0;
   char fpath[_POSIX_PATH_MAX];
+  char *ibuf = NULL;
+  const char *ptr = NULL;
+  HDF *top = hdf->top;
+  STRING line;
+
+  string_init(&line);
 
   if (path == NULL) 
     return nerr_raise(NERR_ASSERT, "Can't read NULL file");
+
+  if (top->fileload)
+  {
+    err = top->fileload(top->fileload_ctx, hdf, path, &ibuf);
+  }
+  else
+  {
   if (path[0] != '/')
   {
     err = hdf_search_path (hdf, path, fpath);
@@ -1998,21 +1829,22 @@
     path = fpath;
   }
 
-  fp = fopen(path, "r");
-  if (fp == NULL)
-  {
-    if (errno == ENOENT)
-    {
-      return nerr_raise(NERR_NOT_FOUND, "File not found: %s", path);
-    }
-    else
-    {
-      return nerr_raise_errno(NERR_IO, "Unable to open file %s", path);
-    }
+    err = ne_load_file (path, &ibuf);
   }
+  if (err) return nerr_pass(err);
 
-  err = hdf_read_file_fp(hdf, fp, path, &line);
-  fclose(fp);
+  ptr = ibuf;
+  err = _hdf_read_string(hdf, &ptr, &line, path, &lineno, INCLUDE_FILE);
+  free(ibuf);
+  string_clear(&line);
   return nerr_pass(err);
 }
 
+void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload)
+{
+  if (hdf == NULL) return;
+  if (hdf->top != NULL) hdf = hdf->top;
+  hdf->fileload_ctx = ctx;
+  hdf->fileload = fileload;
+}
+
diff -Nrub clearsilver-0.10.3/util/neo_hdf.h clearsilver-0.10.4/util/neo_hdf.h
--- clearsilver-0.10.3/util/neo_hdf.h	2005-06-30 17:34:16.000000000 -0700
+++ clearsilver-0.10.4/util/neo_hdf.h	2006-04-06 15:05:43.000000000 -0700
@@ -20,6 +20,23 @@
 
 #define FORCE_HASH_AT 10
 
+typedef struct _hdf HDF;
+
+/* HDFFILELOAD is a callback function to intercept file load requests and
+ * provide templates via another mechanism.  This way you can load templates
+ * that you compiled-into your binary, from in-memory caches, or from a
+ * zip file, etc.  The HDF is provided so you can choose to use the 
+ * hdf_search_path function to find the file.  contents should return
+ * a full malloc copy of the contents of the file, which the parser will
+ * own and free.  Use hdf_register_fileload to set this function for
+ * your top level HDF node.
+ * NOTE: Technically, we shouldn't need a separate copy for each parse, but 
+ * using the separate copy makes this equivalent to the CSFILELOAD function.  We
+ * can change this if we really want to save that copy at the expense of 
+ * slightly more complicated code. */
+typedef NEOERR* (*HDFFILELOAD)(void *ctx, HDF *hdf, const char *filename,
+                              char **contents);
+
 typedef struct _attr
 {
   char *key;
@@ -27,7 +44,7 @@
   struct _attr *next;
 } HDF_ATTR;
 
-typedef struct _hdf
+struct _hdf
 {
   int link;
   int alloc_value;
@@ -48,7 +65,12 @@
   NE_HASH *hash;
   /* When using the HASH, we need to know where to append new children */
   struct _hdf *last_child;
-} HDF;
+
+  /* Should only be set on the head node, used to override the default file
+   * load method */
+  void *fileload_ctx;
+  HDFFILELOAD fileload;
+};
 
 /*
  * Function: hdf_init - Initialize an HDF data set
@@ -539,6 +561,25 @@
  */
 NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full);
 
+/*
+ * Function: hdf_register_fileload - register a fileload function
+ * 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 
+ *              #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.
+ * Input: hdf - pointer to a head HDF node
+ *        ctx - pointer that is passed to the HDFFILELOAD function when called
+ *        fileload - a HDFFILELOAD function
+ * Output: None
+ * Return: None
+ *
+ */
+
+void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload);
+
 __END_DECLS
 
 #endif /* __NEO_HDF_H_ */
diff -Nrub clearsilver-0.10.3/util/neo_str.c clearsilver-0.10.4/util/neo_str.c
--- clearsilver-0.10.3/util/neo_str.c	2005-06-30 17:42:50.000000000 -0700
+++ clearsilver-0.10.4/util/neo_str.c	2006-08-07 13:01:53.000000000 -0700
@@ -591,3 +591,239 @@
   rs[i] = '\0';
   return rs;
 }
+
+// List of all characters that must be escaped
+// List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
+static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`'";
+
+// Check if a single character needs to be escaped
+static BOOL is_reserved_char(char c)
+{
+  int i = 0;
+
+  if (c < 32 || c > 122) {
+    return TRUE;
+  } else {
+    while (EscapedChars[i]) {
+      if (c == EscapedChars[i]) {
+        return TRUE;
+      }
+      ++i;
+    }
+  }
+  return FALSE;
+}
+
+NEOERR *neos_js_escape (const char *in, char **esc)
+{
+  int nl = 0;
+  int l = 0;
+  unsigned char *buf = (unsigned char *)in;
+  unsigned char *s;
+
+  while (buf[l])
+  {
+    if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
+        buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' ||
+        buf[l] == '&' || buf[l] == ';' || buf[l] < 32)
+    {
+      nl += 3;
+    }
+    nl++;
+    l++;
+  }
+
+  s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
+  if (s == NULL)
+    return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
+        buf);
+
+  nl = 0; l = 0;
+  while (buf[l])
+  {
+    if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
+        buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' ||
+        buf[l] == '&' || buf[l] == ';' || buf[l] < 32)
+    {
+      s[nl++] = '\\';
+      s[nl++] = 'x';
+      s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF];
+      s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF];
+      l++;
+    }
+    else
+    {
+      s[nl++] = buf[l++];
+    }
+  }
+  s[nl] = '\0';
+
+  *esc = (char *)s;
+  return STATUS_OK;
+}
+
+
+NEOERR *neos_url_escape (const char *in, char **esc,
+                         const char *other)
+{
+  int nl = 0;
+  int l = 0;
+  int x = 0;
+  unsigned char *buf = (unsigned char *)in;
+  unsigned char *uother = (unsigned char *)other;
+  unsigned char *s;
+  int match = 0;
+
+  while (buf[l])
+  {
+    if (is_reserved_char(buf[l]))
+    {
+      nl += 2;
+    }
+    else if (uother)
+    {
+      x = 0;
+      while (uother[x])
+      {
+        if (uother[x] == buf[l])
+        {
+          nl +=2;
+          break;
+        }
+        x++;
+      }
+    }
+    nl++;
+    l++;
+  }
+
+  s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
+  if (s == NULL)
+    return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
+      buf);
+
+  nl = 0; l = 0;
+  while (buf[l])
+  {
+    match = 0;
+    if (buf[l] == ' ')
+    {
+      s[nl++] = '+';
+      l++;
+    }
+    else
+    {
+      if (is_reserved_char(buf[l]))
+      {
+        match = 1;
+      }
+      else if (uother)
+      {
+        x = 0;
+        while (uother[x])
+        {
+          if (uother[x] == buf[l])
+          {
+            match = 1;
+            break;
+          }
+          x++;
+        }
+      }
+      if (match)
+      {
+        s[nl++] = '%';
+        s[nl++] = "0123456789ABCDEF"[buf[l] / 16];
+        s[nl++] = "0123456789ABCDEF"[buf[l] % 16];
+        l++;
+      }
+      else
+      {
+        s[nl++] = buf[l++];
+      }
+    }
+  }
+  s[nl] = '\0';
+
+  *esc = (char *)s;
+  return STATUS_OK;
+}
+
+NEOERR *neos_html_escape (const char *src, int slen,
+                          char **out)
+{
+  NEOERR *err = STATUS_OK;
+  STRING out_s;
+  int x;
+  char *ptr;
+
+  string_init(&out_s);
+  err = string_append (&out_s, "");
+  if (err) return nerr_pass (err);
+  *out = NULL;
+
+  x = 0;
+  while (x < slen)
+  {
+    ptr = strpbrk(src + x, "&<>\"'\r");
+    if (ptr == NULL || (ptr-src >= slen))
+    {
+      err = string_appendn (&out_s, src + x, slen-x);
+      x = slen;
+    }
+    else
+    {
+      err = string_appendn (&out_s, src + x, (ptr - src) - x);
+      if (err != STATUS_OK) break;
+      x = ptr - src;
+      if (src[x] == '&')
+        err = string_append (&out_s, "&amp;");
+      else if (src[x] == '<')
+        err = string_append (&out_s, "&lt;");
+      else if (src[x] == '>')
+        err = string_append (&out_s, "&gt;");
+      else if (src[x] == '"')
+        err = string_append (&out_s, "&quot;");
+      else if (src[x] == '\'')
+        err = string_append (&out_s, "&#39;");
+      else if (src[x] != '\r')
+        err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]);
+      x++;
+    }
+    if (err != STATUS_OK) break;
+  }
+  if (err) 
+  {
+    string_clear (&out_s);
+    return nerr_pass (err);
+  }
+  *out = out_s.buf;
+  return STATUS_OK;
+}
+
+NEOERR *neos_var_escape (NEOS_ESCAPE context,
+                         const char *in,
+                         char **esc)
+{
+
+  /* Just dup and return if we do nothing. */
+  if (context == NEOS_ESCAPE_NONE ||
+      context == NEOS_ESCAPE_FUNCTION)
+  {
+    *esc = strdup(in);
+    return STATUS_OK;
+  }
+
+  /* Now we escape based on context. This is the order of precedence:
+   * url > script > style > html
+   */
+  if (context & NEOS_ESCAPE_URL)
+    return nerr_pass(neos_url_escape(in, esc, NULL));
+  else if (context & NEOS_ESCAPE_SCRIPT)
+    return nerr_pass(neos_js_escape(in, esc));
+  else if (context & NEOS_ESCAPE_HTML)
+    return nerr_pass(neos_html_escape(in, strlen(in), esc));
+
+  return nerr_raise(NERR_ASSERT, "unknown escape context supplied: %d",
+    context);
+}
diff -Nrub clearsilver-0.10.3/util/neo_str.h clearsilver-0.10.4/util/neo_str.h
--- clearsilver-0.10.3/util/neo_str.h	2005-11-30 19:25:57.000000000 -0800
+++ clearsilver-0.10.4/util/neo_str.h	2006-08-07 13:01:53.000000000 -0700
@@ -51,7 +51,6 @@
   int max;
 } STRING_ARRAY;
 
-
 /* At some point, we should add the concept of "max len" to these so we
  * can't get DoS'd by someone sending us a line without an end point,
  * etc. */
@@ -74,6 +73,17 @@
 
 BOOL reg_search (const char *re, const char *str);
 
+/* NEOS_ESCAPE details the support escape contexts/modes handled
+ * by various NEOS helper methods and reused in CS itself. */
+typedef enum
+{
+  NEOS_ESCAPE_UNDEF    =  0,    /* Used to force eval-time checking */
+  NEOS_ESCAPE_NONE     =  1<<0,
+  NEOS_ESCAPE_HTML     =  1<<1,
+  NEOS_ESCAPE_SCRIPT   =  1<<2,
+  NEOS_ESCAPE_URL      =  1<<3,
+  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, 
                     char **esc);
@@ -81,6 +91,22 @@
 
 char *repr_string_alloc (const char *s);
 
+/* This is the "super" escape call which will call the proper helper
+ * variable escape function based on the passed in context. */
+NEOERR *neos_var_escape (NEOS_ESCAPE context,
+                         const char *in,
+                         char **esc);
+
+/* Generic data escaping helper functions used by neos_contextual_escape
+ * and cs built-ins. */
+NEOERR *neos_url_escape (const char *in, char **esc,
+                         const char *other);
+
+NEOERR *neos_js_escape (const char *in, char **esc);
+
+NEOERR *neos_html_escape (const char *src, int slen,
+                          char **out);
+
 __END_DECLS
 
 #endif /* __NEO_STR_H_ */
diff -Nrub clearsilver-0.10.3/util/test/hdf_copy_test.c clearsilver-0.10.4/util/test/hdf_copy_test.c
--- clearsilver-0.10.3/util/test/hdf_copy_test.c	2005-06-30 11:52:10.000000000 -0700
+++ clearsilver-0.10.4/util/test/hdf_copy_test.c	2006-08-11 16:49:37.000000000 -0700
@@ -8,12 +8,13 @@
 #include "util/neo_hdf.h"
 
 int main(void) {
-  HDF *hdf_1;
+  HDF *hdf_1, *hdf_2;
   HDF *cur_node,*last_node;
 
   hdf_init(&hdf_1);
 
   hdf_read_file(hdf_1,"hdf_copy_test.hdf");
+  hdf_dump(hdf_1,NULL);
 
   cur_node = hdf_get_obj(hdf_1,"Chart");
   last_node = cur_node;
@@ -37,5 +38,13 @@
   hdf_copy(last_node,"next_stage",hdf_get_obj(hdf_1,"TempHolderPlace"));
   hdf_dump(hdf_1,NULL);
 
+  /* Test copy and destroy, make sure we actually copy everything and
+   * don't reference anything */
+  hdf_init(&hdf_2);
+  hdf_copy(hdf_2, "", hdf_1);
+  hdf_destroy(&hdf_1);
+  hdf_dump(hdf_2, NULL);
+  hdf_destroy(&hdf_2);
+
   return 0;
 }
diff -Nrub clearsilver-0.10.3/util/test/hdf_copy_test.hdf clearsilver-0.10.4/util/test/hdf_copy_test.hdf
--- clearsilver-0.10.3/util/test/hdf_copy_test.hdf	2005-06-30 11:52:10.000000000 -0700
+++ clearsilver-0.10.4/util/test/hdf_copy_test.hdf	2006-08-11 16:00:23.000000000 -0700
@@ -19,21 +19,21 @@
 
 Chart {
   next_stage {
-     id                = 1
+     id [attr=1, batter] = 1
      Type              = Mux
      Bucket            = Discrete
      Bucket.FieldId    = QUEUE
      Bucket.Dimension  = Y
      
      next_stage {
-         id                = 2
+         id [attr=2, batter] = 2
          Type              = Mux
          Bucket            = Time
          Bucket.FieldId    = TIME
          Bucket.Dimension  = Y
 
          next_stage {
-             id                = 3
+             id [attr=3] = 3
              Type              = Mux
              Bucket            = Discrete
              Bucket.FieldId    = AGENT
diff -Nrub clearsilver-0.10.3/util/test/hdfloadtest.c clearsilver-0.10.4/util/test/hdfloadtest.c
--- clearsilver-0.10.3/util/test/hdfloadtest.c	2005-06-30 11:52:11.000000000 -0700
+++ clearsilver-0.10.4/util/test/hdfloadtest.c	2006-04-05 19:34:49.000000000 -0700
@@ -36,8 +36,19 @@
 
   for (x = 0; x < reps; x++)
   {
-    /* Half the time we test by loading the file and reading the string */
-    if (x % 2 == 0) 
+    err = hdf_read_file(hdf, file);
+    if (err != STATUS_OK) 
+    {
+      nerr_log_error(err);
+      return -1;
+    }
+  }
+  tend = ne_timef();
+  ne_warn("hdf_read_file test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps);
+
+  tstart = ne_timef();
+
+  for (x = 0; x < reps; x++)
     {
       err = ne_load_file(file, &s);
       if (err != STATUS_OK) 
@@ -53,18 +64,29 @@
       }
       free(s);
     }
-    else
-    {
-      err = hdf_read_file(hdf, file);
+  tend = ne_timef();
+  ne_warn("load/hdf_read_string test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps);
+
+
+  err = ne_load_file(file, &s);
       if (err != STATUS_OK) 
       {
 	nerr_log_error(err);
 	return -1;
       }
+  tstart = ne_timef();
+  for (x = 0; x < reps; x++)
+  {
+    err = hdf_read_string(hdf, s);
+    if (err != STATUS_OK) 
+    {
+      nerr_log_error(err);
+      return -1;
     }
   }
   tend = ne_timef();
-  ne_warn("Load test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps);
+  free(s);
+  ne_warn("hdf_read_string test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps);
   /* hdf_dump(hdf, NULL);  */
 
   hdf_destroy(&hdf);
diff -Nrub clearsilver-0.10.3/util/test/Makefile clearsilver-0.10.4/util/test/Makefile
--- clearsilver-0.10.3/util/test/Makefile	2005-06-30 11:52:10.000000000 -0700
+++ clearsilver-0.10.4/util/test/Makefile	2006-07-12 23:53:21.000000000 -0700
@@ -47,7 +47,7 @@
 ULISTTEST_OBJ = $(ULISTTEST_SRC:%.c=%.o)
 
 CFLAGS += -I$(NEOTONIC_ROOT)/util
-LIBS += -L$(LIB_DIR) -lneo_utl 
+LIBS += -lneo_utl 
 
 TARGETS = $(HDFTEST_EXE) $(LISTDIRTEST_EXE) $(HDFCOPYTEST_EXE) \
 	$(HDFSORTTEST_EXE) $(HDFDEALLOCTEST_EXE) \
@@ -57,34 +57,34 @@
 all: $(TARGETS)
 
 $(ULISTTEST_EXE): $(ULISTTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(ULISTTEST_OBJ) $(LIBS)
+	$(LD) $@ $(ULISTTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HDFTEST_EXE): $(HDFTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HDFTEST_OBJ) $(LIBS)
+	$(LD) $@ $(HDFTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HDFSORTTEST_EXE): $(HDFSORTTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HDFSORTTEST_OBJ) $(LIBS)
+	$(LD) $@ $(HDFSORTTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HDFDEALLOCTEST_EXE): $(HDFDEALLOCTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HDFDEALLOCTEST_OBJ) $(LIBS)
+	$(LD) $@ $(HDFDEALLOCTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HDFLOADTEST_EXE): $(HDFLOADTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HDFLOADTEST_OBJ) $(LIBS) # -lefence
+	$(LD) $@ $(HDFLOADTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence
 
 $(LISTDIRTEST_EXE): $(LISTDIRTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(LISTDIRTEST_OBJ) $(LIBS)
+	$(LD) $@ $(LISTDIRTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HDFCOPYTEST_EXE): $(HDFCOPYTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HDFCOPYTEST_OBJ) $(LIBS) # -lefence
+	$(LD) $@ $(HDFCOPYTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence
 
 $(NETTEST_EXE): $(NETTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(NETTEST_OBJ) $(LIBS)
+	$(LD) $@ $(NETTEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(DATETEST_EXE): $(DATETEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(DATETEST_OBJ) $(LIBS)
+	$(LD) $@ $(DATETEST_OBJ) $(LDFLAGS) $(LIBS)
 
 $(HASHTEST_EXE): $(HASHTEST_OBJ) $(NTR_LIB)
-	$(LD) $@ $(HASHTEST_OBJ) $(LIBS) # -lefence
+	$(LD) $@ $(HASHTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence
 
 clean:
 	$(RM) *.o
