diff --git a/src/modules/Image/blit.c b/src/modules/Image/blit.c
index f0033d76c7c1622ad52ec02dd4e7a6a4841a3ae3..52726e222b8f303fc5effbe0189ce47ec0b52ea3 100644
--- a/src/modules/Image/blit.c
+++ b/src/modules/Image/blit.c
@@ -1,4 +1,4 @@
-/* $Id: blit.c,v 1.7 1997/04/09 01:49:42 mirar Exp $ */
+/* $Id: blit.c,v 1.8 1997/04/18 06:47:18 mirar Exp $ */
 #include "global.h"
 
 /*
@@ -225,10 +225,10 @@ void img_clone(struct image *newimg,struct image *img)
 **!
 **! returns the object called
 **!
-**! object image
+**! arg object image
 **!	image to paste
-**! int x
-**! int y
+**! arg int x
+**! arg int y
 **!	where to paste the image; default is 0,0
 **!
 **! see also: paste_mask, paste_alpha, paste_alpha_color
@@ -289,12 +289,12 @@ void image_paste(INT32 args)
 **!
 **! returns the object called
 **!
-**! object image
+**! arg object image
 **!	image to paste
-**! int alpha
+**! arg int alpha
 **!	alpha channel value
-**! int x
-**! int y
+**! arg int x
+**! arg int y
 **!	where to paste the image; default is 0,0
 **!
 **! see also: paste_mask, paste, paste_alpha_color
@@ -354,12 +354,12 @@ void image_paste_alpha(INT32 args)
 **!
 **! returns the object called
 **!
-**! object image
+**! arg object image
 **!	image to paste
-**! object mask
+**! arg object mask
 **!	mask image
-**! int x
-**! int y
+**! arg int x
+**! arg int y
 **!	where to paste the image; default is 0,0
 **!
 **! see also: paste, paste_alpha, paste_alpha_color
@@ -458,14 +458,14 @@ CHRONO("image_paste_mask end");
 **!
 **! returns the object called
 **!
-**! object mask
+**! arg object mask
 **!     mask image
-**! int r
-**! int g
-**! int b
+**! arg int r
+**! arg int g
+**! arg int b
 **!     what color to paint with; default is current
-**! int x
-**! int y
+**! arg int x
+**! arg int y
 **!     where to paste the image; default is 0,0
 **!
 **! see also: paste_mask, paste_alpha, paste_alpha_color
diff --git a/src/modules/Image/font.c b/src/modules/Image/font.c
index 4fbffbb72687fcf46989fa3bbb0c725ac6798a20..3606cbc376c5a3d21d7d5d489ff67d64e1cf8c22 100644
--- a/src/modules/Image/font.c
+++ b/src/modules/Image/font.c
@@ -5,6 +5,7 @@
 **! class font
 **!
 **! 	Short technical documentation on a font file:
+**!	<pre>
 **!	       struct file_head 
 **!	       {
 **!		  unsigned INT32 cookie;   - 0x464f4e54 
@@ -20,10 +21,11 @@
 **!		  unsigned INT32 spacing;  - spacing to next character
 **!		  unsigned char data[1];   - pixmap data (1byte/pixel)
 **!	       } *ch;
+**!	</pre>
 **!
 */
 
-/* $Id: font.c,v 1.7 1997/04/16 03:10:45 hubbe Exp $ */
+/* $Id: font.c,v 1.8 1997/04/18 06:47:22 mirar Exp $ */
 
 #include "global.h"
 
@@ -468,7 +470,7 @@ void font_height(INT32 args)
 }
 
 /*
-**! method  text_extents(string text,...)
+**! method array(int) text_extents(string text,...)
 **!	Calculate extents of a text-image,
 **!	that would be created by calling <ref>write</ref>
 **!	with the same arguments.
diff --git a/src/modules/Image/image.c b/src/modules/Image/image.c
index 2dd72cff8bb888ac3c5c65061c469c715a8f2873..aa489b9b3c2052cbcf0361f2eb2205f824c8bd7e 100644
--- a/src/modules/Image/image.c
+++ b/src/modules/Image/image.c
@@ -1,4 +1,4 @@
-/* $Id: image.c,v 1.22 1997/04/09 01:49:51 mirar Exp $ */
+/* $Id: image.c,v 1.23 1997/04/18 06:47:26 mirar Exp $ */
 
 /*
 **! module Image
@@ -12,7 +12,7 @@
 
 #include "stralloc.h"
 #include "global.h"
-RCSID("$Id: image.c,v 1.22 1997/04/09 01:49:51 mirar Exp $");
+RCSID("$Id: image.c,v 1.23 1997/04/18 06:47:26 mirar Exp $");
 #include "types.h"
 #include "pike_macros.h"
 #include "object.h"
@@ -1055,13 +1055,13 @@ static INLINE void
 **!	rectangle corners
 **! arg array(array(int)) corner_color
 **!     colors of the corners:
-**!
+**!	<pre>
 **!	({x1y1,x2y1,x1y2,x2y2})
-**!
+**!	</pre>
 **!	each of these is an array of integeres:
-**!
+**!	<pre>
 **!	({r,g,b}) or ({r,g,b,alpha})
-**!
+**!	</pre>
 **!	Default alpha channel value is 0 (opaque).
 */
 
diff --git a/src/modules/Image/mkdoc.pike b/src/modules/Image/mkdoc.pike
index 0ba9f58db754a348e11b199d8b1dd6d2ce09abdc..4992809bb9d1cd33b97a73cdb43b244d5b9d9ac2 100644
--- a/src/modules/Image/mkdoc.pike
+++ b/src/modules/Image/mkdoc.pike
@@ -1,4 +1,5 @@
-#include <stdio.h>
+import Stdio;
+import Array;
 
 mapping parse=([]);
 
@@ -6,27 +7,391 @@ mapping parse=([]);
 
 module : mapping <- moduleM
 	"desc" : text
-	"classes" : mapping <- classM
-		class : mapping
+	"see also" : array of references 
+	"note" : mapping of "desc": text
+	"classes" : mapping 
+		class : mapping <- classM
+	        	"see also" : array of references 
 			"desc" : text
+			"note" : mapping of "desc": text
 			"methods" : array of mappings <- methodM
-				"methods" : list of method names
 				"decl" : array of textlines of declarations
 				"desc" : text
 				"returns" : textline
 				"see also" : array of references 
-				"note" : text
-				"args" : mapping
-					arg : mapping <- argM
-						"type" : textline
-						"desc" : text
+				"note" : mapping of "desc": text
+				"args" : array of mappings <- argM
+					"args" : array of args names and types
+					"desc" : description
+				"names" : multiset of method name(s)
 		
 */
 
-mapping moduleM, classM, methodM, argM;
+mapping moduleM, classM, methodM, argM, nowM, descM;
+
+mapping focM(mapping dest,string name,int line)
+{
+   return dest[name] || (dest[name]=(["_line":line]));
+}
+
+string stripws(string s)
+{
+   sscanf(s,"%*[ \t\n\r]%s",s);
+   s=reverse(s);
+   sscanf(s,"%*[ \t\n\r]%s",s);
+   return reverse(s);
+}
+
+mapping lower_nowM()
+{
+   if (nowM && 
+       (nowM==parse
+	|| nowM==classM
+	|| nowM==methodM
+	|| nowM==moduleM)) return nowM;
+   else return nowM=methodM;
+}
+
+#define complain(X) (X)
+
+mapping keywords=
+(["module":lambda(string arg,int line) 
+	  { nowM=moduleM=focM(parse,stripws(arg),line); classM=methodM=0; 
+	    if (!nowM->classes) nowM->classes=([]); },
+  "class":lambda(string arg,int line) 
+	  { if (!moduleM) return complain("class w/o module");
+	    nowM=classM=focM(moduleM->classes,stripws(arg),line); 
+	    methodM=0; },
+  "method":lambda(string arg,int line)
+	  { if (!classM) return complain("method w/o class");
+	    if (!nowM || methodM!=nowM || methodM->desc || methodM->args) 
+	    { if (!classM->methods) classM->methods=({});
+	      classM->methods+=({methodM=nowM=(["decl":({}),"_line":line])}); }
+	    methodM->decl+=({stripws(arg)}); },
+  "arg":lambda(string arg,int line)
+	  {
+	     if (!methodM) return complain("arg w/o method");
+	     if (nowM!=argM || !argM)
+	     { if (!methodM->args) methodM->args=({});
+	       methodM->args+=({argM=nowM=(["args":({}),"_line":line])}); }
+	     argM->args+=({arg});
+	  },
+  "note":lambda(string arg,int line)
+	  {
+	     if (!lower_nowM()) 
+	        return complain("note w/o method, class or module");
+	     nowM=nowM->note||(nowM->note=(["_line":line]));
+	  },
+  "see":lambda(string arg,int line)
+	  {
+	     if (arg[0..4]!="also:")
+	        return complain("see w/o 'also:'\n");
+	     if (!lower_nowM()) 
+	        return complain("see also w/o method, class or module");
+	     nowM["see also"]=map(arg[5..]/",",stripws);
+	  },
+  "returns":lambda(string arg)
+	  {
+	     if (!methodM) 
+	        return complain("returns w/o method");
+	     methodM->returns=stripws(arg);
+	  }
+]);
+
+string getridoftabs(string s)
+{
+   string res="";
+   for (;;)
+   {
+      int i;
+      if ((i=search(s,"\t"))==-1) return res+s;
+      res+=s[0..i-1]; 
+      s=s[i+1..];
+      res+="         "[(strlen(res)%8)..7];
+   }
+}
+
+string addprefix(string suffix,string prefix)
+{
+   return prefix+suffix;
+}
+
+string make_nice_reference(string refto,string my_prefix)
+{
+   return "<tt><a href=#>"+refto+"</a></tt>";
+}
+
+object(File) make_file(string filename)
+{
+   stdout->write("creating "+filename+"...\n");
+   if (file_size(filename)>0)
+   {
+      rm(filename+"~");
+      mv(filename,filename+"~");
+   }
+   object f=File();
+   if (!f->open(filename,"wtc"))
+   {
+      stdout->write("failed.");
+      exit(1);
+   }
+   return f;
+}
+
+string fixdesc(string s,string prefix)
+{
+   s=stripws(s);
+
+   string t,u,v;
+   t=s; s="";
+   while (sscanf(t,"%s<ref>%s</ref>%s",t,u,v)==3)
+   {
+      s+=t+make_nice_reference(u,prefix);
+      t=v;
+   }
+   s+=t;
+   return replace(s,"\n\n","\n\n<p>");
+}
+
+string standard_doc(mapping info,string myprefix)
+{
+   string res="";
+
+   if (info->desc && stripws(info->desc)!="")
+      res+="\n\n<blockquote>\n"+info->desc+"\n</blockquote>\n";
+   
+   if (info->note)
+      res+="\n\n<h4>NOTE</h4>\n<blockquote>\n"+
+	 fixdesc(info->desc,myprefix)+"\n</blockquote>\n";
+   
+   if (info["see also"])
+   {
+      res+=
+	 "\n\n<h4>SEE ALSO</h4>\n<blockquote>     " +
+	 map(info["see also"],make_nice_reference,myprefix)*",\n     " +
+	 "\n</blockquote>\n";
+   }
+   
+   return res;
+}
+
+void make_an_index(string title,
+		   string file,
+		   mapping info,
+		   string prefix,
+		   string *refs)
+{
+   object f=make_file(file);
+   f->write("<title>Pike documentation: "+title+"</title>\n"+
+	    "<h2>"+title+"</h2>\n"+
+	    standard_doc(info,prefix));
+   if (sizeof(refs))
+   {
+      f->write("<h3>More documentation:</h3>\n <i><tt>" +
+	       map(map(refs,addprefix,prefix),
+		   make_nice_reference,prefix)*"</tt></i><br>\n <i><tt>" +
+	       "</tt></i>\n\n");
+   }
+   f->close();
+}
+		   
+multiset(string) get_method_names(string *decls)
+{
+   string decl,name;
+   multiset(string) names=(<>);
+   foreach (decls,decl)
+   {
+      sscanf(decl,"%*s%*[\t ]%s%*[\t (]%*s",name);
+      names[name]=1;
+   }
+   return names;
+}
+
+string synopsis_to_html(string s)
+{
+   string type,name,arg;
+   if (sscanf(s,"%s%*[ \t]%s(%s",type,name,arg)!=4)
+      sscanf(s,"%s(%s",name,arg),type="";
+
+   return 
+      type+" <b>"+name+"</b>("+
+      replace(arg,({","," "}),({", ","&nbsp"}));
+}
+
+void document_method(object(File) f,
+		     mapping method,
+		     string prefix)
+{
+   stdout->write("documenting "+prefix+" methods "+
+		 sort(indices(method->names))*", "+"...\n");
+   
+   f->write("\n<hr>\n"
+	    // anchors goes here
+	    "<h4>SYNOPSIS</h4>\n"
+	    "<blockquote>\n"
+	    "<tt>"+map(method->decl,synopsis_to_html)*
+	              "<br>\n"+
+	    "</tt>\n</blockquote>\n\n");
+
+   if (method->desc && stripws(method->desc)!="")
+   {
+      f->write("<h4>DESCRIPTION</h4>\n"
+	       "\n\n<blockquote>\n"+
+	       fixdesc(method->desc,prefix)+
+	       "\n</blockquote>\n");
+   }
+
+   if (method->args)
+   {
+      mapping arg;
+      f->write("<h4>ARGUMENTS</h4>\n<blockquote><dl>\n");
+      foreach (method->args,arg)
+      {
+	 f->write("<dt><tt>"+arg->args*"</tt>\n<dt><tt>"+
+		  "</tt>\n  <dd>"+
+		  fixdesc(arg->desc,prefix)+"\n");
+      }
+      f->write("</dl></blockquote>\n");
+   }
+
+   if (method->returns)
+   {
+      f->write("<h4>RETURNS</h4>\n"
+	       "\n\n<blockquote>\n"+method->returns+"\n</blockquote>\n");
+   }
+
+   if (method->note)
+   {
+      f->write("\n\n<h4>NOTE</h4>\n<blockquote>\n"+
+	       method->desc+"\n</blockquote>\n");
+   }
+
+   if (method["see also"])
+   {
+       f->write("\n\n<h4>SEE ALSO</h4>\n<blockquote>     " +
+		map(method["see also"],
+		    make_nice_reference,prefix)*",\n     " +
+		"\n</blockquote>\n");
+   }
+}
+
+void document_class(string title,
+		    string file,
+		    mapping info,
+		    string prefix)
+{
+   stdout->write("documenting "+title+"...\n");
+
+   object(File) f=make_file(file);
+
+   f->write("<title>Pike documentation: "+title+"</title>\n"+
+	    "<h2>"+title+"</h2>\n"+
+	    standard_doc(info,prefix));
+
+   // postprocess methods to get names
+
+   multiset(string) method_names=(<>);
+   string *method_names_arr,method_name;
+   mapping method;
+
+   foreach (info->methods,method)
+      method_names|=(method->names=get_method_names(method->decl));
+
+   method_names_arr=sort(indices(method_names));
+
+   f->write("\n<hr>\n   <i><tt>"+
+	    map(method_names_arr,make_nice_reference,prefix)*
+	       "</tt></i><br>\n   <i><tt>"+"</tt></i><br>\n\n");
+   // alphabetically
+
+   foreach (method_names_arr,method_name)
+      if (method_names[method_name])
+      {
+	 // find it
+	 foreach (info->methods,method)
+	    if ( method->names[method_name] )
+	    {
+	       document_method(f,method,prefix);
+	       method_names-=method->names;
+	    }
+      }
+
+   f->close();
+
+}
+
+void make_doc_files(string dir)
+{
+   stdout->write("modules: "+sort(indices(parse))*", "+"\n");
+
+   string module,clas;
+   
+   foreach (sort(indices(parse)),module)
+   {
+      make_an_index("module "+module,
+		    dir+module+".html", parse[module],
+		    module+".", sort(indices(parse[module]->classes)));
+
+      stdout->write("module "+module+" class(es): "+
+		    sort(indices(parse[module]->classes))*", "+"\n");
+
+      foreach (sort(indices(parse[module]->classes)),clas)
+	 document_class(module+"."+clas,
+			dir+module+"."+clas+".html",
+			parse[module]->classes[clas],
+			module+"."+clas);
+   }
+}
 
 int main()
 {
+   stdout->write("reading data...\n");
+
    string doc=stdin->read(10000000);
-   
+   string s;
+   int line;
+
+   nowM=parse;
+
+   stdout->write("parsing data...\n");
+
+   foreach (doc/"\n",s)
+   {
+      int i;
+      line++;
+      if ((i=search(s,"**!"))!=-1)
+      {
+	 string kw,arg;
+	 sscanf(s[i+3..],"%*[ \t]%s%*[ \t]%s",kw,arg);
+	 if (keywords[kw])
+	 {
+	    string err;
+	    if ( (err=keywords[kw](arg,line)) )
+	    {
+	       stderr->write("Error on line "+line+": "+err+"\n");
+	       return 1;
+	    }
+	 }
+	 else 
+	 {
+	    if (!nowM && !(nowM=descM) )
+	    {
+	       stderr->write("Error on line "+line+": illegal description position\n");
+	       return 1;
+	    }
+	    if (!nowM->desc) nowM->desc="";
+	    else nowM->desc+="\n";
+	    s=getridoftabs(s);
+	    nowM->desc+=s[search(s,"**!")+3..];
+	    descM=nowM; nowM=0;
+	 }
+      }
+   }
+
+//   stderr->write(sprintf("%O",parse));
+
+   stdout->write("making docs...\n\n");
+
+   make_doc_files("doc/");
+   return 0;
 }