From 3756b83b8d5099ab2bd78db010d25320358c7fc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?=
 <grubba@grubba.org>
Date: Wed, 16 Nov 2011 22:11:50 +0100
Subject: [PATCH] Refdoc: Moved make_html and tree-split-autodoc to
 Tools.Standalone.

---
 .../Standalone.pmod/autodoc_to_html.pike      | 1442 +++++++++++++++++
 .../autodoc_to_split_html.pike                |  775 +++++++++
 refdoc/presentation/make_html.pike            | 1376 +---------------
 refdoc/presentation/tree-split-autodoc.pike   |  729 +--------
 4 files changed, 2223 insertions(+), 2099 deletions(-)
 create mode 100644 lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike
 create mode 100644 lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike

diff --git a/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike
new file mode 100644
index 0000000000..db295dbd68
--- /dev/null
+++ b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike
@@ -0,0 +1,1442 @@
+//! AutoDoc XML to HTML converter.
+//!
+//! @seealso
+//!    @[tree_split]
+
+constant description = "AutoDoc XML to HTML converter.";
+
+#define Node Parser.XML.Tree.SimpleNode
+#define XML_COMMENT Parser.XML.Tree.XML_COMMENT
+#define XML_ELEMENT Parser.XML.Tree.XML_ELEMENT
+#define XML_TEXT Parser.XML.Tree.XML_TEXT
+#define DEBUG
+
+Tools.AutoDoc.Flags flags = Tools.AutoDoc.FLAG_NORMAL;
+
+int verbosity = Tools.AutoDoc.FLAG_VERBOSE; //NORMAL;
+
+function resolve_reference; 
+string image_path = "images/";
+string dest_path;
+string default_ns;
+
+//! Layout template headers and trailers.
+mapping lay = ([
+ "docgroup" : "\n\n<hr clear='all' size='1' noshadow='noshadow' />\n<dl>",
+ "_docgroup" : "</dl>\n",
+ "ndochead" : "<dd><p>",
+ "_ndochead" : "</p></dd>\n",
+
+ "dochead" : "\n<dt style='font-family: sans-serif'>",
+ "_dochead" : "</dt>\n",
+ "typehead" : "\n<dt style='font-family: sans-serif'>",
+ "_typehead" : "</dt>\n",
+ "docbody" : "<dd style='font-family: sans-serif'>",
+ "_docbody" : "</dd>",
+ "fixmehead" : "<dt style='font-family: sans-serif; color: red'>",
+ "_fixmehead" : "</dt>\n",
+ "fixmebody" : "<dd style='font-family: sans-serif; color: red'>",
+ "_fixmebody" : "</dd>",
+
+ "parameter" : "<tt><font color='#8000F0'>",
+ "_parameter" : "</font></tt>",
+ "example" : "<dd><pre>",
+ "_example" : "</pre></dd>",
+
+ "pre" : "<font face='courier'><pre>",
+ "_pre" : "</pre></font>",
+ "code" : "<font face='courier'><pre><code>",
+ "_code" : "</code></pre></font>",
+ "expr" : "<font face='courier'><code>",
+ "_expr" : "</code></font>",
+
+]);
+
+//! Path to where in the destination filesystem the images are.
+string image_prefix()
+{
+  return image_path;
+} 
+
+class Position {
+  string file;
+  int line;
+
+  void update(Node n) {
+    mapping m = n->get_attributes();
+    if(!m->file || !m["first-line"]) {
+      werror("Missing attribute in source-position element. %O\n", m);
+      return;
+    }
+    file = m->file;
+    line = (int) m["first-line"];
+  }
+
+  string get() {
+    return file + ":" + line;
+  }
+}
+
+Position position = Position();
+
+string quote(string in) {
+  if(in-" "-"\t"=="") return "";
+  if(String.trim_all_whites(in)=="") return "\n";
+  return Parser.XML.Tree.text_quote(in);
+}
+
+Node get_first_element(Node n) {
+  foreach(n->get_children(), Node c)
+    if(c->get_node_type()==XML_ELEMENT) {
+      if(c->get_any_name()!="source-position")
+	return c;
+      else
+	position->update(c);
+    }
+  error( "Node had no element child.\n" );
+}
+
+int section, subsection;
+
+string low_parse_chapter(Node n, int chapter) {
+  string ret = "";
+  Node dummy = Node(XML_ELEMENT, "dummy", ([]), "");
+  foreach(n->get_elements(), Node c)
+    switch(c->get_any_name()) {
+
+    case "p":
+      dummy->replace_children( ({ c }) );
+      ret += parse_text(dummy);
+      break;
+
+    case "example":
+      ret += "<p><pre>" + c->value_of_node() + "</pre></p>\n";
+      break;
+
+    case "ul":
+    case "ol":
+      ret += (string)c;
+      break;
+
+    case "dl":
+      ret += "<dl>\n";
+      foreach(c->get_elements(), Node cc) {
+	string tag = cc->get_any_name();
+	if(tag!="dt" && tag!="dd")
+	  error("dl contained element %O.\n", tag);
+	ret += "<" + tag + ">" + parse_text(cc) + "</" + tag + ">\n";
+      }
+      ret += "</dl>";
+      break;
+
+    case "matrix":
+      ret += layout_matrix( map(c->get_elements("r")->get_elements("c"), map, parse_text) );
+      break;
+
+    case "section":
+      if(section)
+	error("Section inside section.\n");
+      if(subsection)
+	error("Section inside subsection.\n");
+      section = (int)c->get_attributes()->number;
+      ret += "</dd>\n<dt><a name='" + section + "'></a>\n"
+	"<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+	"<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; " + chapter + "." + section +
+	". " + quote(c->get_attributes()->title ||
+		     // The following for bug compat.
+		     c->get_attributes()->name) +
+	"</font></td></tr></table><br />\n"
+	"</dt>\n<dd>";
+      ret += low_parse_chapter(c, chapter);
+      section = 0;
+      break;
+
+    case "subsection":
+      if(!section)
+	error("Subsection outside section.\n");
+      if(subsection)
+	error("Subsection inside subsection.\n");
+      subsection = (int)c->get_attributes()->number;
+      ret += "</dd><dt>"
+	"<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+	"<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; " + chapter + "." + section + "." + subsection +
+	". " + quote(c->get_attributes()->title) +
+	"</font></td></tr></table><br />\n"
+	"</dt><dd>";
+      ret += low_parse_chapter(c, chapter);
+      subsection = 0;
+      break;
+
+    case "docgroup":
+      ret += parse_docgroup(c);
+      break;
+
+    case "autodoc":
+      ret += parse_autodoc(c);
+      break;
+
+    case "namespace":
+      ret += parse_namespace(c);
+      break;
+
+    case "module":
+      ret += parse_module(c);
+      break;
+
+    case "class":
+      ret += parse_class(c);
+      break;
+
+    case "enum":
+      ret += parse_enum(c);
+      break;
+
+    default:
+      if (!(flags & Tools.AutoDoc.FLAG_KEEP_GOING)) {
+	error("Unknown element %O.\n", c->get_any_name());
+      }
+      break;
+    }
+  return ret;
+}
+
+string parse_chapter(Node n, void|int noheader) {
+  string ret = "";
+  if(!noheader) {
+    ret += "<dl><dt>"
+      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; ";
+    if(n->get_attributes()->number)
+      ret += n->get_attributes()->number + ". ";
+    ret += quote(n->get_attributes()->title) +
+      "</font></td></tr></table><br />\n"
+      "</dt><dd>";
+  }
+
+  ret += low_parse_chapter(n, (int)n->get_attributes()->number);
+
+  if(!noheader)
+    ret = ret + "</dd></dl>"; 
+
+  return ret;
+}
+
+string parse_autodoc(Node n)
+{
+  string ret ="";
+
+  mapping m = n->get_attributes();
+
+  Node c = n->get_first_element("doc");
+  if(c)
+    ret += "<dl>" + parse_doc(c) + "</dl>";
+
+  if((sizeof(n->get_elements("doc"))>1) &&
+     ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+      Tools.AutoDoc.FLAG_DEBUG)) {
+    error( "More than one doc element in autodoc node.\n" );
+  }
+
+  ret += parse_children(n, "docgroup", parse_docgroup);
+  ret += parse_children(n, "namespace", parse_namespace);
+
+  return ret;
+}
+
+string parse_namespace(Node n, void|int noheader)
+{
+  string ret = "";
+
+  mapping m = n->get_attributes();
+  int(0..1) header = !noheader && !(m->hidden) && m->name!=default_ns;
+  if(header)
+    ret += "<dl><dt>"
+      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; Namespace <b>" +
+      m->name + "::</b></font></td></tr></table><br />\n"
+      "</dt><dd>";
+
+  Node c = n->get_first_element("doc");
+  if(c)
+    ret += "<dl>" + parse_doc(c) + "</dl>";
+
+  if((sizeof(n->get_elements("doc"))>1) &&
+     ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+      Tools.AutoDoc.FLAG_DEBUG)) {
+    error( "More than one doc element in namespace node.\n" );
+  }
+
+  ret += parse_children(n, "docgroup", parse_docgroup, noheader);
+  ret += parse_children(n, "enum", parse_enum, noheader);
+  ret += parse_children(n, "class", parse_class, noheader);
+  ret += parse_children(n, "module", parse_module, noheader);
+
+  if(header)
+    ret += "</dd></dl>"; 
+
+  return ret;
+}
+
+string parse_module(Node n, void|int noheader) {
+  string ret ="";
+
+  mapping m = n->get_attributes();
+  int(0..1) header = !noheader && !(m->hidden);
+  if(header)
+    ret += "<dl><dt>"
+      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; Module <b>" +
+      m->class_path + m->name + "</b></font></td></tr></table><br />\n"
+      "</dt><dd>";
+
+  Node c = n->get_first_element("doc");
+  if(c)
+    ret += "<dl>" + parse_doc(c) + "</dl>";
+
+  if((sizeof(n->get_elements("doc"))>1) &&
+     ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+      Tools.AutoDoc.FLAG_DEBUG)) {
+    error( "More than one doc element in module node.\n" );
+  }
+
+  ret += parse_children(n, "docgroup", parse_docgroup, noheader);
+  ret += parse_children(n, "enum", parse_enum, noheader);
+  ret += parse_children(n, "class", parse_class, noheader);
+  ret += parse_children(n, "module", parse_module, noheader);
+
+  if(header)
+    ret += "</dd></dl>"; 
+
+  return ret;
+}
+
+string parse_class(Node n, void|int noheader) {
+  string ret ="";
+  if(!noheader)
+    ret += "<dl><dt>"
+      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; CLASS <b><font color='#005080'>" +
+      n->get_attributes()->class_path + n->get_attributes()->name +
+      "</font></b></font></td></tr></table><br />\n"
+      "</dt><dd>";
+
+  Node c = n->get_first_element("doc");
+
+  if(c)
+    ret += "<dl>" + parse_doc(c) + "</dl>";
+
+  if((sizeof(n->get_elements("doc"))>1) &&
+     ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+      Tools.AutoDoc.FLAG_DEBUG)) {
+    error( "More than one doc element in class node.\n" );
+  }
+
+  ret += parse_children(n, "docgroup", parse_docgroup);
+  ret += parse_children(n, "enum", parse_enum);
+  ret += parse_children(n, "class", parse_class, noheader);
+
+  if(!noheader)
+    ret += "</dd></dl>";
+  return ret;
+}
+
+string parse_enum(Node n, void|int noheader) {
+  string ret ="";
+  if(!noheader)
+    ret += "<dl><dt>"
+      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
+      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; ENUM <b><font color='#005080'>" +
+      n->get_attributes()->class_path + n->get_attributes()->name +
+      "</font></b></font></td></tr></table><br />\n"
+      "</dt><dd>";
+
+  Node c = n->get_first_element("doc");
+
+  if(c)
+    ret += "<dl>" + parse_doc(c) + "</dl>";
+
+  if((sizeof(n->get_elements("doc"))>1) &&
+     ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+      Tools.AutoDoc.FLAG_DEBUG)) {
+    error( "More than one doc element in enum node.\n" );
+  }
+
+  ret += parse_children(n, "docgroup", parse_docgroup);
+
+  if(!noheader)
+    ret += "</dd></dl>";
+  return ret;
+}
+
+string layout_matrix( array(array(string)) rows ) {
+  string ret = "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n"
+    "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n";
+
+  int dim;
+  foreach(rows, array row)
+    dim = max(dim, sizeof(row));
+
+  foreach(rows, array row) {
+    ret += "<tr valign='top'>";
+    if(sizeof(row)<dim)
+      ret += "<td bgcolor='white'>" + row[..sizeof(row)-2]*"</td><td bgcolor='white'>" +
+	"</td><td bgcolor='white' colspan='"+ (dim-sizeof(row)) + "'>" + row[-1] + "</td>";
+    else
+      ret += "<td bgcolor='white'>" + row*"</td><td bgcolor='white'>" + "</td>";
+    ret += "</tr>\n";
+  }
+
+  return ret + "</table></td></tr></table><br />\n";
+}
+
+// ({  ({ array(string)+, void|string  })* })
+void nicebox(array rows, String.Buffer ret) {
+  ret->add( "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n"
+	    "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n" );
+
+  int dim;
+  foreach(rows, array row)
+    dim = max(dim, sizeof(row));
+
+  foreach(rows, array row) {
+    if(sizeof(row)==1) {
+      if(stringp(row[0]))
+	ret->add( "<tr valign='top'><td bgcolor='white' colspan='", (string)dim, "'>",
+		  row[0], "</td></tr>\n" );
+      else
+	foreach(row[0], string elem)
+	  ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td>",
+		    (dim==2?"<td bgcolor='white'>&nbsp;</td>":""), "</tr>\n" );
+    }
+    else if(sizeof(row[0])==1)
+      ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0],
+		"</tt></td><td bgcolor='white'>", row[1], "</td></tr>\n" );
+    else {
+      ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0],
+		"</tt></td><td bgcolor='white' rowspan='", (string)sizeof(row[0]), "'>",
+		row[1], "</td></tr>\n" );
+      foreach(row[0][1..], string elem)
+	ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td></tr>\n" );
+    }
+  }
+
+  ret->add( "</table></td></tr></table><br />\n" );
+}
+
+void build_box(Node n, String.Buffer ret, string first, string second, function layout, void|string header) {
+  array rows = ({});
+  if(header) rows += ({ ({ header }) });
+  foreach(n->get_elements(first), Node d) {
+    array elems = ({});
+    foreach(d->get_elements(second), Node e)
+      elems += ({ layout(e) });
+    if(d->get_first_element("text"))
+      rows += ({ ({ elems, parse_text(d->get_first_element("text")) }) });
+    else
+      rows += ({ ({ elems }) });
+  }
+  nicebox(rows, ret);
+}
+
+string parse_text(Node n, void|String.Buffer ret) {
+  if(n->get_node_type()==XML_TEXT && n->get_text()) {
+    if(ret)
+      ret->add(quote(n->get_test()));
+    else
+      return quote(n->get_text());
+  }
+
+  int cast;
+  if(!ret) {
+    ret = String.Buffer();
+    cast = 1;
+  }
+
+  foreach(n->get_children(), Node c) {
+    int node_type = c->get_node_type();
+    if(c->get_node_type()==XML_TEXT) {
+      // Don't use quote() here since we don't want to strip whitespace.
+      ret->add(Parser.XML.Tree.text_quote (c->get_text()));
+      continue;
+    }
+
+    if(c->get_node_type()==XML_COMMENT)
+      continue;
+
+    if((c->get_node_type()!=XML_ELEMENT) &&
+       ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+	Tools.AutoDoc.FLAG_DEBUG)) {
+      error( "Forbidden node type " + c->get_node_type() + " in doc node.\n" );
+    }
+
+    array rows;
+    string name = c->get_any_name();
+    switch(name) {
+    case "text":
+      ret->add("<dd>");
+      parse_text(c, ret);
+      ret->add("</dd>\n");
+      break;
+
+    case "p":
+    case "b":
+    case "i":
+    case "tt":
+    case "sub":
+    case "sup":
+      ret->add("<", name, ">");
+      parse_text(c, ret);
+      ret->add("</", name, ">");
+      break;
+
+    case "pre":
+      ret->add(lay->pre);
+      parse_text(c, ret);
+      ret->add(lay->_pre);
+      break;
+
+    case "code":
+      ret->add(lay->code);
+      parse_text(c, ret);
+      ret->add(lay->_code);
+      break;
+
+    case "expr":
+      ret->add(lay->expr, replace(parse_text(c), " ", "&nbsp;"), lay->_expr);
+      break;
+
+    case "ref":
+      if(resolve_reference) {
+	ret->add(resolve_reference(parse_text(c), c->get_attributes()));
+	break;
+      }
+      string ref;
+      //ref = c->get_attributes()->resolved;
+      if(!ref) ref = parse_text(c);
+      ret->add("<font face='courier'>", ref, "</font>");
+      break;
+
+    case "dl":
+      ret->add("<dl>", map(c->get_elements("group"), parse_text)*"", "</dl>");
+      break;
+
+    case "item":
+      if(c->get_attributes()->name)
+	ret->add("<dt>", c->get_attributes()->name, "</dt>\n");
+      if(c->count_children() &&
+	 ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+	  Tools.AutoDoc.FLAG_DEBUG)) {
+	error( "dl item has a child.\n" );
+      }
+      break;
+
+    case "mapping":
+      build_box(c, ret, "group", "member",
+		lambda(Node n) {
+		  return "<font color='green'>" +
+		    parse_text(n->get_first_element("index")) +
+		    "</font> : " +
+		    parse_type(get_first_element(n->get_first_element("type")));
+		});
+      break;
+
+    case "array":
+      build_box(c, ret, "group", "elem",
+		lambda(Node n) {
+		  string index ="";
+		  if(n->get_first_element("index"))
+		    index = parse_text(n->get_first_element("index"));
+		  else {
+		    if(n->get_first_element("minindex"))
+		      index = parse_text(n->get_first_element("minindex"));
+		    index += "..";
+		    if(n->get_first_element("maxindex"))
+		      index += parse_text(n->get_first_element("maxindex"));
+		  }
+		  return parse_type(get_first_element(n->get_first_element("type"))) +
+		    " <font color='green'>" + index + "</font>"; }, "Array" );
+      break;
+
+    case "int":
+      build_box(c, ret, "group", "value",
+		lambda(Node n) {
+		  string tmp = "<font color='green'>";
+		  Node min = n->get_first_element("minvalue");
+		  Node max = n->get_first_element("maxvalue");
+		  if(min || max) {
+		    if(min) tmp += parse_text(min);
+		    tmp += "..";
+		    if(max) tmp += parse_text(max);
+		  }
+		  else
+		    tmp += parse_text(n);
+		  return tmp + "</font>";
+		} );
+      break;
+
+    case "mixed":
+      if(c->get_attributes()->name)
+	ret->add("<tt>", c->get_attributes()->name, "</tt> can have any of the following types:<br />");
+      rows = ({});
+      foreach(c->get_elements("group"), Node d)
+	rows += ({ ({
+	  ({ parse_type(get_first_element(d->get_first_element("type"))) }),
+	  parse_text(d->get_first_element("text"))
+	}) });
+      nicebox(rows, ret);
+      break;
+
+    case "string": // Not in XSLT
+      build_box(c, ret, "group", "value",
+		lambda(Node n) {
+		  return "<font color='green'>" + parse_text(n) + "</font>";
+		} );
+      break;
+
+    case "multiset": // Not in XSLT
+      build_box(c, ret, "group", "index",
+		lambda(Node n) {
+		  return "<font color='green'>" +
+		    parse_text(n->get_first_element("value")) + "</font>";
+		} );
+      break;
+
+    case "image": // Not in XSLT
+      mapping m = c->get_attributes();
+      m->src = image_prefix() + m_delete(m, "file");
+      ret->add( sprintf("<img%{ %s='%s'%} />", (array)m) );
+      break;
+
+    case "url": // Not in XSLT
+      if((c->count_children()!=1 && c->get_node_type()!=XML_ELEMENT) &&
+	 ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) ==
+	  Tools.AutoDoc.FLAG_DEBUG)) {
+	error( "url node has not one child. %O\n", c->html_of_node() );
+      }
+      m = c->get_attributes();
+      if(!m->href)
+	m->href=c->value_of_node();
+      ret->add( sprintf("<a%{ %s='%s'%}>%s</a>",
+			(array)m, c->value_of_node()) );
+      break;
+
+    case "section":
+      if(section && !(flags & Tools.AutoDoc.FLAG_KEEP_GOING))
+	error("Section inside section.\n");
+      ret->add ("<h2>", quote (c->get_attributes()->title ||
+			       // The following for bug compat.
+			       c->get_attributes()->name),
+		"</h2>\n");
+      if (!equal (c->get_children()->get_any_name(), ({"text"})) &&
+	  !(flags & Tools.AutoDoc.FLAG_KEEP_GOING))
+	error ("Expected a single <text> element inside <section>.\n");
+      section = -1;
+      parse_text (c->get_children()[0], ret);
+      section = 0;
+      break;
+
+    case "ul":
+      ret->add( "<ul>\n" );
+      foreach(c->get_elements("group"), Node c) {
+	ret->add("<li>");
+	array(Node) d = c->get_elements("item");
+	if(sizeof(d->get_attributes()->name-({0}))) {
+	  ret->add("<b>");
+	  ret->add( String.implode_nicely(d->get_attributes()->name-({0})) );
+	  ret->add("</b>");
+	}
+	Node e = c->get_first_element("text");
+	if(e)
+	  parse_text(e, ret);
+	ret->add("</li>");
+      }
+      ret->add("</ul>");
+      break;
+
+    case "ol":
+      ret->add("<ol>\n");
+      foreach(c->get_elements("group"), Node c) {
+	ret->add("<li>");
+	parse_text(c->get_first_element("text"), ret);
+	ret->add("</li>");
+      }
+      ret->add("</ol>");
+      break;
+
+    case "source-position":
+      position->update(c);
+      break;
+
+    case "matrix":
+      ret->add( layout_matrix( map(c->get_elements("r")->get_elements("c"),
+				   map, parse_text) ) );
+      break;
+
+    case "fixme":
+      ret->add("<font color='red'>FIXME: ");
+      parse_text(c, ret);
+      ret->add("</font>");
+      break;
+
+    // Not really allowed
+    case "br":
+      ret->add( sprintf("<%s%{ %s='%s'%} />", c->get_any_name(), (array)c->get_attributes()) );
+      break;
+    case "table":
+    case "td":
+    case "tr":
+    case "th":
+      ret->add( sprintf("<%s%{ %s='%s'%}>%s</%s>",
+			c->get_any_name(), (array)c->get_attributes(),
+			parse_text(c), c->get_any_name()) );
+      break;
+
+    default:
+      if (verbosity >= Tools.AutoDoc.FLAG_DEBUG) {
+	werror("\n%s\n", (string)c);
+	error( "Illegal element \"" + name + "\" in mode text.\n" );
+      }
+      break;
+    }
+  }
+
+  if(cast)
+    return ret->get();
+}
+
+string parse_doc(Node n, void|int no_text) {
+  string ret ="";
+
+  Node c = n->get_first_element("text");
+  if(c)
+    ret += lay->dochead + "Description" + lay->_dochead +
+      lay->docbody + parse_text(c) + lay->_docbody;
+
+  foreach(n->get_elements("group"), Node c) {
+    Node header = c->get_first_element();
+    string name = header->get_any_name();
+    switch(name) {
+    case "param":
+      foreach(c->get_elements("param"), Node d)
+	ret += lay->dochead + "Parameter " + lay->parameter +
+	  quote(d->get_attributes()->name) + lay->_parameter + lay->_dochead +
+	  "<dd></dd>";
+      if (c = c->get_first_element("text")) {
+	ret += lay->docbody + parse_text(c) + lay->_docbody;
+      }
+      break;
+
+    case "seealso":
+      ret += lay->dochead + "See also" + lay->_dochead;
+      if (c = c->get_first_element("text")) {
+	ret += lay->docbody + parse_text(c) + lay->_docbody;
+      }
+      break;
+
+    case "fixme":
+      ret += lay->fixmehead + "FIXME" + lay->_fixmehead;
+      if (c = c->get_first_element("text")) {
+	ret += 	lay->fixmebody + parse_text(c) + lay->_fixmebody;
+      }
+      break;
+
+    case "deprecated":
+      ret += lay->dochead + String.capitalize(name) + lay->_dochead;
+      array(Node) replacements = header->get_elements("name");
+      if (sizeof(replacements)) {
+	ret += lay->docbody +
+	  "<p>Replaced by " +
+	  String.implode_nicely(map(replacements, parse_text)) + ".</p>\n" +
+	  lay->_docbody;
+      }
+      if (c = c->get_first_element("text")) {
+	ret += lay->docbody + parse_text(c) + lay->_docbody;
+      }
+      break;
+
+    case "bugs":
+    case "note":
+    case "returns":
+    case "throws":
+      ret += lay->dochead + String.capitalize(name) + lay->_dochead;
+      if (c = c->get_first_element("text")) {
+	ret += lay->docbody + parse_text(c) + lay->_docbody;
+      }
+      break;
+
+    case "example":
+      ret += lay->dochead + "Example" + lay->_dochead;
+      if (c = c->get_first_element("text")) {
+	ret += lay->example + parse_text(c) + lay->_example;
+      }
+      break;
+
+    default:
+      error( "Unknown group type \"" + name + "\".\n" );
+    }
+  }
+
+  return ret;
+}
+
+string parse_type(Node n, void|string debug) {
+  if(n->get_node_type()!=XML_ELEMENT)
+    return "";
+
+  string ret = "";
+  Node c, d;
+  switch(n->get_any_name()) {
+
+  case "object":
+    if(n->count_children()) {
+      if (resolve_reference) {
+	ret += "<font color='#005080'>" +
+	  resolve_reference(n->value_of_node(), n->get_attributes()) +
+	  "</font>";
+      } else {
+	ret += "<font color='#005080'>" + n->value_of_node() + "</font>";
+      }
+    } else
+      ret += "<font color='#202020'>object</font>";
+    break;
+
+  case "type":
+    ret += "<font color='#202020'>type</font>";
+    if (n->count_children() && (c = get_first_element(n)) &&
+	(c->get_any_name() != "mixed")) {
+      ret += "(" + parse_type(c) + ")";
+    }
+    break;
+
+  case "multiset":
+    ret += "<font color='#202020'>multiset</font>";
+    c = n->get_first_element("indextype");
+    if(c) ret += "(" + parse_type( get_first_element(c) ) + ")";
+    break;
+
+  case "array":
+    ret += "<font color='#202020'>array</font>";
+    c = n->get_first_element("valuetype");
+    if(c) ret += "(" + parse_type( get_first_element(c) ) + ")";
+    break;
+
+  case "mapping":
+    ret += "<font color='#202020'>mapping</font>";
+    c = n->get_first_element("indextype");
+    d = n->get_first_element("valuetype");
+    if(c && d)
+      ret += "(" + parse_type( get_first_element(c) ) + ":" +
+	parse_type( get_first_element(d) ) + ")";
+#ifdef DEBUG
+    if( !c != !d )
+      error( "Indextype/valuetype defined while the other is not in mapping.\n" );
+#endif
+    break;
+
+  case "function":
+    ret += "<font color='#202020'>function</font>";
+    array(Node) args = n->get_elements("argtype");
+    d = n->get_first_element("returntype");
+    // Doing different than the XSL here. Must both
+    // argtype and returntype be defined?
+    if(args || d) {
+      ret += "(";
+      if(args) ret += map(args->get_children() * ({}), parse_type)*", ";
+      ret += ":";
+      if(d) ret += parse_type( get_first_element(d) );
+      else ret += "<font color='#202020'>void</font>";
+      ret += ")";
+    }
+    break;
+
+  case "varargs":
+#ifdef DEBUG
+    if(!n->count_children())
+      error( "varargs node must have child node.\n" );
+#endif
+    ret += parse_type(get_first_element(n)) + " ... ";
+    break;
+
+  case "or":
+    ret += map(filter(n->get_children(),
+		      lambda(Node n){
+			return n->get_node_type()==XML_ELEMENT;
+		      }), parse_type)*"|";
+    break;
+
+  case "string": case "void": case "program": case "mixed": case "float":
+  case "zero":
+    ret += "<font color='#202020'>" + n->get_any_name() + "</font>";
+    break;
+
+  case "int":
+    ret += "<font color='#202020'>int</font>";
+    c = n->get_first_element("min");
+    d = n->get_first_element("max");
+    if(c && d)
+      ret += "(" + c->value_of_node() + ".." + d->value_of_node() + ")";
+    else if(c)
+      ret += "(" + c->value_of_node() + "..)";
+    else if(d)
+      ret += "(.." + d->value_of_node() + ")";
+    break;
+
+  case "attribute":
+    string attr = n->get_first_element("attribute")->value_of_node();
+    string subtype =
+      parse_type(n->get_first_element("subtype")->get_first_element());
+    if (n->get_first_element("prefix")) {
+      if (attr == "\"deprecated\"") {
+	ret += "<font color='#600000'>__deprecated__</font> " + subtype;
+      } else {
+	ret += sprintf("__attribute__(%s) %s", attr, subtype);
+      }
+    } else if (attr == "\"deprecated\"") {
+      ret += "<font color='#600000'>__deprecated__</font>(" + subtype + ")";
+    } else {
+      ret += sprintf("__attribute__(%s, %s)", attr, subtype);
+    }
+    break;
+
+    // Modifiers:
+  case "extern": // Not in XSLT
+    ret += "extern ";
+    break;
+  case "final": // Not in XSLT
+  case "nomask": // Not in XSLT
+    ret += "final ";
+    break;
+  case "inline": // Not in XSLT
+  case "local": // Not in XSLT
+    ret += "local ";
+    break;
+  case "optional": // Not in XSLT
+    ret += "optional ";
+    break;
+  case "private": // Not in XSLT
+    ret += "private ";
+    break;
+  case "protected": // Not in XSLT
+  case "static": // Not in XSLT
+    ret += "protected ";
+    break;
+  case "public": // Not in XSLT
+    // Ignored.
+    break;
+  case "variant": // Not in XSLT
+    ret += "variant ";
+    break;
+
+  default:
+    error( "Illegal element " + n->get_any_name() + " in mode type.\n" );
+    break;
+  }
+  return ret;
+}
+
+void resolve_class_paths(Node n, string|void path, Node|void parent)
+{
+  if (!path) path = "";
+  mapping attrs = n->get_attributes() || ([]);
+  string name = attrs->name;
+  switch(n->get_any_name()) {
+  case "arguments":
+  case "returntype":
+  case "type":
+  case "doc":
+  case "source-position":
+  case "modifiers":
+  case "classname":
+    // We're not interrested in the stuff under the above nodes.
+    return;
+  default:
+    if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) {
+      werror("Unhandled node: %O path:%O name: %O, parent: %O\n",
+	     n->get_any_name(), path, name, parent&&parent->get_any_name());
+    }
+    // FALL_THROUGH
+  case "":
+  case "docgroup":
+    break;
+    
+  case "manual":
+  case "dir":
+  case "file":
+  case "chapter":
+  case "section":
+  case "subsection":
+    foreach(filter(n->get_children(),
+		   lambda(Node n) {
+		     return (<"manual", "dir", "file", "chapter",
+			      "section", "subsection", "autodoc">)
+		       [n->get_any_name()];
+		   }), Node child) {
+      resolve_class_paths(child, "", n);
+    }
+    return;
+  case "autodoc":
+    path = "";
+    break;
+  case "namespace":
+    if ((<"", "lfun">)[name]) {
+      // Censor namespaces other than :: and lfun::
+      path = name+"::";
+    } else {
+      path = "";
+    }
+    break;
+  case "class":
+  case "module":
+    attrs->class_path = path;
+    path += name + ".";
+    break;
+  case "enum":
+    // Enum names don't show up in the path.
+    attrs->class_path = path;
+    break;
+  case "method":
+    // Special case for create().
+    if (name == "create") {
+      // Get rid of the extra dot.
+      attrs->class_path = path[..sizeof(path)-2];
+    }
+    else
+    {
+      // Hide the class path for methods.
+      attrs->class_path = "";
+    }
+    return;
+  case "constant":
+  case "inherit":
+  case "import":
+  case "typedef":
+  case "variable":
+    // These don't have children.
+    attrs->class_path = path;
+    return;
+  }
+  foreach(n->get_children(), Node child)
+  {
+    resolve_class_paths(child, path, n);
+  }
+}
+
+#if 0
+// DEAD code (for reference only).
+string render_class_path(Node n,int|void class_only)
+{
+  array a = reverse(n->get_ancestors(0));
+  array b = a->get_any_name();
+  int root;
+  foreach( ({ "manual", "dir", "file", "chapter",
+	      "section", "subsection" }), string node)
+    root = max(root, search(b, node));
+  a = a[root+1..];
+  if(sizeof(a) && a[0]->get_any_name() == "autodoc")
+    a = a[1..];
+  if (!sizeof(a)) return "";
+  string ret = "";
+  if ((sizeof(a) > 1) && a[-2]->get_any_name() == "enum") {
+    // Enum names don't show up in the path.
+    a = a[..sizeof(a)-3] + a[sizeof(a)-1..];
+  }
+  if (a[0]->get_any_name() == "namespace") {
+    a = a->get_attributes()->name;
+    a[0] += "::";
+    if ((<"::","lfun::">)[a[0]]) {
+      // Censor namespaces other than :: and lfun::
+      ret = a[0];
+    }
+    a = a[1..];
+  } else {
+    a = a->get_attributes()->name;
+  }
+  ret += a * ".";
+  if (!sizeof(ret) || ret[-1] == ':') return ret;
+  if((<"class", "module", "enum">)[n->get_any_name()])
+    return ret + ".";
+
+  // Note we need two calls to get_parent() to skip over the docgroup.
+  Node parent = n->get_parent()->get_parent();
+
+  // Hide the enum part.
+  if (parent->get_any_name()=="enum") parent = parent->get_parent();
+
+  if(parent->get_any_name()=="class")
+    if( !class_only )
+      return ""; //ret + "()->";
+    else
+      return ret;
+  if(parent->get_any_name()=="module")
+    return ret + ".";
+  if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) {
+    werror("Failure, raw path: %{%O, %}\n",
+	   reverse(n->get_ancestors(0))->get_any_name());
+  }
+  return " (could not resolve) ";
+#ifdef DEBUG
+  error( "Parent module is " + n->get_parent()->get_any_name() + ".\n" );
+#else
+    return "";
+#endif
+}
+#endif /* 0 */
+
+string parse_not_doc(Node n) {
+  string ret = "";
+  int method, argument, variable, const, typedf;
+
+  foreach(n->get_children(), Node c) {
+
+    if(c->get_node_type()!=XML_ELEMENT)
+      continue;
+
+    Node cc;
+    switch(c->get_any_name()) {
+
+    case "doc":
+      continue;
+
+    case "source-position":
+      position->update(c);
+      continue;
+
+    case "method":
+      if(method++) ret += "<br />\n";
+#ifdef DEBUG
+      if(!c->get_first_element("returntype"))
+	continue;
+	// error( "No returntype element in method element.\n" );
+#endif
+      switch( c->get_attributes()->name )
+      {
+	case "create":
+	  ret += "<tt>" + parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children
+	  ret += " ";
+	  ret += c->get_attributes()->class_path+"<b>(</b>";
+	  ret += parse_not_doc( c->get_first_element("arguments") );
+	  ret += "<b>)</b></tt>";
+	  break;
+	default:
+	  ret += "<tt>";
+	  cc = c->get_first_element("modifiers");
+	  if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
+	  ret += parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children
+	  ret += " ";
+	  ret += c->get_attributes()->class_path;
+	  ret += "<b><font color='#000066'>" + c->get_attributes()->name + "</font>(</b>";
+	  ret += parse_not_doc( c->get_first_element("arguments") );
+	  ret += "<b>)</b></tt>";
+	  break;
+      }
+      break;
+
+    case "argument":
+      if(argument++) ret += ", ";
+      cc = c->get_first_element("value");
+      if(cc) ret += "<font color='green'>" + cc->value_of_node() + "</font>";
+      else if( !c->count_children() );
+      else if( get_first_element(c)->get_any_name()=="type") {
+	ret += parse_type(get_first_element(get_first_element(c)));
+	if(c->get_attributes()->name)
+	  ret += " <font color='#005080'>" +
+	    c->get_attributes()->name + "</font>";
+      }
+      else
+	error( "Malformed argument element.\n" + c->html_of_node() + "\n" );
+      break;
+
+    case "variable":
+      if(variable++) ret += "<br />\n";
+      ret += "<tt>";
+      cc = c->get_first_element("modifiers");
+      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
+      ret += parse_type(get_first_element(c->get_first_element("type")), "variable") + " " +
+	c->get_attributes()->class_path + "<b><font color='#F000F0'>" + c->get_attributes()->name +
+	"</font></b></tt>";
+      break;
+
+    case "constant":
+      if(const++) ret += "<br />\n";
+      ret += "<tt>";
+      cc = c->get_first_element("modifiers");
+      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
+      ret += "constant ";
+      if (Node type = c->get_first_element ("type"))
+	ret += parse_type (get_first_element (type), "constant") + " ";
+      ret += c->get_attributes()->class_path;
+      ret += "<font color='#F000F0'>" + c->get_attributes()->name + "</font>";
+      cc = c->get_first_element("typevalue");
+      if(cc) ret += " = " + parse_type(get_first_element(cc));
+      ret += "</tt>";
+      break;
+
+    case "typedef":
+      if(typedf++) ret += "<br />\n";
+      ret += "<tt>";
+      cc = c->get_first_element("modifiers");
+      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
+      ret += "typedef ";
+      ret += parse_type(get_first_element(c->get_first_element("type")), "typedef") + " " +
+	c->get_attributes()->class_path + "<font color='#F000F0'>" + c->get_attributes()->name +
+	"</font></tt>";
+      break;
+
+    case "inherit":
+      ret += "<font face='courier'>";
+      cc = c->get_first_element("modifiers");
+      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
+      ret += "inherit ";
+      Node n = c->get_first_element("classname");
+      if (resolve_reference) {
+	ret += "</font>" +
+	  resolve_reference(n->value_of_node(), n->get_attributes());
+      } else {
+	ret += Parser.encode_html_entities(n->value_of_node()) + "</font>";
+      }
+      if (c->get_attributes()->name) {
+	ret += "<font face='courier'> : " + "<font color='#F000F0'>" +
+	  Parser.encode_html_entities(c->get_attributes()->name) +
+	  "</font></font>";
+      }
+      break;
+
+    // We don't need import information.
+    case "import":
+      break;
+
+    default:
+      error( "Illegal element " + c->get_any_name() + " in !doc.\n" );
+      break;
+    }
+  }
+
+  return ret;
+}
+
+int foo;
+
+string parse_docgroup(Node n) {
+  mapping m = n->get_attributes();
+  string ret = lay->docgroup;
+
+  if(lay->typehead) {
+    ret += lay->typehead;
+    if(m["homogen-type"]) {
+      string type = "<font face='Helvetica'>" +
+	quote(String.capitalize(m["homogen-type"])) +
+	"</font>\n";
+      if(m["homogen-name"]) {
+	ret += type + "<font size='+1'><b>" +
+	  quote((m->belongs?m->belongs+" ":"") + m["homogen-name"]) +
+	  "</b></font>\n";
+      } else {
+	array(string) names =
+	  Array.uniq(map(n->get_elements(m["homogen-type"]),
+			 lambda(Node child) {
+			   return child->get_attributes()->name ||
+			     child->value_of_node();
+			 }));
+	foreach(names, string name)
+	  ret += type + "<font size='+1'><b>" + name + "</b></font><br />\n";
+      }
+    }
+    else
+      ret += "Syntax";
+    ret += lay->_typehead;
+  }
+
+  ret += lay->ndochead;
+
+  ret += parse_not_doc(n);
+
+  ret += lay->_ndochead;
+
+  foreach(n->get_elements("doc"), Node c)
+    ret += parse_doc(c);
+
+  return ret + lay->_docgroup;
+}
+
+string parse_children(Node n, string tag, function cb, mixed ... args) {
+  string ret = "";
+  foreach(n->get_elements(tag), Node c)
+    ret += cb(c, @args);
+
+  return ret;
+}
+
+string manual_title = "Pike Reference Manual";
+string frame_html(string res, void|string title) {
+  title = title || manual_title;
+  return "<html><head><title>" + quote(title) + "</title></head>\n"
+    "<body bgcolor='white' text='black'>\n" + res +
+    "</body></html>";
+}
+
+string layout_toploop(Node n, Git.Export|void exporter) {
+  string res = "";
+  foreach(n->get_elements(), Node c)
+    switch(c->get_any_name()) {
+
+    case "dir":
+      if (exporter) {
+	layout_toploop(c, exporter);
+	break;
+      }
+      string cwd;
+      if(dest_path)
+      {
+        cwd=getcwd();
+        cd(dest_path);
+      }  
+      Stdio.mkdirhier(c->get_attributes()->name);
+      layout_toploop(c);
+      if(cwd)
+        cd(cwd);
+      break;
+
+    case "file":
+      if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) {
+	werror("\t%s\n", c->get_attributes()->name);
+      }
+      if (exporter) {
+	exporter->filemodify(Git.MODE_FILE, c->get_attributes()->name);
+	exporter->data(frame_html(layout_toploop(c)));
+	break;
+      }
+      if(dest_path)
+      {
+        cwd=getcwd();
+        cd(dest_path);
+      }  
+      Stdio.write_file( c->get_attributes()->name,
+			frame_html(layout_toploop(c)) );
+      if(cwd)
+        cd(cwd);
+      break;
+
+    case "chapter":
+      res += parse_chapter(c);
+      break;
+
+    default:
+      error("Unknown element %O.\n", c->get_any_name());
+    }
+  return res;
+}
+
+int low_main(string title, string input_file, string|void output_file,
+	     Git.Export|void exporter)
+{
+  int t = time();
+  string file = Stdio.read_file(input_file);
+  if(!file)
+    error( "Could not read %s.\n", input_file );
+  if(!sizeof(file))
+    error( "%s is empty.\n", input_file );
+
+  // We are only interested in what's in the
+  // module container.
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) {
+    werror("Parsing %O...\n", input_file);
+  }
+  Node n = Parser.XML.Tree.simple_parse_input(file);
+
+  Node n2 = n->get_first_element("manual");
+  if(!n2) {
+    n2 = n->get_first_element("autodoc");
+    if(!n2) {
+      if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) {
+	werror("Not an autodoc XML file.\n");
+      }
+      return 1;
+    }
+
+    Node chap = Node(XML_ELEMENT, "chapter",
+		     (["title":title||"Documentation"]), 0);
+    chap->add_child( n2 );
+
+    string fn = output_file || "index.html";
+    Node file = Node(XML_ELEMENT, "file", (["name":fn]), 0);
+    file->add_child( chap );
+
+    Node top = Node(XML_ELEMENT, "manual", ([]), 0);
+    top->add_child( file );
+    n = top;
+  }
+  else
+    n = n2;
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) {
+    werror("Resolving class paths...\n");
+  }
+  resolve_class_paths(n);
+
+  mapping m = n->get_attributes();
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) {
+    werror("Layouting...\n");
+  }
+  manual_title = m->title || (m->version?"Reference Manual for "+m->version:"Pike Reference Manual");
+  layout_toploop(n, exporter);
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) {
+    werror("Took %d seconds.\n\n", time()-t);
+  }
+
+  return 0;
+}
+
+int main(int num, array args) {
+
+  string title;
+
+  foreach(Getopt.find_all_options(args, ({
+    ({ "img",     Getopt.HAS_ARG, "--img"   }),
+    ({ "dest",    Getopt.HAS_ARG, "--dest"  }),
+    ({ "title",   Getopt.HAS_ARG, "--title" }),
+    ({ "defns",   Getopt.HAS_ARG, "--default-ns" }),
+    ({ "verbose", Getopt.NO_ARG,  "-v,--verbose"/"," }),
+    ({ "quiet",   Getopt.NO_ARG,  "-q,--quiet"/"," }),
+    ({ "help",    Getopt.NO_ARG,  "--help"  }),
+  })), array opt)
+    switch(opt[0]) {
+    case "img":
+      image_path = opt[1];
+      break;
+    case "dest":
+      dest_path = opt[1];
+      break;
+    case "title":
+      title = opt[1];
+      break;
+    case "defns":
+      default_ns = opt[1];
+      break;
+    case "verbose":
+      if (verbosity < Tools.AutoDoc.FLAG_DEBUG) {
+	verbosity += 1;
+	flags = (flags & ~Tools.AutoDoc.FLAG_VERB_MASK) | verbosity;
+      }
+      break;
+    case "quiet":
+      flags &= ~Tools.AutoDoc.FLAG_VERB_MASK;
+      verbosity = Tools.AutoDoc.FLAG_QUIET;
+      break;
+    case "help":
+      write(#"pike -x autodoc_to_html [args] <input file> [<output file>]
+--img=<image path>
+--dest=<destination path>
+
+--title=<document title>
+");
+      break;
+    }
+  args = Getopt.get_args(args)[1..];
+
+  if(!sizeof(args))
+    error( "No input file given.\n" );
+
+  return low_main(title, @args);
+}
diff --git a/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike
new file mode 100644
index 0000000000..5785e425ee
--- /dev/null
+++ b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike
@@ -0,0 +1,775 @@
+/*
+ */
+
+inherit .autodoc_to_html;
+
+mapping (string:Node) refs   = ([ ]);
+string default_namespace;
+
+string extra_prefix = "";
+string image_prefix()
+{
+  return extra_prefix + ::image_prefix();
+}
+
+int unresolved;
+mapping profiling = ([]);
+#define PROFILE int profilet=gethrtime
+#define ENDPROFILE(X) profiling[(X)] += gethrtime()-profilet;
+
+string cquote(string n)
+{
+  string ret="";
+  // n = replace(n, ([ "&gt;":">", "&lt;":"<", "&amp;":"&" ]));
+  while(sscanf((string)n,"%[._a-zA-Z0-9]%c%s",string safe, int c, n)==3) {
+    switch(c) {
+    default:  ret += sprintf("%s_%02X",safe,c); break;
+    case '+': ret += sprintf("%s_add",safe); break;
+    case '`': ret += sprintf("%s_backtick",safe);  break;
+    case '=': ret += sprintf("%s_eq",safe); break;
+    }
+  }
+  ret += n;
+  return ret;
+}
+
+string create_reference(string from, string to, string text) {
+  array a = (to/"::");
+  switch(sizeof(a)) {
+  case 2:
+    if (sizeof(a[1])) {
+      // Split trailing module path.
+      a = a[0..0] + a[1]/".";
+    } else {
+      // Get rid of trailing "".
+      a = a[0..0];
+    }
+    a[0] += "::";
+    break;
+  case 1:
+    a = a[0]/".";
+    break;
+  default:
+    error("Bad reference: %O\n", to);
+  }
+  return "<font face='courier'><a href='" +
+    "../"*max(sizeof(from/"/") - 2, 0) + map(a, cquote)*"/" + ".html'>" +
+    text +
+    "</a></font>";
+}
+
+multiset missing = (< "foreach", "catch", "throw", "sscanf", "gauge", "typeof" >);
+
+
+class Node
+{
+  string type;
+  string name;
+  string data;
+  array(Node) class_children  = ({ });
+  array(Node) module_children = ({ });
+  array(Node) enum_children = ({ });
+  array(Node) method_children = ({ });
+
+  Node parent;
+
+  string _sprintf() {
+    return sprintf("Node(%O,%O,%d)", type, name, data?sizeof(data):0);
+  }
+
+  void create(string _type, string _name, string _data, void|Node _parent)
+  {
+    if(!_type || !_name) throw( ({ "No type or name\n", backtrace() }) );
+    type = _type;
+    name = _name;
+    parent = _parent;
+    data = get_parser()->finish( _data )->read();
+
+    string path = raw_class_path();
+    refs[path] = this_object();
+
+    sort(class_children->name, class_children);
+    sort(module_children->name, module_children);
+    sort(enum_children->name, enum_children);
+    sort(method_children->name, method_children);
+
+    method_children = check_uniq(method_children);
+
+    /*
+    foreach(method_children, Node m)
+      if( (<"create","destroy">)[m->name] ) {
+	method_children -= ({ m });
+	string d;
+	sscanf(m->data, "%*s<docgroup%s/docgroup>", d);
+	if(d)
+	  data += "<docgroup" + d + "/docgroup>";
+      }
+    */
+
+    data = make_faked_wrapper(data);
+  }
+
+  array(Node) check_uniq(array children) {
+    array names = children->name;
+    foreach(Array.uniq(names), string n)
+      if(sizeof(filter(names, lambda(string in) { return in==n; }))!=1) {
+	string d="";
+	Parser.HTML parser = Parser.HTML();
+	parser->case_insensitive_tag(1);
+	parser->xml_tag_syntax(3);
+	parser->add_container("docgroup",
+          lambda(Parser.HTML p, mapping m, string c) {
+	    d += sprintf("<docgroup%{ %s='%s'%}>%s</docgroup>",
+			 (array)m, c);
+	    return "";
+	  });
+
+	foreach(children, Node c)
+	  if(c->name==n) parser->feed(c->data);
+	parser->finish();
+	d = make_faked_wrapper(d);
+
+	foreach(children; int i; Node c)
+	  if(c->name==n) {
+	    if(d) {
+	      c->data = d;
+	      d = 0;
+	    }
+	    else children[i] = 0;
+	  }
+	children -= ({ 0 });
+      }
+    return children;
+  }
+
+  protected string parse_node(Parser.HTML p, mapping m, string c) {
+    if(!m->name) error("Unnamed %O %O\n", p->tag_name(), m);
+    this_object()[p->tag_name()+"_children"] +=
+      ({ Node( p->tag_name(), m->name, c, this_object() ) });
+    return "";
+  }
+
+  array(string) my_parse_docgroup(Parser.HTML p, mapping m, string c)
+  {
+    foreach(({"homogen-name", "belongs"}), string attr) {
+      if (m[attr]) m[attr] = Parser.parse_html_entities(m[attr]);
+    }
+    if(m["homogen-type"]) {
+      switch( m["homogen-type"] ) {
+
+      case "method":
+	if( m["homogen-name"] ) {
+	  string name = m["homogen-name"];
+	  if(m->belongs) {
+	    if(m->belongs[-1]==':') name = m->belongs + name;
+	    else name = m->belongs + "." + name;
+	  }
+	  method_children +=
+	    ({ Node( "method", name, c, this_object() ) });
+	  return ({ "" });
+	}
+
+	// Several different methods documented with the same blurb.
+	array names = ({});
+	Parser.HTML parser = Parser.HTML();
+	parser->case_insensitive_tag(1);
+	parser->xml_tag_syntax(0);
+	parser->add_tag("method",
+			lambda(Parser.HTML p, mapping m) {
+			  names += ({ Parser.parse_html_entities(m->name) });
+			} );
+	parser->finish(c);
+	foreach(Array.uniq(names) - ({ 0, "" }), string name) {
+	  method_children +=
+	    ({ Node( "method", name, c, this_object() ) });
+	}
+	return ({ "" });
+	break;
+
+      case "constant":
+      case "variable":
+      case "inherit":
+	string path = raw_class_path();
+	if(sizeof(path) && (path[-1] != ':')) path += ".";
+	if(!m["homogen-name"]) {
+	  Parser.HTML()->add_tags
+	    ( ([ "constant":
+		 lambda(Parser.HTML p, mapping m, string c) {
+		   string name = Parser.parse_html_entities(m->name);
+		   refs[path + name] =
+		     Node( "constant", name, "", this_object());
+		 },
+		 "variable":
+		 lambda(Parser.HTML p, mapping m, string c) {
+		   string name = Parser.parse_html_entities(m->name);
+		   refs[path + name] =
+		     Node( "variable", name, "", this_object());
+		 },
+		 "inherit":
+		 lambda(Parser.HTML p, mapping m, string c) {
+		   if (m->name) {
+		     string name = Parser.parse_html_entities(m->name);
+		     refs[path + name] =
+		       Node( "inherit", name, "", this_object());
+		   }
+		 },
+	    ]) )->finish(c);
+	}
+	else
+	  refs[path + m["homogen-name"]] =
+	    Node( m["homogen-type"], m["homogen-name"], "", this_object());
+	break;
+
+      }
+    }
+
+    return 0;
+  }
+
+  protected Parser.HTML get_parser() {
+    Parser.HTML parser = Parser.HTML();
+
+    parser->case_insensitive_tag(1);
+    parser->xml_tag_syntax(3);
+
+    parser->add_container("docgroup", my_parse_docgroup);
+    parser->add_container("module", parse_node);
+    parser->add_container("class",  parse_node);
+    parser->add_container("enum",  parse_node);
+
+    return parser;
+  }
+
+  string make_faked_wrapper(string s)
+  {
+    if(type=="method")
+      s = sprintf("<docgroup homogen-type='method' homogen-name='%s'>\n"
+		  "%s\n</docgroup>\n",
+		  Parser.encode_html_entities(name), s);
+    else
+      s = sprintf("<%s name='%s'>\n%s\n</%s>\n",
+		  type, Parser.encode_html_entities(name), s, type);
+    if(parent)
+      return parent->make_faked_wrapper(s);
+    else
+      return s;
+  }
+
+  string _make_filename_low;
+  string make_filename_low()
+  {
+    if(_make_filename_low) return _make_filename_low;
+    if (type == "namespace") {
+      _make_filename_low = parent->make_filename_low()+"/"+cquote(name+"::");
+    } else {
+      _make_filename_low = parent->make_filename_low()+"/"+cquote(name);
+    }
+    return _make_filename_low;
+  }
+
+  string make_filename()
+  {
+    return make_filename_low()[5..]+".html";
+  }
+
+  string make_link(Node to)
+  {
+    // FIXME: Optimize the length of relative links
+    int num_segments = sizeof(make_filename()/"/") - 1;
+    return ("../"*num_segments)+to->make_filename();
+  }
+
+  array(Node) get_ancestors()
+  {
+    PROFILE();
+    array tmp = ({ this_object() }) + parent->get_ancestors();
+    ENDPROFILE("get_ancestors");
+    return tmp;
+  }
+
+  string my_resolve_reference(string _reference, mapping vars)
+  {
+    array(string) resolved = vars->resolved && vars->resolved/"\0";
+    if(default_namespace && has_prefix(_reference, default_namespace+"::"))
+      _reference = _reference[sizeof(default_namespace)+2..];
+
+    if(vars->param)
+      return "<font face='courier'>" + _reference + "</font>";
+
+    if (resolved) {
+      foreach (resolved, string resolution) {
+	Node res_obj;
+
+	if(res_obj = refs[resolution]) {
+	  while(res_obj && (<"constant","variable">)[res_obj->type]) {
+	    res_obj = res_obj->parent;
+	  }
+	  if (!res_obj && verbosity) {
+	    werror("Found no page to link to for reference %O (%O)\n",
+		   _reference, resolution);
+	    return sprintf("<font face='courier'>" + _reference + "</font>");
+	  }
+	  // FIXME: Assert that the reference is correct?
+	  return create_reference(make_filename(),
+				  res_obj->raw_class_path(),
+				  _reference);
+	}
+
+	// Might be a reference to a parameter.
+	// Try cutting of the last section, and retry.
+	array(string) tmp = resolution/".";
+	if ((sizeof(tmp) > 1) && (res_obj = refs[tmp[..sizeof(tmp)-2]*"."])) {
+	  if (res_obj == this_object()) {
+	    return sprintf("<font face='courier'>" + _reference + "</font>");
+	  }
+	  return create_reference(make_filename(),
+				  res_obj->raw_class_path(),
+				  _reference);
+	}
+
+	if (!zero_type(refs[resolution]) &&
+	    (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)) {
+	  werror("Reference %O (%O) is %O!\n",
+		 _reference, resolution, refs[resolution]);
+	}
+      }
+      if (!missing[vars->resolved] && !has_prefix(_reference, "lfun::") &&
+	  verbosity) {
+	werror("Missed reference %O (%{%O, %}) in %s\n",
+	       _reference, resolved||({}), make_class_path());
+#if 0
+	werror("Potential refs:%O\n",
+	       sort(map(resolved,
+			lambda (string resolution) {
+			  return filter(indices(refs),
+					glob(resolution[..sizeof(resolution)/2]+
+					     "*", indices(refs)[*]));
+			}) * ({})));
+#endif /* 0 */
+      }
+      unresolved++;
+    }
+    return "<font face='courier'>" + _reference + "</font>";
+  }
+
+  string _make_class_path;
+  string _raw_class_path;
+  string make_class_path(void|int(0..1) header)
+  {
+    if(_make_class_path) return _make_class_path;
+    array a = reverse(parent->get_ancestors());
+
+    _make_class_path = "";
+    _raw_class_path = "";
+    foreach(a, Node n)
+    {
+      // Hide most namepaces from the class path.
+      if (n->type == "namespace") {
+	_raw_class_path += n->name + "::";
+	if ((<"","lfun">)[n->name]) {
+	  _make_class_path += n->name + "::";
+	}
+      } else {
+	_raw_class_path += n->name + ".";
+	_make_class_path += n->name;
+	if(n->type=="class")
+	  _make_class_path += "()->";
+	else if(n->type=="module")
+	  _make_class_path += ".";
+      }
+    }
+    _make_class_path += name;
+    _raw_class_path += name;
+
+    if(type=="method") {
+      _make_class_path += "()";
+    } else if (type == "namespace") {
+      _make_class_path += "::";
+      _raw_class_path += "::";
+    }
+
+    return _make_class_path;
+  }
+  string raw_class_path(void|int(0..1) header)
+  {
+    if(_raw_class_path) return _raw_class_path;
+    make_class_path(header);
+    return _raw_class_path;
+  }
+
+  string make_navbar_really_low(array(Node) children, string what)
+  {
+    if(!sizeof(children)) return "";
+
+    String.Buffer res = String.Buffer(3000);
+    res->add("<tr><td nowrap='nowrap'><br /><b>", what, "</b></td></tr>\n");
+
+    foreach(children, Node node)
+    {
+      string my_name = Parser.encode_html_entities(node->name);
+      if(node->type=="method")
+	my_name+="()";
+      else if (node->type == "namespace") {
+	my_name="<b>"+my_name+"::</b>";
+      }
+      else 
+	my_name="<b>"+my_name+"</b>";
+
+      res->add("<tr><td nowrap='nowrap'>&nbsp;");
+      if(node==this_object())
+	res->add( my_name );
+      else
+	res->add( "<a href='", make_link(node), "'>", my_name, "</a>" );
+      res->add("</td></tr>\n");
+    }
+    return (string)res;
+  }
+
+  string make_hier_list(Node node)
+  {
+    string res="";
+
+    if(node)
+    {
+      if(node->type=="namespace" && node->name==default_namespace)
+	node = node->parent;
+      res += make_hier_list(node->parent);
+
+      string my_class_path =
+	(node->is_TopNode)?"[Top]":node->make_class_path();
+
+      if(node == this_object())
+	res += sprintf("<b>%s</b><br />\n",
+		       Parser.encode_html_entities(my_class_path));
+      else
+	res += sprintf("<a href='%s'><b>%s</b></a><br />\n",
+		       make_link(node),
+		       Parser.encode_html_entities(my_class_path));
+    }
+    return res;
+  }
+
+  string make_navbar_low(Node root)
+  {
+    string res="";
+
+    res += make_hier_list(root);
+
+    res+="<table border='0' cellpadding='1' cellspacing='0' class='sidebar'>";
+
+    res += make_navbar_really_low(root->module_children, "Modules");
+
+    res += make_navbar_really_low(root->class_children, "Classes");
+
+    if(root->is_TopNode)
+      res += make_navbar_really_low(root->namespace_children, "Namespaces");
+    else {
+      res += make_navbar_really_low(root->enum_children, "Enums");
+      res += make_navbar_really_low(root->method_children, "Methods");
+    }
+
+    return res+"</table>";
+  }
+
+  string make_navbar()
+  {
+    if(type=="method")
+      return make_navbar_low(parent);
+    else
+      return make_navbar_low(this_object());
+  }
+
+  array(Node) find_siblings()
+  {
+    return
+      parent->class_children+
+      parent->module_children+
+      parent->enum_children+
+      parent->method_children;
+  }
+
+  array(Node) find_children()
+  {
+    return
+      class_children+
+      module_children+
+      enum_children+
+      method_children;
+  }
+
+  Node find_prev_node()
+  {
+    array(Node) siblings = find_siblings();
+    int index = search( siblings, this_object() );
+
+    Node tmp;
+
+    if(index==0 || index == -1)
+      return parent;
+
+    tmp = siblings[index-1];
+
+    while(sizeof(tmp->find_children()))
+      tmp = tmp->find_children()[-1];
+
+    return tmp;
+  }
+
+  Node find_next_node(void|int dont_descend)
+  {
+    if(!dont_descend && sizeof(find_children()))
+      return find_children()[0];
+
+    array(Node) siblings = find_siblings();
+    int index = search( siblings, this_object() );
+
+    Node tmp;
+    if(index==sizeof(siblings)-1)
+      tmp = parent->find_next_node(1);
+    else
+      tmp = siblings[index+1];
+    return tmp;
+  }
+
+  protected string make_content() {
+    PROFILE();
+    string err;
+    Parser.XML.Tree.Node n;
+    if(err = catch( n = Parser.XML.Tree.parse_input(data)[0] )) {
+      werror(err + "\n" + data);
+      exit(1);
+    }
+    ENDPROFILE("XML.Tree");
+
+    resolve_reference = my_resolve_reference;
+
+    String.Buffer contents = String.Buffer(100000);
+    resolve_class_paths(n);
+    contents->add( parse_children(n, "docgroup", parse_docgroup, 1) );
+    contents->add( parse_children(n, "namespace", parse_namespace, 1) );
+    contents->add( parse_children(n, "module", parse_module, 1) );
+    contents->add( parse_children(n, "class", parse_class, 1) );
+    contents->add( parse_children(n, "enum", parse_enum, 1) );
+
+    n->zap_tree();
+
+    return (string)contents;
+  }
+
+  void make_html(string template, string path, Git.Export|void exporter)
+  {
+    class_children->make_html(template, path, exporter);
+    module_children->make_html(template, path, exporter);
+    enum_children->make_html(template, path, exporter);
+    method_children->make_html(template, path, exporter);
+
+    int num_segments = sizeof(make_filename()/"/")-1;
+    string style = ("../"*num_segments)+"style.css";
+    extra_prefix = "../"*num_segments;
+
+    Node prev = find_prev_node();
+    Node next = find_next_node();
+    string next_url="", next_title="", prev_url="", prev_title="";
+    if(next) {
+      next_title = next->make_class_path();
+      next_url   = make_link(next);
+    }
+    if(prev) {
+      prev_title = prev->make_class_path();
+      prev_url   = make_link(prev);
+    }
+
+    string res = replace(template,
+      (["$navbar$": make_navbar(),
+	"$contents$": make_content(),
+	"$prev_url$": prev_url,
+	"$prev_title$": _Roxen.html_encode_string(prev_title),
+	"$next_url$": next_url,
+	"$next_title$": _Roxen.html_encode_string(next_title),
+	"$type$": String.capitalize(type),
+	"$title$": _Roxen.html_encode_string(make_class_path(1)),
+	"$style$": style,
+	"$dotdot$": sizeof(extra_prefix)?extra_prefix:".",
+	"$imagedir$":image_prefix(),
+	"$filename$": _Roxen.html_encode_string(make_filename()),
+      ]));
+
+    if (exporter) {
+      exporter->filemodify(Git.MODE_FILE, path + "/" + make_filename());
+      exporter->data(res);
+    } else {
+      Stdio.mkdirhier(combine_path(path+"/"+make_filename(), "../"));
+      Stdio.write_file(path+"/"+make_filename(), res);
+    }
+  }
+}
+
+class TopNode {
+  inherit Node;
+
+  constant is_TopNode = 1;
+  array(Node) namespace_children = ({ });
+
+  void create(string _data) {
+    PROFILE();
+    Parser.HTML parser = Parser.HTML();
+    parser->case_insensitive_tag(1);
+    parser->xml_tag_syntax(3);
+    parser->add_container("autodoc",
+			  lambda(Parser.HTML p, mapping args, string c) {
+			    return ({ c }); });
+
+    _data = parser->finish(_data)->read();
+    ::create("autodoc", "", _data);
+    sort(namespace_children->name, namespace_children);
+    foreach(namespace_children, Node x)
+      if(x->type=="namespace" && x->name==default_namespace) {
+	//	namespace_children -= ({ x });
+	class_children += x->class_children;
+	module_children += x->module_children;
+	enum_children += x->enum_children;
+	method_children += x->method_children;
+      }
+    type = "autodoc";
+    ENDPROFILE("top_create");
+  }
+
+  Parser.HTML get_parser() {
+    Parser.HTML parser = ::get_parser();
+    parser->add_container("namespace", parse_node);
+    return parser;
+  }
+
+  string make_filename_low() { return "__index"; }
+  string make_filename() { return "index.html"; }
+  array(Node) get_ancestors() { return ({ }); }
+  int(0..0) find_prev_node() { return 0; }
+  int(0..0) find_next_node() { return 0; }
+  string make_class_path(void|int(0..1) header) {
+    if(header && sizeof(method_children)) {
+      if(default_namespace)
+	return "namespace "+default_namespace;
+      else
+	return "Namespaces";
+    }
+    return "";
+  }
+  string raw_class_path(void|int(0..1) header) {
+    return "";
+  }
+
+  string make_method_page(array(Node) children)
+  {
+    String.Buffer res = String.Buffer(3500);
+    foreach(children, Node node)
+      res->add("&nbsp;<a href='", make_link(node), "'>",
+	       Parser.encode_html_entities(node->name),
+	       "()</a><br />\n");
+    return (string)res;
+  }
+
+  string make_content() {
+    resolve_reference = my_resolve_reference;
+    if(!sizeof(method_children)) return "";
+
+    string contents = "<table class='sidebar'><tr>";
+    foreach(method_children/( sizeof(method_children)/4.0 ),
+            array(Node) children)
+      contents += "<td nowrap='nowrap' valign='top'>" +
+	make_method_page(children) + "</td>";
+
+    contents += "</tr><tr><td colspan='4' nowrap='nowrap'>" +
+      parse_children(Parser.XML.Tree.parse_input(data),
+		     "docgroup", parse_docgroup, 1) +
+      "</td></tr></table>";
+
+    return contents;
+  }
+
+  void make_html(string template, string path, Git.Export|void exporter) {
+    PROFILE();
+    namespace_children->make_html(template, path, exporter);
+    ::make_html(template, path, exporter);
+    ENDPROFILE("top_make_html");
+  }
+}
+
+int low_main(string doc_file, string template_file, string outdir,
+	     Git.Export|void exporter)
+{
+  PROFILE();
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)
+    werror("Reading refdoc blob %s...\n", doc_file);
+  string doc = Stdio.read_file(doc_file);
+  if(!doc) {
+    werror("Failed to load refdoc blob %s.\n", doc_file);
+    return 1;
+  }
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)
+    werror("Reading template file %s...\n", template_file);
+  string template = Stdio.read_file(template_file);
+  if(!template) {
+    werror("Failed to load template %s.\n", template_file);
+    return 1;
+  }
+  mapping m = localtime(time());
+  template = replace(template,
+		     ([ "$version$":version(),
+			"$date$":sprintf("%4d-%02d-%02d",
+					 m->year+1900, m->mon+1, m->mday),
+		     ]) );
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)
+    werror("Splitting to destination directory %s...\n", outdir);
+  TopNode top = TopNode(doc);
+  top->make_html(template, outdir, exporter);
+  ENDPROFILE("main");
+
+  if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)
+    foreach(sort(indices(profiling)), string f)
+      werror("%s: %.1f\n", f, profiling[f]/1000000.0);
+  if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) {
+    werror("%d unresolved references.\n", unresolved);
+    werror("%d documented constants/variables/functions/classes/modules.\n",
+	   sizeof(refs));
+  }
+  return 0;
+}
+
+int main(int argc, array(string) argv)
+{
+  foreach(Getopt.find_all_options(argv, ({
+    ({ "verbose", Getopt.NO_ARG,  "-v,--verbose"/"," }),
+    ({ "quiet",   Getopt.NO_ARG,  "-q,--quiet"/"," }),
+    ({ "help",    Getopt.NO_ARG,  "--help"  }),
+  })), array opt)
+    switch(opt[0]) {
+    case "verbose":
+      if (verbosity < Tools.AutoDoc.FLAG_DEBUG) {
+	verbosity += 1;
+	flags = (flags & ~Tools.AutoDoc.FLAG_VERB_MASK) | verbosity;
+      }
+      break;
+    case "quiet":
+      flags &= ~Tools.AutoDoc.FLAG_VERB_MASK;
+      verbosity = Tools.AutoDoc.FLAG_QUIET;
+      break;
+    case "help":
+      write("pike -x autodoc_to_split_html [-v|--verbose] [-q|--quiet]\n"
+	    "\t[--help] <input-file> <template-file> <output-dir>"
+	    " [<namespace>]\n");
+      return 0;
+    }
+  argv = Getopt.get_args(argv)[1..];
+  if(sizeof(argv)<4) {
+    werror("Too few arguments.\n"
+	   "pike -x autodoc_to_split_html [-v|--verbose] [-q|--quiet]\n"
+	   "\t[--help] <input-file> <template-file> <output-dir>"
+	   " [<namespace>]\n");
+    return 1;
+  }
+  if(argc>4) default_namespace=argv[4];
+
+  return low_main(argv[1], argv[2], argv[3]);
+}
diff --git a/refdoc/presentation/make_html.pike b/refdoc/presentation/make_html.pike
index 2e251e05c9..53b9475d0a 100644
--- a/refdoc/presentation/make_html.pike
+++ b/refdoc/presentation/make_html.pike
@@ -1,1373 +1,5 @@
-#define Node Parser.XML.Tree.SimpleNode
-#define XML_COMMENT Parser.XML.Tree.XML_COMMENT
-#define XML_ELEMENT Parser.XML.Tree.XML_ELEMENT
-#define XML_TEXT Parser.XML.Tree.XML_TEXT
-#define DEBUG
+/*
+ * Compat place-holder.
+ */
 
-function resolve_reference; 
-string image_path = "images/";
-string dest_path;
-string default_ns;
-
-mapping lay = ([
- "docgroup" : "\n\n<hr clear='all' size='1' noshadow='noshadow' />\n<dl>",
- "_docgroup" : "</dl>\n",
- "ndochead" : "<dd><p>",
- "_ndochead" : "</p></dd>\n",
-
- "dochead" : "\n<dt style='font-family: sans-serif'>",
- "_dochead" : "</dt>\n",
- "typehead" : "\n<dt style='font-family: sans-serif'>",
- "_typehead" : "</dt>\n",
- "docbody" : "<dd style='font-family: sans-serif'>",
- "_docbody" : "</dd>",
- "fixmehead" : "<dt style='font-family: sans-serif; color: red'>",
- "_fixmehead" : "</dt>\n",
- "fixmebody" : "<dd style='font-family: sans-serif; color: red'>",
- "_fixmebody" : "</dd>",
-
- "parameter" : "<tt><font color='#8000F0'>",
- "_parameter" : "</font></tt>",
- "example" : "<dd><pre>",
- "_example" : "</pre></dd>",
-
- "pre" : "<font face='courier'><pre>",
- "_pre" : "</pre></font>",
- "code" : "<font face='courier'><pre><code>",
- "_code" : "</code></pre></font>",
- "expr" : "<font face='courier'><code>",
- "_expr" : "</code></font>",
-
-]);
-
-string image_prefix()
-{
-  return image_path;
-} 
-
-class Position {
-  string file;
-  int line;
-
-  void update(Node n) {
-    mapping m = n->get_attributes();
-    if(!m->file || !m["first-line"]) {
-      werror("Missing attribute in source-position element. %O\n", m);
-      return;
-    }
-    file = m->file;
-    line = (int) m["first-line"];
-  }
-
-  string get() {
-    return file + ":" + line;
-  }
-}
-
-Position position = Position();
-
-string quote(string in) {
-  if(in-" "-"\t"=="") return "";
-  if(String.trim_all_whites(in)=="") return "\n";
-  return Parser.XML.Tree.text_quote(in);
-}
-
-Node get_first_element(Node n) {
-  foreach(n->get_children(), Node c)
-    if(c->get_node_type()==XML_ELEMENT) {
-      if(c->get_any_name()!="source-position")
-	return c;
-      else
-	position->update(c);
-    }
-  error( "Node had no element child.\n" );
-}
-
-int section, subsection;
-
-string low_parse_chapter(Node n, int chapter) {
-  string ret = "";
-  Node dummy = Node(XML_ELEMENT, "dummy", ([]), "");
-  foreach(n->get_elements(), Node c)
-    switch(c->get_any_name()) {
-
-    case "p":
-      dummy->replace_children( ({ c }) );
-      ret += parse_text(dummy);
-      break;
-
-    case "example":
-      ret += "<p><pre>" + c->value_of_node() + "</pre></p>\n";
-      break;
-
-    case "ul":
-    case "ol":
-      ret += (string)c;
-      break;
-
-    case "dl":
-      ret += "<dl>\n";
-      foreach(c->get_elements(), Node cc) {
-	string tag = cc->get_any_name();
-	if(tag!="dt" && tag!="dd")
-	  error("dl contained element %O.\n", tag);
-	ret += "<" + tag + ">" + parse_text(cc) + "</" + tag + ">\n";
-      }
-      ret += "</dl>";
-      break;
-
-    case "matrix":
-      ret += layout_matrix( map(c->get_elements("r")->get_elements("c"), map, parse_text) );
-      break;
-
-    case "section":
-      if(section)
-	error("Section inside section.\n");
-      if(subsection)
-	error("Section inside subsection.\n");
-      section = (int)c->get_attributes()->number;
-      ret += "</dd>\n<dt><a name='" + section + "'></a>\n"
-	"<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-	"<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; " + chapter + "." + section +
-	". " + quote(c->get_attributes()->title ||
-		     // The following for bug compat.
-		     c->get_attributes()->name) +
-	"</font></td></tr></table><br />\n"
-	"</dt>\n<dd>";
-      ret += low_parse_chapter(c, chapter);
-      section = 0;
-      break;
-
-    case "subsection":
-      if(!section)
-	error("Subsection outside section.\n");
-      if(subsection)
-	error("Subsection inside subsection.\n");
-      subsection = (int)c->get_attributes()->number;
-      ret += "</dd><dt>"
-	"<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-	"<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; " + chapter + "." + section + "." + subsection +
-	". " + quote(c->get_attributes()->title) +
-	"</font></td></tr></table><br />\n"
-	"</dt><dd>";
-      ret += low_parse_chapter(c, chapter);
-      subsection = 0;
-      break;
-
-    case "docgroup":
-      ret += parse_docgroup(c);
-      break;
-
-    case "autodoc":
-      ret += parse_autodoc(c);
-      break;
-
-    case "namespace":
-      ret += parse_namespace(c);
-      break;
-
-    case "module":
-      ret += parse_module(c);
-      break;
-
-    case "class":
-      ret += parse_class(c);
-      break;
-
-    case "enum":
-      ret += parse_enum(c);
-      break;
-
-    default:
-      error("Unknown element %O.\n", c->get_any_name());
-    }
-  return ret;
-}
-
-string parse_chapter(Node n, void|int noheader) {
-  string ret = "";
-  if(!noheader) {
-    ret += "<dl><dt>"
-      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; ";
-    if(n->get_attributes()->number)
-      ret += n->get_attributes()->number + ". ";
-    ret += quote(n->get_attributes()->title) +
-      "</font></td></tr></table><br />\n"
-      "</dt><dd>";
-  }
-
-  ret += low_parse_chapter(n, (int)n->get_attributes()->number);
-
-  if(!noheader)
-    ret = ret + "</dd></dl>"; 
-
-  return ret;
-}
-
-string parse_autodoc(Node n)
-{
-  string ret ="";
-
-  mapping m = n->get_attributes();
-
-  Node c = n->get_first_element("doc");
-  if(c)
-    ret += "<dl>" + parse_doc(c) + "</dl>";
-
-#ifdef DEBUG
-  if(sizeof(n->get_elements("doc"))>1)
-    error( "More than one doc element in autodoc node.\n" );
-#endif
-
-  ret += parse_children(n, "docgroup", parse_docgroup);
-  ret += parse_children(n, "namespace", parse_namespace);
-
-  return ret;
-}
-
-string parse_namespace(Node n, void|int noheader)
-{
-  string ret = "";
-
-  mapping m = n->get_attributes();
-  int(0..1) header = !noheader && !(m->hidden) && m->name!=default_ns;
-  if(header)
-    ret += "<dl><dt>"
-      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; Namespace <b>" +
-      m->name + "::</b></font></td></tr></table><br />\n"
-      "</dt><dd>";
-
-  Node c = n->get_first_element("doc");
-  if(c)
-    ret += "<dl>" + parse_doc(c) + "</dl>";
-
-#ifdef DEBUG
-  if(sizeof(n->get_elements("doc"))>1)
-    error( "More than one doc element in namespace node.\n" );
-#endif
-
-  ret += parse_children(n, "docgroup", parse_docgroup, noheader);
-  ret += parse_children(n, "enum", parse_enum, noheader);
-  ret += parse_children(n, "class", parse_class, noheader);
-  ret += parse_children(n, "module", parse_module, noheader);
-
-  if(header)
-    ret += "</dd></dl>"; 
-
-  return ret;
-}
-
-string parse_module(Node n, void|int noheader) {
-  string ret ="";
-
-  mapping m = n->get_attributes();
-  int(0..1) header = !noheader && !(m->hidden);
-  if(header)
-    ret += "<dl><dt>"
-      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; Module <b>" +
-      m->class_path + m->name + "</b></font></td></tr></table><br />\n"
-      "</dt><dd>";
-
-  Node c = n->get_first_element("doc");
-  if(c)
-    ret += "<dl>" + parse_doc(c) + "</dl>";
-
-#ifdef DEBUG
-  if(sizeof(n->get_elements("doc"))>1)
-    error( "More than one doc element in module node.\n" );
-#endif
-
-  ret += parse_children(n, "docgroup", parse_docgroup, noheader);
-  ret += parse_children(n, "enum", parse_enum, noheader);
-  ret += parse_children(n, "class", parse_class, noheader);
-  ret += parse_children(n, "module", parse_module, noheader);
-
-  if(header)
-    ret += "</dd></dl>"; 
-
-  return ret;
-}
-
-string parse_class(Node n, void|int noheader) {
-  string ret ="";
-  if(!noheader)
-    ret += "<dl><dt>"
-      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; CLASS <b><font color='#005080'>" +
-      n->get_attributes()->class_path + n->get_attributes()->name +
-      "</font></b></font></td></tr></table><br />\n"
-      "</dt><dd>";
-
-  Node c = n->get_first_element("doc");
-
-  if(c)
-    ret += "<dl>" + parse_doc(c) + "</dl>";
-
-#ifdef DEBUG
-  if(sizeof(n->get_elements("doc"))>1)
-    error( "More than one doc element in class node.\n" );
-#endif
-
-  ret += parse_children(n, "docgroup", parse_docgroup);
-  ret += parse_children(n, "enum", parse_enum);
-  ret += parse_children(n, "class", parse_class, noheader);
-
-  if(!noheader)
-    ret += "</dd></dl>";
-  return ret;
-}
-
-string parse_enum(Node n, void|int noheader) {
-  string ret ="";
-  if(!noheader)
-    ret += "<dl><dt>"
-      "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>"
-      "<td bgcolor='#EEEEEE'><font size='+3'>&nbsp; ENUM <b><font color='#005080'>" +
-      n->get_attributes()->class_path + n->get_attributes()->name +
-      "</font></b></font></td></tr></table><br />\n"
-      "</dt><dd>";
-
-  Node c = n->get_first_element("doc");
-
-  if(c)
-    ret += "<dl>" + parse_doc(c) + "</dl>";
-
-#ifdef DEBUG
-  if(sizeof(n->get_elements("doc"))>1)
-    error( "More than one doc element in enum node.\n" );
-#endif
-
-  ret += parse_children(n, "docgroup", parse_docgroup);
-
-  if(!noheader)
-    ret += "</dd></dl>";
-  return ret;
-}
-
-string layout_matrix( array(array(string)) rows ) {
-  string ret = "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n"
-    "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n";
-
-  int dim;
-  foreach(rows, array row)
-    dim = max(dim, sizeof(row));
-
-  foreach(rows, array row) {
-    ret += "<tr valign='top'>";
-    if(sizeof(row)<dim)
-      ret += "<td bgcolor='white'>" + row[..sizeof(row)-2]*"</td><td bgcolor='white'>" +
-	"</td><td bgcolor='white' colspan='"+ (dim-sizeof(row)) + "'>" + row[-1] + "</td>";
-    else
-      ret += "<td bgcolor='white'>" + row*"</td><td bgcolor='white'>" + "</td>";
-    ret += "</tr>\n";
-  }
-
-  return ret + "</table></td></tr></table><br />\n";
-}
-
-// ({  ({ array(string)+, void|string  })* })
-void nicebox(array rows, String.Buffer ret) {
-  ret->add( "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n"
-	    "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n" );
-
-  int dim;
-  foreach(rows, array row)
-    dim = max(dim, sizeof(row));
-
-  foreach(rows, array row) {
-    if(sizeof(row)==1) {
-      if(stringp(row[0]))
-	ret->add( "<tr valign='top'><td bgcolor='white' colspan='", (string)dim, "'>",
-		  row[0], "</td></tr>\n" );
-      else
-	foreach(row[0], string elem)
-	  ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td>",
-		    (dim==2?"<td bgcolor='white'>&nbsp;</td>":""), "</tr>\n" );
-    }
-    else if(sizeof(row[0])==1)
-      ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0],
-		"</tt></td><td bgcolor='white'>", row[1], "</td></tr>\n" );
-    else {
-      ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0],
-		"</tt></td><td bgcolor='white' rowspan='", (string)sizeof(row[0]), "'>",
-		row[1], "</td></tr>\n" );
-      foreach(row[0][1..], string elem)
-	ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td></tr>\n" );
-    }
-  }
-
-  ret->add( "</table></td></tr></table><br />\n" );
-}
-
-void build_box(Node n, String.Buffer ret, string first, string second, function layout, void|string header) {
-  array rows = ({});
-  if(header) rows += ({ ({ header }) });
-  foreach(n->get_elements(first), Node d) {
-    array elems = ({});
-    foreach(d->get_elements(second), Node e)
-      elems += ({ layout(e) });
-    if(d->get_first_element("text"))
-      rows += ({ ({ elems, parse_text(d->get_first_element("text")) }) });
-    else
-      rows += ({ ({ elems }) });
-  }
-  nicebox(rows, ret);
-}
-
-string parse_text(Node n, void|String.Buffer ret) {
-  if(n->get_node_type()==XML_TEXT && n->get_text()) {
-    if(ret)
-      ret->add(quote(n->get_test()));
-    else
-      return quote(n->get_text());
-  }
-
-  int cast;
-  if(!ret) {
-    ret = String.Buffer();
-    cast = 1;
-  }
-
-  foreach(n->get_children(), Node c) {
-    int node_type = c->get_node_type();
-    if(c->get_node_type()==XML_TEXT) {
-      // Don't use quote() here since we don't want to strip whitespace.
-      ret->add(Parser.XML.Tree.text_quote (c->get_text()));
-      continue;
-    }
-
-    if(c->get_node_type()==XML_COMMENT)
-      continue;
-
-#ifdef DEBUG
-    if(c->get_node_type()!=XML_ELEMENT) {
-      error( "Forbidden node type " + c->get_node_type() + " in doc node.\n" );
-    }
-#endif
-
-    array rows;
-    string name = c->get_any_name();
-    switch(name) {
-    case "text":
-      ret->add("<dd>");
-      parse_text(c, ret);
-      ret->add("</dd>\n");
-      break;
-
-    case "p":
-    case "b":
-    case "i":
-    case "tt":
-    case "sub":
-    case "sup":
-      ret->add("<", name, ">");
-      parse_text(c, ret);
-      ret->add("</", name, ">");
-      break;
-
-    case "pre":
-      ret->add(lay->pre);
-      parse_text(c, ret);
-      ret->add(lay->_pre);
-      break;
-
-    case "code":
-      ret->add(lay->code);
-      parse_text(c, ret);
-      ret->add(lay->_code);
-      break;
-
-    case "expr":
-      ret->add(lay->expr, replace(parse_text(c), " ", "&nbsp;"), lay->_expr);
-      break;
-
-    case "ref":
-      if(resolve_reference) {
-	ret->add(resolve_reference(parse_text(c), c->get_attributes()));
-	break;
-      }
-      string ref;
-      //ref = c->get_attributes()->resolved;
-      if(!ref) ref = parse_text(c);
-      ret->add("<font face='courier'>", ref, "</font>");
-      break;
-
-    case "dl":
-      ret->add("<dl>", map(c->get_elements("group"), parse_text)*"", "</dl>");
-      break;
-
-    case "item":
-      if(c->get_attributes()->name)
-	ret->add("<dt>", c->get_attributes()->name, "</dt>\n");
-#ifdef DEBUG
-      if(c->count_children())
-	error( "dl item has a child.\n" );
-#endif
-      break;
-
-    case "mapping":
-      build_box(c, ret, "group", "member",
-		lambda(Node n) {
-		  return "<font color='green'>" +
-		    parse_text(n->get_first_element("index")) +
-		    "</font> : " +
-		    parse_type(get_first_element(n->get_first_element("type")));
-		});
-      break;
-
-    case "array":
-      build_box(c, ret, "group", "elem",
-		lambda(Node n) {
-		  string index ="";
-		  if(n->get_first_element("index"))
-		    index = parse_text(n->get_first_element("index"));
-		  else {
-		    if(n->get_first_element("minindex"))
-		      index = parse_text(n->get_first_element("minindex"));
-		    index += "..";
-		    if(n->get_first_element("maxindex"))
-		      index += parse_text(n->get_first_element("maxindex"));
-		  }
-		  return parse_type(get_first_element(n->get_first_element("type"))) +
-		    " <font color='green'>" + index + "</font>"; }, "Array" );
-      break;
-
-    case "int":
-      build_box(c, ret, "group", "value",
-		lambda(Node n) {
-		  string tmp = "<font color='green'>";
-		  Node min = n->get_first_element("minvalue");
-		  Node max = n->get_first_element("maxvalue");
-		  if(min || max) {
-		    if(min) tmp += parse_text(min);
-		    tmp += "..";
-		    if(max) tmp += parse_text(max);
-		  }
-		  else
-		    tmp += parse_text(n);
-		  return tmp + "</font>";
-		} );
-      break;
-
-    case "mixed":
-      if(c->get_attributes()->name)
-	ret->add("<tt>", c->get_attributes()->name, "</tt> can have any of the following types:<br />");
-      rows = ({});
-      foreach(c->get_elements("group"), Node d)
-	rows += ({ ({
-	  ({ parse_type(get_first_element(d->get_first_element("type"))) }),
-	  parse_text(d->get_first_element("text"))
-	}) });
-      nicebox(rows, ret);
-      break;
-
-    case "string": // Not in XSLT
-      build_box(c, ret, "group", "value",
-		lambda(Node n) {
-		  return "<font color='green'>" + parse_text(n) + "</font>";
-		} );
-      break;
-
-    case "multiset": // Not in XSLT
-      build_box(c, ret, "group", "index",
-		lambda(Node n) {
-		  return "<font color='green'>" +
-		    parse_text(n->get_first_element("value")) + "</font>";
-		} );
-      break;
-
-    case "image": // Not in XSLT
-      mapping m = c->get_attributes();
-      m->src = image_prefix() + m_delete(m, "file");
-      ret->add( sprintf("<img%{ %s='%s'%} />", (array)m) );
-      break;
-
-    case "url": // Not in XSLT
-#ifdef DEBUG
-      if(c->count_children()!=1 && c->get_node_type()!=XML_ELEMENT)
-	error( "url node has not one child. %O\n", c->html_of_node() );
-#endif
-      m = c->get_attributes();
-      if(!m->href)
-	m->href=c->value_of_node();
-      ret->add( sprintf("<a%{ %s='%s'%}>%s</a>",
-			(array)m, c->value_of_node()) );
-      break;
-
-    case "section":
-      if(section)
-	error("Section inside section.\n");
-      ret->add ("<h2>", quote (c->get_attributes()->title ||
-			       // The following for bug compat.
-			       c->get_attributes()->name),
-		"</h2>\n");
-      if (!equal (c->get_children()->get_any_name(), ({"text"})))
-	error ("Expected a single <text> element inside <section>.\n");
-      section = -1;
-      parse_text (c->get_children()[0], ret);
-      section = 0;
-      break;
-
-    case "ul":
-      ret->add( "<ul>\n" );
-      foreach(c->get_elements("group"), Node c) {
-	ret->add("<li>");
-	array(Node) d = c->get_elements("item");
-	if(sizeof(d->get_attributes()->name-({0}))) {
-	  ret->add("<b>");
-	  ret->add( String.implode_nicely(d->get_attributes()->name-({0})) );
-	  ret->add("</b>");
-	}
-	Node e = c->get_first_element("text");
-	if(e)
-	  parse_text(e, ret);
-	ret->add("</li>");
-      }
-      ret->add("</ul>");
-      break;
-
-    case "ol":
-      ret->add("<ol>\n");
-      foreach(c->get_elements("group"), Node c) {
-	ret->add("<li>");
-	parse_text(c->get_first_element("text"), ret);
-	ret->add("</li>");
-      }
-      ret->add("</ol>");
-      break;
-
-    case "source-position":
-      position->update(c);
-      break;
-
-    case "matrix":
-      ret->add( layout_matrix( map(c->get_elements("r")->get_elements("c"),
-				   map, parse_text) ) );
-      break;
-
-    case "fixme":
-      ret->add("<font color='red'>FIXME: ");
-      parse_text(c, ret);
-      ret->add("</font>");
-      break;
-
-    // Not really allowed
-    case "br":
-      ret->add( sprintf("<%s%{ %s='%s'%} />", c->get_any_name(), (array)c->get_attributes()) );
-      break;
-    case "table":
-    case "td":
-    case "tr":
-    case "th":
-      ret->add( sprintf("<%s%{ %s='%s'%}>%s</%s>",
-			c->get_any_name(), (array)c->get_attributes(),
-			parse_text(c), c->get_any_name()) );
-      break;
-
-    default:
-#ifdef DEBUG
-      werror("\n%s\n", (string)c);
-      error( "Illegal element \"" + name + "\" in mode text.\n" );
-#endif
-      break;
-    }
-  }
-
-  if(cast)
-    return ret->get();
-}
-
-string parse_doc(Node n, void|int no_text) {
-  string ret ="";
-
-  Node c = n->get_first_element("text");
-  if(c)
-    ret += lay->dochead + "Description" + lay->_dochead +
-      lay->docbody + parse_text(c) + lay->_docbody;
-
-  foreach(n->get_elements("group"), Node c) {
-    Node header = c->get_first_element();
-    string name = header->get_any_name();
-    switch(name) {
-    case "param":
-      foreach(c->get_elements("param"), Node d)
-	ret += lay->dochead + "Parameter " + lay->parameter +
-	  quote(d->get_attributes()->name) + lay->_parameter + lay->_dochead +
-	  "<dd></dd>";
-      if (c = c->get_first_element("text")) {
-	ret += lay->docbody + parse_text(c) + lay->_docbody;
-      }
-      break;
-
-    case "seealso":
-      ret += lay->dochead + "See also" + lay->_dochead;
-      if (c = c->get_first_element("text")) {
-	ret += lay->docbody + parse_text(c) + lay->_docbody;
-      }
-      break;
-
-    case "fixme":
-      ret += lay->fixmehead + "FIXME" + lay->_fixmehead;
-      if (c = c->get_first_element("text")) {
-	ret += 	lay->fixmebody + parse_text(c) + lay->_fixmebody;
-      }
-      break;
-
-    case "deprecated":
-      ret += lay->dochead + String.capitalize(name) + lay->_dochead;
-      array(Node) replacements = header->get_elements("name");
-      if (sizeof(replacements)) {
-	ret += lay->docbody +
-	  "<p>Replaced by " +
-	  String.implode_nicely(map(replacements, parse_text)) + ".</p>\n" +
-	  lay->_docbody;
-      }
-      if (c = c->get_first_element("text")) {
-	ret += lay->docbody + parse_text(c) + lay->_docbody;
-      }
-      break;
-
-    case "bugs":
-    case "note":
-    case "returns":
-    case "throws":
-      ret += lay->dochead + String.capitalize(name) + lay->_dochead;
-      if (c = c->get_first_element("text")) {
-	ret += lay->docbody + parse_text(c) + lay->_docbody;
-      }
-      break;
-
-    case "example":
-      ret += lay->dochead + "Example" + lay->_dochead;
-      if (c = c->get_first_element("text")) {
-	ret += lay->example + parse_text(c) + lay->_example;
-      }
-      break;
-
-    default:
-      error( "Unknown group type \"" + name + "\".\n" );
-    }
-  }
-
-  return ret;
-}
-
-string parse_type(Node n, void|string debug) {
-  if(n->get_node_type()!=XML_ELEMENT)
-    return "";
-
-  string ret = "";
-  Node c, d;
-  switch(n->get_any_name()) {
-
-  case "object":
-    if(n->count_children()) {
-      if (resolve_reference) {
-	ret += "<font color='#005080'>" +
-	  resolve_reference(n->value_of_node(), n->get_attributes()) +
-	  "</font>";
-      } else {
-	ret += "<font color='#005080'>" + n->value_of_node() + "</font>";
-      }
-    } else
-      ret += "<font color='#202020'>object</font>";
-    break;
-
-  case "type":
-    ret += "<font color='#202020'>type</font>";
-    if (n->count_children() && (c = get_first_element(n)) &&
-	(c->get_any_name() != "mixed")) {
-      ret += "(" + parse_type(c) + ")";
-    }
-    break;
-
-  case "multiset":
-    ret += "<font color='#202020'>multiset</font>";
-    c = n->get_first_element("indextype");
-    if(c) ret += "(" + parse_type( get_first_element(c) ) + ")";
-    break;
-
-  case "array":
-    ret += "<font color='#202020'>array</font>";
-    c = n->get_first_element("valuetype");
-    if(c) ret += "(" + parse_type( get_first_element(c) ) + ")";
-    break;
-
-  case "mapping":
-    ret += "<font color='#202020'>mapping</font>";
-    c = n->get_first_element("indextype");
-    d = n->get_first_element("valuetype");
-    if(c && d)
-      ret += "(" + parse_type( get_first_element(c) ) + ":" +
-	parse_type( get_first_element(d) ) + ")";
-#ifdef DEBUG
-    if( !c != !d )
-      error( "Indextype/valuetype defined while the other is not in mapping.\n" );
-#endif
-    break;
-
-  case "function":
-    ret += "<font color='#202020'>function</font>";
-    array(Node) args = n->get_elements("argtype");
-    d = n->get_first_element("returntype");
-    // Doing different than the XSL here. Must both
-    // argtype and returntype be defined?
-    if(args || d) {
-      ret += "(";
-      if(args) ret += map(args->get_children() * ({}), parse_type)*", ";
-      ret += ":";
-      if(d) ret += parse_type( get_first_element(d) );
-      else ret += "<font color='#202020'>void</font>";
-      ret += ")";
-    }
-    break;
-
-  case "varargs":
-#ifdef DEBUG
-    if(!n->count_children())
-      error( "varargs node must have child node.\n" );
-#endif
-    ret += parse_type(get_first_element(n)) + " ... ";
-    break;
-
-  case "or":
-    ret += map(filter(n->get_children(),
-		      lambda(Node n){
-			return n->get_node_type()==XML_ELEMENT;
-		      }), parse_type)*"|";
-    break;
-
-  case "string": case "void": case "program": case "mixed": case "float":
-  case "zero":
-    ret += "<font color='#202020'>" + n->get_any_name() + "</font>";
-    break;
-
-  case "int":
-    ret += "<font color='#202020'>int</font>";
-    c = n->get_first_element("min");
-    d = n->get_first_element("max");
-    if(c && d)
-      ret += "(" + c->value_of_node() + ".." + d->value_of_node() + ")";
-    else if(c)
-      ret += "(" + c->value_of_node() + "..)";
-    else if(d)
-      ret += "(.." + d->value_of_node() + ")";
-    break;
-
-  case "attribute":
-    string attr = n->get_first_element("attribute")->value_of_node();
-    string subtype =
-      parse_type(n->get_first_element("subtype")->get_first_element());
-    if (n->get_first_element("prefix")) {
-      if (attr == "\"deprecated\"") {
-	ret += "<font color='#600000'>__deprecated__</font> " + subtype;
-      } else {
-	ret += sprintf("__attribute__(%s) %s", attr, subtype);
-      }
-    } else if (attr == "\"deprecated\"") {
-      ret += "<font color='#600000'>__deprecated__</font>(" + subtype + ")";
-    } else {
-      ret += sprintf("__attribute__(%s, %s)", attr, subtype);
-    }
-    break;
-
-    // Modifiers:
-  case "extern": // Not in XSLT
-    ret += "extern ";
-    break;
-  case "final": // Not in XSLT
-  case "nomask": // Not in XSLT
-    ret += "final ";
-    break;
-  case "inline": // Not in XSLT
-  case "local": // Not in XSLT
-    ret += "local ";
-    break;
-  case "optional": // Not in XSLT
-    ret += "optional ";
-    break;
-  case "private": // Not in XSLT
-    ret += "private ";
-    break;
-  case "protected": // Not in XSLT
-  case "static": // Not in XSLT
-    ret += "protected ";
-    break;
-  case "public": // Not in XSLT
-    // Ignored.
-    break;
-  case "variant": // Not in XSLT
-    ret += "variant ";
-    break;
-
-  default:
-    error( "Illegal element " + n->get_any_name() + " in mode type.\n" );
-    break;
-  }
-  return ret;
-}
-
-void resolve_class_paths(Node n, string|void path, Node|void parent)
-{
-  if (!path) path = "";
-  mapping attrs = n->get_attributes() || ([]);
-  string name = attrs->name;
-  switch(n->get_any_name()) {
-  case "arguments":
-  case "returntype":
-  case "type":
-  case "doc":
-  case "source-position":
-  case "modifiers":
-  case "classname":
-    // We're not interrested in the stuff under the above nodes.
-    return;
-  default:
-    werror("Unhandled node: %O path:%O name: %O, parent: %O\n",
-	   n->get_any_name(), path, name, parent&&parent->get_any_name());
-    // FALL_THROUGH
-  case "":
-  case "docgroup":
-    break;
-    
-  case "manual":
-  case "dir":
-  case "file":
-  case "chapter":
-  case "section":
-  case "subsection":
-    foreach(filter(n->get_children(),
-		   lambda(Node n) {
-		     return (<"manual", "dir", "file", "chapter",
-			      "section", "subsection", "autodoc">)
-		       [n->get_any_name()];
-		   }), Node child) {
-      resolve_class_paths(child, "", n);
-    }
-    return;
-  case "autodoc":
-    path = "";
-    break;
-  case "namespace":
-    if ((<"", "lfun">)[name]) {
-      // Censor namespaces other than :: and lfun::
-      path = name+"::";
-    } else {
-      path = "";
-    }
-    break;
-  case "class":
-  case "module":
-    attrs->class_path = path;
-    path += name + ".";
-    break;
-  case "enum":
-    // Enum names don't show up in the path.
-    attrs->class_path = path;
-    break;
-  case "method":
-    // Special case for create().
-    if (name == "create") {
-      // Get rid of the extra dot.
-      attrs->class_path = path[..sizeof(path)-2];
-    }
-    else
-    {
-      // Hide the class path for methods.
-      attrs->class_path = "";
-    }
-    return;
-  case "constant":
-  case "inherit":
-  case "import":
-  case "typedef":
-  case "variable":
-    // These don't have children.
-    attrs->class_path = path;
-    return;
-  }
-  foreach(n->get_children(), Node child)
-  {
-    resolve_class_paths(child, path, n);
-  }
-}
-
-#if 0
-// DEAD code (for reference only).
-string render_class_path(Node n,int|void class_only)
-{
-  array a = reverse(n->get_ancestors(0));
-  array b = a->get_any_name();
-  int root;
-  foreach( ({ "manual", "dir", "file", "chapter",
-	      "section", "subsection" }), string node)
-    root = max(root, search(b, node));
-  a = a[root+1..];
-  if(sizeof(a) && a[0]->get_any_name() == "autodoc")
-    a = a[1..];
-  if (!sizeof(a)) return "";
-  string ret = "";
-  if ((sizeof(a) > 1) && a[-2]->get_any_name() == "enum") {
-    // Enum names don't show up in the path.
-    a = a[..sizeof(a)-3] + a[sizeof(a)-1..];
-  }
-  if (a[0]->get_any_name() == "namespace") {
-    a = a->get_attributes()->name;
-    a[0] += "::";
-    if ((<"::","lfun::">)[a[0]]) {
-      // Censor namespaces other than :: and lfun::
-      ret = a[0];
-    }
-    a = a[1..];
-  } else {
-    a = a->get_attributes()->name;
-  }
-  ret += a * ".";
-  if (!sizeof(ret) || ret[-1] == ':') return ret;
-  if((<"class", "module", "enum">)[n->get_any_name()])
-    return ret + ".";
-
-  // Note we need two calls to get_parent() to skip over the docgroup.
-  Node parent = n->get_parent()->get_parent();
-
-  // Hide the enum part.
-  if (parent->get_any_name()=="enum") parent = parent->get_parent();
-
-  if(parent->get_any_name()=="class")
-    if( !class_only )
-      return ""; //ret + "()->";
-    else
-      return ret;
-  if(parent->get_any_name()=="module")
-    return ret + ".";
-  werror("Failure, raw path: %{%O, %}\n",
-	 reverse(n->get_ancestors(0))->get_any_name());
-  return " (could not resolve) ";
-#ifdef DEBUG
-  error( "Parent module is " + n->get_parent()->get_any_name() + ".\n" );
-#else
-    return "";
-#endif
-}
-#endif /* 0 */
-
-string parse_not_doc(Node n) {
-  string ret = "";
-  int method, argument, variable, const, typedf;
-
-  foreach(n->get_children(), Node c) {
-
-    if(c->get_node_type()!=XML_ELEMENT)
-      continue;
-
-    Node cc;
-    switch(c->get_any_name()) {
-
-    case "doc":
-      continue;
-
-    case "source-position":
-      position->update(c);
-      continue;
-
-    case "method":
-      if(method++) ret += "<br />\n";
-#ifdef DEBUG
-      if(!c->get_first_element("returntype"))
-	continue;
-	// error( "No returntype element in method element.\n" );
-#endif
-      switch( c->get_attributes()->name )
-      {
-	case "create":
-	  ret += "<tt>" + parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children
-	  ret += " ";
-	  ret += c->get_attributes()->class_path+"<b>(</b>";
-	  ret += parse_not_doc( c->get_first_element("arguments") );
-	  ret += "<b>)</b></tt>";
-	  break;
-	default:
-	  ret += "<tt>";
-	  cc = c->get_first_element("modifiers");
-	  if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
-	  ret += parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children
-	  ret += " ";
-	  ret += c->get_attributes()->class_path;
-	  ret += "<b><font color='#000066'>" + c->get_attributes()->name + "</font>(</b>";
-	  ret += parse_not_doc( c->get_first_element("arguments") );
-	  ret += "<b>)</b></tt>";
-	  break;
-      }
-      break;
-
-    case "argument":
-      if(argument++) ret += ", ";
-      cc = c->get_first_element("value");
-      if(cc) ret += "<font color='green'>" + cc->value_of_node() + "</font>";
-      else if( !c->count_children() );
-      else if( get_first_element(c)->get_any_name()=="type") {
-	ret += parse_type(get_first_element(get_first_element(c)));
-	if(c->get_attributes()->name)
-	  ret += " <font color='#005080'>" +
-	    c->get_attributes()->name + "</font>";
-      }
-      else
-	error( "Malformed argument element.\n" + c->html_of_node() + "\n" );
-      break;
-
-    case "variable":
-      if(variable++) ret += "<br />\n";
-      ret += "<tt>";
-      cc = c->get_first_element("modifiers");
-      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
-      ret += parse_type(get_first_element(c->get_first_element("type")), "variable") + " " +
-	c->get_attributes()->class_path + "<b><font color='#F000F0'>" + c->get_attributes()->name +
-	"</font></b></tt>";
-      break;
-
-    case "constant":
-      if(const++) ret += "<br />\n";
-      ret += "<tt>";
-      cc = c->get_first_element("modifiers");
-      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
-      ret += "constant ";
-      if (Node type = c->get_first_element ("type"))
-	ret += parse_type (get_first_element (type), "constant") + " ";
-      ret += c->get_attributes()->class_path;
-      ret += "<font color='#F000F0'>" + c->get_attributes()->name + "</font>";
-      cc = c->get_first_element("typevalue");
-      if(cc) ret += " = " + parse_type(get_first_element(cc));
-      ret += "</tt>";
-      break;
-
-    case "typedef":
-      if(typedf++) ret += "<br />\n";
-      ret += "<tt>";
-      cc = c->get_first_element("modifiers");
-      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
-      ret += "typedef ";
-      ret += parse_type(get_first_element(c->get_first_element("type")), "typedef") + " " +
-	c->get_attributes()->class_path + "<font color='#F000F0'>" + c->get_attributes()->name +
-	"</font></tt>";
-      break;
-
-    case "inherit":
-      ret += "<font face='courier'>";
-      cc = c->get_first_element("modifiers");
-      if(cc) ret += map(cc->get_children(), parse_type)*" " + " ";
-      ret += "inherit ";
-      Node n = c->get_first_element("classname");
-      if (resolve_reference) {
-	ret += "</font>" +
-	  resolve_reference(n->value_of_node(), n->get_attributes());
-      } else {
-	ret += Parser.encode_html_entities(n->value_of_node()) + "</font>";
-      }
-      if (c->get_attributes()->name) {
-	ret += "<font face='courier'> : " + "<font color='#F000F0'>" +
-	  Parser.encode_html_entities(c->get_attributes()->name) +
-	  "</font></font>";
-      }
-      break;
-
-    // We don't need import information.
-    case "import":
-      break;
-
-    default:
-      error( "Illegal element " + c->get_any_name() + " in !doc.\n" );
-      break;
-    }
-  }
-
-  return ret;
-}
-
-int foo;
-
-string parse_docgroup(Node n) {
-  mapping m = n->get_attributes();
-  string ret = lay->docgroup;
-
-  if(lay->typehead) {
-    ret += lay->typehead;
-    if(m["homogen-type"]) {
-      string type = "<font face='Helvetica'>" +
-	quote(String.capitalize(m["homogen-type"])) +
-	"</font>\n";
-      if(m["homogen-name"]) {
-	ret += type + "<font size='+1'><b>" +
-	  quote((m->belongs?m->belongs+" ":"") + m["homogen-name"]) +
-	  "</b></font>\n";
-      } else {
-	array(string) names =
-	  Array.uniq(map(n->get_elements(m["homogen-type"]),
-			 lambda(Node child) {
-			   return child->get_attributes()->name ||
-			     child->value_of_node();
-			 }));
-	foreach(names, string name)
-	  ret += type + "<font size='+1'><b>" + name + "</b></font><br />\n";
-      }
-    }
-    else
-      ret += "Syntax";
-    ret += lay->_typehead;
-  }
-
-  ret += lay->ndochead;
-
-  ret += parse_not_doc(n);
-
-  ret += lay->_ndochead;
-
-  foreach(n->get_elements("doc"), Node c)
-    ret += parse_doc(c);
-
-  return ret + lay->_docgroup;
-}
-
-string parse_children(Node n, string tag, function cb, mixed ... args) {
-  string ret = "";
-  foreach(n->get_elements(tag), Node c)
-    ret += cb(c, @args);
-
-  return ret;
-}
-
-string manual_title = "Pike Reference Manual";
-string frame_html(string res, void|string title) {
-  title = title || manual_title;
-  return "<html><head><title>" + quote(title) + "</title></head>\n"
-    "<body bgcolor='white' text='black'>\n" + res +
-    "</body></html>";
-}
-
-string layout_toploop(Node n) {
-  string res = "";
-  foreach(n->get_elements(), Node c)
-    switch(c->get_any_name()) {
-
-    case "dir":
-      string cwd;
-      if(dest_path)
-      {
-        cwd=getcwd();
-        cd(dest_path);
-      }  
-      Stdio.mkdirhier(c->get_attributes()->name);
-      layout_toploop(c);
-      if(cwd)
-        cd(cwd);
-      break;
-
-    case "file":
-      if(dest_path)
-      {
-        cwd=getcwd();
-        cd(dest_path);
-      }  
-      Stdio.write_file( c->get_attributes()->name,
-			frame_html(layout_toploop(c)) );
-      if(cwd)
-        cd(cwd);
-      break;
-
-    case "chapter":
-      res += parse_chapter(c);
-      break;
-
-    default:
-      error("Unknown element %O.\n", c->get_any_name());
-    }
-  return res;
-}
-
-int main(int num, array args) {
-
-  int t = time();
-  string title;
-
-  foreach(Getopt.find_all_options(args, ({
-    ({ "img",   Getopt.HAS_ARG, "--img"   }),
-    ({ "dest",  Getopt.HAS_ARG, "--dest"  }),
-    ({ "title", Getopt.HAS_ARG, "--title" }),
-    ({ "defns", Getopt.HAS_ARG, "--default-ns" }),
-    ({ "help",  Getopt.NO_ARG,  "--help"  }),
-  })), array opt)
-    switch(opt[0]) {
-    case "img":
-      image_path = opt[1];
-      break;
-    case "dest":
-      dest_path = opt[1];
-      break;
-    case "title":
-      title = opt[1];
-      break;
-    case "defns":
-      default_ns = opt[1];
-      break;
-    case "help":
-      write(#"make_html.pike [args] <input file> [<output file>]
---img=<image path>
---dest=<destination path>
-
---title=<document title>
-");
-      break;
-    }
-  args = Getopt.get_args(args)[1..];
-
-  if(!sizeof(args))
-    error( "No input file given.\n" );
-
-  string file = Stdio.read_file(args[0]);
-  if(!file)
-    error( "Could not read %s.\n", args[0] );
-  if(!sizeof(file))
-    error( "%s is empty.\n", args[0] );
-
-  // We are only interested in what's in the
-  // module container.
-  werror("Parsing %O...\n", args[0]);
-  Node n = Parser.XML.Tree.simple_parse_input(file);
-
-  Node n2 = n->get_first_element("manual");
-  if(!n2) {
-    n2 = n->get_first_element("autodoc");
-    if(!n2) {
-      werror("Not an autodoc XML file.\n");
-      return 0;
-    }
-
-    Node chap = Node(XML_ELEMENT, "chapter",
-		     (["title":title||"Documentation"]), 0);
-    chap->add_child( n2 );
-
-    string fn = sizeof(args)>1 ? args[1] : "index.html";
-    Node file = Node(XML_ELEMENT, "file", (["name":fn]), 0);
-    file->add_child( chap );
-
-    Node top = Node(XML_ELEMENT, "manual", ([]), 0);
-    top->add_child( file );
-    n = top;
-  }
-  else
-    n = n2;
-
-  werror("Resolving class paths...\n");
-  resolve_class_paths(n);
-
-  mapping m = n->get_attributes();
-
-  werror("Layouting...\n");
-  manual_title = m->title || (m->version?"Reference Manual for "+m->version:"Pike Reference Manual");
-  layout_toploop(n);
-  werror("Took %d seconds.\n\n", time()-t);
-
-  return 0;
-}
+inherit Tools.Standalone.autodoc_to_html;
diff --git a/refdoc/presentation/tree-split-autodoc.pike b/refdoc/presentation/tree-split-autodoc.pike
index 799d51f505..0aa83cd205 100644
--- a/refdoc/presentation/tree-split-autodoc.pike
+++ b/refdoc/presentation/tree-split-autodoc.pike
@@ -1,730 +1,5 @@
 /*
+ * Compat place-holder.
  */
 
-inherit "make_html.pike";
-
-mapping (string:Node) refs   = ([ ]);
-string default_namespace;
-
-string extra_prefix = "";
-string image_prefix()
-{
-  return extra_prefix + ::image_prefix();
-}
-
-int unresolved;
-mapping profiling = ([]);
-#define PROFILE int profilet=gethrtime
-#define ENDPROFILE(X) profiling[(X)] += gethrtime()-profilet;
-
-string cquote(string n)
-{
-  string ret="";
-  // n = replace(n, ([ "&gt;":">", "&lt;":"<", "&amp;":"&" ]));
-  while(sscanf((string)n,"%[._a-zA-Z0-9]%c%s",string safe, int c, n)==3) {
-    switch(c) {
-    default:  ret += sprintf("%s_%02X",safe,c); break;
-    case '+': ret += sprintf("%s_add",safe); break;
-    case '`': ret += sprintf("%s_backtick",safe);  break;
-    case '=': ret += sprintf("%s_eq",safe); break;
-    }
-  }
-  ret += n;
-  return ret;
-}
-
-string create_reference(string from, string to, string text) {
-  array a = (to/"::");
-  switch(sizeof(a)) {
-  case 2:
-    if (sizeof(a[1])) {
-      // Split trailing module path.
-      a = a[0..0] + a[1]/".";
-    } else {
-      // Get rid of trailing "".
-      a = a[0..0];
-    }
-    a[0] += "::";
-    break;
-  case 1:
-    a = a[0]/".";
-    break;
-  default:
-    error("Bad reference: %O\n", to);
-  }
-  return "<font face='courier'><a href='" +
-    "../"*max(sizeof(from/"/") - 2, 0) + map(a, cquote)*"/" + ".html'>" +
-    text +
-    "</a></font>";
-}
-
-multiset missing = (< "foreach", "catch", "throw", "sscanf", "gauge", "typeof" >);
-
-
-class Node
-{
-  string type;
-  string name;
-  string data;
-  array(Node) class_children  = ({ });
-  array(Node) module_children = ({ });
-  array(Node) enum_children = ({ });
-  array(Node) method_children = ({ });
-
-  Node parent;
-
-  string _sprintf() {
-    return sprintf("Node(%O,%O,%d)", type, name, data?sizeof(data):0);
-  }
-
-  void create(string _type, string _name, string _data, void|Node _parent)
-  {
-    if(!_type || !_name) throw( ({ "No type or name\n", backtrace() }) );
-    type = _type;
-    name = _name;
-    parent = _parent;
-    data = get_parser()->finish( _data )->read();
-
-    string path = raw_class_path();
-    refs[path] = this_object();
-
-    sort(class_children->name, class_children);
-    sort(module_children->name, module_children);
-    sort(enum_children->name, enum_children);
-    sort(method_children->name, method_children);
-
-    method_children = check_uniq(method_children);
-
-    /*
-    foreach(method_children, Node m)
-      if( (<"create","destroy">)[m->name] ) {
-	method_children -= ({ m });
-	string d;
-	sscanf(m->data, "%*s<docgroup%s/docgroup>", d);
-	if(d)
-	  data += "<docgroup" + d + "/docgroup>";
-      }
-    */
-
-    data = make_faked_wrapper(data);
-  }
-
-  array(Node) check_uniq(array children) {
-    array names = children->name;
-    foreach(Array.uniq(names), string n)
-      if(sizeof(filter(names, lambda(string in) { return in==n; }))!=1) {
-	string d="";
-	Parser.HTML parser = Parser.HTML();
-	parser->case_insensitive_tag(1);
-	parser->xml_tag_syntax(3);
-	parser->add_container("docgroup",
-          lambda(Parser.HTML p, mapping m, string c) {
-	    d += sprintf("<docgroup%{ %s='%s'%}>%s</docgroup>",
-			 (array)m, c);
-	    return "";
-	  });
-
-	foreach(children, Node c)
-	  if(c->name==n) parser->feed(c->data);
-	parser->finish();
-	d = make_faked_wrapper(d);
-
-	foreach(children; int i; Node c)
-	  if(c->name==n) {
-	    if(d) {
-	      c->data = d;
-	      d = 0;
-	    }
-	    else children[i] = 0;
-	  }
-	children -= ({ 0 });
-      }
-    return children;
-  }
-
-  protected string parse_node(Parser.HTML p, mapping m, string c) {
-    if(!m->name) error("Unnamed %O %O\n", p->tag_name(), m);
-    this_object()[p->tag_name()+"_children"] +=
-      ({ Node( p->tag_name(), m->name, c, this_object() ) });
-    return "";
-  }
-
-  array(string) my_parse_docgroup(Parser.HTML p, mapping m, string c)
-  {
-    foreach(({"homogen-name", "belongs"}), string attr) {
-      if (m[attr]) m[attr] = Parser.parse_html_entities(m[attr]);
-    }
-    if(m["homogen-type"]) {
-      switch( m["homogen-type"] ) {
-
-      case "method":
-	if( m["homogen-name"] ) {
-	  string name = m["homogen-name"];
-	  if(m->belongs) {
-	    if(m->belongs[-1]==':') name = m->belongs + name;
-	    else name = m->belongs + "." + name;
-	  }
-	  method_children +=
-	    ({ Node( "method", name, c, this_object() ) });
-	  return ({ "" });
-	}
-
-	// Several different methods documented with the same blurb.
-	array names = ({});
-	Parser.HTML parser = Parser.HTML();
-	parser->case_insensitive_tag(1);
-	parser->xml_tag_syntax(0);
-	parser->add_tag("method",
-			lambda(Parser.HTML p, mapping m) {
-			  names += ({ Parser.parse_html_entities(m->name) });
-			} );
-	parser->finish(c);
-	foreach(Array.uniq(names) - ({ 0, "" }), string name) {
-	  method_children +=
-	    ({ Node( "method", name, c, this_object() ) });
-	}
-	return ({ "" });
-	break;
-
-      case "constant":
-      case "variable":
-      case "inherit":
-	string path = raw_class_path();
-	if(sizeof(path) && (path[-1] != ':')) path += ".";
-	if(!m["homogen-name"]) {
-	  Parser.HTML()->add_tags
-	    ( ([ "constant":
-		 lambda(Parser.HTML p, mapping m, string c) {
-		   string name = Parser.parse_html_entities(m->name);
-		   refs[path + name] =
-		     Node( "constant", name, "", this_object());
-		 },
-		 "variable":
-		 lambda(Parser.HTML p, mapping m, string c) {
-		   string name = Parser.parse_html_entities(m->name);
-		   refs[path + name] =
-		     Node( "variable", name, "", this_object());
-		 },
-		 "inherit":
-		 lambda(Parser.HTML p, mapping m, string c) {
-		   if (m->name) {
-		     string name = Parser.parse_html_entities(m->name);
-		     refs[path + name] =
-		       Node( "inherit", name, "", this_object());
-		   }
-		 },
-	    ]) )->finish(c);
-	}
-	else
-	  refs[path + m["homogen-name"]] =
-	    Node( m["homogen-type"], m["homogen-name"], "", this_object());
-	break;
-
-      }
-    }
-
-    return 0;
-  }
-
-  protected Parser.HTML get_parser() {
-    Parser.HTML parser = Parser.HTML();
-
-    parser->case_insensitive_tag(1);
-    parser->xml_tag_syntax(3);
-
-    parser->add_container("docgroup", my_parse_docgroup);
-    parser->add_container("module", parse_node);
-    parser->add_container("class",  parse_node);
-    parser->add_container("enum",  parse_node);
-
-    return parser;
-  }
-
-  string make_faked_wrapper(string s)
-  {
-    if(type=="method")
-      s = sprintf("<docgroup homogen-type='method' homogen-name='%s'>\n"
-		  "%s\n</docgroup>\n",
-		  Parser.encode_html_entities(name), s);
-    else
-      s = sprintf("<%s name='%s'>\n%s\n</%s>\n",
-		  type, Parser.encode_html_entities(name), s, type);
-    if(parent)
-      return parent->make_faked_wrapper(s);
-    else
-      return s;
-  }
-
-  string _make_filename_low;
-  string make_filename_low()
-  {
-    if(_make_filename_low) return _make_filename_low;
-    if (type == "namespace") {
-      _make_filename_low = parent->make_filename_low()+"/"+cquote(name+"::");
-    } else {
-      _make_filename_low = parent->make_filename_low()+"/"+cquote(name);
-    }
-    return _make_filename_low;
-  }
-
-  string make_filename()
-  {
-    return make_filename_low()[5..]+".html";
-  }
-
-  string make_link(Node to)
-  {
-    // FIXME: Optimize the length of relative links
-    int num_segments = sizeof(make_filename()/"/") - 1;
-    return ("../"*num_segments)+to->make_filename();
-  }
-
-  array(Node) get_ancestors()
-  {
-    PROFILE();
-    array tmp = ({ this_object() }) + parent->get_ancestors();
-    ENDPROFILE("get_ancestors");
-    return tmp;
-  }
-
-  string my_resolve_reference(string _reference, mapping vars)
-  {
-    array(string) resolved = vars->resolved && vars->resolved/"\0";
-    if(default_namespace && has_prefix(_reference, default_namespace+"::"))
-      _reference = _reference[sizeof(default_namespace)+2..];
-
-    if(vars->param)
-      return "<font face='courier'>" + _reference + "</font>";
-
-    if (resolved) {
-      foreach (resolved, string resolution) {
-	Node res_obj;
-
-	if(res_obj = refs[resolution]) {
-	  while(res_obj && (<"constant","variable">)[res_obj->type]) {
-	    res_obj = res_obj->parent;
-	  }
-	  if (!res_obj) {
-	    werror("Found no page to link to for reference %O (%O)\n",
-		   _reference, resolution);
-	    return sprintf("<font face='courier'>" + _reference + "</font>");
-	  }
-	  // FIXME: Assert that the reference is correct?
-	  return create_reference(make_filename(),
-				  res_obj->raw_class_path(),
-				  _reference);
-	}
-
-	// Might be a reference to a parameter.
-	// Try cutting of the last section, and retry.
-	array(string) tmp = resolution/".";
-	if ((sizeof(tmp) > 1) && (res_obj = refs[tmp[..sizeof(tmp)-2]*"."])) {
-	  if (res_obj == this_object()) {
-	    return sprintf("<font face='courier'>" + _reference + "</font>");
-	  }
-	  return create_reference(make_filename(),
-				  res_obj->raw_class_path(),
-				  _reference);
-	}
-
-	if (!zero_type(refs[resolution])) {
-	  werror("Reference %O (%O) is %O!\n",
-		 _reference, resolution, refs[resolution]);
-	}
-      }
-      if (!missing[vars->resolved] && !has_prefix(_reference, "lfun::")) {
-	werror("Missed reference %O (%{%O, %}) in %s\n",
-	       _reference, resolved||({}), make_class_path());
-#if 0
-	werror("Potential refs:%O\n",
-	       sort(map(resolved,
-			lambda (string resolution) {
-			  return filter(indices(refs),
-					glob(resolution[..sizeof(resolution)/2]+
-					     "*", indices(refs)[*]));
-			}) * ({})));
-#endif /* 0 */
-      }
-      unresolved++;
-    }
-    return "<font face='courier'>" + _reference + "</font>";
-  }
-
-  string _make_class_path;
-  string _raw_class_path;
-  string make_class_path(void|int(0..1) header)
-  {
-    if(_make_class_path) return _make_class_path;
-    array a = reverse(parent->get_ancestors());
-
-    _make_class_path = "";
-    _raw_class_path = "";
-    foreach(a, Node n)
-    {
-      // Hide most namepaces from the class path.
-      if (n->type == "namespace") {
-	_raw_class_path += n->name + "::";
-	if ((<"","lfun">)[n->name]) {
-	  _make_class_path += n->name + "::";
-	}
-      } else {
-	_raw_class_path += n->name + ".";
-	_make_class_path += n->name;
-	if(n->type=="class")
-	  _make_class_path += "()->";
-	else if(n->type=="module")
-	  _make_class_path += ".";
-      }
-    }
-    _make_class_path += name;
-    _raw_class_path += name;
-
-    if(type=="method") {
-      _make_class_path += "()";
-    } else if (type == "namespace") {
-      _make_class_path += "::";
-      _raw_class_path += "::";
-    }
-
-    return _make_class_path;
-  }
-  string raw_class_path(void|int(0..1) header)
-  {
-    if(_raw_class_path) return _raw_class_path;
-    make_class_path(header);
-    return _raw_class_path;
-  }
-
-  string make_navbar_really_low(array(Node) children, string what)
-  {
-    if(!sizeof(children)) return "";
-
-    String.Buffer res = String.Buffer(3000);
-    res->add("<tr><td nowrap='nowrap'><br /><b>", what, "</b></td></tr>\n");
-
-    foreach(children, Node node)
-    {
-      string my_name = Parser.encode_html_entities(node->name);
-      if(node->type=="method")
-	my_name+="()";
-      else if (node->type == "namespace") {
-	my_name="<b>"+my_name+"::</b>";
-      }
-      else 
-	my_name="<b>"+my_name+"</b>";
-
-      res->add("<tr><td nowrap='nowrap'>&nbsp;");
-      if(node==this_object())
-	res->add( my_name );
-      else
-	res->add( "<a href='", make_link(node), "'>", my_name, "</a>" );
-      res->add("</td></tr>\n");
-    }
-    return (string)res;
-  }
-
-  string make_hier_list(Node node)
-  {
-    string res="";
-
-    if(node)
-    {
-      if(node->type=="namespace" && node->name==default_namespace)
-	node = node->parent;
-      res += make_hier_list(node->parent);
-
-      string my_class_path =
-	(node->is_TopNode)?"[Top]":node->make_class_path();
-
-      if(node == this_object())
-	res += sprintf("<b>%s</b><br />\n",
-		       Parser.encode_html_entities(my_class_path));
-      else
-	res += sprintf("<a href='%s'><b>%s</b></a><br />\n",
-		       make_link(node),
-		       Parser.encode_html_entities(my_class_path));
-    }
-    return res;
-  }
-
-  string make_navbar_low(Node root)
-  {
-    string res="";
-
-    res += make_hier_list(root);
-
-    res+="<table border='0' cellpadding='1' cellspacing='0' class='sidebar'>";
-
-    res += make_navbar_really_low(root->module_children, "Modules");
-
-    res += make_navbar_really_low(root->class_children, "Classes");
-
-    if(root->is_TopNode)
-      res += make_navbar_really_low(root->namespace_children, "Namespaces");
-    else {
-      res += make_navbar_really_low(root->enum_children, "Enums");
-      res += make_navbar_really_low(root->method_children, "Methods");
-    }
-
-    return res+"</table>";
-  }
-
-  string make_navbar()
-  {
-    if(type=="method")
-      return make_navbar_low(parent);
-    else
-      return make_navbar_low(this_object());
-  }
-
-  array(Node) find_siblings()
-  {
-    return
-      parent->class_children+
-      parent->module_children+
-      parent->enum_children+
-      parent->method_children;
-  }
-
-  array(Node) find_children()
-  {
-    return
-      class_children+
-      module_children+
-      enum_children+
-      method_children;
-  }
-
-  Node find_prev_node()
-  {
-    array(Node) siblings = find_siblings();
-    int index = search( siblings, this_object() );
-
-    Node tmp;
-
-    if(index==0 || index == -1)
-      return parent;
-
-    tmp = siblings[index-1];
-
-    while(sizeof(tmp->find_children()))
-      tmp = tmp->find_children()[-1];
-
-    return tmp;
-  }
-
-  Node find_next_node(void|int dont_descend)
-  {
-    if(!dont_descend && sizeof(find_children()))
-      return find_children()[0];
-
-    array(Node) siblings = find_siblings();
-    int index = search( siblings, this_object() );
-
-    Node tmp;
-    if(index==sizeof(siblings)-1)
-      tmp = parent->find_next_node(1);
-    else
-      tmp = siblings[index+1];
-    return tmp;
-  }
-
-  protected string make_content() {
-    PROFILE();
-    string err;
-    Parser.XML.Tree.Node n;
-    if(err = catch( n = Parser.XML.Tree.parse_input(data)[0] )) {
-      werror(err + "\n" + data);
-      exit(1);
-    }
-    ENDPROFILE("XML.Tree");
-
-    resolve_reference = my_resolve_reference;
-
-    String.Buffer contents = String.Buffer(100000);
-    resolve_class_paths(n);
-    contents->add( parse_children(n, "docgroup", parse_docgroup, 1) );
-    contents->add( parse_children(n, "namespace", parse_namespace, 1) );
-    contents->add( parse_children(n, "module", parse_module, 1) );
-    contents->add( parse_children(n, "class", parse_class, 1) );
-    contents->add( parse_children(n, "enum", parse_enum, 1) );
-
-    n->zap_tree();
-
-    return (string)contents;
-  }
-
-  void make_html(string template, string path)
-  {
-    class_children->make_html(template, path);
-    module_children->make_html(template, path);
-    enum_children->make_html(template, path);
-    method_children->make_html(template, path);
-
-    int num_segments = sizeof(make_filename()/"/")-1;
-    string style = ("../"*num_segments)+"style.css";
-    extra_prefix = "../"*num_segments;
-
-    Node prev = find_prev_node();
-    Node next = find_next_node();
-    string next_url="", next_title="", prev_url="", prev_title="";
-    if(next) {
-      next_title = next->make_class_path();
-      next_url   = make_link(next);
-    }
-    if(prev) {
-      prev_title = prev->make_class_path();
-      prev_url   = make_link(prev);
-    }
-
-    string res = replace(template,
-      (["$navbar$": make_navbar(),
-	"$contents$": make_content(),
-	"$prev_url$": prev_url,
-	"$prev_title$": _Roxen.html_encode_string(prev_title),
-	"$next_url$": next_url,
-	"$next_title$": _Roxen.html_encode_string(next_title),
-	"$type$": String.capitalize(type),
-	"$title$": _Roxen.html_encode_string(make_class_path(1)),
-	"$style$": style,
-	"$dotdot$": sizeof(extra_prefix)?extra_prefix:".",
-	"$imagedir$":image_prefix(),
-	"$filename$": _Roxen.html_encode_string(make_filename()),
-      ]));
-
-    Stdio.mkdirhier(combine_path(path+"/"+make_filename(), "../"));
-    Stdio.write_file(path+"/"+make_filename(), res);
-  }
-}
-
-class TopNode {
-  inherit Node;
-
-  constant is_TopNode = 1;
-  array(Node) namespace_children = ({ });
-
-  void create(string _data) {
-    PROFILE();
-    Parser.HTML parser = Parser.HTML();
-    parser->case_insensitive_tag(1);
-    parser->xml_tag_syntax(3);
-    parser->add_container("autodoc",
-			  lambda(Parser.HTML p, mapping args, string c) {
-			    return ({ c }); });
-
-    _data = parser->finish(_data)->read();
-    ::create("autodoc", "", _data);
-    sort(namespace_children->name, namespace_children);
-    foreach(namespace_children, Node x)
-      if(x->type=="namespace" && x->name==default_namespace) {
-	//	namespace_children -= ({ x });
-	class_children += x->class_children;
-	module_children += x->module_children;
-	enum_children += x->enum_children;
-	method_children += x->method_children;
-      }
-    type = "autodoc";
-    ENDPROFILE("top_create");
-  }
-
-  Parser.HTML get_parser() {
-    Parser.HTML parser = ::get_parser();
-    parser->add_container("namespace", parse_node);
-    return parser;
-  }
-
-  string make_filename_low() { return "__index"; }
-  string make_filename() { return "index.html"; }
-  array(Node) get_ancestors() { return ({ }); }
-  int(0..0) find_prev_node() { return 0; }
-  int(0..0) find_next_node() { return 0; }
-  string make_class_path(void|int(0..1) header) {
-    if(header && sizeof(method_children)) {
-      if(default_namespace)
-	return "namespace "+default_namespace;
-      else
-	return "Namespaces";
-    }
-    return "";
-  }
-  string raw_class_path(void|int(0..1) header) {
-    return "";
-  }
-
-  string make_method_page(array(Node) children)
-  {
-    String.Buffer res = String.Buffer(3500);
-    foreach(children, Node node)
-      res->add("&nbsp;<a href='", make_link(node), "'>",
-	       Parser.encode_html_entities(node->name),
-	       "()</a><br />\n");
-    return (string)res;
-  }
-
-  string make_content() {
-    resolve_reference = my_resolve_reference;
-    if(!sizeof(method_children)) return "";
-
-    string contents = "<table class='sidebar'><tr>";
-    foreach(method_children/( sizeof(method_children)/4.0 ),
-            array(Node) children)
-      contents += "<td nowrap='nowrap' valign='top'>" +
-	make_method_page(children) + "</td>";
-
-    contents += "</tr><tr><td colspan='4' nowrap='nowrap'>" +
-      parse_children(Parser.XML.Tree.parse_input(data),
-		     "docgroup", parse_docgroup, 1) +
-      "</td></tr></table>";
-
-    return contents;
-  }
-
-  void make_html(string template, string path) {
-    PROFILE();
-    namespace_children->make_html(template, path);
-    ::make_html(template, path);
-    ENDPROFILE("top_make_html");
-  }
-}
-
-int main(int argc, array(string) argv)
-{
-  PROFILE();
-  if(argc<4) {
-    werror("Too few arguments. (in-file, template, out-dir (, namespace))\n");
-    return 1;
-  }
-  if(argc>4) default_namespace=argv[4];
-
-  werror("Reading refdoc blob %s...\n", argv[1]);
-  string doc = Stdio.read_file(argv[1]);
-  if(!doc) {
-    werror("Failed to load refdoc blob %s.\n", argv[1]);
-    return 1;
-  }
-
-  werror("Reading template file %s...\n", argv[2]);
-  string template = Stdio.read_file(argv[2]);
-  if(!template) {
-    werror("Failed to load template %s.\n", argv[2]);
-    return 1;
-  }
-  mapping m = localtime(time());
-  template = replace(template,
-		     ([ "$version$":version(),
-			"$date$":sprintf("%4d-%02d-%02d",
-					 m->year+1900, m->mon+1, m->mday),
-		     ]) );
-
-  werror("Splitting to destination directory %s...\n", argv[3]);
-  TopNode top = TopNode(doc);
-  top->make_html(template, argv[3]);
-  ENDPROFILE("main");
-
-  foreach(sort(indices(profiling)), string f)
-    werror("%s: %.1f\n", f, profiling[f]/1000000.0);
-  werror("%d unresolved references.\n", unresolved);
-  werror("%d documented constants/variables/functions/classes/modules.\n",
-	 sizeof(refs));
-  return 0;
-}
+inherit Tools.Standalone.autodoc_to_split_html;
-- 
GitLab