Commit Jeroen Witmond's xml-to-text translator, and use it for the FAQ.
Less double maintenance, hoorah!



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@4830 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/FAQ.txt b/FAQ.txt
deleted file mode 100644
index 063a161..0000000
--- a/FAQ.txt
+++ /dev/null
@@ -1,361 +0,0 @@
-Valgrind FAQ, version 2.1.2
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Last revised 18 July 2004
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-1. Background
-2. Compiling, installing and configuring
-3. Valgrind aborts unexpectedly
-4. Valgrind behaves unexpectedly
-5. Memcheck doesn't find my bug
-6. Miscellaneous
-
-
------------------------------------------------------------------
-1. Background
------------------------------------------------------------------
-
-1.1. How do you pronounce "Valgrind"?
-
-The "Val" as in the world "value".  The "grind" is pronounced with a
-short 'i' -- ie. "grinned" (rhymes with "tinned") rather than "grined"
-(rhymes with "find").
-
-Don't feel bad:  almost everyone gets it wrong at first.
-
------------------------------------------------------------------
-
-1.2. Where does the name "Valgrind" come from?
-
-From Nordic mythology.  Originally (before release) the project was
-named Heimdall, after the watchman of the Nordic gods.  He could "see a
-hundred miles by day or night, hear the grass growing, see the wool
-growing on a sheep's back" (etc).  This would have been a great name,
-but it was already taken by a security package "Heimdal".
-
-Keeping with the Nordic theme, Valgrind was chosen.  Valgrind is the
-name of the main entrance to Valhalla (the Hall of the Chosen Slain in
-Asgard).  Over this entrance there resides a wolf and over it there is
-the head of a boar and on it perches a huge eagle, whose eyes can see to
-the far regions of the nine worlds.  Only those judged worthy by the
-guardians are allowed to pass through Valgrind.  All others are refused
-entrance.
-
-It's not short for "value grinder", although that's not a bad guess.
-
-
------------------------------------------------------------------
-2. Compiling, installing and configuring
------------------------------------------------------------------
-
-2.1. When I trying building Valgrind, 'make' dies partway with an
-     assertion failure, something like this: make: expand.c:489:
-      
-        allocated_variable_append: Assertion 
-        `current_variable_set_list->next != 0' failed.
-      
-It's probably a bug in 'make'.  Some, but not all, instances of version 3.79.1
-have this bug, see www.mail-archive.com/bug-make@gnu.org/msg01658.html.  Try
-upgrading to a more recent version of 'make'.  Alternatively, we have heard
-that unsetting the CFLAGS environment variable avoids the problem.
-
-
------------------------------------------------------------------
-3. Valgrind aborts unexpectedly
------------------------------------------------------------------
-
-3.1. Programs run OK on Valgrind, but at exit produce a bunch of errors a bit
-     like this
-
-    ==20755== Invalid read of size 4
-    ==20755==    at 0x40281C8A: _nl_unload_locale (loadlocale.c:238)
-    ==20755==    by 0x4028179D: free_mem (findlocale.c:257)
-    ==20755==    by 0x402E0962: __libc_freeres (set-freeres.c:34)
-    ==20755==    by 0x40048DCC: vgPlain___libc_freeres_wrapper 
-                                              (vg_clientfuncs.c:585)
-    ==20755==    Address 0x40CC304C is 8 bytes inside a block of size 380 free'd
-    ==20755==    at 0x400484C9: free (vg_clientfuncs.c:180)
-    ==20755==    by 0x40281CBA: _nl_unload_locale (loadlocale.c:246)
-    ==20755==    by 0x40281218: free_mem (setlocale.c:461)
-    ==20755==    by 0x402E0962: __libc_freeres (set-freeres.c:34)
-
-    and then die with a segmentation fault.
-
-When the program exits, Valgrind runs the procedure __libc_freeres() in
-glibc.  This is a hook for memory debuggers, so they can ask glibc to
-free up any memory it has used.  Doing that is needed to ensure that
-Valgrind doesn't incorrectly report space leaks in glibc.
-
-Problem is that running __libc_freeres() in older glibc versions causes
-this crash.  
-
-WORKAROUND FOR 1.1.X and later versions of Valgrind: use the
---run-libc-freeres=no flag.  You may then get space leak reports for
-glibc-allocations (please _don't_ report these to the glibc people,
-since they are not real leaks), but at least the program runs.
-
------------------------------------------------------------------
-
-3.2. My (buggy) program dies like this:
-      valgrind: vg_malloc2.c:442 (bszW_to_pszW): 
-                Assertion `pszW >= 0' failed.
-
-If Memcheck (the memory checker) shows any invalid reads, invalid writes
-and invalid frees in your program, the above may happen.  Reason is that
-your program may trash Valgrind's low-level memory manager, which then
-dies with the above assertion, or something like this.  The cure is to
-fix your program so that it doesn't do any illegal memory accesses.  The
-above failure will hopefully go away after that.
-
------------------------------------------------------------------
-
-3.3. My program dies, printing a message like this along the way: 
-
-      disInstr: unhandled instruction bytes: 0x66 0xF 0x2E 0x5
-
-Older versions did not support some x86 instructions, particularly
-SSE/SSE2 instructions.  Try a newer Valgrind;  we now support almost all
-instructions.  If it still happens with newer versions, if the failing
-instruction is an SSE/SSE2 instruction, you might be able to recompile
-your program without it by using the flag -march to gcc.  Either way,
-let us know and we'll try to fix it.
-
-Another possibility is that your program has a bug and erroneously jumps
-to a non-code address, in which case you'll get a SIGILL signal.
-Memcheck/Addrcheck may issue a warning just before this happens, but they
-might not if the jump happens to land in addressable memory.
-
-
------------------------------------------------------------------
-4. Valgrind behaves unexpectedly
------------------------------------------------------------------
-
-4.1. My threaded server process runs unbelievably slowly on Valgrind.
-     So slowly, in fact, that at first I thought it had completely
-     locked up.
-
-We are not completely sure about this, but one possibility is that
-laptops with power management fool Valgrind's timekeeping mechanism,
-which is (somewhat in error) based on the x86 RDTSC instruction.  A
-"fix" which is claimed to work is to run some other cpu-intensive
-process at the same time, so that the laptop's power-management
-clock-slowing does not kick in.  We would be interested in hearing more
-feedback on this.
-
-Another possible cause is that versions prior to 1.9.6 did not support
-threading on glibc 2.3.X systems well.  Hopefully the situation is much
-improved with 1.9.6 and later versions.
-
------------------------------------------------------------------
-
-4.2. My program uses the C++ STL and string classes.  Valgrind
-     reports 'still reachable' memory leaks involving these classes
-     at the exit of the program, but there should be none.
-
-First of all: relax, it's probably not a bug, but a feature.  Many
-implementations of the C++ standard libraries use their own memory pool
-allocators.  Memory for quite a number of destructed objects is not
-immediately freed and given back to the OS, but kept in the pool(s) for
-later re-use.  The fact that the pools are not freed at the exit() of
-the program cause Valgrind to report this memory as still reachable.
-The behaviour not to free pools at the exit() could be called a bug of
-the library though.
-
-Using gcc, you can force the STL to use malloc and to free memory as
-soon as possible by globally disabling memory caching.  Beware!  Doing
-so will probably slow down your program, sometimes drastically.
-
-- With gcc 2.91, 2.95, 3.0 and 3.1, compile all source using the STL
-  with -D__USE_MALLOC. Beware! This is removed from gcc starting with
-  version 3.3.
-
-- With gcc 3.2.2 and later, you should export the environment variable
-  GLIBCPP_FORCE_NEW before running your program.
-
-- With gcc 3.4 and later, that variable has changed name to
-  GLIBCXX_FORCE_NEW.
-
-There are other ways to disable memory pooling: using the malloc_alloc
-template with your objects (not portable, but should work for gcc) or
-even writing your own memory allocators. But all this goes beyond the
-scope of this FAQ.  Start by reading
-http://gcc.gnu.org/onlinedocs/libstdc++/ext/howto.html#3 if you
-absolutely want to do that. But beware:
-
-1) there are currently changes underway for gcc which are not totally
-   reflected in the docs right now ("now" == 26 Apr 03)
-
-2) allocators belong to the more messy parts of the STL and people went
-   at great lengths to make it portable across platforms. Chances are
-   good that your solution will work on your platform, but not on
-   others.
-
------------------------------------------------------------------------------
-4.3. The stack traces given by Memcheck (or another tool) aren't helpful.
-     How can I improve them?
-
-If they're not long enough, use --num-callers to make them longer.
-
-If they're not detailed enough, make sure you are compiling with -g to add
-debug information.  And don't strip symbol tables (programs should be
-unstripped unless you run 'strip' on them;  some libraries ship stripped).
-
-Also, for leak reports involving shared objects, if the shared object is
-unloaded before the program terminates, Valgrind will discard the debug
-information and the error message will be full of "???" entries.  The
-workaround here is to avoid calling dlclose() on these shared objects.
-
-Also, -fomit-frame-pointer and -fstack-check can make stack traces worse.
-
-Some example sub-traces:
-
- With debug information and unstripped (best):
-
-    Invalid write of size 1
-       at 0x80483BF: really (malloc1.c:20)
-       by 0x8048370: main (malloc1.c:9)
-
- With no debug information, unstripped:
-
-    Invalid write of size 1
-       at 0x80483BF: really (in /auto/homes/njn25/grind/head5/a.out)
-       by 0x8048370: main (in /auto/homes/njn25/grind/head5/a.out)
-
- With no debug information, stripped:
-
-    Invalid write of size 1
-       at 0x80483BF: (within /auto/homes/njn25/grind/head5/a.out)
-       by 0x8048370: (within /auto/homes/njn25/grind/head5/a.out)
-       by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so)
-       by 0x80482CC: (within /auto/homes/njn25/grind/head5/a.out)
-
- With debug information and -fomit-frame-pointer:
-
-    Invalid write of size 1
-       at 0x80483C4: really (malloc1.c:20)
-       by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so)
-       by 0x80482CC: ??? (start.S:81)
-
- A leak error message involving an unloaded shared object:
-
-    84 bytes in 1 blocks are possibly lost in loss record 488 of 713
-       at 0x1B9036DA: operator new(unsigned) (vg_replace_malloc.c:132)
-       by 0x1DB63EEB: ???
-       by 0x1DB4B800: ???
-       by 0x1D65E007: ???
-       by 0x8049EE6: main (main.cpp:24)
-
------------------------------------------------------------------------------
-4.4. The stack traces given by Memcheck (or another tool) seem to
-     have the wrong function name in them.  What's happening?
-
-Occasionally Valgrind stack traces get the wrong function names.
-This is caused by glibc using aliases to effectively give one function
-two names.  Most of the time Valgrind chooses a suitable name, but
-very occasionally it gets it wrong.
-
-Examples we know of are printing 'bcmp' instead of 'memcmp', 'index'
-instead of 'strchr', and 'rindex' instead of 'strrchr'.
-
------------------------------------------------------------------
-5. Memcheck doesn't find my bug
------------------------------------------------------------------
-
-5.1. I try running "valgrind --tool=memcheck my_program" and get
-     Valgrind's startup message, but I don't get any errors and I know
-     my program has errors.
-
-There are two possible causes of this.
-   
-First, by default, Valgrind only traces the top-level process.  So if your
-program spawns children, they won't be traced by Valgrind by default.
-Also, if your program is started by a shell script, Perl script, or
-something similar, Valgrind will trace the shell, or the Perl
-interpreter, or equivalent.
-
-To trace child processes, use the --trace-children=yes option.
-
-If you are tracing large trees of processes, it can be less disruptive
-to have the output sent over the network.  Give Valgrind the flag
---log-socket=127.0.0.1:12345 (if you want logging output sent to port
-12345 on localhost).  You can use the valgrind-listener program to
-listen on that port:
-
-  valgrind-listener 12345 
-  
-Obviously you have to start the listener process first.  See the
-documentation for more details.
-
-Second, if your program is statically linked, most Valgrind tools won't
-work as well, because they won't be able to replace certain functions,
-such as malloc(), with their own versions.  A key indicator of this is
-if Memcheck says:
-
-  No malloc'd blocks -- no leaks are possible
-
-when you know your program calls malloc().  The workaround is to avoid
-statically linking your program.
-
------------------------------------------------------------------
-
-5.2. Why doesn't Memcheck find the array overruns in this program?
-
-       int static[5];
-
-       int main(void)
-       {
-          int stack[5];
-
-          static[5] = 0;
-          stack [5] = 0;
-          
-          return 0;
-       }
-
-Unfortunately, Memcheck doesn't do bounds checking on static or stack
-arrays.  We'd like to, but it's just not possible to do in a reasonable
-way that fits with how Memcheck works.  Sorry.
-
-
------------------------------------------------------------------
-6. Miscellaneous
------------------------------------------------------------------
-
-6.1. I tried writing a suppression but it didn't work.  Can you 
-     write my suppression for me?
-
-Yes!  Use the --gen-suppressions=yes feature to spit out suppressions
-automatically for you.  You can then edit them if you like, eg.
-combining similar automatically generated suppressions using wildcards
-like '*'.
-
-If you really want to write suppressions by hand, read the manual
-carefully.  Note particularly that C++ function names must be _mangled_.
-
------------------------------------------------------------------
-
-6.2. With Memcheck/Addrcheck's memory leak detector, what's the
-     difference between "definitely lost", "possibly lost", "still
-     reachable", and "suppressed"?
-
-The details are in section 3.6 of the manual.
-
-In short:
-
-  - "definitely lost" means your program is leaking memory -- fix it!
-
-  - "possibly lost" means your program is probably leaking memory,
-    unless you're doing funny things with pointers.
-
-  - "still reachable" means your program is probably ok -- it didn't
-    free some memory it could have.  This is quite common and often
-    reasonable.  Don't use --show-reachable=yes if you don't want to see
-    these reports.
-
-  - "suppressed" means that a leak error has been suppressed.  There are
-    some suppressions in the default suppression files.  You can ignore
-    suppressed errors.
-
------------------------------------------------------------------
-
-(this is the end of the FAQ.)
diff --git a/Makefile.am b/Makefile.am
index 95f29cf..2f95718 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,7 +42,6 @@
 	@PERL@ tests/vg_regtest $(TOOLS)
 
 EXTRA_DIST = \
-	FAQ.txt \
 	ACKNOWLEDGEMENTS \
 	README_DEVELOPERS \
 	README_PACKAGERS \
diff --git a/docs/Makefile.am b/docs/Makefile.am
index fee2b2a..b8307f0 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -7,7 +7,7 @@
 # Comment out the next line to skip building print docs.  The default
 # is not to skip building print docs.  Note, after changing it
 # you of course need to re-run configure to make it take effect.
-BUILD_ALL_DOCS=yes
+BUILD_ALL_DOCS=no
 
 ##-------------------------------------------------------------
 ## END OF HACK
@@ -44,18 +44,24 @@
 
 # stylesheet processor
 XSLTPROC       = xsltproc
-XSLTPROC_FLAGS = --nonet --xinclude 
+XSLTPROC_FLAGS = --xinclude 
 
 # stylesheets
 XSL_HTML_CHUNK_STYLE  = $(mylibdir)/vg-html-chunk.xsl
 XSL_HTML_SINGLE_STYLE = $(mylibdir)/vg-html-single.xsl
 XSL_FO_STYLE          = $(mylibdir)/vg-fo.xsl
 
-all-docs: html-docs print-docs
+all-docs: FAQ.txt html-docs print-docs
 
 valid:
 	$(XMLLINT) $(XMLLINT_FLAGS) $(myxmldir)/index.xml
 
+# The text version of the FAQ.
+FAQ.txt: $(myxmldir)/FAQ.xml $(myxmldir)/vg-entities.xml $(mylibdir)/*.xslt
+	$(XSLTPROC) $(XSLTPROC_FLAGS) $(mylibdir)/untag-inline.xslt $(myxmldir)/FAQ.xml | \
+	$(XSLTPROC) $(XSLTPROC_FLAGS) $(mylibdir)/docbook2text.xslt - > FAQ.txt
+	
+
 # chunked html
 html-docs:
 	@echo "Generating html files..."
@@ -113,14 +119,17 @@
 # This is done at 'make dist' time.  It builds the html and print docs
 # and copies them into the docs/ directory in the tarball.
  ifeq ($(BUILD_ALL_DOCS),yes)
-dist-hook: html-docs print-docs
+dist-hook: FAQ.txt html-docs print-docs
 	cp -r html $(distdir)
+	cp FAQ.txt $(distdir)/..
 	cp print/index.pdf $(distdir)
 	cp print/index.ps $(distdir)
  else
-dist-hook: html-docs
+dist-hook: FAQ.txt html-docs
+	cp FAQ.txt $(distdir)/..
 	cp -r html $(distdir)
  endif
 
 distclean-local:
 	rm -rf html print
+	rm -f $(top_builddir)/FAQ.txt
diff --git a/docs/README b/docs/README
index af8fa16..6f6b5e3 100644
--- a/docs/README
+++ b/docs/README
@@ -178,12 +178,6 @@
 - http://cvs.sourceforge.net/viewcvs.py/perl-xml/perl-xml-faq/
 
 
-TODO CRUCIAL:
--------------
-- Need to generate text FAQ from the valgrind/docs/xml/FAQ.xml (done at 'make
-  dist' time along with the HTML docs using the "dist-hook"), and remove the
-  old text FAQ which is in valgrind/.
-
 TODO LESS CRUCIAL:
 ------------------
 - add the HOWTO doc?
diff --git a/docs/lib/Makefile.am b/docs/lib/Makefile.am
index 627e39d..c589900 100644
--- a/docs/lib/Makefile.am
+++ b/docs/lib/Makefile.am
@@ -1,6 +1,16 @@
 EXTRA_DIST = \
+	README_XML2TXT.txt \
+	copy.xslt \
+	text.justify.xslt \
+	docbook2text.xslt \
+	text.wrap.xslt \
+	str.dup.xslt \
+	untag-inline.xslt \
+	str.find-last.xslt \
 	vg-common.xsl \
 	vg-fo.xsl \
 	vg-html-chunk.xsl \
 	vg-html-single.xsl \
 	vg_basic.css
+
+	
diff --git a/docs/lib/README_XML2TXT.txt b/docs/lib/README_XML2TXT.txt
new file mode 100644
index 0000000..3d70bec
--- /dev/null
+++ b/docs/lib/README_XML2TXT.txt
@@ -0,0 +1,59 @@
+README.txt file for the FAQ.xml to FAQ.txt transformer.
+=======================================================
+
+In valgrind-3.1.SVN, file docs/README contains, under the heading
+"TODO CRUCIAL", the item "Need to generate text FAQ from the
+valgrind/docs/xml/FAQ.xml (done at 'make dist' time along with the
+HTML docs using the 'dist-hook'), and remove the old text FAQ which is
+in valgrind/."  This is an attempt to resolve this item using xsltproc
+and a number of xml style sheets. It is a hack in the sense that it
+does not support all of docbook, but only the subset currently used by
+file docs/xml/FAQ.xml.
+
+The transformation is done in two stages:
+
+1. Inline elements (literal, computeroutput and ulink) are replaced
+   with the corresponding text by stylesheet untag-inline.xslt. This
+   stylesheet uses copy.xslt to handle the non-inline elements.
+
+2. The actual formatting is done by stylesheet docbook2text.xslt. It
+   uses stylesheets str.dup.xslt, str.find-last.xslt,
+   text.justify.xslt and text.wrap.xslt to handle the formatting of
+   the text into a column with the approproate width and indentation.
+
+Stylesheets untag-inline.xslt and docbook2text.xslt are original
+work. Stylesheets copy.xslt, str.dup.xslt, str.find-last.xslt,
+text.justify.xslt and text.wrap.xslt are copied with some adaptations
+from the examples supplied with the XSLT Cookbook by Sal Mangano, (C)
+2003 O'Reilly & Associates, ISBN 0-596-00372-2. The O'Reilly Policy on
+Re-Use of Code Examples from Books
+<http://www.oreilly.com/pub/a/oreilly/ask_tim/2001/codepolicy.html>
+allows the use of these style sheets in valgrind for this purpose.
+
+The tarball contains:
+
+- File README.txt (this file).
+
+- the stylesheets copy.xslt, docbook2text.xslt, str.dup.xslt,
+  str.find-last.xslt, text.justify.xslt, text.wrap.xslt and
+  untag-inline.xslt.
+
+- File faq.txt.patch adds the generation of FAQ.txt to file
+  docs/Makefile.am. It is based on the assumptions that
+  * file FAQ.txt will be generated in directory docs.
+  * the stylesheets will be stored in directory docs/lib.
+  If a different output width is required (e.g. 80), you can use
+  command 
+    $(XSLTPROC) $(XSLTPROC_FLAGS) $(libdir)/untag-inline.xslt $(xmldir)/FAQ.xml | \
+    $(XSLTPROC) $(XSLTPROC_FLAGS) --stringparam width 80 $(libdir)/docbook2text.xslt - > FAQ.txt
+
+- File FAQ.txt as generated on my system (Debian 3.1).
+
+Software used:
+xsltproc was compiled against libxml 20616, libxslt 10112 and libexslt 810
+libxslt 10112 was compiled against libxml 20616
+libexslt 810 was compiled against libxml 20616
+
+
+Copyright 2005 Jeroen N. Witmond, jnw@xs4all.nl
+GNU GENERAL PUBLIC LICENSE Version 2, June 1991 applies.
diff --git a/docs/lib/copy.xslt b/docs/lib/copy.xslt
new file mode 100644
index 0000000..b2c565d
--- /dev/null
+++ b/docs/lib/copy.xslt
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!-- This file was copied with some adaptations from the examples
+supplied with the XSLT Cookbook by Sal Mangano, (C) 2003 O'Reilly &
+Associates, ISBN 0-596-00372-2. -->
+
+<xsl:output method="xml"/>
+
+<xsl:template match="/ | node() | @* | comment() | processing-instruction()">
+  <xsl:copy>
+    <xsl:apply-templates select="node() | @*"/>
+  </xsl:copy>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/lib/docbook2text.xslt b/docs/lib/docbook2text.xslt
new file mode 100644
index 0000000..becc862
--- /dev/null
+++ b/docs/lib/docbook2text.xslt
@@ -0,0 +1,317 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- sgml -*- -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:text="http://www.ora.com/XSLTCookbook/namespaces/text"
+  xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
+
+<xsl:include href="text.wrap.xslt"/>
+
+<xsl:strip-space elements="*"/>
+<xsl:preserve-space elements="screen programlisting"/>
+<xsl:output method="text"/>
+
+<!-- Transform a subset of docbook/xml into plain text. This -->
+<!-- stylesheet assumes that all elements that are to be formatted -->
+<!-- inline already have been handled by untag-inline.xslt. -->
+
+<!-- Maximum number of characters on one line. --> 
+<xsl:param name="width" select="72"/>
+
+<!-- This stylesheet uses two named templates. The template named -->
+<!-- "header" writes its input, followed by a line of '~'s of -->
+<!-- the same length. The template named "ruler" produces a line of -->
+<!-- $width '-'s. They are implemented at the bottom of this file. -->
+
+<xsl:template match="/">
+  <!-- Title 'page' stuff. -->
+  <xsl:call-template name="header">
+    <xsl:with-param name="input" select="book/title"/>
+  </xsl:call-template>
+
+  <xsl:call-template name="header">
+    <xsl:with-param name="input" select="book/bookinfo/releaseinfo"/>
+  </xsl:call-template>
+  <xsl:text>&#xa;</xsl:text>
+
+  <!-- List of sections. -->
+  <xsl:text>Table of Contents&#xa;</xsl:text>
+  <xsl:apply-templates select="book/sect1" mode="toc"/>
+  <xsl:text>&#xa;</xsl:text>
+
+  <!-- Contents. -->
+  <xsl:apply-templates select="book/sect1"/>
+</xsl:template>
+
+<xsl:template match="sect1" mode="toc">
+  <xsl:value-of select="position()"/>
+  <xsl:text>. </xsl:text>
+  <xsl:value-of select="title"/>
+  <xsl:text>&#xa;</xsl:text>
+</xsl:template>
+
+<!-- Processing of the contents starts here. -->
+
+<xsl:template match="sect1">
+  <!-- The FAQ contains two types of sect1: those containing a
+    qandaset (actually an unspecified number), and those containing a
+    sequence of paras. To get the number of blank lines right, these
+    must be treated separately. -->
+
+  <xsl:variable name="sectno" select="position()"/>
+
+  <xsl:call-template name="ruler"/>
+  <xsl:value-of select="$sectno"/>
+  <xsl:text>. </xsl:text>
+  <xsl:value-of select="title"/>
+  <xsl:text>&#xa;</xsl:text>
+  <xsl:call-template name="ruler"/>
+  <xsl:text>&#xa;</xsl:text>
+
+  <xsl:choose>
+    <xsl:when test="qandaset">
+      <xsl:apply-templates select="qandaset|para">
+        <xsl:with-param name="sectno" select="$sectno"/>
+      </xsl:apply-templates>
+    </xsl:when>
+
+    <xsl:when test="para">
+      <xsl:for-each select="para|screen|programlisting|itemizedlist|orderedlist">
+        <xsl:apply-templates select="."/>
+
+        <xsl:if test="position() != last()">
+            <xsl:text>&#xa;</xsl:text>
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:when>
+
+    <!-- Oops. sect1 contains elements we do not yet handle. -->
+    <xsl:otherwise>
+      <xsl:message>template match="sect1": Encountered
+      &lt;<xsl:value-of select="name(.)"/>&gt;.</xsl:message>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:text>&#xa;</xsl:text>
+</xsl:template>
+
+<xsl:template match="qandaset">
+  <xsl:param name="sectno"/>
+
+  <xsl:apply-templates select="qandaentry">
+    <xsl:with-param name="sectno" select="$sectno"/>
+  </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="qandaentry">
+  <xsl:param name="sectno"/>
+
+  <xsl:variable name="questno" select="position()"/>
+
+  <xsl:variable name="prefix">
+    <xsl:value-of select="$sectno"/>
+    <xsl:text>.</xsl:text>
+    <xsl:value-of select="$questno"/>
+    <xsl:text>. </xsl:text>
+  </xsl:variable>
+  <xsl:variable name="prefix-length" select="string-length($prefix)"/>
+
+  <xsl:if test="$questno > 1">
+    <xsl:text>&#xa;</xsl:text>
+    <xsl:call-template name="ruler"/>
+    <xsl:text>&#xa;</xsl:text>
+  </xsl:if>
+
+  <xsl:apply-templates select="question">
+    <xsl:with-param name="prefix" select="$prefix"/>
+    <xsl:with-param name="indent" select="$prefix-length"/>
+  </xsl:apply-templates>
+
+  <xsl:text>&#xa;</xsl:text>
+
+  <xsl:apply-templates select="answer"/>
+</xsl:template>
+
+<xsl:template match="question">
+  <xsl:param name="prefix"/>
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:for-each select="para|screen|programlisting">
+    <xsl:choose>
+      <xsl:when test="position() = 1 and $prefix">
+        <xsl:value-of select="$prefix"/>
+        <xsl:apply-templates select=".">
+          <xsl:with-param name="indent" select="$indent"/>
+          <xsl:with-param name="dofirst" select="0"/>
+        </xsl:apply-templates>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates select=".">
+          <xsl:with-param name="indent" select="$indent"/>
+        </xsl:apply-templates>
+      </xsl:otherwise>
+    </xsl:choose>
+
+    <xsl:if test="position() != last()">
+        <xsl:text>&#xa;</xsl:text>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="answer">
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:for-each select="para|screen|programlisting|itemizedlist|orderedlist">
+    <xsl:apply-templates select=".">
+      <xsl:with-param name="indent" select="$indent"/>
+    </xsl:apply-templates>
+
+    <xsl:if test="position() != last()">
+        <xsl:text>&#xa;</xsl:text>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="para">
+  <xsl:param name="indent" select="0"/>
+  <xsl:param name="dofirst" select="1"/>
+
+  <xsl:for-each select="node()">
+    <xsl:choose>
+      <!-- Lists and blocks as children. -->
+      <xsl:when test="self::screen|self::programlisting|
+		      self::itemizedlist|self::orderedlist">
+        <xsl:apply-templates select=".">
+          <xsl:with-param name="indent" select="$indent"/>
+        </xsl:apply-templates>
+
+        <xsl:if test="position() != last()">
+            <xsl:text>&#xa;</xsl:text>
+        </xsl:if>
+      </xsl:when>
+
+      <!-- Text. Inline elements have been flattened by
+           untag-inline.xslt. -->
+      <xsl:when test="self::text()">
+        <xsl:choose>
+          <xsl:when test="position() = 1 and $dofirst = 0">
+            <xsl:apply-templates select="." mode="text:wrap">
+              <xsl:with-param name="width" select="$width"/>
+              <xsl:with-param name="indent" select="$indent"/>
+              <xsl:with-param name="dofirst" select="0"/>
+            </xsl:apply-templates>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="." mode="text:wrap">
+              <xsl:with-param name="width" select="$width"/>
+              <xsl:with-param name="indent" select="$indent"/>
+            </xsl:apply-templates>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+
+      <!-- Oops. para contains elements we do not yet handle. -->
+      <xsl:otherwise>
+        <xsl:message>template match="para": Encountered
+        &lt;<xsl:value-of select="name(.)"/>&gt;.</xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="screen|programlisting">
+  <xsl:param name="indent" select="0"/>
+  <xsl:param name="dofirst" select="1"/><!-- ignored. -->
+
+  <xsl:variable name="myindent" select=" $indent + 2 " />
+
+  <xsl:apply-templates select="." mode="text:dump">
+    <xsl:with-param name="input" select="text()"/>
+    <xsl:with-param name="indent" select="$myindent"/>
+  </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="itemizedlist">
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:variable name="prefix" select=" '* ' "/>
+  <xsl:variable name="prefix-length" select="string-length($prefix)"/>
+
+  <xsl:for-each select="listitem">
+    <xsl:apply-templates select=".">
+      <xsl:with-param name="prefix" select="$prefix"/>
+      <xsl:with-param name="indent" select="$prefix-length"/>
+    </xsl:apply-templates>
+
+    <xsl:if test="position() != last()">
+        <xsl:text>&#xa;</xsl:text>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="orderedlist">
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:for-each select="listitem">
+    <xsl:variable name="prefix">
+      <xsl:value-of select="position()"/>
+      <xsl:text>. </xsl:text>
+    </xsl:variable>
+    <xsl:variable name="prefix-length" select="string-length($prefix)"/>
+
+    <xsl:apply-templates select=".">
+      <xsl:with-param name="prefix" select="$prefix"/>
+      <xsl:with-param name="indent" select="$prefix-length"/>
+    </xsl:apply-templates>
+
+    <xsl:if test="position() != last()">
+        <xsl:text>&#xa;</xsl:text>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="listitem">
+  <xsl:param name="prefix"/>
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:for-each select="para|screen|programlisting">
+    <xsl:choose>
+      <xsl:when test="position() = 1 and $prefix">
+        <xsl:value-of select="$prefix"/>
+        <xsl:apply-templates select=".">
+          <xsl:with-param name="indent" select="$indent"/>
+          <xsl:with-param name="dofirst" select="0"/>
+        </xsl:apply-templates>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates select=".">
+          <xsl:with-param name="indent" select="$indent"/>
+        </xsl:apply-templates>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- Named utility templates. -->
+
+<xsl:template name="header">
+  <xsl:param name="input" select="0"/>
+  <xsl:variable name="input-length" select="string-length($input)"/>
+
+  <xsl:value-of select="$input"/>
+  <xsl:text>&#xa;</xsl:text>
+
+  <xsl:call-template name="str:dup">
+    <xsl:with-param name="input" select=" '~' "/>
+    <xsl:with-param name="count" select="$input-length"/>
+  </xsl:call-template>
+  <xsl:text>&#xa;</xsl:text>
+</xsl:template>
+
+<xsl:template name="ruler">
+  <xsl:call-template name="str:dup">
+    <xsl:with-param name="input" select=" '-' "/>
+    <xsl:with-param name="count" select="$width"/>
+  </xsl:call-template>
+  <xsl:text>&#xa;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/lib/str.dup.xslt b/docs/lib/str.dup.xslt
new file mode 100644
index 0000000..564b342
--- /dev/null
+++ b/docs/lib/str.dup.xslt
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
+
+<!-- This file was copied with some adaptations from the examples
+supplied with the XSLT Cookbook by Sal Mangano, (C) 2003 O'Reilly &
+Associates, ISBN 0-596-00372-2. -->
+
+  <xsl:template name="str:dup">
+    <xsl:param name="input"/>
+    <xsl:param name="count" select="1"/>
+
+    <xsl:choose>
+      <xsl:when test="not($count) or not($input)"/>
+      <xsl:when test="$count = 1">
+        <xsl:value-of select="$input"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- If $count is odd append an extra copy of input -->
+        <xsl:if test="$count mod 2">
+          <xsl:value-of select="$input"/>
+        </xsl:if>
+        <!-- Recursively apply template after doubling input and 
+			halving count -->
+        <xsl:call-template name="str:dup">
+          <xsl:with-param name="input" select="concat($input,$input)"/>
+          <xsl:with-param name="count" select="floor($count div 2)"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/lib/str.find-last.xslt b/docs/lib/str.find-last.xslt
new file mode 100644
index 0000000..2700fd3
--- /dev/null
+++ b/docs/lib/str.find-last.xslt
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="str.find-last"
+  xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings" extension-element-prefixes="str">
+
+<!-- This file was copied with some adaptations from the examples
+supplied with the XSLT Cookbook by Sal Mangano, (C) 2003 O'Reilly &
+Associates, ISBN 0-596-00372-2. -->
+
+<xsl:template name="str:substring-before-last"> 
+  <xsl:param name="input"/>
+  <xsl:param name="substr"/>
+  
+  <xsl:if test="$substr and contains($input, $substr)">
+    <xsl:variable name="temp" select="substring-after($input, $substr)" />
+    <xsl:value-of select="substring-before($input, $substr)" />
+    <xsl:if test="contains($temp, $substr)">
+      <xsl:value-of select="$substr" />
+      <xsl:call-template name="str:substring-before-last">
+        <xsl:with-param name="input" select="$temp" />
+        <xsl:with-param name="substr" select="$substr" />
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+  
+</xsl:template>
+
+
+<xsl:template name="str:substring-after-last">
+  <xsl:param name="input"/>
+  <xsl:param name="substr"/>
+  
+  <!-- Extract the string which comes after the first occurence -->
+  <xsl:variable name="temp" select="substring-after($input,$substr)"/>
+  
+  <xsl:choose>
+  	<xsl:when test="$substr and contains($temp,$substr)">
+  		<xsl:call-template name="str:substring-after-last">
+  			<xsl:with-param name="input" select="$temp"/>
+  			<xsl:with-param name="substr" select="$substr"/>
+  		</xsl:call-template>
+  	</xsl:when>
+  	<xsl:otherwise>
+  		<xsl:value-of select="$temp"/>
+  	</xsl:otherwise>
+  </xsl:choose>
+</xsl:template> 
+
+
+<xsl:template match="xsl:stylesheet[@id='str.find-last'] | xsl:include[@href='str.find-last.xslt'] " >
+<tests>
+
+<!-- before -->
+	<test name="str:substring-before-last with no occurences of yes">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'No occurences' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	
+	<test name="str:substring-before-last starts with yes">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'yes occurences' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+
+	<test name="str:substring-before-last starts with yes and ends with yes">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'yes occurences yes' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+
+	<test name="str:substring-before-last 3 yes">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'yesyesyes' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	<test name="str:substring-before-last empty input">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	
+	<test name="str:substring-before-last empty search">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'No occurences' "/>
+	</xsl:call-template>
+
+	</test>
+
+	<test name="str:substring-before-last large">
+	<xsl:call-template name="str:substring-before-last">
+		<xsl:with-param name="input" select=" 'yesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyesyes' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+
+
+	</test>
+
+<!-- after -->
+
+	<test name="str:substring-after-last with no occurences of yes">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'No occurences' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	
+	<test name="str:substring-after-last starts with yes">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'yes occurences' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+
+	<test name="str:substring-after-last starts with yes and ends with yes">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'yes occurences yes' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+
+	<test name="str:substring-after-last 3 yes">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'yesyesyes' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+
+	<test name="str:substring-after-last 3 yes then no">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'yesyesyesno' "/>
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	
+	<test name="str:substring-after-last empty input">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="substr" select=" 'yes' "/>
+	</xsl:call-template>
+	</test>
+	
+	<test name="str:substring-after-last empty search">
+	<xsl:call-template name="str:substring-after-last">
+		<xsl:with-param name="input" select=" 'No occurences' "/>
+	</xsl:call-template>
+
+	</test>
+
+</tests>
+</xsl:template>
+
+ <xsl:template match="text()"/>
+  
+</xsl:stylesheet>
diff --git a/docs/lib/text.justify.xslt b/docs/lib/text.justify.xslt
new file mode 100644
index 0000000..a6a8992
--- /dev/null
+++ b/docs/lib/text.justify.xslt
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings"
+  xmlns:text="http://www.ora.com/XSLTCookbook/namespaces/text" extension-element-prefixes="text">
+
+<!-- This file was copied with some adaptations from the examples
+supplied with the XSLT Cookbook by Sal Mangano, (C) 2003 O'Reilly &
+Associates, ISBN 0-596-00372-2. -->
+
+<xsl:include href="str.dup.xslt"/>
+
+<xsl:template name="text:justify">
+  <xsl:param name="value" /> 
+  <xsl:param name="width" select="10"/>
+  <xsl:param name="align" select=" 'left' "/>
+
+  <!-- Truncate if too long -->  
+  <xsl:variable name="output" select="substring($value,1,$width)"/>
+  
+  <xsl:choose>
+    <xsl:when test="$align = 'left'">
+      <xsl:value-of select="$output"/>
+      <xsl:call-template name="str:dup">
+        <xsl:with-param name="input" select=" ' ' "/>
+        <xsl:with-param name="count" select="$width - string-length($output)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$align = 'right'">
+      <xsl:call-template name="str:dup">
+        <xsl:with-param name="input" select=" ' ' "/>
+        <xsl:with-param name="count" select="$width - string-length($output)"/>
+      </xsl:call-template>
+      <xsl:value-of select="$output"/>
+    </xsl:when>
+    <xsl:when test="$align = 'center'">
+      <xsl:call-template name="str:dup">
+        <xsl:with-param name="input" select=" ' ' "/>
+        <xsl:with-param name="count" select="floor(($width - string-length($output)) div 2)"/>
+      </xsl:call-template>
+      <xsl:value-of select="$output"/>
+      <xsl:call-template name="str:dup">
+        <xsl:with-param name="input" select=" ' ' "/>
+        <xsl:with-param name="count" select="ceiling(($width - string-length($output)) div 2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>INVALID ALIGN</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/lib/text.wrap.xslt b/docs/lib/text.wrap.xslt
new file mode 100644
index 0000000..e77819e
--- /dev/null
+++ b/docs/lib/text.wrap.xslt
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="text.wrap"
+  xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings" 
+  xmlns:text="http://www.ora.com/XSLTCookbook/namespaces/text" exclude-result-prefixes="text">
+
+<!-- This file was copied with some adaptations from the examples
+supplied with the XSLT Cookbook by Sal Mangano, (C) 2003 O'Reilly &
+Associates, ISBN 0-596-00372-2. -->
+
+<xsl:include href="str.find-last.xslt"/>
+<xsl:include href="text.justify.xslt"/>
+
+<xsl:template match="node() | @*" mode="text:wrap" name="text:wrap">
+  <xsl:param name="input" select="normalize-space()"/> 
+  <xsl:param name="width" select="70"/>
+  <xsl:param name="indent" select="0"/>
+  <xsl:param name="dofirst" select="1"/>
+  <xsl:param name="align" select=" 'left' "/>
+
+  <xsl:variable name="effective" select="$width - $indent"/>
+
+  <xsl:if test="$input">
+    <xsl:variable name="line">
+      <xsl:choose>
+        <xsl:when test="string-length($input) > $effective">
+          <xsl:variable name="candidate-line" select="substring($input,1,$effective)"/>
+          <xsl:choose>
+            <xsl:when test="contains($candidate-line,' ')">
+              <xsl:call-template name="str:substring-before-last">
+                  <xsl:with-param name="input" select="$candidate-line"/>
+                  <xsl:with-param name="substr" select=" ' ' "/>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$candidate-line"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$input"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+  
+    <xsl:if test="$line">
+      <xsl:if test="$dofirst">
+        <xsl:call-template name="str:dup">
+          <xsl:with-param name="input" select=" ' ' "/>
+          <xsl:with-param name="count" select="$indent"/>
+        </xsl:call-template>
+      </xsl:if>
+
+      <xsl:call-template name="text:justify">
+        <xsl:with-param name="value" select="$line"/>
+        <xsl:with-param name="width" select="$effective"/>
+        <xsl:with-param name="align" select="$align"/>
+      </xsl:call-template>
+      <xsl:text>&#xa;</xsl:text>
+    </xsl:if>  
+
+    <xsl:call-template name="text:wrap">
+      <xsl:with-param name="input" select="substring($input, string-length($line) + 2)"/>
+      <xsl:with-param name="width" select="$width"/>
+      <xsl:with-param name="indent" select="$indent"/>
+      <xsl:with-param name="align" select="$align"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="node() | @*" mode="text:dump" name="text:dump">
+  <xsl:param name="input" select="text()"/>
+  <xsl:param name="indent" select="0"/>
+
+  <xsl:if test="$input">
+    <xsl:variable name="line">
+      <xsl:choose>
+        <xsl:when test="contains($input, '&#xA;')">
+          <xsl:value-of select="substring-before($input, '&#xA;')"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$input"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+  
+    <xsl:if test="$line">
+      <xsl:call-template name="str:dup">
+        <xsl:with-param name="input" select=" ' ' "/>
+        <xsl:with-param name="count" select="$indent"/>
+      </xsl:call-template>
+      <xsl:value-of select="$line"/>
+      <xsl:text>&#xa;</xsl:text>
+    </xsl:if>
+
+    <xsl:call-template name="text:dump">
+      <xsl:with-param name="input" select="substring-after($input, '&#xA;')"/>
+      <xsl:with-param name="indent" select="$indent"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/lib/untag-inline.xslt b/docs/lib/untag-inline.xslt
new file mode 100644
index 0000000..ea553a5
--- /dev/null
+++ b/docs/lib/untag-inline.xslt
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- sgml -*- -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:include href="copy.xslt"/>
+
+<xsl:strip-space elements="*"/>
+
+<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
+
+<!-- Preprocess a docbook/xml file, replacing elements that are to be -->
+<!-- formatted inline with the corresponding text.  -->
+
+<xsl:template match="literal|computeroutput">
+  <xsl:value-of select="text()"/>
+</xsl:template>
+
+<xsl:template match="ulink">
+  <xsl:variable name="url" select="normalize-space(@url)"/>
+  <xsl:variable name="text" select="normalize-space(text())"/>
+
+  <xsl:if test="$text and $text != $url">
+    <xsl:text>'</xsl:text><xsl:value-of select="$text"/><xsl:text>' </xsl:text>
+  </xsl:if>
+  <xsl:text>&lt;</xsl:text><xsl:value-of select="$url"/><xsl:text>&gt;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/docs/xml/FAQ.xml b/docs/xml/FAQ.xml
index f8b8c12..25f44b0 100644
--- a/docs/xml/FAQ.xml
+++ b/docs/xml/FAQ.xml
@@ -305,21 +305,27 @@
 
   <para>Some example sub-traces:</para>
 
-  <para>With debug information and unstripped (best):</para>
+   <itemizedlist>
+     <listitem>
+       <para>With debug information and unstripped (best):</para>
 <programlisting>
 Invalid write of size 1
    at 0x80483BF: really (malloc1.c:20)
    by 0x8048370: main (malloc1.c:9)
 </programlisting>
+     </listitem>
 
-  <para>With no debug information, unstripped:</para>
+     <listitem>
+       <para>With no debug information, unstripped:</para>
 <programlisting>
 Invalid write of size 1
    at 0x80483BF: really (in /auto/homes/njn25/grind/head5/a.out)
    by 0x8048370: main (in /auto/homes/njn25/grind/head5/a.out)
 </programlisting>
+     </listitem>
 
-  <para>With no debug information, stripped:</para>
+     <listitem>
+       <para>With no debug information, stripped:</para>
 <programlisting>
 Invalid write of size 1
    at 0x80483BF: (within /auto/homes/njn25/grind/head5/a.out)
@@ -327,17 +333,20 @@
    by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so)
    by 0x80482CC: (within /auto/homes/njn25/grind/head5/a.out)
 </programlisting>
+     </listitem>
 
-  <para>With debug information and -fomit-frame-pointer:</para>
+     <listitem>
+       <para>With debug information and -fomit-frame-pointer:</para>
 <programlisting>
 Invalid write of size 1
    at 0x80483C4: really (malloc1.c:20)
    by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so)
    by 0x80482CC: ??? (start.S:81)
 </programlisting>
+     </listitem>
 
-  <para>A leak error message involving an unloaded shared object:</para>
-
+     <listitem>
+      <para>A leak error message involving an unloaded shared object:</para>
 <programlisting>
 84 bytes in 1 blocks are possibly lost in loss record 488 of 713
    at 0x1B9036DA: operator new(unsigned) (vg_replace_malloc.c:132)
@@ -346,6 +355,8 @@
    by 0x1D65E007: ???
    by 0x8049EE6: main (main.cpp:24)
 </programlisting>
+     </listitem>
+   </itemizedlist>
 
  </answer>
 </qandaentry>