diff --git a/bin/htmlify_docs.lpc b/bin/htmlify_docs.lpc
index 837f9fb30288029611bf2f7c297024dd812126f4..1671350ce02d34f5d22f1320d5de994434b66996 100644
--- a/bin/htmlify_docs.lpc
+++ b/bin/htmlify_docs.lpc
@@ -46,6 +46,7 @@ string fippel_path(string path)
 {
   sscanf(path,"./%s",path);
   path=replace(path,"/","_");
+  if(path[strlen(path)-5..]==".bmml") path=path[..strlen(path)-6];
   if(path[strlen(path)-5..]!=".html") path+=".html";
 
   return path;
@@ -210,7 +211,7 @@ string syntax_magic(string s)
 
 string html_quote(string s)
 {
-  return replace(s,({"&","<",">"}),({"&amp;","&lt;","&gt;"}));
+  return replace(s,({"uLPC","&","<",">"}),({"�LPC","&amp;","&lt;","&gt;"}));
 }
 
 string html_unquote(string s)
@@ -232,6 +233,83 @@ string mkdocument(string s,string title)
 
 inherit "/precompiled/regexp":is_example;
 
+list(string) indexes_done=(<>);
+string mkindex(string topic, int head)
+{
+  string head;
+  string a,ret;
+  ret="";
+
+  indexes_done[topic]=1;
+
+  switch(topic)
+  {
+  case "programs":
+    head="<b>Builtin programs:</b>\n";
+    ret="<ul>\n";
+    foreach(sort_array(m_indices(pages)),a)
+    {
+      if(-1 == search(a,"/")) continue;
+      
+      ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
+    }
+    
+    ret+="</ul>\n";
+    break;
+    
+  case "other":
+    head="<b>Other pages</b>\n";
+    ret="<ul>\n";
+    foreach(sort_array(m_indices(pages) - `+(@m_values(keywords))),a)
+    {
+      if(-1 != search(a,"/")) continue;
+      ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
+    }
+    ret+="</ul>\n";
+    break;
+    
+  case "efuns":
+    head="<b>All builtin functions:</b>\n";
+    ret="<ul>\n";
+    foreach(sort_array(m_indices(all_efuns())),a)
+    {
+      a=html_quote(a);
+      if(pages[a])
+      {
+	ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
+      }else{
+	perror("Warning: no page for function: "+a+".\n");
+      }
+    }
+    ret+="</ul>\n";
+    break;
+    
+  default:
+    if(!keywords[topic])
+    {
+      perror("Unknown keyword "+topic+".\n");
+      return "";
+    }
+
+    head="<a name="+topic+">";
+    head+="<b>"+capitalize(b)+"</a>";
+    head+=short(b);
+    head+="</b>\n";
+    ret="<ul>\n";
+    foreach(keywords[b],a)
+    {
+      a=html_quote(a);
+      ret+="<li><a href="+pages[a]+">"+a+"</a>"+ short(a) +"\n";
+    }
+    ret+="</ul></a>\n";
+    break;
+  }
+
+  if(head) ret=head+ret;
+
+  return ret;
+}
+
 /* Convert a page */
 string convert_page(string path, string fname)
 {
@@ -263,6 +341,7 @@ string convert_page(string path, string fname)
 
       sections=explode(part,"\n\n");
       
+      /* Merge sections that does not have a header together */
       for(section=0;section<sizeof(sections);section++)
       {
 	if(!strlen(sections[section]) ||
@@ -345,6 +424,11 @@ string convert_page(string path, string fname)
 	case "DIRECTIVE":
 	case "PREPROCESSOR DIRECTIVES":
 	  rest="<tt>"+magic(rest,1)+"</tt>";
+
+	case "RELATED FUNCTIONS":
+	  a=name;
+	  sscanf(rest,"%*skeyword %[a-z/A-Z0-9]",a);
+	  rest=mkindex(a, 0);
 	}
 
 	sections[headno]="<dt>"+
@@ -361,6 +445,57 @@ string convert_page(string path, string fname)
     }
     output=mkdocument(implode(parts,"<hr noshade size=1>\n"),"uLPC: "+name);
   }
+  else if(path[strlen(path)-5..]==".bmml")
+  {
+    string *sections;
+    string title;
+    int section;
+
+    cont=replace(cont,"$version",version());
+    cont=html_quote(cont);
+    sections=explode(cont,"\n\n");
+    
+    for(section=0;section<sizeof(sections);section++)
+    {
+      string tmp,pre,a;
+      tmp=sections[section];
+      sscanf(tmp,"%[\t ]",per);
+      
+      switch(space)
+      {
+      case "":
+	tmp="<h1><center>"+tmp+"</center></h1>";
+	title=tmp;
+	break;
+	
+      case " ":
+	sscanf(tmp," %s",tmp);
+	tmp="<h2>"+tmp+"</h2>";
+	break;
+	
+      case "  ":
+	sscanf(tmp,"  %s",tmp);
+	tmp=replace(tmp,"\n  ","\n");
+	tmp=more_magic(tmp);
+	break;
+	
+      case "\t":
+	sscanf(tmp,"\t%s %s",pre, a);
+	switch(pre)
+	{
+	case "KEYWORD_INDEX":
+	  tmp=mkindex(a, 1);
+
+	default:
+	  perror("Unknown directive: "+pre+".\n");
+	}
+
+      }
+    }
+    cont=implode(sections,"\n<p>\n");
+
+    return mkdocument(cont, title || "uLPC manual");
+  }
   else if(path[strlen(path)-5..]==".html")
   {
     if(sscanf(cont,"<title>%s</title>",part))
@@ -415,12 +550,31 @@ string convert_page(string path, string fname)
 }
 
 
+string short(string s)
+{
+  return short_descs[s] ? " - "+short_descs[s] : "";
+}
+
+string convert_dot_bmml(string path, string fname)
+{
+  string output;
+  
+  output="";
+  
+  if(path[strlen(path)-5..]==".bmml")
+  {
+  }
+  return 0;
+}
+
+int writepages;
+
 void scanfiles(string path, string fname)
 {
   string nf,np;
-  nf=convert_page(path, fname);
+  nf=convert(path, fname);
 
-  if(nf && strlen(nf))
+  if(nf && strlen(nf) && writepages)
   {
     np=combine_path(new_path,fippel_path(path));
 //    write("Writing "+np+".\n");
@@ -499,7 +653,7 @@ void scanlinks(string path, string fname)
 void traversedir(string path,function fun)
 {
   string file;
-  foreach(get_dir(path) - ({"CVS","RCS"}),file)
+  foreach(get_dir(path) - ({"CVS","RCS",".cvsignore"}),file)
   {
     string tmp;
     if(file[-1]=='~') continue;
@@ -517,75 +671,6 @@ void traversedir(string path,function fun)
   }
 }
 
-string short(string s)
-{
-  return short_descs[s] ? " - "+short_descs[s] : "";
-}
-/** Create an index for our newly created stuff **/
-string mkindex()
-{
-  string ret;
-  string a,b;
-  mapping tmp=pages+([]);
-
-  ret="";
-
-  ret+="<H1>"+version()+" index</h1>\n";
-  
-  ret+="<H2><b>Keyword lists</b></H2>\n<dl>\n";
-  foreach(sort_array(m_indices(keywords)),b)
-  {
-    ret+="<a name="+b+">";
-    ret+="<dt><h2>"+capitalize(b);
-    ret+=short(b);
-    ret+="</h2><dd>\n";
-    ret+="<ul>\n";
-    foreach(keywords[b],a)
-    {
-      a=html_quote(a);
-      ret+="<li><a href="+pages[a]+">"+a+"</a>"+ short(a) +"\n";
-      m_delete(tmp,a);
-    }
-    ret+="</ul></a>\n";
-  }
-  ret+="</dl>\n";
-
-  ret+="<H1>All builtin functions:</H1>\n<ul>\n";
-  
-  foreach(sort_array(m_indices(all_efuns())),a)
-  {
-    a=html_quote(a);
-    if(pages[a])
-    {
-      ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
-    }else{
-      perror("Warning: no page for function: "+a+".\n");
-    }
-    m_delete(tmp,a);
-  }
-  ret+="</ul>\n";
-
-  ret+="</dl><H1>Builtin programs:</H1>\n<ul>\n";
-  foreach(sort_array(m_indices(tmp)),a)
-  {
-    if(-1 == search(a,"/")) continue;
-
-    ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
-    m_delete(tmp,a);
-  }
-  
-  ret+="</ul>\n";
-
-  ret+="<H1>Other pages</H1>\n<ul>\n";
-  foreach(sort_array(m_indices(tmp)),a)
-  {
-    ret+="<li><a href="+pages[a]+">"+a+"</a>"+short(a)+"\n";
-  }
-  
-  ret+="</ul>\n";
-  return mkdocument(ret,"uLPC documentation index");
-}
-
 int main(int argc, string *argv)
 {
   string np;
@@ -605,12 +690,17 @@ int main(int argc, string *argv)
   new_path=combine_path(getcwd(),argv[2]);
   cd(argv[1]);
   traversedir(".",scanlinks);
-  write("Processing pages.\n");
+
+  write("Scanning pages.\n");
+  writepages=0;
+  traversedir(".",scanfiles);
+
+  write("Writing html.\n");
+  writepages=0;
   traversedir(".",scanfiles);
 
-  write("Making index.\n");
-  np=combine_path(new_path,"index.html");
-  if(file_size(np)>=0)
-    rm(np);
-  write_file(np,mkindex());
+  foreach(indices(keywords) - indices(indexes_done),np)
+  {
+    perror("Keywords never indexed: "+np+"\n");
+  }
 }