diff -Nbru clearsilver-0.5/Makefile clearsilver-0.6/Makefile
--- clearsilver-0.5/Makefile	Fri Mar 15 13:50:52 2002
+++ clearsilver-0.6/Makefile	Thu Apr 25 18:58:13 2002
@@ -13,7 +13,9 @@
 
 OUTDIRS = bin libs
 
-all: output_dir
+all: cs
+
+cs: output_dir
 	@for mdir in $(SUBDIRS); do \
 		$(MAKE) -C $$mdir; \
 	done
@@ -70,14 +72,15 @@
 		mkdir -p $$mdir; \
 	done
 
-CS_DISTDIR = clearsilver-0.4.1
-CS_LABEL = CLEARSILVER-0_4_1
-CS_FILES = LICENSE CS_LICENSE rules.mk Makefile util cs cgi python scripts mod_ecs
+CS_DISTDIR = clearsilver-0.6
+CS_LABEL = CLEARSILVER-0_6_0
+CS_FILES = LICENSE CS_LICENSE rules.mk Makefile util cs cgi python scripts mod_ecs imd
 cs_dist:
 	rm -rf $(CS_DISTDIR)
 	cvs -q tag -F $(CS_LABEL) $(CS_FILES)
 	mkdir -p $(CS_DISTDIR)
 	cvs -z3 -q export -r $(CS_LABEL) -d $(CS_DISTDIR) neotonic
+	-rm -rf $(CS_DISTDIR)/CVS
 	$(MAKE) -C $(CS_DISTDIR) man
 	tar chozf $(CS_DISTDIR).tar.gz $(CS_DISTDIR)
 	
@@ -89,3 +92,7 @@
 	mkdir -p $(TRAKKEN_DISTDIR)
 	cvs -z3 -q export -r $(TRAKKEN_LABEL) -d $(TRAKKEN_DISTDIR) neotonic
 	tar chozf $(TRAKKEN_DISTDIR).tar.gz $(TRAKKEN_DISTDIR)
+
+trakken: cs
+	$(MAKE) -C retrieve
+	$(MAKE) -C trakken
diff -Nbru clearsilver-0.5/cs/Makefile clearsilver-0.6/cs/Makefile
--- clearsilver-0.5/cs/Makefile	Thu Mar 28 01:18:51 2002
+++ clearsilver-0.6/cs/Makefile	Fri Apr 19 19:02:08 2002
@@ -23,7 +23,7 @@
 
 TARGETS = $(CS_LIB) $(CSTEST_EXE) test
 
-CS_TESTS = test.cs test2.cs test3.cs test4.cs test5.cs test6.cs test7.cs test8.cs test9.cs test10.cs test11.cs
+CS_TESTS = test.cs test2.cs test3.cs test4.cs test5.cs test6.cs test7.cs test8.cs test9.cs test10.cs test11.cs test12.cs
 
 all: $(TARGETS)
 
@@ -42,6 +42,7 @@
 		rm -f $$test.gold; \
 		./cstest test.hdf $$test > $$test.gold; \
 	done
+	@echo "Generated Gold Files"
 
 test: $(CSTEST_EXE) $(CS_TESTS)
 	@echo "Running cs regression tests"
diff -Nbru clearsilver-0.5/cs/csparse.c clearsilver-0.6/cs/csparse.c
--- clearsilver-0.5/cs/csparse.c	Thu Mar 28 17:40:56 2002
+++ clearsilver-0.6/cs/csparse.c	Thu Apr 25 18:48:13 2002
@@ -605,6 +605,8 @@
   {
     if (!strcmp (map->name, name))
     {
+      if (map->type == CS_TYPE_VAR)
+      {
       if (c == NULL)
       {
 	return nerr_pass (hdf_set_value (map->value.h, NULL, value));
@@ -615,6 +617,32 @@
 	return nerr_pass (hdf_set_value (map->value.h, c+1, value));
       }
     }
+      else
+      {
+	if (c == NULL)
+	{
+	  char *tmp = NULL;
+	  /* If this is a string, it might be what we're setting,
+	   * ie <?cs set:value = value ?>
+	   */
+	  if (map->type == CS_TYPE_STRING && map->alloc)
+	    tmp = map->value.s;
+	  map->type = CS_TYPE_STRING;
+	  map->alloc = 1;
+	  map->value.s = strdup(value);
+	  if (tmp != NULL) free(tmp);
+	  if (map->value.s == NULL && value != NULL)
+	    return nerr_raise(NERR_NOMEM, 
+		"Unable to allocate memory to set var");
+
+	  return STATUS_OK;
+	}
+	else {
+	  ne_warn("WARNING!! Trying to set sub element '%s' of local variable '%s' which doesn't map to an HDF variable, ignoring", c+1, map->name);
+	  return STATUS_OK;
+	}
+      }
+    }
     map = map->next;
   }
   if (c != NULL) *c = '.';
@@ -2413,18 +2441,14 @@
       if (val.alloc) free(val.s);
     }
   }
-  /* automatically handle cases where the step is backwards */
   if (((step < 0) && (start < end)) || 
       ((step > 0) && (end < start)))
   {
-    x = start;
-    start = end;
-    end = x;
+    iter = 0;
   }
-  if (step == 0)
+  else if (step == 0)
   {
-    if (start == end) iter = 1;
-    else iter = 0;
+    iter = 0;
   }
   else 
   {
@@ -2596,79 +2620,6 @@
 	node->node_num);
 
   return buf;
-}
-
-static char *repr_string_alloc (char *s)
-{
-  int l,x,i;
-  int nl = 0;
-  char *rs;
-
-  if (s == NULL)
-  {
-    return strdup("NULL");
-  }
-
-  l = strlen(s);
-  for (x = 0; x < l; x++)
-  {
-    if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
-    {
-      nl++;
-    }
-    else
-    {
-      if (s[x] == '\n' || s[x] == '\t' || s[x] == '\r' || s[x] == '"' ||
-	  s[x] == '\\')
-      {
-	nl += 2;
-      }
-      else nl += 4;
-    }
-  }
-
-  rs = (char *) malloc ((nl+3) * sizeof(char));
-  if (rs == NULL)
-    return NULL;
-
-  i = 0;
-  rs[i++] = '"';
-  for (x = 0; x < l; x++)
-  {
-    if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
-    {
-      rs[i++] = s[x];
-    }
-    else
-    {
-      rs[i++] = '\\';
-      switch (s[x])
-      {
-	case '\n':
-	  rs[i++] = 'n';
-	  break;
-	case '\t':
-	  rs[i++] = 't';
-	  break;
-	case '\r':
-	  rs[i++] = 'r';
-	  break;
-	case '"':
-	  rs[i++] = '"';
-	  break;
-	case '\\':
-	  rs[i++] = '\\';
-	  break;
-	default:
-	  sprintf(&(rs[i]), "%03o", (s[x] & 0377));
-	  i += 3;
-	  break;
-      }
-    }
-  }
-  rs[i++] = '"';
-  rs[i] = '\0';
-  return rs;
 }
 
 static NEOERR *dump_node_pre_c (CSPARSE *parse, CSTREE *node, FILE *fp)
diff -Nbru clearsilver-0.5/cs/test.hdf clearsilver-0.6/cs/test.hdf
--- clearsilver-0.5/cs/test.hdf	Thu Mar 28 17:40:56 2002
+++ clearsilver-0.6/cs/test.hdf	Fri Apr 19 19:02:08 2002
@@ -79,3 +79,41 @@
 Query.sort = t
 Query.sort_dir = u
 CGI.box.cur.min_box_idx = 1
+
+
+Files.0.Name = Desktop
+Files.0.Type = dir
+Files.0.Sub.0.Name = Bookmarks.html
+Files.0.Sub.0.Type = file
+Files.0.Sub.1.Name = History.txt
+Files.0.Sub.1.Type = file
+Files.0.Sub.2.Name = Resume.doc
+Files.0.Sub.2.Type = file
+Files.0.Sub.3.Name = Favorites
+Files.0.Sub.3.Type = dir
+Files.0.Sub.3.Sub.0.Name = foo
+Files.0.Sub.3.Sub.0.Type = file
+Files.0.Sub.3.Sub.1.Name = bar
+Files.0.Sub.3.Sub.1.Type = file
+Files.0.Sub.3.Sub.2.Name = boo
+Files.0.Sub.3.Sub.2.Type = file
+Files.0.Sub.3.Sub.3.Name = baz
+Files.0.Sub.3.Sub.3.Type = file
+Files.0.Sub.3.Sub.4.Name = faq
+Files.0.Sub.3.Sub.4.Type = file
+Files.0.Sub.3.Sub.5.Name = far
+Files.0.Sub.3.Sub.5.Type = file
+Files.1.Name = .cshrc
+Files.1.Type = file
+Files.2.Name = .login
+Files.2.Type = file
+Files.2.Name = Mail
+Files.2.Type = dir
+Files.2.Sub.0.Name = inbox
+Files.2.Sub.0.Type = file
+Files.2.Sub.1.Name = received
+Files.2.Sub.1.Type = file
+Files.2.Sub.2.Name = sent
+Files.2.Sub.2.Type = file
+Files.2.Sub.2.Name = postponed
+Files.2.Sub.2.Type = file
diff -Nbru clearsilver-0.5/cs/test12.cs clearsilver-0.6/cs/test12.cs
--- clearsilver-0.5/cs/test12.cs	Wed Dec 31 16:00:00 1969
+++ clearsilver-0.6/cs/test12.cs	Fri Apr 19 22:54:42 2002
@@ -0,0 +1,30 @@
+
+
+<?cs def:display_files(files) ?>
+   <ul>
+   <?cs each:file = files ?>
+     <li><?cs var:file.Name ?></li>
+     <?cs if:file.Sub.0.Name ?>
+       <?cs call:display_files(file.Sub) ?>
+     <?cs /if ?>
+   <?cs /each ?>
+   </ul>
+<?cs /def ?>
+
+<?cs call:display_files(Files) ?>
+
+<?cs def:display_files2(files, spc) ?>
+   <?cs # This tests whether we can set a local var ?>
+   <?cs # Also, whether we can set a local var to itself.. ?>
+   <?cs set:spc = spc ?>
+   <?cs each:file = files ?>
+     <?cs var:spc ?><?cs var:file.Name ?><br>
+     <?cs if:file.Sub.0.Name ?>
+       <?cs call:display_files2(file.Sub, spc + "&nbsp;") ?>
+     <?cs /if ?>
+   <?cs /each ?>
+<?cs /def ?>
+
+<?cs set:blank = "" ?>
+<?cs call:display_files2(Files, blank) ?>
+
diff -Nbru clearsilver-0.5/cs/test12.cs.gold clearsilver-0.6/cs/test12.cs.gold
--- clearsilver-0.5/cs/test12.cs.gold	Wed Dec 31 16:00:00 1969
+++ clearsilver-0.6/cs/test12.cs.gold	Fri Apr 19 22:54:42 2002
@@ -0,0 +1,156 @@
+Parsing test12.cs
+
+
+
+
+
+   <ul>
+   
+     <li>Desktop</li>
+     
+       
+   <ul>
+   
+     <li>Bookmarks.html</li>
+     
+   
+     <li>History.txt</li>
+     
+   
+     <li>Resume.doc</li>
+     
+   
+     <li>Favorites</li>
+     
+       
+   <ul>
+   
+     <li>foo</li>
+     
+   
+     <li>bar</li>
+     
+   
+     <li>boo</li>
+     
+   
+     <li>baz</li>
+     
+   
+     <li>faq</li>
+     
+   
+     <li>far</li>
+     
+   
+   </ul>
+
+     
+   
+   </ul>
+
+     
+   
+     <li>.cshrc</li>
+     
+   
+     <li>Mail</li>
+     
+       
+   <ul>
+   
+     <li>inbox</li>
+     
+   
+     <li>received</li>
+     
+   
+     <li>postponed</li>
+     
+   
+   </ul>
+
+     
+   
+   </ul>
+
+
+
+
+
+
+   
+   
+   
+   
+     Desktop<br>
+     
+       
+   
+   
+   
+   
+     &nbsp;Bookmarks.html<br>
+     
+   
+     &nbsp;History.txt<br>
+     
+   
+     &nbsp;Resume.doc<br>
+     
+   
+     &nbsp;Favorites<br>
+     
+       
+   
+   
+   
+   
+     &nbsp;&nbsp;foo<br>
+     
+   
+     &nbsp;&nbsp;bar<br>
+     
+   
+     &nbsp;&nbsp;boo<br>
+     
+   
+     &nbsp;&nbsp;baz<br>
+     
+   
+     &nbsp;&nbsp;faq<br>
+     
+   
+     &nbsp;&nbsp;far<br>
+     
+   
+
+     
+   
+
+     
+   
+     .cshrc<br>
+     
+   
+     Mail<br>
+     
+       
+   
+   
+   
+   
+     &nbsp;inbox<br>
+     
+   
+     &nbsp;received<br>
+     
+   
+     &nbsp;postponed<br>
+     
+   
+
+     
+   
+
+
diff -Nbru clearsilver-0.5/cs/test7.cs clearsilver-0.6/cs/test7.cs
--- clearsilver-0.5/cs/test7.cs	Tue Nov 13 13:58:15 2001
+++ clearsilver-0.6/cs/test7.cs	Fri Apr 19 22:54:42 2002
@@ -12,3 +12,9 @@
 
 broken
 <?cs loop:x = #1, #205, #-2 ?><?cs var:x ?>, <?cs /loop ?>
+
+<?cs def:do_loop(var1, var2, var3) ?>
+  <?cs loop:x = var1, var2, var3 ?><?cs var:x ?>, <?cs /loop ?>
+<?cs /def ?>
+
+<?cs call:do_loop(#1, #20, #2) ?>
diff -Nbru clearsilver-0.5/cs/test7.cs.gold clearsilver-0.6/cs/test7.cs.gold
--- clearsilver-0.5/cs/test7.cs.gold	Tue Nov 13 13:58:15 2001
+++ clearsilver-0.6/cs/test7.cs.gold	Fri Apr 19 22:54:42 2002
@@ -13,3 +13,9 @@
 
 broken
 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 173, 171, 169, 167, 165, 163, 161, 159, 157, 155, 153, 151, 149, 147, 145, 143, 141, 139, 137, 135, 133, 131, 129, 127, 125, 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, 97, 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 
+
+
+
+
+  1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 
+
diff -Nbru clearsilver-0.5/python/neo_util.c clearsilver-0.6/python/neo_util.c
--- clearsilver-0.5/python/neo_util.c	Sat Apr 13 17:49:50 2002
+++ clearsilver-0.6/python/neo_util.c	Thu Apr 25 18:47:20 2002
@@ -193,6 +193,66 @@
   return rv;
 }
 
+static PyObject * p_hdf_get_attr (PyObject *self, PyObject *args)
+{
+  HDFObject *ho = (HDFObject *)self;
+  PyObject *rv, *item;
+  char *name;
+  HDF_ATTR *attr;
+
+  if (!PyArg_ParseTuple(args, "s:getAttrs(name)", &name))
+    return NULL;
+
+  rv = PyList_New(0);
+  if (rv == NULL) return NULL;
+  Py_INCREF(rv);
+  attr = hdf_get_attr (ho->data, name);
+  while (attr != NULL)
+  {
+    item = Py_BuildValue("(s,s)", attr->key, attr->value);
+    if (item == NULL)
+    {
+      Py_DECREF(rv); 
+      return NULL;
+    }
+    if (PyList_Append(rv, item) == -1)
+    {
+      Py_DECREF(rv); 
+      return NULL;
+    }
+    attr = attr->next;
+  }
+  return rv;
+}
+
+static PyObject * p_hdf_obj_attr (PyObject *self, PyObject *args)
+{
+  HDFObject *ho = (HDFObject *)self;
+  PyObject *rv, *item;
+  HDF_ATTR *attr;
+
+  rv = PyList_New(0);
+  if (rv == NULL) return NULL;
+  Py_INCREF(rv);
+  attr = hdf_obj_attr (ho->data);
+  while (attr != NULL)
+  {
+    item = Py_BuildValue("(s,s)", attr->key, attr->value);
+    if (item == NULL)
+    {
+      Py_DECREF(rv); 
+      return NULL;
+    }
+    if (PyList_Append(rv, item) == -1)
+    {
+      Py_DECREF(rv); 
+      return NULL;
+    }
+    attr = attr->next;
+  }
+  return rv;
+}
+
 static PyObject * p_hdf_obj_child (PyObject *self, PyObject *args)
 {
   HDFObject *ho = (HDFObject *)self;
@@ -227,6 +287,23 @@
   return rv;
 }
 
+static PyObject * p_hdf_obj_top (PyObject *self, PyObject *args)
+{
+  HDFObject *ho = (HDFObject *)self;
+  PyObject *rv;
+  HDF *r;
+
+  r = hdf_obj_top (ho->data);
+  if (r == NULL)
+  {
+    rv = Py_None;
+    Py_INCREF(rv);
+    return rv;
+  }
+  rv = p_hdf_to_object (r, 0);
+  return rv;
+}
+
 static PyObject * p_hdf_obj_name (PyObject *self, PyObject *args)
 {
   HDFObject *ho = (HDFObject *)self;
@@ -279,6 +356,36 @@
   return rv;
 }
 
+static PyObject * p_hdf_set_attr (PyObject *self, PyObject *args)
+{
+  HDFObject *ho = (HDFObject *)self;
+  PyObject *rv;
+  char *name, *value, *key;
+  NEOERR *err;
+
+  if (!PyArg_ParseTuple(args, "ssO:setValue(name, key, value)", &name, &key, &rv))
+    return NULL;
+
+  if (PyString_Check(rv))
+  {
+    value = PyString_AsString(rv);
+  } 
+  else if (rv == Py_None)
+  {
+    value = NULL;
+  }
+  else
+  {
+    return PyErr_Format(PyExc_TypeError, "Invalid type for value, expected None or string");
+  }
+  err = hdf_set_attr (ho->data, name, key, value);
+  if (err) return p_neo_error(err); 
+
+  rv = Py_None;
+  Py_INCREF(rv);
+  return rv;
+}
+
 static PyObject * p_hdf_read_file (PyObject *self, PyObject *args)
 {
   HDFObject *ho = (HDFObject *)self;
@@ -368,11 +475,12 @@
   HDFObject *ho = (HDFObject *)self;
   NEOERR *err;
   char *s = NULL;
+  int ignore = 0;
 
-  if (!PyArg_ParseTuple(args, "s:readString(string)", &s))
+  if (!PyArg_ParseTuple(args, "s|i:readString(string)", &s, &ignore))
     return NULL;
 
-  err = hdf_read_string (ho->data, s);
+  err = hdf_read_string_ignore (ho->data, s, ignore);
   if (err) return p_neo_error(err); 
   Py_INCREF (Py_None);
   return Py_None;
@@ -429,11 +537,15 @@
   {"getValue", p_hdf_get_value, METH_VARARGS, NULL},
   {"getObj", p_hdf_get_obj, METH_VARARGS, NULL},
   {"getChild", p_hdf_get_child, METH_VARARGS, NULL},
+  {"getAttrs", p_hdf_get_attr, METH_VARARGS, NULL},
   {"child", p_hdf_obj_child, METH_VARARGS, NULL},
   {"next", p_hdf_obj_next, METH_VARARGS, NULL},
   {"name", p_hdf_obj_name, METH_VARARGS, NULL},
   {"value", p_hdf_obj_value, METH_VARARGS, NULL},
+  {"top", p_hdf_obj_top, METH_VARARGS, NULL},
+  {"attrs", p_hdf_obj_attr, METH_VARARGS, NULL},
   {"setValue", p_hdf_set_value, METH_VARARGS, NULL},
+  {"setAttr", p_hdf_set_attr, METH_VARARGS, NULL},
   {"readFile", p_hdf_read_file, METH_VARARGS, NULL},
   {"writeFile", p_hdf_write_file, METH_VARARGS, NULL},
   {"readString", p_hdf_read_string, METH_VARARGS, NULL},
diff -Nbru clearsilver-0.5/util/neo_hdf.c clearsilver-0.6/util/neo_hdf.c
--- clearsilver-0.5/util/neo_hdf.c	Thu Apr 18 12:24:53 2002
+++ clearsilver-0.6/util/neo_hdf.c	Thu Apr 25 18:47:18 2002
@@ -70,6 +70,21 @@
   return STATUS_OK;
 }
 
+static void _dealloc_hdf_attr(HDF_ATTR **attr)
+{
+  HDF_ATTR *next;
+
+  while ((*attr) != NULL)
+  {
+    next = (*attr)->next;
+    if ((*attr)->key) free((*attr)->key);
+    if ((*attr)->value) free((*attr)->value);
+    free(*attr);
+    *attr = next;
+  }
+  *attr = NULL;
+}
+
 static void _dealloc_hdf (HDF **hdf)
 {
   if (*hdf == NULL) return;
@@ -88,6 +103,10 @@
       free ((*hdf)->value);
     (*hdf)->value = NULL;
   }
+  if ((*hdf)->attr != NULL)
+  {
+    _dealloc_hdf_attr(&((*hdf)->attr));
+  }
   free (*hdf);
   *hdf = NULL;
 }
@@ -267,6 +286,72 @@
   return obj;
 }
 
+HDF_ATTR* hdf_get_attr (HDF *hdf, char *name)
+{
+  HDF *obj;
+  _walk_hdf(hdf, name, &obj);
+  if (obj != NULL) return obj->attr;
+  return NULL;
+}
+
+NEOERR* hdf_set_attr (HDF *hdf, char *name, char *key, char *value)
+{
+  HDF *obj;
+  HDF_ATTR *attr, *last;
+
+  _walk_hdf(hdf, name, &obj);
+  if (obj == NULL) 
+    return nerr_raise(NERR_ASSERT, "Unable to set attribute on none existant node");
+
+  if (obj->attr != NULL)
+  {
+    attr = obj->attr;
+    last = attr;
+    while (attr != NULL)
+    {
+      if (!strcmp(attr->key, key))
+      {
+	if (attr->value) free(attr->value);
+	/* a set of NULL deletes the attr */
+	if (value == NULL)
+	{
+	  if (last == obj->attr)
+	    obj->attr = attr->next;
+	  else
+	    last->next = attr->next;
+	  free(attr->key);
+	  free(attr);
+	  return STATUS_OK;
+	}
+	attr->value = strdup(value);
+	if (attr->value == NULL)
+	  return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
+	return STATUS_OK;
+      }
+      last = attr;
+      attr = attr->next;
+    }
+    last->next = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR));
+    if (last->next == NULL)
+      return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
+    attr = last->next;
+  }
+  else
+  {
+    if (value == NULL) return STATUS_OK;
+    obj->attr = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR));
+    if (obj->attr == NULL)
+      return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
+    attr = obj->attr;
+  }
+  attr->key = strdup(key);
+  attr->value = strdup(value);
+  if (attr->key == NULL || attr->value == NULL)
+    return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
+
+  return STATUS_OK;
+}
+
 HDF* hdf_obj_child (HDF *hdf)
 {
   HDF *obj;
@@ -286,6 +371,18 @@
   return hdf->next;
 }
 
+HDF* hdf_obj_top (HDF *hdf)
+{
+  if (hdf == NULL) return NULL;
+  return hdf->top;
+}
+
+HDF_ATTR* hdf_obj_attr (HDF *hdf)
+{
+  if (hdf == NULL) return NULL;
+  return hdf->attr;
+}
+
 char* hdf_obj_name (HDF *hdf)
 {
   if (hdf == NULL) return NULL;
@@ -306,7 +403,50 @@
   return hdf->value;
 }
 
-NEOERR* _set_value (HDF *hdf, char *name, char *value, int dup, int wf, int link)
+void _merge_attr (HDF_ATTR *dest, HDF_ATTR *src)
+{
+  HDF_ATTR *da, *ld;
+  HDF_ATTR *sa, *ls;
+  BOOL found;
+
+  sa = src;
+  ls = src;
+  while (sa != NULL)
+  {
+    da = dest;
+    ld = da;
+    found = 0;
+    while (da != NULL)
+    {
+      if (!strcmp(da->key, sa->key))
+      {
+	if (da->value) free(da->value);
+	da->value = sa->value;
+	sa->value = NULL;
+	found = 1;
+	break;
+      }
+      ld = da;
+      da = da->next;
+    }
+    if (!found)
+    {
+      ld->next = sa;
+      ls->next = sa->next;
+      if (src == sa) src = sa->next;
+      ld->next->next = NULL;
+      sa = ls->next;
+    }
+    else
+    {
+      ls = sa;
+      sa = sa->next;
+    }
+  }
+  _dealloc_hdf_attr(&src);
+}
+
+NEOERR* _set_value (HDF *hdf, char *name, char *value, int dup, int wf, int link, HDF_ATTR *attr)
 {
   NEOERR *err;
   HDF *hn, *hp, *hs;
@@ -322,6 +462,17 @@
   /* HACK: allow setting of this node by passing an empty name */
   if (name == NULL || name[0] == '\0')
   {
+    /* handle setting attr first */
+    if (hdf->attr == NULL)
+    {
+      hdf->attr = attr;
+    }
+    else
+    {
+      _merge_attr(hdf->attr, attr);
+    }
+    /* if we're setting ourselves to ourselves... */
+    if (hdf->value == value) return STATUS_OK;
     if (hdf->alloc_value)
     {
       free(hdf->value);
@@ -377,6 +528,7 @@
       {
 	err = _alloc_hdf (&hp, n, x, value, dup, wf, hdf->top);
 	if (link) hp->link = 1;
+	hp->attr = attr;
       }
       if (err != STATUS_OK)
 	return nerr_pass (err);
@@ -387,6 +539,16 @@
     }
     else if (s == NULL)
     {
+      /* handle setting attr first */
+      if (hp->attr == NULL)
+      {
+	hp->attr = attr;
+      }
+      else
+      {
+	_merge_attr(hp->attr, attr);
+      }
+      if (hp->value == value) return STATUS_OK;
       if (hp->alloc_value)
       {
 	free(hp->value);
@@ -423,12 +585,17 @@
 
 NEOERR* hdf_set_value (HDF *hdf, char *name, char *value)
 {
-  return nerr_pass(_set_value (hdf, name, value, 1, 1, 0));
+  return nerr_pass(_set_value (hdf, name, value, 1, 1, 0, NULL));
+}
+
+NEOERR* hdf_set_value_attr (HDF *hdf, char *name, char *value, HDF_ATTR *attr)
+{
+  return nerr_pass(_set_value (hdf, name, value, 1, 1, 0, attr));
 }
 
 NEOERR* hdf_set_symlink (HDF *hdf, char *src, char *dest)
 {
-  return nerr_pass(_set_value (hdf, src, dest, 1, 1, 1));
+  return nerr_pass(_set_value (hdf, src, dest, 1, 1, 1, NULL));
 }
 
 NEOERR* hdf_set_int_value (HDF *hdf, char *name, int value)
@@ -436,12 +603,12 @@
   char buf[256];
 
   snprintf (buf, sizeof(buf), "%d", value);
-  return nerr_pass(_set_value (hdf, name, buf, 1, 1, 0));
+  return nerr_pass(_set_value (hdf, name, buf, 1, 1, 0, NULL));
 }
 
 NEOERR* hdf_set_buf (HDF *hdf, char *name, char *value)
 {
-  return nerr_pass(_set_value (hdf, name, value, 0, 1, 0));
+  return nerr_pass(_set_value (hdf, name, value, 0, 1, 0, NULL));
 }
 
 NEOERR* hdf_set_copy (HDF *hdf, char *dest, char *src)
@@ -449,7 +616,7 @@
   HDF *node;
   if ((_walk_hdf(hdf, src, &node) == 0) && (node->value != NULL))
   {
-    return nerr_pass(_set_value (hdf, dest, node->value, 0, 0, 0));
+    return nerr_pass(_set_value (hdf, dest, node->value, 0, 0, 0, NULL));
   }
   return nerr_raise (NERR_NOT_FOUND, "Unable to find %s", src);
 }
@@ -581,6 +748,41 @@
   return STATUS_OK;
 }
 
+/* this will delete any existing attributes from the node */
+static NEOERR * _copy_attrs (HDF *dest, HDF *src)
+{
+  HDF_ATTR *da, *sa;
+
+  sa = src->attr;
+  if (sa == NULL) return STATUS_OK;
+  if (dest->attr != NULL)
+  {
+    _dealloc_hdf_attr(&(dest->attr));
+  }
+  dest->attr = (HDF_ATTR *) calloc (1, sizeof(HDF_ATTR));
+  if (dest->attr == NULL)
+    return nerr_raise(NERR_NOMEM, "Unable to allocate memory for attribute");
+  da = dest->attr;
+  da->key = strdup(sa->key);
+  da->value = strdup(sa->value);
+  if (da->key == NULL || da->value == NULL)
+    return nerr_raise(NERR_NOMEM, "Unable to allocate memory for attribute");
+  sa = sa->next;
+  while (sa != NULL)
+  {
+    da->next = (HDF_ATTR *) calloc (1, sizeof(HDF_ATTR));
+    if (da->next == NULL)
+      return nerr_raise(NERR_NOMEM, "Unable to allocate memory for attribute");
+    da = da->next;
+    da->key = strdup(sa->key);
+    da->value = strdup(sa->value);
+    if (da->key == NULL || da->value == NULL)
+      return nerr_raise(NERR_NOMEM, "Unable to allocate memory for attribute");
+    sa = sa->next;
+  }
+  return STATUS_OK;
+}
+
 static NEOERR * _copy_nodes (HDF *dest, HDF *src)
 {
   NEOERR *err = STATUS_OK;
@@ -632,6 +834,8 @@
       }
     }
     if (err) return nerr_pass(err);
+    err = _copy_attrs(dt, st);
+    if (err) return nerr_pass(err);
     err = _copy_nodes (dt, st);
     if (err) return nerr_pass(err);
     st = st->next;
@@ -646,7 +850,7 @@
 
   if (_walk_hdf(dest, name, &node) == -1)
   {
-    err = _set_value (dest, name, NULL, 0, 0, 0);
+    err = _set_value (dest, name, NULL, 0, 0, 0, NULL);
     if (err) return nerr_pass (err);
     if (_walk_hdf(dest, name, &node) == -1)
       return nerr_raise(NERR_ASSERT, "Um, this shouldn't happen");
@@ -654,6 +858,8 @@
   return nerr_pass (_copy_nodes (node, src));
 }
 
+/* BUG: currently, this only prints something if there is a value...
+ * but we now allow attributes on nodes with no value... */
 NEOERR* hdf_dump(HDF *hdf, char *prefix)
 {
   char *p;
@@ -664,12 +870,37 @@
     if (hdf->link) op = ':';
     if (prefix)
     {
-      printf("%s.%s %c %s\n", prefix, hdf->name, op, hdf->value);
+      printf("%s.%s ", prefix, hdf->name);
     }
     else
     {
-      printf("%s %c %s\n", hdf->name, op, hdf->value);
+      printf("%s ", hdf->name);
     }
+    if (hdf->attr)
+    {
+      HDF_ATTR *attr = hdf->attr;
+      char *v = NULL;
+      fputs("[", stdout);
+      while (attr != NULL)
+      {
+	if (attr->value == NULL || !strcmp(attr->value, "1"))
+	  printf("%s", attr->key);
+	else
+	{
+	  v = repr_string_alloc(attr->value);
+
+	  if (v == NULL) 
+	    return nerr_raise(NERR_NOMEM, "Unable to repr attr %s value %s", attr->key, attr->value);
+	  printf("%s=%s", attr->key, v);
+	  free(v);
+	}
+	if (attr->next)
+	  fputs(", ", stdout);
+	attr = attr->next;
+      }
+      printf("] ");
+    }
+    printf("%c %s\n", op, hdf->value);
   }
   if (hdf->child)
   {
@@ -695,9 +926,11 @@
 NEOERR* hdf_dump_str(HDF *hdf, char *prefix, int compact, STRING *str)
 {
   NEOERR *err;
-  char *p;
-  char op = '=';
+  char *p, op;
 
+  while (hdf != NULL)
+  {
+    op = '=';
   if (hdf->value)
   {
     if (hdf->link) op = ':';
@@ -710,6 +943,37 @@
       err = string_append (str, hdf->name);
     }
     if (err) return nerr_pass (err);
+      if (hdf->attr)
+      {
+	HDF_ATTR *attr = hdf->attr;
+	char *v = NULL;
+
+	err = string_append(str, " [");
+	if (err) return nerr_pass(err);
+	while (attr != NULL)
+	{
+	  if (attr->value == NULL || !strcmp(attr->value, "1"))
+	    err = string_append(str, attr->key);
+	  else
+	  {
+	    v = repr_string_alloc(attr->value);
+
+	    if (v == NULL) 
+	      return nerr_raise(NERR_NOMEM, "Unable to repr attr %s value %s", attr->key, attr->value);
+	    err = string_appendf(str, "%s=%s", attr->key, v);
+	    free(v);
+	  }
+	  if (err) return nerr_pass(err);
+	  if (attr->next)
+	  {
+	    err = string_append(str, ", ");
+	    if (err) return nerr_pass(err);
+	  }
+	  attr = attr->next;
+	}
+	err = string_append(str, "] ");
+	if (err) return nerr_pass(err);
+      }
     if (strchr (hdf->value, '\n'))
     {
       if (hdf->value[strlen(hdf->value)-1] != '\n')
@@ -749,10 +1013,7 @@
     }
     if (err) return nerr_pass (err);
   }
-  if (hdf->next)
-  {
-    err = hdf_dump_str (hdf->next, prefix, compact, str);
-    if (err) return nerr_pass (err);
+    hdf = hdf->next;
   }
   return STATUS_OK;
 }
@@ -760,26 +1021,54 @@
 NEOERR* hdf_dump_format (HDF *hdf, int lvl, FILE *fp)
 {
   char prefix[256];
-  char op = '=';
+  char op;
 
   memset(prefix, ' ', 256);
   if (lvl > 127)
     lvl = 127;
   prefix[lvl*2] = '\0';
 
+  while (hdf != NULL)
+  {
+    op = '=';
   if (hdf->value)
   {
     if (hdf->link) op = ':';
+      fprintf(fp, "%s%s", prefix, hdf->name);
+      if (hdf->attr)
+      {
+	HDF_ATTR *attr = hdf->attr;
+	char *v = NULL;
+	fputs("[", fp);
+	while (attr != NULL)
+	{
+	  if (attr->value == NULL || !strcmp(attr->value, "1"))
+	    fprintf(fp, "%s", attr->key);
+	  else
+	  {
+	    v = repr_string_alloc(attr->value);
+
+	    if (v == NULL) 
+	      return nerr_raise(NERR_NOMEM, "Unable to repr attr %s value %s", attr->key, attr->value);
+	    fprintf(fp, "%s=%s", attr->key, v);
+	    free(v);
+	  }
+	  if (attr->next)
+	    fputs(", ", fp);
+	  attr = attr->next;
+	}
+	fputs("] ", fp);
+      }
     if (strchr (hdf->value, '\n'))
     {
       if (hdf->value[strlen(hdf->value)-1] != '\n')
-	fprintf(fp, "%s%s << EOM\n%s\nEOM\n", prefix, hdf->name, hdf->value);
+	  fprintf(fp, " << EOM\n%s\nEOM\n", hdf->value);
       else
-	fprintf(fp, "%s%s << EOM\n%sEOM\n", prefix, hdf->name, hdf->value);
+	  fprintf(fp, " << EOM\n%sEOM\n", hdf->value);
     }
     else
     {
-      fprintf(fp, "%s%s %c %s\n", prefix, hdf->name, op, hdf->value);
+	fprintf(fp, " %c %s\n", op, hdf->value);
     }
   }
   if (hdf->child)
@@ -795,9 +1084,7 @@
       hdf_dump_format(hdf->child, lvl, fp);
     }
   }
-  if (hdf->next)
-  {
-    hdf_dump_format(hdf->next, lvl, fp);
+    hdf = hdf->next;
   }
   return STATUS_OK;
 }
@@ -857,21 +1144,158 @@
   return x;
 }
 
-static NEOERR* _hdf_read_string (HDF *hdf, char **str, int *line)
+/* attributes are of the form [key1, key2, key3=value, key4="repr"] */
+static NEOERR* parse_attr(char **str, HDF_ATTR **attr)
+{
+  NEOERR *err = STATUS_OK;
+  char *s = *str;
+  char save = '\0';
+  char *k, *v;
+  STRING buf;
+  char c;
+  HDF_ATTR *ha, *hal = NULL;
+
+  *attr = NULL;
+
+  string_init(&buf);
+  while (*s && *s != ']')
+  {
+    k = s;
+    v = NULL;
+    while (*s && *s != '=' && *s != ',' && *s != ']') s++;
+    if (*s == '\0')
+    {
+      _dealloc_hdf_attr(attr);
+      return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str);
+    }
+    if (*s == '=')
+    {
+      *s = '\0';
+      s++;
+      SKIPWS(s);
+      if (*s == '"')
+      {
+	s++;
+	while (*s && *s != '"') 
+	{
+	  if (*s == '\\')
+	  {
+	    if (isdigit(*(s+1)))
+	    {
+	      s++;
+	      c = *s - '0';
+	      if (isdigit(*(s+1)))
+	      {
+		s++;
+		c = (c * 8) + (*s - '0');
+		if (isdigit(*(s+1)))
+		{
+		  s++;
+		  c = (c * 8) + (*s - '0');
+		}
+	      }
+	    }
+	    else
+	    {
+	      s++;
+	      if (*s == 'n') c = '\n';
+	      else if (*s == 't') c = '\t';
+	      else if (*s == 'r') c = '\r';
+	      else c = *s;
+	    }
+	    err = string_append_char(&buf, c);
+	  }
+	  else
+	  {
+	    err = string_append_char(&buf, *s);
+	  }
+	  if (err)
+	  {
+	    string_clear(&buf);
+	    _dealloc_hdf_attr(attr);
+	    return nerr_pass(err);
+	  }
+	  s++;
+	}
+	if (*s == '\0')
+	{
+	  _dealloc_hdf_attr(attr);
+	  string_clear(&buf);
+	  return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str);
+	}
+	s++;
+	v = buf.buf;
+      }
+      else
+      {
+	v = s;
+	while (*s && *s != ' ' && *s != ',' && *s != ']') s++;
+	if (*s == '\0')
+	{
+	  _dealloc_hdf_attr(attr);
+	  return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str);
+	}
+	save = *s;
+	*s = '\0';
+	v = neos_strip(v);
+      }
+    }
+    else
+    {
+      save = *s;
+      *s = '\0';
+      v = "1";
+    }
+    ha = (HDF_ATTR*) calloc (1, sizeof(HDF_ATTR));
+    if (ha == NULL)
+    {
+      _dealloc_hdf_attr(attr);
+      string_clear(&buf);
+      return nerr_raise(NERR_NOMEM, "Unable to load attributes: %s", s);
+    }
+    if (*attr == NULL) *attr = ha;
+    ha->key = strdup(neos_strip(k));
+    ha->value = strdup(v);
+    if (ha->key == NULL || ha->value == NULL)
+    {
+      _dealloc_hdf_attr(attr);
+      string_clear(&buf);
+      return nerr_raise(NERR_NOMEM, "Unable to load attributes: %s", s);
+    }
+    if (hal != NULL) hal->next = ha;
+    hal = ha;
+    string_clear(&buf);
+    if (save) *s = save;
+    SKIPWS(s);
+    if (*s == ',') s++;
+  }
+  if (*s == '\0')
+  {
+    _dealloc_hdf_attr(attr);
+    return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str);
+  }
+  *str = s+1;
+  return STATUS_OK;
+}
+
+static NEOERR* _hdf_read_string (HDF *hdf, char **str, int *line, int ignore)
 {
   NEOERR *err;
   HDF *lower;
   char buf[4096];
   char *s;
   char *name, *value;
+  HDF_ATTR *attr = NULL;
 
   while (_copy_line(str, buf, sizeof(buf)) != 0)
   {
+    attr = NULL;
     (*line)++;
     s = buf;
     SKIPWS(s);
     if (!strncmp(s, "#include ", 9))
     {
+      if (!ignore)
       return nerr_raise (NERR_PARSE, "[%d]: #include not supported in string parse", *line);
     }
     else if (s[0] == '#')
@@ -894,21 +1318,24 @@
       /* Valid hdf name is [0-9a-zA-Z_.]+ */
       name = s;
       while (*s && (isalnum(*s) || *s == '_' || *s == '.')) s++;
-      /*
-      if (*s != '\0')
-      {
-	*s++ = '\0';
-      }
-      */
       SKIPWS(s);
 
+      if (s[0] == '[') /* attributes */
+      {
+	*s = '\0';
+	name = neos_strip(name);
+	s++;
+	err = parse_attr(&s, &attr);
+	if (err) return nerr_pass_ctx(err, "In String %d", *line);
+	SKIPWS(s);
+      }
       if (s[0] == '=') /* assignment */
       {
 	*s = '\0';
 	name = neos_strip(name);
 	s++;
 	value = neos_strip(s);
-	err = hdf_set_value (hdf, name, value);
+	err = _set_value (hdf, name, value, 1, 1, 0, attr);
 	if (err != STATUS_OK)
 	  return nerr_pass_ctx(err, "In String %d", *line);
       }
@@ -918,7 +1345,7 @@
 	name = neos_strip(name);
 	s++;
 	value = neos_strip(s);
-	err = hdf_set_symlink (hdf, name, value);
+	err = _set_value (hdf, name, value, 1, 1, 1, attr);
 	if (err != STATUS_OK)
 	  return nerr_pass_ctx(err, "In string %d", *line);
       }
@@ -929,12 +1356,16 @@
 	lower = hdf_get_obj (hdf, name);
 	if (lower == NULL)
 	{
-	  err = hdf_set_value (hdf, name, NULL);
+	  err = _set_value (hdf, name, NULL, 1, 1, 0, attr);
 	  if (err != STATUS_OK) 
 	    return nerr_pass_ctx(err, "In string %d", *line);
 	  lower = hdf_get_obj (hdf, name);
 	}
-	err = _hdf_read_string (lower, str, line);
+	else
+	{
+	  err = _set_value (lower, NULL, lower->value, 1, 1, 0, attr);
+	}
+	err = _hdf_read_string (lower, str, line, ignore);
 	if (err != STATUS_OK) 
 	  return nerr_pass_ctx(err, "In string %d", *line);
       }
@@ -977,7 +1408,7 @@
 		  *line, name, mmax);
 	  }
 	}
-	err = hdf_set_buf(hdf, name, m);
+	err = _set_value (hdf, name, m, 0, 1, 0, attr);
 	if (err != STATUS_OK)
 	{
 	  free (m);
@@ -998,7 +1429,13 @@
 NEOERR * hdf_read_string (HDF *hdf, char *str)
 {
   int line = 0;
-  return nerr_pass (_hdf_read_string (hdf, &str, &line));
+  return nerr_pass (_hdf_read_string (hdf, &str, &line, 0));
+}
+
+NEOERR * hdf_read_string_ignore (HDF *hdf, char *str, int ignore)
+{
+  int line = 0;
+  return nerr_pass (_hdf_read_string (hdf, &str, &line, ignore));
 }
 
 static int count_newlines (char *s)
@@ -1020,6 +1457,7 @@
   NEOERR *err;
   STRING str;
   HDF *lower;
+  HDF_ATTR *attr = NULL;
   char buf[4096];
   char *s;
   char *name, *value;
@@ -1030,6 +1468,7 @@
   if (err) return nerr_pass(err);
   while (str.len != 0)
   {
+    attr = NULL;
     (*line)++;
     s = str.buf;
     SKIPWS(s);
@@ -1067,21 +1506,24 @@
       /* Valid hdf name is [0-9a-zA-Z_.]+ */
       name = s;
       while (*s && (isalnum(*s) || *s == '_' || *s == '.')) s++;
-      /*
-      if (*s != '\0')
-      {
-	*s++ = '\0';
-      }
-      */
       SKIPWS(s);
 
+      if (s[0] == '[') /* attributes */
+      {
+	*s = '\0';
+	name = neos_strip(name);
+	s++;
+	err = parse_attr(&s, &attr);
+	if (err) return nerr_pass_ctx(err, "In file %s:%d", path, *line);
+	SKIPWS(s);
+      }
       if (s[0] == '=') /* assignment */
       {
 	*s = '\0';
 	name = neos_strip(name);
 	s++;
 	value = neos_strip(s);
-	err = hdf_set_value (hdf, name, value);
+	err = _set_value (hdf, name, value, 1, 1, 0, attr);
 	if (err != STATUS_OK)
 	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
       }
@@ -1091,7 +1533,7 @@
 	name = neos_strip(name);
 	s++;
 	value = neos_strip(s);
-	err = hdf_set_symlink (hdf, name, value);
+	err = _set_value (hdf, name, value, 1, 1, 1, attr);
 	if (err != STATUS_OK)
 	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
       }
@@ -1102,11 +1544,15 @@
 	lower = hdf_get_obj (hdf, name);
 	if (lower == NULL)
 	{
-	  err = hdf_set_value (hdf, name, NULL);
+	  err = _set_value (hdf, name, NULL, 1, 1, 0, attr);
 	  if (err != STATUS_OK) 
 	    return nerr_pass_ctx(err, "In file %s:%d", path, *line);
 	  lower = hdf_get_obj (hdf, name);
 	}
+	else
+	{
+	  err = _set_value (lower, NULL, lower->value, 1, 1, 0, attr);
+	}
 	err = hdf_read_file_fp(lower, fp, path, line);
 	if (err != STATUS_OK) 
 	  return nerr_pass_ctx(err, "In file %s:%d", path, *line);
@@ -1151,7 +1597,7 @@
 		  path, *line, name, mmax);
 	  }
 	}
-	err = hdf_set_buf(hdf, name, m);
+	err = _set_value (hdf, name, m, 0, 1, 0, attr);
 	if (err != STATUS_OK)
 	{
 	  free (m);
diff -Nbru clearsilver-0.5/util/neo_hdf.h clearsilver-0.6/util/neo_hdf.h
--- clearsilver-0.5/util/neo_hdf.h	Mon Apr 15 17:37:10 2002
+++ clearsilver-0.6/util/neo_hdf.h	Thu Apr 25 18:47:18 2002
@@ -17,6 +17,13 @@
 #include "neo_err.h"
 #include "ulist.h"
 
+typedef struct _attr
+{
+  char *key;
+  char *value;
+  struct _attr *next;
+} HDF_ATTR;
+
 typedef struct _hdf
 {
   int link;
@@ -24,6 +31,7 @@
   char *name;
   int name_len;
   char *value;
+  struct _attr *attr;
   struct _hdf *top;
   struct _hdf *next;
   struct _hdf *child;
@@ -38,8 +46,12 @@
 
 HDF* hdf_get_obj (HDF *hdf, char *name);
 HDF* hdf_get_child (HDF *hdf, char *name);
+HDF_ATTR* hdf_get_attr (HDF *hdf, char *name);
+NEOERR* hdf_set_attr (HDF *hdf, char *name, char *key, char *value);
 HDF* hdf_obj_child (HDF *hdf);
 HDF* hdf_obj_next (HDF *hdf);
+HDF* hdf_obj_top (HDF *hdf);
+HDF_ATTR* hdf_obj_attr (HDF *hdf);
 char* hdf_obj_name (HDF *hdf);
 char* hdf_obj_value (HDF *hdf);
 
@@ -53,8 +65,7 @@
 /*
  * Function: hdf_sort_obj - sort the children of an HDF node 
  * Description: hdf_sort_obj will sort the children of an HDF node,
- *              based on the given comparison function.  The current
- *              implementation uses a bubble sort, so be warned.
+ *              based on the given comparison function.
  * Input: h - HDF node
  *        compareFunc - function which returns 1,0,-1 depending on some 
  *                      criteria.  Given two children of h
@@ -67,6 +78,7 @@
 NEOERR* hdf_write_file (HDF *hdf, char *path);
 
 NEOERR* hdf_read_string (HDF *hdf, char *s);
+NEOERR* hdf_read_string_ignore (HDF *hdf, char *s, int ignore);
 NEOERR* hdf_write_string (HDF *hdf, char **s);
 
 NEOERR* hdf_dump (HDF *hdf, char *prefix);
diff -Nbru clearsilver-0.5/util/neo_str.c clearsilver-0.6/util/neo_str.c
--- clearsilver-0.5/util/neo_str.c	Thu Apr 11 18:10:59 2002
+++ clearsilver-0.6/util/neo_str.c	Thu Apr 25 18:47:18 2002
@@ -415,3 +415,76 @@
   if (i && o) s[o] = '\0';
   return s;
 }
+
+char *repr_string_alloc (char *s)
+{
+  int l,x,i;
+  int nl = 0;
+  char *rs;
+
+  if (s == NULL)
+  {
+    return strdup("NULL");
+  }
+
+  l = strlen(s);
+  for (x = 0; x < l; x++)
+  {
+    if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
+    {
+      nl++;
+    }
+    else
+    {
+      if (s[x] == '\n' || s[x] == '\t' || s[x] == '\r' || s[x] == '"' ||
+	  s[x] == '\\')
+      {
+	nl += 2;
+      }
+      else nl += 4;
+    }
+  }
+
+  rs = (char *) malloc ((nl+3) * sizeof(char));
+  if (rs == NULL)
+    return NULL;
+
+  i = 0;
+  rs[i++] = '"';
+  for (x = 0; x < l; x++)
+  {
+    if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
+    {
+      rs[i++] = s[x];
+    }
+    else
+    {
+      rs[i++] = '\\';
+      switch (s[x])
+      {
+	case '\n':
+	  rs[i++] = 'n';
+	  break;
+	case '\t':
+	  rs[i++] = 't';
+	  break;
+	case '\r':
+	  rs[i++] = 'r';
+	  break;
+	case '"':
+	  rs[i++] = '"';
+	  break;
+	case '\\':
+	  rs[i++] = '\\';
+	  break;
+	default:
+	  sprintf(&(rs[i]), "%03o", (s[x] & 0377));
+	  i += 3;
+	  break;
+      }
+    }
+  }
+  rs[i++] = '"';
+  rs[i] = '\0';
+  return rs;
+}
diff -Nbru clearsilver-0.5/util/neo_str.h clearsilver-0.6/util/neo_str.h
--- clearsilver-0.5/util/neo_str.h	Thu Apr 11 18:10:59 2002
+++ clearsilver-0.6/util/neo_str.h	Thu Apr 25 18:47:18 2002
@@ -63,6 +63,8 @@
 NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, char *escape, char **esc);
 UINT8 *neos_unescape (UINT8 *s, int buflen, char esc_char);
 
+char *repr_string_alloc (char *s);
+
 __END_DECLS
 
 #endif /* __NEO_STR_H_ */
diff -Nbru clearsilver-0.5/util/test/hdftest.c clearsilver-0.6/util/test/hdftest.c
--- clearsilver-0.5/util/test/hdftest.c	Mon Apr 15 17:37:11 2002
+++ clearsilver-0.6/util/test/hdftest.c	Thu Apr 25 18:47:17 2002
@@ -82,6 +82,31 @@
     nerr_log_error(err);
     return -1;
   }
+  err = hdf_set_attr (hdf, "Beware.The.Butter", "Lang", "en");
+  if (err != STATUS_OK) 
+  {
+    nerr_log_error(err);
+    return -1;
+  }
+  err = hdf_set_attr (hdf, "Beware.The.Butter", "Lang", "1");
+  if (err != STATUS_OK) 
+  {
+    nerr_log_error(err);
+    return -1;
+  }
+  err = hdf_set_attr (hdf, "Beware.The.Butter", "Lang", NULL);
+  if (err != STATUS_OK) 
+  {
+    nerr_log_error(err);
+    return -1;
+  }
+
+  err = hdf_read_file (hdf, "test.hdf");
+  if (err != STATUS_OK) 
+  {
+    nerr_log_error(err);
+    return -1;
+  }
   hdf_dump(hdf, NULL);
 
 
@@ -108,13 +133,6 @@
       nerr_log_error(err);
       return -1;
     }
-  }
-
-  err = hdf_read_file (hdf, "test.hdf");
-  if (err != STATUS_OK) 
-  {
-    nerr_log_error(err);
-    return -1;
   }
 
   tstart = ne_timef();
diff -Nbru clearsilver-0.5/util/test/test.hdf clearsilver-0.6/util/test/test.hdf
--- clearsilver-0.5/util/test/test.hdf	Fri Dec 22 00:31:27 2000
+++ clearsilver-0.6/util/test/test.hdf	Thu Apr 25 18:47:17 2002
@@ -7,13 +7,17 @@
 Test.Embedded {
   Foo = Bar
   Bar {
-    Baz = Boom
+    Baz [Lang] = Boom
   }
-  Bar = Barn
-  bar = Door
+  Bar [Lang=en] = Barn
+  bar [tag="help me"]= Door
   Bar2 {
     Born = Again
   }
+  Attr1 [tag="baseball",fielder=catcher,basehit] = 1
+  Attr2 [tag="baseball", fielder=catcher,basehit] = 1
+  Attr3 [tag="baseball",fielder=catcher, basehit] = 1
+  Attr4 [tag="baseball", fielder=catcher, basehit] = 1
 }
 
 #include "test2.hdf"
