diff --git a/tutorial/Makefile b/tutorial/Makefile
index 4a07031ef45e36c1b968f9a4541cb7a72ea4e915..29f162e6a1e1be409b2b6cfd5493cbe5d7e9f634 100644
--- a/tutorial/Makefile
+++ b/tutorial/Makefile
@@ -35,6 +35,8 @@ the_image_module.html: Image.wmml the_image_module.wmml
 clean:
 	rm *.html *.md illustration_cache illustration*.gif 
 
+rebuild: Image.wmml Mysql.wmml all
+
 export:
 	tar czvTf tutorial.files tutorial.tar.gz 
 	tar czvTf tutorial_onepage.files tutorial_onepage.tar.gz 
diff --git a/tutorial/extending.wmml b/tutorial/extending.wmml
new file mode 100644
index 0000000000000000000000000000000000000000..a1d5f19bfb06ccd2a3553094fc4983903c5d6479
--- /dev/null
+++ b/tutorial/extending.wmml
@@ -0,0 +1,601 @@
+<chapter title="Pike internals - how to extend Pike">
+The rest of this book describes how Pike works and how to extend it with
+your own functions written in C or C++. Even if you are not interested in
+extending Pike, the information in this section can make you understand
+Pike better and thus make you a better Pike programmer. From this point on
+I will assume that the reader knows C or C++.
+<p>
+<section title="The master object" name=master.pike>
+Pike is a very dynamic language. Sometimes that is not enough, sometimes you
+want to change the way Pike handles errors, loads modules or start scripts.
+All this and much more can be changed by modifying the <b>master object</b>.
+The <b>master object</b> is a Pike object like any other object, but it is
+loaded before anything else and is expected to perform certain things for
+the Pike executable. The Pike executable cannot function without a master
+object to take care of these things. Here is a list of the methods needed
+in the <b>master object</b>:
+<dl>
+<dt> <tt>program cast_to_program(string <i>program_name</i>, string <i>current_file</i>)</tt>
+<dd> This function is called whenever someone performs a cast from a string
+     to a program.
+<dt> <tt>program handle_inherit(string <i>program_name</i>, string <i>current_file</i>)</tt>
+<dd> This is called whenever a Pike program which uses inherit with a string
+     argument is called. It is expected to return the program to inherit.
+<dt> <tt>void handle_error(array <i>trace</i>)</tt>
+<dd> This function is expected to write the error messages when a
+     run time error occurs. The argument is of the form
+     <tt>({"<i>error_description</i>", backtrace() })</tt>. If any error
+     occurs in this routine Pike will dump core.
+<dt> <tt>program cast_to_program(string <i>program_name</i>, string <i>current_file</i>)</tt>
+<dd> This function is called whenever someone performs a cast from a string
+     to an object.
+<dt> <tt>mixed resolv(string <i>identifier</i>, string <i>current_file</i>)</tt>
+<dd> This function is called whenever the compiler finds an unknown identifier
+     in a program. It is normally used for loading modules.
+     It is supposed to return <tt>([])[0]</tt> if the master doesn't know what
+     the value should be, and the value in question otherwise.
+<dt> <tt>void _main(array(string) <i>argv</i>, array(string) <i>env</i>)</tt>
+<dd> This function is supposed to start a Pike script. It receives all
+     the command line arguments in the first array and all environment
+     variables on the form <tt>"<i>var</i>=<i>value</i>"</tt>.
+     _main is called as soon as all modules and setup is done.
+<dt> <tt>void compile_error(string <i>file</i>, int <i>line</i>, string <i>err</i>)</tt>
+<dd> This function is called whenever a compile error is encountered. Normally
+     it just writes a message to stderr.
+<dt> <tt>string handle_include(string <i>file</i>, string <i>current_file</i>, int <i>local_include</i>)</tt>
+<dd> This function is used to locate include files. <i>file</i> is the file
+     name the user wants to include, and <i>local_include</i> is 1 if
+     the user used double quotes rather than lesser-than, greater-than to
+     quote the file name. Otherwise it is zero.
+</dl>
+<p>
+Aside from the above functions, which are expected from the Pike binary,
+the master object is also expected to provide functions used by Pike
+scripts. The current master add the following global functions:
+<dl><dd>
+	add_include_path,
+	remove_include_path,
+	add_module_path,
+	remove_module_path,
+	add_program_path,
+	remove_program_path,
+	master,
+	describe_backtrace,
+	mkmultiset,
+	strlen,
+	new,
+	clone,
+	UNDEFINED,
+	write,
+	getenv and putenv.
+</dl>
+<p>
+There are at least two ways to change the behavior of the master object.
+(Except for editing it directly, which would cause other Pike scripts not
+ to run in most cases.) You can either copy the master object, modify it
+and use the command line option <tt>-m</tt> to load your file instead of
+the default master object. However, since there might be more functionality
+added to the master object in the future I do not recommend this.
+<p>
+A better way is to write an object that inherits the master and then calls
+replace_master with the new object as argument. This should be far more
+future-safe. Although I can not guarantee that the interface between Pike
+and the master object will not change in the future, so be careful if you
+do this.
+<p>
+Let's look an example:
+<example language=pike>
+	#!/usr/local/bin/pike
+
+	class new_master {
+	  inherit "/master";
+
+ 	  void create()
+ 	  {
+ 	    /* You need to copy the values from the old master to the new */
+ 	    /* NOTE: At this point we are still using the old master */
+ 	    object old_master = master();
+ 	    object new_master = this_object();
+ 
+ 	    foreach(indices(old_master), string varname)
+ 	    {
+ 	      /* The catch is needed since we can't assign constants */
+ 	      catch { new_master[varname] = old_master[varname]; };
+ 	    }
+ 	  }
+
+	  void handle_error(array trace)
+	  {
+	    Stdio.write_file("error log",describe_backtrace(trace));
+	  }
+	};
+
+	int main(int argc, array(string) argv)
+	{
+	  replace_master(new_master());
+	  /* Run rest of program */
+	  exit(0);
+	}
+</example>
+This example installs a master object which logs run time errors to file
+instead of writing them to stderr.
+<p>
+</section>
+
+<section title="Data types from the inside">
+This section describes the different data types used inside the Pike
+interpreter. It is nessesary to have at least a basic understanding of
+these before you write Pike extentions.
+
+<section title="struct svalue">
+An svalue is the most central data structure in the Pike interpreter. It
+is used to hold values on the stack, local variables, items in arrays and
+mappings and a lot more. Any of the data types described in <ref to=types>
+can be stored in an svalue.
+<p>
+A <tt>struct svalue</tt> has three members:
+<dl>
+<dt><tt>short type;</tt>
+<dd>This says what type of value is actually stored in the svalue. Valid
+    values are <tt>T_INT</tt>, <tt>T_FLOAT</tt>, <tt>T_STRING</tt>,
+    <tt>T_ARRAY</tt>, <tt>T_MAPPING</tt>, <tt>T_MULTISET</tt>,
+    <tt>T_FUNCTION</tt>, <tt>T_PROGRAM</tt>, <tt>T_OBJECT</tt>.
+    In certain situations, other values are used in the type field, but
+    those are reserved for internal Pike use only.
+<dt><tt>short subtype;</tt>
+<ul>
+<li>For integers (<tt>T_INT</tt>) this value is one of <tt>NUMBER_NUMBER</tt>,
+    <tt>NUMBER_UNDEFINED</tt> or <tt>NUMBER_DESTRUCTED</tt>. See
+    <link to=zero_type>zero_type</link> for more information.
+<li>For functions (<tt>T_FUNCTION</tt>) this value identifies which method
+    this svalue refers to. For builtin functions, this value is
+    <tt>FUNCTION_BUILTIN</tt> (which is the same as <tt>USHRT_MAX</tt>).
+</ul>
+<dt><tt>union anything u</tt>
+<dd>This union contains the data. Depending on what the <tt>type</tt> member
+    is, you can access one of the following union members:
+<!-- FIXME: these needs to be linked to the approperiate chapters -->
+<table border=1>
+<tr><th><tt>type</tt> is:</th><th>member to use:</th><th>notes:</th></tr>
+<tr><td><tt>T_INT</tt></td><td><tt>INT_TYPE integer</tt></td><td></td></tr>
+<tr><td><tt>T_FLOAT</tt></td><td><tt>FLOAT_TYPE float_number</tt></td><td></td></tr>
+<tr><td><tt>T_STRING</tt></td><td><tt>struct pike_string *string</tt></td><td></td></tr>
+<tr><td><tt>T_ARRAY</tt></td><td><tt>struct array *array</tt></td><td></td></tr>
+<tr><td><tt>T_MAPPING</tt></td><td><tt>struct mapping *mapping</tt></td><td></td></tr>
+<tr><td><tt>T_MULTISET</tt></td><td><tt>struct multiset *multiset</tt></td><td></td></tr>
+<tr><td><tt>T_OBJECT</tt></td><td><tt>struct object *object</tt></td><td></td></tr>
+<tr><td><tt>T_PROGRAM</tt></td><td><tt>struct program *program</tt></td><td></td></tr>
+<tr><td><tt>T_FUNCTION</tt></td><td><tt>struct callble *efun</tt></td><td>If <tt>subtype == FUNCTION_BUILTIN</tt></td></tr>
+<tr><td><tt>T_FUNCTION</tt></td><td><tt>struct object *object</tt></td><td>If <tt>subtype != FUNCTION_BUILTIN</tt></td></tr>
+</table>
+</dl>
+<p>
+Of course there are a whole bunch of functions for operating on svalues:
+<function name=free_svalue title="free the contents of an svalue">
+<man_syntax>
+void free_svalue(struct svalue *<i>s</i>);
+</man_syntax>
+<man_description>
+This function is actually a macro, it will the contents of <i>s</i>. 
+It does not however free <i>s</i> itself. After calling free_svalue,
+the contents of <i>s</i> is undefined, and you should not be surprised
+if your computer blows up if you try to access the it's contents.
+Also note that this doesn't nessecarily free whatever the svalue is
+pointing to, it only frees one reference. If that reference is the last
+one, the object/array/mapping/whatever will indeed be freed.
+</man_description>
+<man_note>
+This function will *not* call Pike code or error().
+</man_note>
+</function>
+
+<HR NEWPAGE>
+
+<function name=free_svalues title="free many svalues">
+<man_syntax>
+void free_svalues(struct svalue *<i>s</i>, INT32 <i>howmany</i>, TYPE_FIELD <i>type_hint</i>);
+</man_syntax>
+<man_description>
+This function does the same as <tt>free_svalue</tt> but operates on several
+svalues. The <i>type_hint</i> is used for optimization and should be set
+to BIT_MIXED if you don't know exactly what types are beeing freed.
+</man_description>
+<man_note>
+This function will *not* call Pike code or error().
+</man_note>
+<man_see>
+free_svalue, TYPE_FIELD
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=assign_svalue title="copy an svalue to another svalue">
+<man_syntax>
+void assign_svalue(struct svalue *<i>to</i>, sstruct svalue *<i>from</i>);
+</man_syntax>
+<man_description>
+This function frees the contents of <i>to</i> and then copies the contents
+of <i>from</i> into <i>to</i>. If the value in <i>from</i> uses refcounts,
+they will be increased to reflect this copy.
+</man_description>
+<man_note>
+This function will *not* call Pike code or error().
+</man_note>
+<man_see>
+free_svalue, assign_svalue_no_free
+</man_see>
+</function>
+
+
+<HR NEWPAGE>
+
+<function name=assign_svalue_no_free title="copy an svalue to another svalue">
+<man_syntax>
+void assign_svalue_no_free(struct svalue *<i>to</i>, sstruct svalue *<i>from</i>);
+</man_syntax>
+<man_description>
+This function does the same as assign_svalue() but does not free the contents
+of <i>to</i> before overwriting it. This should be used when <i>to</i> has not
+been initialized yet. If this funcion is incorrectly, memory leaks will occur.
+On the other hand, if you call assign_svalue on an uninitialized svalue, a
+core dump or bus error will most likely occur.
+</man_description>
+<man_note>
+This function will *not* call Pike code or error().
+</man_note>
+<man_see>
+assign_svalue, free_svalue
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=IS_ZERO title="check if an svalue is true or false">
+<man_syntax>
+int IS_ZERO(struct svalue *<i>s</i>);
+</man_syntax>
+<man_description>
+This macro returns 1 if <i>s</i> is false and 0 if <i>s</i> is true.
+</man_description>
+<man_note>
+This macro will evaluate <i>s</i> several times.<br>
+This macro may call Pike code and/or error().
+</man_note>
+<man_see>
+is_eq
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=is_eq title="check if two svalues contains the same value">
+<man_syntax>
+int is_eq(struct svalue *<i>a</i>, struct svalue *<i>b</i>);
+</man_syntax>
+<man_description>
+This function returns 1 if <i>a</i> and <i>b</i> contain the same value.
+This is the same as the <tt>`==</tt> operator in pike.
+</man_description>
+<man_note>
+This function may call Pike code and/or error().
+</man_note>
+<man_see>
+IS_ZERO, is_lt, is_gt, is_le, is_ge, is_equal
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=is_equal title="check if two svalues are equal">
+<man_syntax>
+int is_equal(struct svalue *<i>a</i>, struct svalue *<i>b</i>);
+</man_syntax>
+<man_description>
+This function returns 1 if <i>a</i> and <i>b</i> contains equal values.
+This is the same as the function <tt>equal</tt> in pike.
+</man_description>
+<man_note>
+This function may call Pike code and/or error().
+</man_note>
+<man_see>
+equal, is_eq
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<anchor name=is_gt>
+<anchor name=is_le>
+<anchor name=is_ge>
+<function name=is_lt title="compare the contents of two svalues">
+<man_syntax>
+int is_lt(struct svalue *<i>a</i>, struct svalue *<i>b</i>);<br>
+int is_le(struct svalue *<i>a</i>, struct svalue *<i>b</i>);<br>
+int is_gt(struct svalue *<i>a</i>, struct svalue *<i>b</i>);<br>
+int is_ge(struct svalue *<i>a</i>, struct svalue *<i>b</i>);
+</man_syntax>
+<man_description>
+These functions are equal to the pike operators <tt>`&lt;</tt>,
+<tt>`&lt;=</tt>, <tt>`&gt;</tt>, <tt>`&gt;=</tt> respectively.
+For instance <tt>is_lt</tt> will return 1 if the contents of
+<i>a</i> is lesser than the contents of <i>b</i>.
+</man_description>
+<man_note>
+This function may call Pike code and/or error(). For instance, it will
+call error() if you try to compare values which cannot be compared such
+as comparing an integer to an array.
+</man_note>
+<man_see>
+IS_ZERO, is_eq
+</man_see>
+</function>
+</anchor>
+</anchor>
+</anchor>
+
+
+</section>
+
+<section title="struct pike_string">
+A <tt>struct pike_string</tt> is the internal representation of a
+<tt>string</tt>. Since Pike relies heavily on string manipulation, there
+are quite a few features and quirks to using this data structure. The most
+important part is that strings are shared. This means that after a string
+has been entered into the shared string table it must <i>never</i> be modified.
+Since some other thread might be using the very same string, it is not even
+permitted to change a shared string temporarily and then change it back.
+<p>
+A <tt>struct pike_string</tt> has these members:
+<dl>
+<dt><tt>INT32 refs;</tt>
+<dd>The references to this string.
+<dt><tt>INT32 length;</tt>
+<dd>This is the length of the string.
+<dt><tt>unsigned INT32 hval;</tt>
+<dd>This is the internal hash value for the string, you should not have to
+    use this member for any reason.
+<dt><tt>struct pike_string *next;</tt>
+<dd>This points to the next string in the hash table. Internal use only.
+<dt><tt>int size_shift;</tt>
+<dd>This represents the size of the characters in the string. Currently
+    size_shift has three valid values: 0, 1 and 2. These values means that
+    the characters in the string are 1, 2 and 4 bytes long respectively.
+<dt><tt>char str[1];</tt>
+<dd>This is the actual data. Note that you should never use this member
+    directly. Use <tt>STR0</tt>, <tt>STR1</tt> and <tt>STR2</tt> instead.
+</dl>
+<h2>General string management</h2>
+Since pike strings are shared, you can compare them by using <tt>==</tt>.
+FIXME -- add more here.
+
+<HR NEWPAGE>
+
+<anchor name=STR2>
+<anchor name=STR1>
+<function name=STR0 title="Get a pointer to a 'char'">
+<man_syntax>
+p_wchar0 *STR0(struct pike_string *<i>s</i>);<br>
+p_wchar1 *STR1(struct pike_string *<i>s</i>);<br>
+p_wchar2 *STR2(struct pike_string *<i>s</i>);
+</man_syntax>
+<man_description>
+These macros return raw C pointers to the data in the string <i>s</i>.
+Note that you may only use <tt>STR0</tt> on strings where
+<tt>size_shift</tt> is 0, <tt>STR1</tt> on strings where <tt>size_shift</tt>
+is 1 and <tt>STR2</tt>on strings where <tt>size_shift</tt> is 2. When compiled
+with <tt>DEBUG</tt> these macros will call <tt>fatal</tt> if used on strings
+with the wrong <tt>size_shift</tt>.
+</man_description>
+<man_note>
+All pike strings have been zero-terminated for your convenience.<br>
+The zero-termination is not included in the length of the string.
+</man_note>
+</function>
+</anchor>
+</anchor>
+
+<HR NEWPAGE>
+
+<function name=free_string title="Free a reference to a pike_string">
+<man_syntax>
+void free_string(struct pike_string *<i>s</i>);
+</man_syntax>
+<man_description>
+This function frees one reference to a pike string and if that is the last
+reference, it will free the string itself. As with all refcounting functions
+you should be careful about how you use it. If you forget to call this when
+you should, a memory leak will occur. If you call this function when you
+shouldn't Pike will most likely crash.
+</man_description>
+</function>
+
+<HR NEWPAGE>
+
+<function name=make_shared_string title="Make a new shared string">
+<man_syntax>
+struct pike_string *make_shared_string(char *<i>str</i>);
+</man_syntax>
+<man_description>
+This function takes a null terminated C string as argument and returns a
+<tt>pike_string</tt> with the same contents. It does not free or change
+<i>str</i>. The returned string will have a reference which will be up
+to you to free with <tt>free_string</tt> unless you send the string to
+a function such as <tt>push_string</tt> which eats the reference for you. 
+</man_description>
+<man_see>
+free_string, push_string, begin_shared_string, make_shared_binary_string
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=make_shared_binary_string title="Make a new binary shared string">
+<man_syntax>
+struct pike_string *make_shared_binary_string(char *<i>str</i>, INT32 <i>len</i>);
+</man_syntax>
+<man_description>
+This function does essentially the same thing as <tt>make_shared_string</tt>,
+but you give it the length of the string <i>str</i> as a second argument.
+This allows for strings with zeros in them. It is also more efficient to
+call this routine if you already know the length of the string <i>str</i>.
+</man_description>
+<man_see>
+free_string, push_string, begin_shared_string, make_shared_binary_string
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=begin_shared_string title="Start building a shared string">
+<man_syntax>
+struct pike_string *begin_shared_string(INT32 <i>len</i>);
+</man_syntax>
+<man_description>
+This function is used to allocate a new shared string with a specified length
+which has not been created yet. The returned string is not yet shared and
+should be initialized with data before calling <tt>end_shared_string</tt>
+on it.
+<p>
+If after calling this function you decide that you do not need this string
+after all, you can simply call <tt>free</tt> on the returned string to
+free it. It is also possible to call
+<tt>free_string(end_shared_string(<i>s</i>))</tt> but that would be much less
+effective.
+</man_description>
+<man_example language=c>
+	// This is in effect equal to s=make_shared_string("test")
+	struct pike_string *s=begin_shared_string(4);
+	STR0(s)[0]='t';
+	STR0(s)[1]='e';
+	STR0(s)[2]='s';
+	STR0(s)[3]='t';
+	s=end_shared_string(s);
+</man_example>
+<man_see>
+free_string, push_string, make_shared_string, end_shared_string
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=end_shared_string title="Insert a pre-allocated string into the shared string table">
+<man_syntax>
+struct pike_string *end_shared_string(struct pike_string *<i>s</i>);
+</man_syntax>
+<man_description>
+This function is used to finish constructing a pike string previously
+allocated with <tt>begin_shared_string</tt> or
+<tt>begin_wide_shared_string</tt>. It will insert the string into the shared
+string table. If there already is such a string in the shared string table
+then <i>s</i> will be freed and that string will be returned instead.
+After calling this function, you may not modify the string any more.
+As with <tt>make_shared_string</tt> this function returns a string with
+a reference which it is your responsibility to free.
+</man_description>
+<man_see>
+begin_shared_string, begin_wide_shared_string
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<function name=begin_wide_shared_string title="Start building a wide shared string">
+<man_syntax>
+struct pike_string *begin_wide_shared_string(INT32 <i>len</i>, int <i>size_shift</i>);
+</man_syntax>
+<man_description>
+This function is a more generic version of <tt>begin_shared_string</tt>.
+It allocates space for a string of length <i>len</i> where each character
+is <tt>1 &lt;&;lt; <i>size_shift</i></tt> bytes. As with <tt>begin_shared_string</tt>
+it is your responsibility to initialize the string and call
+<tt>end_shared_string</tt>.
+on it.
+</man_description>
+<man_example language=c>
+	struct pike_string *s=begin_wide_shared_string(1,2);
+	STR2(s)[0]=4711;
+	s=end_shared_string(s);
+</man_example>
+<man_see>
+begin_shared_string, end_shared_string, make_shared_string, make_shared_string1, make_shared_string2
+</man_see>
+</function>
+
+<HR NEWPAGE>
+
+<anchor name=make_shared_string2>
+<anchor name=make_shared_binary_string2>
+<anchor name=make_shared_binary_string1>
+<function name=make_shared_string1 title="Make a wide shared string">
+<man_syntax>
+struct pike_string *make_shared_string1(p_whcar1 *<i>str</i>);<br>
+struct pike_string *make_shared_binary_string1(p_whcar1 *<i>str</i>,INT32 <i>len</i>);<br>
+struct pike_string *make_shared_string2(p_whcar2 *<i>str</i>);<br>
+struct pike_string *make_shared_binary_string2(p_whcar2 *<i>str</i>,INT32 <i>len</i>);
+</man_syntax>
+<man_description>
+These functions are the wide string equivialents of
+<tt>make_shared_string</tt> and <tt>make_shared_binary_string</tt>.
+Of course, the functions ending in 1 use 2-byte characters and the ones
+ending in 2 use 4-byte characters.
+</man_description>
+<man_see>
+make_shared_string, make_shared_binary_string, begin_wide_shared_string
+</man_see>
+</function>
+</anchor>
+</anchor>
+</anchor>
+
+
+</section> <!-- pike_string -->
+
+<section title="struct array">
+</section> <!-- array -->
+
+<section title="struct mapping">
+</section> <!-- array -->
+
+<section title="struct object">
+</section> <!-- array -->
+
+<section title="struct program">
+</section> <!-- array -->
+
+</section>
+
+<section title="The interpreter">
+<ul>
+<li> The stack
+<li> {ref_}push_*
+</ul>
+</section>
+
+<h2> Functional overview </h2>
+
+<h2>Overview of the Pike source</h2>
+<dl>
+<dt> library files
+<dd> 
+  <dl>
+  <dt> callback
+  </dl>
+
+<dt> compiler
+<dt> backend
+<dt> callback
+<dt> constants
+<dt> docode
+<dt> 
+</dl>
+
+<ul>
+  <li>Overview of the Pike source
+  <li>The master object
+  <li>The file structure of a module
+  <li>Data types from the inside
+  <li>Writing portable modules: autoconf
+  <li>Other useful functions
+</ul>
+</chapter>
diff --git a/tutorial/html.pike b/tutorial/html.pike
index c7c61de6ed5b893dda42fd6f427f4bfae10c73a8..215ffb1e6b500248a69f9ff23fe74a4666a0c6da 100644
--- a/tutorial/html.pike
+++ b/tutorial/html.pike
@@ -255,6 +255,26 @@ SGML low_index_to_wmml(INDEX data, string prefix)
   return ret;
 }
 
+int count_index_lines(SGML z)
+{
+  int ret;
+  if(!z) return 0;
+  foreach(z, TAG foo)
+    {
+      if(objectp(foo))
+      {
+	switch(foo->tag)
+	{
+	  case "h2": ret++;
+	  case "dd":
+	  case "dt": ret++;
+	  default:   ret+=count_index_lines(foo->data);
+	}
+      }
+    }
+  return ret;
+}
+
 SGML index_to_wmml(INDEX data)
 {
   SGML ret=({});
@@ -262,7 +282,8 @@ SGML index_to_wmml(INDEX data)
     {
       if(sizeof(data[key]) > !!data[key][0])
       {
-	ret+=({
+	ret+=
+	  ({
 	  Sgml.Tag("dt"),
 	    Sgml.Tag("h2",([]),0,({key})),
 	      "\n",
@@ -272,9 +293,37 @@ SGML index_to_wmml(INDEX data)
       }
     }
 
+#if 1
+  int total_lines=count_index_lines(ret);
+  werror("\nTOTAL LINES: %d\n",total_lines);
+  int lines,x;
+  for(x=0;x<sizeof(ret);x+=2)
+  {
+    lines+=count_index_lines(ret[x..x+1]);
+    werror("LINES: %d\n",lines);
+    if(lines*2>total_lines) break;
+  }
+  return ({
+    Sgml.Tag("table",([]),0,
+	     ({
+	       Sgml.Tag("tr",(["valign":"top"]),0,
+			({
+			  Sgml.Tag("td",([]),0,
+				   ({
+				     Sgml.Tag("dl",([]),0, ret[..x-1])
+				       })),
+			  Sgml.Tag("td",([]),0,
+				   ({
+				     Sgml.Tag("dl",([]),0, ret[x..])
+				       })),
+			    })),
+		 })),
+	});
+#else
   return ({
     Sgml.Tag("dl",([]),0, ret)
 	});
+#endif
 }
 
 string name_to_link(string x)
diff --git a/tutorial/tutorial.wmml b/tutorial/tutorial.wmml
index 7ff28fcbba830677f8ce2044263aa60579830cab..9459c67fbef123840b51a51acd2c5f3bcb8bd5de 100644
--- a/tutorial/tutorial.wmml
+++ b/tutorial/tutorial.wmml
@@ -9324,7 +9324,7 @@ Defined as: return o[name];
 
 <function name=Simulate.map_regexp title="filter an array through a regexp">
 <man_syntax>
-array(string) regexp(array(string) <I>arr</I>, string <I>reg</I>);
+array(string) map_regexp(array(string) <I>arr</I>, string <I>reg</I>);
 </man_syntax>
 <man_description>
 Returns those strings in arr that matches the regexp in reg.
@@ -9614,6 +9614,8 @@ prints.
 
  -->
 
+<include file=Mysql.wmml>
+
 </chapter>
 
 <chapter title="The preprocessor">
@@ -12733,6 +12735,7 @@ instead of writing them to stderr.
 
 -->
 
+<include file=extending.wmml>
 
 <appendix title="Terms and jargon">
 <dl>
diff --git a/tutorial/wmmltohtml2 b/tutorial/wmmltohtml2
index f0df1109da0f9479ea82cc60d27507e65fdd015f..5b42ff3bfc01b8949bbfb7765e68b4b9758c4a70 100755
--- a/tutorial/wmmltohtml2
+++ b/tutorial/wmmltohtml2
@@ -10,22 +10,22 @@ import ".";
 int main(int argc, string *argv)
 {	
   mixed *ER=catch {
-    program output=(program)argv[1];
+    program output=(program)argv[2];
     werror("Reading ");
-    string input=stdin->read(0x7fffffff);
+    string input=Stdio.read_file(argv[1]);
     werror("Lexing ");
-    mixed data=Sgml.lex(input,"stdin");
+    mixed data=Sgml.lex(input,argv[1]);
     werror("Grouping ");
     data=Sgml.group(data);
     werror("Verifying\n");
-    Wmml.verify(data,input,"stdin");
+    Wmml.verify(data,input,argv[1]);
     werror("Concretizing\n");
     WMML wmml=Wmml.make_concrete_wmml(data);
 #ifdef WALL
     werror("Undocumented functions: \n%-#75s\n",(indices(all_constants()) - indices(wmml->index_data))*"\n");
 #endif
     werror("\nWriting output\n");
-    output()->output(argv[2],wmml);
+    output()->output(argv[3],wmml);
     write("Ok\n");
     exit(0);
   };