diff --git a/tutorial/Wmml.pmod b/tutorial/Wmml.pmod
index 2bedb5771664714b60edc76e2c2586dc28857ca0..1f611165a2b12c261248d7e19a407c99f090d902 100644
--- a/tutorial/Wmml.pmod
+++ b/tutorial/Wmml.pmod
@@ -54,100 +54,104 @@ static private int verify_any(SGML data,
       }
       switch(x->tag)
       {
-      default:
-	werror("Unknown tag "+x->tag+" near "+x->location()+".\n");
-	werror(in->dump());
-	i=0;
-	break;
-
-      case "font":
-      case "firstpage":
-      case "preface":
-      case "introduction":
-
-      case "chapter":
-      case "appendix":
-      case "i":
-      case "b":
-      case "a":
-      case "anchor":
-      case "tt":
-      case "pre":
-      case "tr":
-      case "td":
-      case "table":
-      case "box":
-      case "h1":
-      case "h2":
-      case "h3":
-      case "dl":
-      case "ul":
-      case "section":
-      case "center":
-      case "ol":
-      case "encaps":
-      case "th":
-      case "illustration":
-      case "strong":
-      case "link":
-
-	case "man_nb":
-	case "module":
-	case "class":
-	case "method":
-        case "variable":
-	case "function":
-        case "constant":
-	case "man_description":
-	case "man_see":
-	case "man_syntax":
-	case "man_bugs":
-	case "man_example":
-	case "man_title":
-	case "man_arguments":
-	case "man_returns":
-	case "man_note":
-
-      case "ex_identifier":
-      case "ex_keyword":
-      case "ex_string":
-      case "ex_comment":
-      case "ex_meta":
-      case "example":
+	 default:
+	    werror("Unknown tag "+x->tag+" near "+x->location()+".\n");
+	    werror(in->dump());
+	    i=0;
+	    break;
+
+	 case "font":
+	 case "firstpage":
+	 case "preface":
+	 case "introduction":
+
+	 case "chapter":
+	 case "appendix":
+	 case "i":
+	 case "b":
+	 case "a":
+	 case "anchor":
+	 case "tt":
+	 case "pre":
+	 case "tr":
+	 case "td":
+	 case "table":
+	 case "h1":
+	 case "h2":
+	 case "h3":
+	 case "dl":
+	 case "ul":
+	 case "section":
+	 case "center":
+	 case "ol":
+	 case "encaps":
+	 case "th":
+	 case "illustration":
+	 case "link":
+
+	 case "exercises":
+	 case "exercise":
+
+	 case "man_nb":
+	 case "module":
+	 case "class":
+	 case "method":
+	 case "variable":
+	 case "function":
+	 case "constant":
+	 case "man_description":
+	 case "man_see":
+	 case "man_syntax":
+	 case "man_bugs":
+	 case "man_example":
+	 case "man_title":
+	 case "man_arguments":
+	 case "man_returns":
+	 case "man_note":
+
+	 case "ex_identifier":
+	 case "ex_keyword":
+	 case "ex_string":
+	 case "ex_comment":
+	 case "ex_meta":
+	 case "example":
+
+	 case "data_description":
+	 case "elem": // in data_description
 	 
-      case "aargdesc":
-      case "aarg":
-      case "adesc":
-	if(!x->data)
-	{
-	  werror("Tag "+x->tag+" not closed near "+x->location()+".\n");
-	  werror(in->dump());
-	  i=0;
-	}
-
-	break;
+	 case "aargdesc": // in man_arguments
+	 case "aarg":	  // in man_arguments
+	 case "adesc":    // in man_arguments
+	    if(!x->data)
+	    {
+	       werror("Tag "+x->tag+" not closed near "+x->location()+".\n");
+	       werror(in->dump());
+	       i=0;
+	    }
 
-      case "ex_indent":
-      case "ex_br":
-      case "include":
-      case "dt":
-      case "dd":
-      case "li":
-      case "ref":
-      case "hr":
-      case "br":
-      case "img":
-      case "image":
-      case "table-of-contents":
-      case "index":
-	if(x->data)
-	{
-	  werror("Tag "+x->tag+" should not be closed near "+x->location()+"\n");
-	  werror(in->dump());
-	  i=0;
-	}
-      case "p":
-      case "wbr":
+	    break;
+
+	 case "ex_indent":
+	 case "ex_br":
+	 case "include":
+	 case "dt":
+	 case "dd":
+	 case "li":
+	 case "ref":
+	 case "hr":
+	 case "br":
+	 case "img":
+	 case "image":
+	 case "table-of-contents":
+	 case "index":
+	    if(x->data)
+	    {
+	       werror("Tag "+x->tag+" should not be closed near "+x->location()+"\n");
+	       werror(in->dump());
+	       i=0;
+	    }
+	 case "p":
+	 case "wbr":
       }
 
       if(x->data)
diff --git a/tutorial/html.pike b/tutorial/html.pike
index ebc34c55732ff4eda449c88f34d1decd03e38b17..46b69e2d404c2ee7e232ba26206dd62d782ebcd6 100644
--- a/tutorial/html.pike
+++ b/tutorial/html.pike
@@ -335,6 +335,82 @@ int cpos;
 
 SGML wmml_to_html(SGML data);
 
+SGML preify(SGML in,int id)
+{
+   array r=({});
+   foreach (in,TAG z)
+      if (!stringp(z))
+	 if (z->tag=="br")
+	    r+=({"\n"+" "*id});
+	 else
+	 {
+	    z->data=preify(z->data,id);
+	    r+=({z});
+	 }
+      else 
+	 r+=({replace(z,"\n","")});
+   return r;
+}
+
+SGML data_description(mapping arg,int pos,array(object) data,
+		      object orig)
+{
+   array d=({}); 
+   //    ({ type, name, value, pos, desc })
+   // or ({ ?,    0,    0,     pos, group description })
+
+   string type=arg->type||"",subtype="";
+   sscanf(type,"%s(%s)",type,subtype);
+
+   foreach (data,TAG data)
+      if (!stringp(data))
+	 switch (data->tag)
+	 {
+	    case "elem":
+	       d+=({({data->params->type||subtype, /* 0 */
+		      data->params->name,          /* 1 */
+		      data->params->value,         /* 2 */
+		      data->pos,                   /* 3 */
+		      data->data})});              /* 4 */
+	       break;
+	    default:
+	       werror("Warning: Found tag "+data->tag+" in data_description"
+		      +" (near "+data->location()+")\n");
+	 }
+   switch (type)
+   {
+      case "array":
+      {
+	 SGML ret=({});
+	 int ns,id,nl,ins,n=0;
+	 ins=strlen(""+sizeof(column(d,2)-({0})))+2;
+	 ns=max(@Array.map(d,
+			   lambda(array z)
+			   {
+			      return strlen(z[0]||"")+strlen(z[2]||"");
+			   }))+1;
+	 if (ns>30) id=8,nl=1; else id=ns+5+ins,nl=0;
+	 foreach (d,array t)
+	    if (t[2])
+	    {
+	       ret+=({sprintf("   %-*s %*s ",
+			      ns,t[0]+" "+t[2],ins,"["+n+"]")});
+	       ret+=({Sgml.Tag("i",([]),t[3],
+			       (nl?({"\n"+" "*id}):({}))+
+			       preify(t[4],id)),"\n"});
+	       n++;
+	    }
+	    else /* group title */
+	       ret+=({"   ",Sgml.Tag("b",([]),t[3],preify(t[4],id)),"\n"});
+	 return ({Sgml.Tag("pre",([]),pos,({"({\n"})+ret+({"})\n"}))});
+      }
+      default:
+	 werror("Warning: Illegal/unimplemented data type %O"
+		" (near "+orig->location()+")\n",type);
+   }
+   return ({});
+}
+
 /* Partially destructive! */
 SGML convert(SGML data)
 {
@@ -418,6 +494,17 @@ SGML convert(SGML data)
 			   Sgml.Tag("br")}));
 	    continue;
 
+	 case "exercises":
+	    ret+=convert(({Sgml.Tag("box",([]),data->pos,data->data)}));
+	    continue;
+	 case "exercise":
+	    ret+=convert(({Sgml.Tag("li",([]),data->pos,data->data)}));
+	    continue;
+
+	 case "data_description":
+	    ret+=data_description(data->params,data->pos,data->data,data);
+	    continue;
+
 	 case "ref":
 	 {
 	    string to=data->params->to;
diff --git a/tutorial/tutorial.wmml b/tutorial/tutorial.wmml
index 38d8d27e455faebb775615d3dd5ef36cc84d8e5c..e40399ab3235eabcb35c157c8f06e7eeb8806df3 100644
--- a/tutorial/tutorial.wmml
+++ b/tutorial/tutorial.wmml
@@ -863,24 +863,36 @@ to do as you continue to read this book. The complete listing of this example ca
 </section>
 
 <section title="Simple exercises">
-<box>
-<ul>
-<li> Make a program which writes hello world 10 times.
-<li> Modify hello_world.pike to write the first argument to the program.
-<li> Make a program that writes a hello_world program to stdout
-     when executed.
-<li> Modify the register program to store data about programs and diskettes
-     instead of songs and records.
-<li> Add code to the register program that checks that the user typed
-     an argument when required. The program should notify the user and
-     wait to receive more commands instead of exiting with an error message.
-<li> Add code to the register program to check that the arguments to
-     <tt>show_record</tt> and <tt>delete_records</tt> are numbers. Also
-     make sure that the number isn't less than one or bigger than the
-     available number of records.
-<li> Rewrite the register program and put all the code in main().
-</ul>
-</box>
+<exercises>
+<exercise>
+    Make a program which writes hello world 10 times.
+</exercise>
+<exercise>
+    Modify hello_world.pike to write the first argument to the program.
+</exercise>
+<exercise>
+    Make a program that writes a hello_world program to stdout
+    when executed.
+</exercise>
+<exercise>
+    Modify the register program to store data about programs and diskettes
+    instead of songs and records.
+</exercise>
+<exercise>
+    Add code to the register program that checks that the user typed
+    an argument when required. The program should notify the user and
+    wait to receive more commands instead of exiting with an error message.
+</exercise>
+<exercise>
+    Add code to the register program to check that the arguments to
+    <tt>show_record</tt> and <tt>delete_records</tt> are numbers. Also
+    make sure that the number isn't less than one or bigger than the
+    available number of records.
+</exercise>
+<exercise>
+    Rewrite the register program and put all the code in main().
+</exercise>
+</exercises>
 </section>
 </chapter>
 
@@ -1184,22 +1196,28 @@ is run.
 </section>
 
 <section title=Exercises>
-
-<box>
-<ul>
-<li> End all functions in the examples in chapter two with a return statement.
-<li> Change all <tt>foreach</tt> loops to <tt>for</tt> or <tt>while</tt> loops.
-<li> Make the <tt>find_song</tt> function in chapter 2 return when the first
+<exercises>
+<exercise>
+     End all functions in the examples in chapter two with a return statement.
+</exercise><exercise>
+     Change all <tt>foreach</tt> loops to <tt>for</tt> or <tt>while</tt> loops.
+</exercise><exercise>
+     Make the <tt>find_song</tt> function in chapter 2 return when the first
      matching song is found.
-<li> Make the <tt>find_song</tt> function write the number of the record
+</exercise><exercise>
+     Make the <tt>find_song</tt> function write the number of the record
      the song is on.
-<li> If you failed to get the program to work properly in the last exercise
+</exercise><exercise>
+     If you failed to get the program to work properly in the last exercise
      of chapter 2, try it again now.
-<li> Make a program that writes all the numbers from 1 to 1000.
-<li> Modify the program in the previous exercise to NOT write numbers divisible by 3, 7 or 17.
-<li> Make a program that writes all the prime numbers between 1 and 1000.
-</ul>
-</box>
+</exercise><exercise>
+     Make a program that writes all the numbers from 1 to 1000.
+</exercise><exercise>
+     Modify the program in the previous exercise to NOT write numbers divisible by 3, 7 or 17.
+</exercise><exercise>
+     Make a program that writes all the prime numbers between 1 and 1000.
+</exercise>
+</exercises>
 </section>
 </chapter>
 
@@ -3178,12 +3196,16 @@ expect, in which case you are better off not using operator overloading.
 </section>
 
 <section title="Simple exercises">
-<box>
-<ul>
-<li> Make a program that clones 10 hello world and then runs main() in
+<exercises>
+<exercise>
+     Make a program that clones 10 hello world and then runs main() in
      each one of them.
-<li> Modify the register program to use an object for each record.
-<li> Modify the register program to use the following search function:
+
+</exercise><exercise>
+     Modify the register program to use an object for each record.
+
+</exercise><exercise>
+     Modify the register program to use the following search function:
 <example language=pike>
 	void find_song(string title)
 	{
@@ -3204,9 +3226,8 @@ expect, in which case you are better off not using operator overloading.
 	  if(!hits) write("Not found.\n");
 	}
 </example>
-	
-</ul>
-</box>
+</exercise>
+</exercises>	
 <!-- FIXME add more examples above -->
 </section>
 </chapter>
@@ -3501,21 +3522,28 @@ or like this:
 </section>
 
 <section title="Simple exercises">
-<box>
-<ul>
-<li> Save the hello_world.pike program as hello_world.pike.pmod, then make
+<exercises>
+<exercise>
+     Save the hello_world.pike program as hello_world.pike.pmod, then make
      a program that loads this module and calls its main().
-<li> Make a directory called <tt>Programs.pmod</tt> and put all the examples you
+
+</exercise><exercise>
+     Make a directory called <tt>Programs.pmod</tt> 
+     and put all the examples you
      have written so far in it. Make a program that runs one of those
      programs. Make sure the program can be modified to run another of
      your examples by changing what module it loads.
-<li> Copy the file hello_world.pike.pmod to programs/module.pike.pmod and
+
+</exercise><exercise>
+     Copy the file hello_world.pike.pmod to programs/module.pike.pmod and
      then write a program that runs hello_world without actually using the
      identifier <tt>hello_world</tt>.
-<li> Try putting <tt>Programs.pmod</tt> in another directory and then try to
+
+</exercise><exercise>
+     Try putting <tt>Programs.pmod</tt> in another directory and then try to
      run the programs from the last two examples. 
-</ul>
-</box>
+</exercise>
+</exercises>
 </section>
 </chapter>
 
@@ -11198,21 +11226,20 @@ array(int) file-&gt;stat();
 <man_description>
 file_stat returns an array of integers describing some properties<br>
 about the file. Currently file_stat returns 7 entries:<br>
-<pre>
+
 <data_description type=array(int)>
 <elem value=mode>file mode, protection bits etc. etc. </elem>
 <elem value=size>file size for regular files, <br>
-      	    -2 for dirs,<br>
-      	    -3 for links,<br>
-      	    -4 for otherwise </elem>
+-2 for dirs,<br>
+-3 for links,<br>
+-4 for otherwise </elem>
 <elem value=atime>last access time </elem>
 <elem value=mtime>last modify time </elem>
 <elem value=ctime>last status time change </elem>
 <elem value=uid>The user who owns this file</elem>
 <elem value=gid>The group this file belongs to</elem>
 </data_description>
-</pre>
-</tt>
+
 If you give 1 as a second argument, file_stat does not follow links.<br>
 You can never get -3 as size if you don't give a second argument.<br>
 
@@ -11845,13 +11872,12 @@ first object in this list, and next_object(o) the next object in the
 list after o.
 </man_description>
 <man_example>
-<pre>
+<example language=pike>
 /* This example calls shutting_down() in all cloned objects */
 object o;
 for(o=next_object();o;o=next_object(o))
 o-&gt;shutting_down();<br>
-</pre>
-</dl>
+</example>
 </man_example>
 <man_see>
 clone, destruct
diff --git a/tutorial/wmml.wmml b/tutorial/wmml.wmml
index 4415c50c24748d40b3cf625bd6d295e76402f8e7..31a42526372530bc8d50c2690cbe9d01710fb184 100644
--- a/tutorial/wmml.wmml
+++ b/tutorial/wmml.wmml
@@ -100,8 +100,8 @@ wbr
 
 <section title="reference markup tags">
 
-method name= title= 
 module name= title= mansuffix= 
+method name= title= 
 class name= title=
 constant name= title=
 function name= fullpath= title=