diff --git a/tutorial/.cvsignore b/tutorial/.cvsignore
index 9c793269167190edeed4eed8deaa2fb276c7f66c..fa3187439b49eb5b9b5135de74956b42f520e21d 100644
--- a/tutorial/.cvsignore
+++ b/tutorial/.cvsignore
@@ -3,6 +3,7 @@
 .nfs*
 Calendar.wmml
 Image.wmml
+Math.wmml
 Mysql.wmml
 Parser.wmml
 Protocols*.wmml
@@ -44,5 +45,6 @@ tutorial.ps.bz2
 tutorial.ps.gz
 tutorial.tex
 tutorial.toc
+tutorial.xml
 tutorial_*.html
 tutorial_onepage.tex
diff --git a/tutorial/.gitignore b/tutorial/.gitignore
index edac2cebbb5bb39e4bb6c1beca69276deda5cc97..c889ca53c26d6791d61d7a5b1c8da2c33f7dd52f 100644
--- a/tutorial/.gitignore
+++ b/tutorial/.gitignore
@@ -3,6 +3,7 @@
 /.nfs*
 /Calendar.wmml
 /Image.wmml
+/Math.wmml
 /Mysql.wmml
 /Parser.wmml
 /Protocols*.wmml
@@ -44,5 +45,6 @@
 /tutorial.ps.gz
 /tutorial.tex
 /tutorial.toc
+/tutorial.xml
 /tutorial_*.html
 /tutorial_onepage.tex
diff --git a/tutorial/Sgml.pmod b/tutorial/Sgml.pmod
index d314ef4415a905b2ae36021997b54fd7e42f1128..fdfeca085892d088a4058de41a7a4594411e6fd5 100644
--- a/tutorial/Sgml.pmod
+++ b/tutorial/Sgml.pmod
@@ -80,10 +80,6 @@ class Tag
   }
 };
 
-class PropTag
-{
-};
-
 #define TAG object(Tag)|string
 #define SGML array(TAG)
 
diff --git a/tutorial/Wmml.pmod b/tutorial/Wmml.pmod
index 2c006c68440986d22ae4ee9a3f7c77fae4bcda40..b9df1f0d798623edddbd7c9275e54667eb90c373 100644
--- a/tutorial/Wmml.pmod
+++ b/tutorial/Wmml.pmod
@@ -32,10 +32,126 @@ import Sgml;
  * Solution two:
  *   Same as above, but using another field in the Tag class.
  *
+ * Solution three: (implemented)
+ *   Add a function to the Tag class which can access the contained
+ *   tags as a mapping.
+ *
  */
 
 SGML low_make_concrete_wmml(SGML data);
 
+#define TNAME(X) (objectp(X) ? X->tag : "")
+#define PROCESS(X,T) foreach(X,TAG T) switch(objectp(T) ? T->tag : "")
+
+string expand_macro_string(string s, TAG tag)
+{
+  array(string) data=s/"$";
+  for(int e=1;e<sizeof(data);e+=2)
+  {
+    if(data[e]=="")
+    {
+      data[e]="$";
+    }else{
+      data[e]=tag->params[data[e]];
+    }
+  }
+  return data*"";
+}
+
+SGML expand_macro(SGML macro, TAG tag)
+{
+  SGML ret=({});
+  
+  foreach(macro, TAG t)
+    {
+      if(objectp(t))
+      {
+	if(sscanf(t->tag,"%s:%s",string tagtype, string what))
+	{
+	  switch(tagtype)
+	  {
+	    case "param": ret+=({ tag->params[what] }); continue;
+	    case "taglist": ret+=tag->find(what); continue;
+	    case "tag": ret+=tag->find(what)->data; continue;
+	  }
+	}else{
+	  switch(t->tag)
+	  {
+	    case "content": ret+=tag->data; continue;
+	  }
+	}
+
+	ret+=({
+	  Tag(t->tag,
+	      map(t->params, expand_macro_string, tag),
+	      t->pos,
+	      expand_macro(t->data, tag),
+	      t->file)
+	});
+      }else{
+	ret+=({ expand_macro_string(t, tag) });
+      }
+    }
+  return ret;
+}
+
+mapping(string:SGML) macros=([]);
+
+SGML expand_macros(SGML x)
+{
+  if(!x) return 0;
+
+  SGML ret=({});
+
+  foreach(x, TAG t)
+    {
+      if(objectp(t))
+      {
+	switch(t->tag)
+	{
+	  case "include":
+	  {
+	    string filename=t->params->file;
+	    werror("Including %s...\n",filename);
+	    string file=Stdio.read_file(filename);
+	    if (file)
+	    {
+	      ret+=expand_macros(group(lex(file,filename)));
+	    } else {
+	      werror(sprintf("File %O not found specified in tag %O near %s\n",
+			     filename, t->tag, t->location()));
+	    }
+	    continue;
+	  }
+	  
+	  case "defmacro":
+	    macros[t->name]=t->data;
+	    continue;
+
+	  default:
+	    if(SGML macro=macros[t->name])
+	    {
+	      m_delete(macros, t->name); /* Avoid recursive expansion */
+	      ret+=expand_macros( expand_macro(macro, t) );
+	      macros[t->name]=macros;
+	      continue;
+	    }
+	}
+	ret+=({
+	  Tag(t->tag,
+	      t->params,
+	      t->pos,
+	      expand_macros(t->data),
+	      t->file)
+	});
+	continue;
+      }
+      ret+=({t});
+    }
+  return ret;
+}
+
+
 class Trace
 {
   Trace prev;
@@ -187,7 +303,7 @@ static private int verify_any(SGML data,
 	 case "insert_added_appendices":
 	 case "ex_indent":
 	 case "ex_br":
-	 case "include":
+//	 case "include":
 	 case "dt":
 	 case "dd":
 	 case "li":
@@ -800,13 +916,6 @@ SGML fix_module(TAG tag, string name)
   return res;
 }
 
-#define TNAME(X) (objectp(X) ? X->tag : "")
-#define PROCESS(X,T) foreach(X,TAG T) switch(objectp(T) ? T->tag : "")
-
-
-SGML explode(SGML data, multiset tags)
-{
-}
 
 SGML low_make_concrete_wmml(SGML data)
 {
@@ -929,22 +1038,6 @@ SGML low_make_concrete_wmml(SGML data)
 	  metadata+=tag->data;
 	  continue;
 	  
-	case "include":
-	{
-	  string filename=tag->params->file;
-	  werror("Reading %s...\n",filename);
-	  string file=Stdio.read_file(filename);
-	  if (file) {
-	    SGML tmp=group(lex(file,filename));
-	    verify(tmp,file,filename);
-	    ret+=low_make_concrete_wmml(tmp);
-	  } else {
-	    werror(sprintf("File %O not found specified in tag %O near %s\n",
-			   filename, tag->tag, tag->location()));
-	  }
-	  continue;
-	}
-	  
 	case "chapter":
 	  ret+=fix_section(tag,chapterE);
 	  chapterE->inc();
diff --git a/tutorial/tutorial.wmml b/tutorial/tutorial.wmml
index 896362aac0c3c0d0f961e23887794f64c399e83c..83eca340a19564fe4bb5eff226629571d5004c2e 100644
--- a/tutorial/tutorial.wmml
+++ b/tutorial/tutorial.wmml
@@ -66,8 +66,8 @@ _typeof
 all_threads (link to threads)
 	atan2
 	atexit -fixed by hubbe
-(chmod fixed /Mirar)
-chroot
+(chmod fixed /Mirar) 	(Move to system module? -Hubbe)
+chroot		need ref
 cleargroups
 closelog
 endpwent
@@ -78,22 +78,22 @@ get_all_users
 get_groups_for_user
 getgrent
 getgroups
-getegid
-geteuid
-getgid
+getegid		need ref
+geteuid		need ref
+getgid		need ref
 getgrgid
 getgrnam
-gethostbyaddr
-gethostbyname
-gethostname
+gethostbyaddr	need ref
+gethostbyname	need ref
+gethostname	need ref
 gethrtime
 gethrvtime
-getpgrp
+getpgrp	need ref
 getppid
 getpwent
 getpwnam
 getpwuid
-getuid
+getuid	need ref
 	gmtime - already exists /Hubbe
 hardlink
 initgroups
@@ -112,7 +112,7 @@ setgid
 setgrent
 setgroups
 setpwent
-setuid
+setuid		need ref
 	sgn - documented /hubbe
 symlink
 syslog
@@ -141,6 +141,7 @@ all global functions are included (like threads and Stdio-stuff).
 If the Threads och Stdio-functions begin to work in the modules,
 this comment can be erased.
 
+
 Locale.Charset.*
 Process.create_process ?  done  -hubbe
 
@@ -7029,7 +7030,6 @@ initgroups, getgroups, getgid, setgid, getegid, setegid
 </function>
 
 
-
 <function name=setuid title="set the user ID" fullpath>
 <man_syntax>
 void setuid(int <I>uid</I>);<br>
diff --git a/tutorial/wmmltohtml2 b/tutorial/wmmltohtml2
index 92f8aa582125112eab08454804a0c45a654d0ac3..a28ba20a0b2561a95dd0c9491436b2c94d1fe8a7 100755
--- a/tutorial/wmmltohtml2
+++ b/tutorial/wmmltohtml2
@@ -35,6 +35,10 @@ int main(int argc, string *argv)
     mixed data=Sgml.lex(input,argv[1]);
     werror("Grouping ");
     data=Sgml.group(data);
+
+    werror("Expanding macros\n");
+    data=Wmml.expand_macros(data);
+
     werror("Verifying\n");
     Wmml.verify(data,input,argv[1]);
     werror("Concretizing\n");