]> granicus.if.org Git - docbook-dsssl/commitdiff
Initial revision
authorJens Emmerich <jensem@users.sourceforge.net>
Sat, 23 Feb 2002 02:17:22 +0000 (02:17 +0000)
committerJens Emmerich <jensem@users.sourceforge.net>
Sat, 23 Feb 2002 02:17:22 +0000 (02:17 +0000)
contrib/tools/emacs/doctoc/README [new file with mode: 0644]
contrib/tools/emacs/doctoc/doctoc.el [new file with mode: 0644]
contrib/tools/emacs/doctoc/toc.pl [new file with mode: 0755]

diff --git a/contrib/tools/emacs/doctoc/README b/contrib/tools/emacs/doctoc/README
new file mode 100644 (file)
index 0000000..2ea4797
--- /dev/null
@@ -0,0 +1,48 @@
+DOCbook Table Of Contents -- doctoc
+===================================
+
+Overview
+--------
+
+The doctoc package provides a navigation aid for editing richly
+structured DocBook documents with (X)Emacs. It generates a table of
+contents (TOC) for the current buffer using an external perl script at
+the moment. The TOC is displayed in a seperate buffer with a suiteable
+major mode to jump to the respective destination.
+
+Most of the current implementation was meant to be a prototype but
+turned out to be extremely useful for me already. Speed is no issue
+compared to PSGML's parsing time.
+
+Installation
+------------
+
+Put toc.pl somewhere in your $PATH. It needs perl5 with XML::Parser
+module. Installation instructions for doctoc.el can be found directly
+in the file. It can be byte-compiled, but that's not neccesary,
+haven't seen any noticeable delay caused by doctoc.el in comparison to
+toc.pl.
+
+Future Plans
+------------
+
+1. Removal of XEmacs specific stuff
+2. Use autoloads, customize package, other Emacs conventions
+3. Check if PSGMLs parse tree can/should be used
+4. Support for parsed external entities, i.e. documents split into
+   multiple files, with single TOC (not possible with XML::Parser)
+5. Look into using speedbar
+6. ID/IDREF display/selection in TOC
+
+Credits
+-------
+
+I do miss RefTeX's/AucTeX's elaborate features, all ideas borrowed
+there.
+
+Contact
+-------
+
+Please use the SourceForge facilities (DocBook open repository
+project) or contact me at Jens.Emmerich@itp.uni-leipzig.de and be
+patient.
diff --git a/contrib/tools/emacs/doctoc/doctoc.el b/contrib/tools/emacs/doctoc/doctoc.el
new file mode 100644 (file)
index 0000000..37be87a
--- /dev/null
@@ -0,0 +1,356 @@
+;; doctoc.el --- Handle table of contents for docbook
+
+;; Copyright (C) 2001, 2002 Jens Emmerich <Jens.Emmerich@itp.uni-leipzig.de>
+;; Version: 1.3
+;; Keywords: wp, sgml, xml, docbook
+
+;; Maintainer: Jens Emmerich
+
+;; This file is neither part of GNU Emacs nor XEmacs.
+
+;; This Program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; version 2.
+
+;; This Program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs or XEmacs; see the file COPYING.  If not,
+;; write to the Free Software Foundation Inc., 59 Temple Place - Suite
+;; 330, Boston, MA 02111-1307, USA.
+
+;;; Compatible with (at least): XEmacs 21.1, 21.4
+
+;;; Commentary:
+
+;; This file provides support for am interactive table of contents for
+;; docbook(x) documents using an external parser (toc.pl). In fact it
+;; is quite general, it only (re-)starts a process, pipes the current
+;; buffer through it and can jump to the line given at the end of the
+;; line. Unlike RefTeX, it only handles the current buffer.
+
+;; Install into load-path and add something like
+;;
+;; (defun my-sgml-setup ()
+;;   "Customize psgml for xml."
+;;   (load "doctoc") 
+;;   ;; ... more customizations
+;;   (define-key sgml-mode-map [(control c)(=)] 'doctoc-toc-current-line))
+;;
+;; (add-hook 'sgml-mode-hook 'my-sgml-setup)
+;;
+;; to your init.el (or .emacs) file.
+;;
+;; Keybindings are somewhat inspired by RefTeX:
+;;
+;;  'return close TOC buffer and jump to the selected entry
+;;  'space  show buffer around selected entry
+;;  'q      close TOC and return to where C-= left off
+;;  'g      re-generate TOC
+;;  [(shift button2)] show buffer around selected entry (same as 'space)
+;;  [(shift button3)] show buffer around selected entry (same as 'space) 
+;;  'button2  close TOC buffer and jump to the selected entry (as 'return)
+;;  'button3  close TOC buffer and jump to the selected entry (as 'return)
+
+
+;;; Code:
+
+
+(defvar doctoc-toc-buffer "*TOC %s*"
+  "Buffer holding the toc for a docbook document")
+
+(defvar doctoc-toc-command "toc.pl 2> /dev/null"
+  "Command producing the toc to stdout from stdin docbook document")
+
+(defvar doctoc-line-number-regexp 
+  "[^\n\r]* \\([0-9]+\\)[\r\n]"
+"Regexp for finding the next line number in toc.
+Should also match in outline-minor-mode.")
+
+(defun doctoc-toc ()
+  "Create or select a table of contents for current (docbook-) buffer"
+  (interactive)
+  (let
+      ((buffer (get-buffer (format doctoc-toc-buffer (buffer-name)))))
+    (if buffer
+       (doctoc-select-toc buffer)
+      (doctoc-make-toc))))
+
+(defun doctoc-kill-toc ()
+  "Delete toc buffer of current buffer, if any"
+  (interactive)
+  (let
+      ((buffer (get-buffer (format doctoc-toc-buffer (buffer-name)))))
+    (if buffer
+       (kill-buffer buffer))))
+
+(defun doctoc-regenerate ()
+  "Regenerate visited table of contents"
+  (interactive)
+  (let
+      ((toc-buffer (current-buffer))
+       (line  (count-lines 1 (min (point-max) (1+ (point)))))
+       (doc-buffer document-buffer))
+    (message "Regenerating toc ... ")
+    (setq buffer-read-only nil)
+    (erase-buffer)
+    (save-excursion
+      (set-buffer doc-buffer)
+      (shell-command-on-region (point-min) (point-max)
+                              doctoc-toc-command toc-buffer))
+    (doctoc-make-extents)
+    (setq buffer-read-only t)
+    (message "Regenerating toc ... done")
+    (goto-line line)
+    ;; strange, but recenter fixes a problem (only lines in current
+    ;; window are displayed, otherwise goto-line has no effect)
+    (recenter '(t))
+    (sit-for 4e4 t)
+    (message nil)))
+
+(defun doctoc-make-toc ()
+  "Generate a toc for the docbook-xml document in current buffer
+Return the buffer holding the toc"
+  (interactive)
+  (let ((toc-buffer 
+        (get-buffer-create (format doctoc-toc-buffer (buffer-name))))
+       (doc-buffer (current-buffer))
+       (win-config (current-window-configuration)))
+    (make-local-hook 'kill-buffer-hook)
+    (add-hook 'kill-buffer-hook 'doctoc-kill-toc)
+    (save-excursion
+      (set-buffer toc-buffer)
+      (setq buffer-disble-undo t)
+      (setq buffer-read-only nil)
+      (erase-buffer))
+    (message "Generating toc ... ")
+    (shell-command-on-region (point-min) (point-max)
+                            doctoc-toc-command toc-buffer)
+    (switch-to-buffer-other-window toc-buffer)
+    (doctoc-mode)
+    (make-local-variable 'document-buffer)
+    (make-local-variable 'window-configuration)
+    (setq document-buffer doc-buffer
+         window-configuration win-config)
+    (doctoc-make-extents)
+    (message "Generating toc ... done")
+    (sit-for 4e4)
+    (message nil)
+    toc-buffer))
+
+(defun doctoc-select-toc (buffer)
+  "Jump to the toc in BUFFER"
+  (let ((win-config (current-window-configuration)))
+    (pop-to-buffer buffer)
+    (setq window-configuration win-config)))
+
+(defun doctoc-back ()
+  "Leave doctoc buffer and switch back to buffer 'doc-buffer"
+  (interactive)
+  (let ((dest-buffer document-buffer))
+    (set-window-configuration window-configuration)
+    (switch-to-buffer dest-buffer)))
+
+(defun doctoc-find-line ()
+  "Search for next line number as given by 'doctoc-line-number-regexp
+Line number is returned, nil if not found. Keep point."
+  (save-excursion
+    (doctoc-find-line-move)))
+
+(defun doctoc-find-line-move ()
+  "Search for next line number as given by 'doctoc-line-number-regexp
+Line number is returned, nil if not found. Point is left at beginning
+of matching line."
+  (beginning-of-line)
+  (while (not (or (looking-at doctoc-line-number-regexp) (eobp)))
+    (forward-line))
+  (if (looking-at doctoc-line-number-regexp)
+      (string-to-int (buffer-substring
+                     (match-beginning 1) (match-end 1)))
+    nil))
+
+(defun doctoc-goto-line-toc (line)
+  "Search LINE in toc and move point to it.
+Goto first line where line number at end <= LINE."
+  (while
+      (let
+         ((found-line (doctoc-find-line-move)))
+       (and found-line (< found-line line) (not (eobp))))
+    (forward-line)))
+       
+(defun doctoc-toc-current-line ()
+  "Goto toc entry corresponding to point"
+  (interactive)
+  (let ((goal-line (line-number)))
+    (doctoc-toc)
+    (doctoc-goto-line-toc goal-line)))
+
+(defun doctoc-jump ()
+  "Jump back to line given by the number at eol to buffer 'doc-buffer"
+  (interactive)
+  (let ((dest-buffer document-buffer)
+       (lineno (doctoc-find-line)))
+    (if lineno
+       (progn
+         (set-window-configuration window-configuration)
+         (switch-to-buffer dest-buffer)
+         (goto-line lineno))
+      (message "No toc entry at point."))))
+
+(defun doctoc-jump-mouse ()
+  (interactive)
+  (goto-char (event-closest-point current-mouse-event))
+  (doctoc-jump))
+   
+(defun doctoc-show ()
+  "Show the line given by the number at eol 'doc-buffer in other window"
+  (interactive)
+  (let 
+      ((lineno (doctoc-find-line)))
+    (if lineno
+       (let ((extent
+              (progn 
+                (save-selected-window
+                  (switch-to-buffer-other-window document-buffer)
+                  (goto-line lineno)
+                  (recenter '(t))
+                  (doctoc-highlight-title)))))
+         (if (extentp extent)
+             (let ((inhibit-quit t))
+               (sit-for 4e4)           ; 'for-e-ver'
+               (delete-extent extent)))))))
+
+(defun doctoc-show-mouse ()
+  "Show the line given by the number at eol 'doc-buffer in other window
+The title is shown as long as the button is pressed"
+  (interactive)
+  (goto-char (event-closest-point current-mouse-event))
+  (let 
+      ((lineno (doctoc-find-line)))
+    (if lineno
+       (save-window-excursion
+         (let ((extent
+                (progn 
+                  (switch-to-buffer-other-window document-buffer)
+                  (goto-line lineno)
+                  (recenter '(t))
+                  (doctoc-highlight-title))))
+           (if (extentp extent)
+               (let ((inhibit-quit t))
+                 (sit-for 4e4)         ; 'for-e-ver'
+                 (delete-extent extent))))))))
+   
+;; highlight next title at/after current line and return extent
+;; return nil if no title was found
+(defun doctoc-highlight-title ()
+  (let ((tstart (progn 
+                 (beginning-of-line)
+                 (if (re-search-forward "<title[^>]*>" (point-max) t)
+                     (match-end 0)
+                   nil))))
+    (if tstart
+       (progn
+         (goto-char tstart)
+         (if (re-search-forward "</title>" (point-max) t)
+             (let ((extent 
+                    (make-extent tstart (match-beginning 0))))
+               (set-extent-face extent 'highlight)
+               extent))))))
+
+             
+(defvar doctoc-mode-map
+   (let ((m (make-sparse-keymap)))
+       (set-keymap-name m 'doctoc-mode-map)
+       (define-key m 'return 'doctoc-jump)
+       (define-key m 'space 'doctoc-show)
+       (define-key m 'q 'doctoc-back)
+       (define-key m 'g 'doctoc-regenerate)
+       (define-key m [(shift button2)] 'doctoc-show-mouse)
+       (define-key m [(shift button3)] 'doctoc-show-mouse)
+       (define-key m 'button2 'doctoc-jump-mouse)
+       (define-key m 'button3 'doctoc-jump-mouse)
+       m)
+       "Keymap for doctoc mode")
+
+
+(defun doctoc-mode ()
+  "Major mode for visiting a table of contents containing line numbers
+The buffer refereced by the line numbers is given by document-buffer.
+This variable should be buffer-local.
+
+Commands are:
+\\{doctoc-mode-map}"
+  (interactive)
+  (kill-all-local-variables)
+  (setq buffer-read-only t)
+  (use-local-map doctoc-mode-map)
+  (setq major-mode 'doctoc-mode)
+  (setq mode-name "doctoc")
+  (setq truncate-lines t)
+  ;; length of match defines level => also match spaces
+  (setq outline-regexp "^[0-9]+\\(\\.[0-9]+\\)* +\\b")
+  (outline-minor-mode 1)
+  ;; turn off horizontal scrollbars in this buffer
+  ;; toc.pl produces short enough lines
+  (when (featurep 'scrollbar)
+    (set-specifier scrollbar-height (cons (current-buffer) 0)))
+  (run-hooks 'doctoc-mode-hook))
+
+;;; Faces used for Highlighting
+
+(make-face 'doctoc-level1-face)
+(set-face-foreground 'doctoc-level1-face "red")
+(make-face-bold 'doctoc-level1-face)
+(make-face 'doctoc-level2-face)
+(set-face-foreground 'doctoc-level2-face "blue")
+(make-face-bold 'doctoc-level2-face)
+(make-face 'doctoc-level3-face)
+(set-face-foreground 'doctoc-level3-face "blue")
+
+;; 'doctoc-level-faces a la font-lock-keywords 
+(defvar doctoc-level-faces
+  (list
+   '("^ *[0-9]+ +\\(.*[^0-9]\n\\)*.*[0-9]$" . doctoc-level1-face)
+   '("^ *[0-9]+\\.[0-9]+ +\\(.*[^0-9]\n\\)*.*[0-9]$" . doctoc-level2-face)
+   '("^ *[0-9]+\\.[0-9]+\\.[0-9]+ +\\(.*[^0-9]\n\\)*.*[0-9]$" . doctoc-level3-face))
+  "A list of cons cells (regexp . face) determining faces for doctoc buffers
+The regexp is matched at the beginning of a line end extends
+to the line number given for that toc-entry. The first
+matching regexp is used.")
+
+;;;; Extent handling
+;;; This is probably stronly XEmacs dependent
+
+;; parse buffer and set extents for highlighting
+(defun doctoc-make-extents ()
+  (goto-char 0)
+  (while (not (eobp))
+    (let ((bgn (progn
+                (beginning-of-line)
+                (point)))
+         (end (progn
+                (re-search-forward doctoc-line-number-regexp (point-max) t)
+                (re-search-backward "\\>" (point-min) t))))
+      (doctoc-set-text-properties bgn end)
+      (forward-line)))
+  (goto-char 0))
+           
+;; make extent from start to end and set face according
+;; to doctoc-level-faces
+(defun doctoc-set-text-properties (start end)
+  (let ((title-extent (make-extent start end))
+       (faces doctoc-level-faces)
+       (face nil))
+      (save-excursion
+       (goto-char start)
+       (while (and faces (not face))
+         (if (looking-at (caar faces))
+             (setq face (cdar faces))
+           (setq faces (cdr faces)))))
+      (set-extent-property title-extent 'start-open t)
+      (set-extent-property title-extent 'end-open t)
+      (set-extent-face     title-extent (or face 'default))
+      (set-extent-mouse-face title-extent 'highlight)))
diff --git a/contrib/tools/emacs/doctoc/toc.pl b/contrib/tools/emacs/doctoc/toc.pl
new file mode 100755 (executable)
index 0000000..6521990
--- /dev/null
@@ -0,0 +1,273 @@
+#!/usr/bin/perl -w
+# Generate a table of contents for a docbook document
+# tags are case insensitive
+# Copyright (C) 2001 Jens Emmerich <Jens.Emmerich@itp.uni-leipzig.de>
+# 2001-04-13 Jens Emmerich
+#
+## This Program is free software; you can redistribute it and/or
+## modify it under the terms of the GNU General Public License as
+## published by the Free Software Foundation; version 2.
+##
+## This Program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## This program is intended (but not limited) to be used together with (X)Emacs.
+## You should have received a copy of the GNU General Public License
+## along with GNU Emacs or XEmacs; see the file COPYING.  If not,
+## write to the Free Software Foundation Inc., 59 Temple Place - Suite
+## 330, Boston, MA 02111-1307, USA.
+
+use XML::Parser;
+use integer;
+use strict;
+#use utf8;                     # for recent perl versions; can be
+                                # omitted, results in some "garbage" in
+                                # output then
+use Unicode::String;           # used to convert utf8 to latin1
+
+package Toc;
+# levels:
+#       >=0 absolute level
+#        -1 additional level relative to current, numbered
+#        -2 additional level relative to current, not numbered,
+#           doesn't increase level
+#        -3 don't include into toc
+
+# all ellements which can have a title (docbook 3.1)
+%Toc::levels = (
+           "abstract"           => -1,
+           "appendix"           => -2,
+           "artheader"          => -3,
+           "article"            =>  1,
+           "authorblurb"        => -3,
+           "bibliodiv"          => -3,
+           "biblioentry"        => -3,
+           "bibliography"       => -1,
+           "bibliomixed"        => -3,
+           "bibliomset"         => -3,
+           "biblioset"          => -3,
+           "blockquote"         => -3,
+           "book"               =>  0,
+           "bookbiblio"         => -3,
+           "bookinfo"           => -3,
+           "calloutlist"        => -3,
+           "caution"            => -3,
+           "chapter"            =>  1,
+           "dedication"         => -1,
+           "docinfo"            => -1,
+           "equation"           => -3,
+           "example"            => -3,
+           "figure"             => -3,
+           "formalpara"         => -2,
+           "glossary"           => -1,
+           "glossdiv"           => -3,
+           "important"          => -1,
+           "index"              => -1,
+           "indexdiv"           => -3,
+           "legalnotice"        => -1,
+           "lot"                => -1,
+           "msg"                => -3,
+           "msgexplan"          => -3,
+           "msgmain"            => -3,
+           "msgrel"             => -3,
+           "msgsub"             => -3,
+           "note"               => -3,
+           "para"               => -2,
+           "part"               =>  0,
+           "partintro"          => -1,
+           "preface"            => -1,
+           "procedure"          => -3,
+           "reference"          => -3,
+           "refmeta"            => -3,
+           "refsect1"           =>  2,
+           "refsect1info"       => -3,
+           "refsect2"           =>  3,
+           "refsect2info"       => -3,
+           "refsect3"           =>  4,
+           "refsect3info"       => -3,
+           "refsect4"           =>  5,
+           "refsect5"           =>  6,
+           "refsynopsisdiv"     => -3,
+           "refsynopsisdivinfo" => -3,
+           "sect1"              =>  2,
+           "sect1info"          => -3,
+           "sect2"              =>  3,
+           "sect2info"          => -3,
+           "sect3"              =>  4,
+           "sect3info"          => -3,
+           "sect4"              =>  5,
+           "sect4info"          => -3,
+           "sect5"              =>  6,
+           "sect5info"          => -3,
+           "section"            => -1,
+           "segmentedlist"      => -3,
+           "seriesinfo"         => -1,
+           "set"                => -1,
+           "setindex"           => -1,
+           "setinfo"            => -3,
+           "sidebar"            => -3,
+           "simplesect"         => -1,
+           "step"               => -3,
+           "table"              => -3,
+           "tip"                => -3,
+           "toc"                => -1,
+           "variablelist"       => -3,
+           "warning"            => -3,
+);
+
+@Toc::secnums = (0) x (max(values %Toc::levels)+1); # section numbers
+$Toc::parent = "";             # parent of last <title>
+$Toc::parentline_no = 0;       # line no of parent
+$Toc::minlevel = 0;            # =0 for book, =1 for article
+                                # detected automatically
+$Toc::currentlevel = 0;                # level of current $title
+$Toc::title = "";              # current <title> or <titleabbrev>
+$Toc::extraindent_step = 0;    # additionally indent toc entry 
+                                # by this much per level
+$Toc::width = 75;              # 3 characters needed for emacs' outline minor mode
+$Toc::fill_character = ".";    # "ยท" might be nice in some fonts
+
+# return largest
+sub max {
+  my $max=shift(@_);
+  foreach my $i (@_) {
+    $max=$i if $i > $max;
+  }
+  return $max;
+}
+
+# emit an toc entry for $parent, $title
+sub tocentry {
+  use Text::Wrap;
+  use TexT::Tabs;
+  my $text         = $Toc::title;
+  $Toc::title = "";
+  my $parent_level = $Toc::levels{$Toc::parent};
+  my $line_no      = $Toc::parentline_no;
+  my $clevel       = $Toc::currentlevel;
+  my $mlevel       = $Toc::minlevel;
+  my $extra_indent = max(0,($clevel-$mlevel)*$Toc::extraindent_step);
+
+  # strip tags and superflous whitespace
+  $text=~s/<[^>]+>//g;
+  $text=~s/\s+/ /g;
+
+  # generate complete number
+  my $num="";
+  if($parent_level > -2) {
+    # numbered entry
+    for (my $i=$mlevel+1; $i<=$clevel; $i++) {
+      $num .= $Toc::secnums[$i].".";
+    }
+    chop($num) if $num;
+    $num .= " " x (3*($clevel-$mlevel  )-1-length($num));
+  } else {
+    $num  = " " x (3*($clevel-$mlevel+2)-1);    # unnumbered entry
+  }
+
+  # wrap title
+  my $pre1 = (" " x $extra_indent).$num." ";
+  my $pre2 = " " x length($pre1);
+  my $lp = length($line_no);
+  $Text::Wrap::columns = $Toc::width-1-$lp;
+  $text =~ tr/\t/ /;
+  $text = wrap($pre1, $pre2, $text);
+  $text = Text::Tabs::expand($text);
+
+
+  # fill last line with points, $l is bare length of last line
+  my $l=length($text)-(rindex($text,"\n")+1);
+  my $fill=$Toc::width-$lp-$l;
+  unless($fill < 2) {
+    $fill = " ".($Toc::fill_character x ($fill-2))." ";
+  } else {
+    $fill = " ";
+  }
+
+  print "$text$fill$line_no\n";
+}
+
+# start tag handlers
+sub first_stag {
+  my $parser=shift;
+  my $tagname=lc(shift);
+  if($tagname eq "article") {
+    $Toc::minlevel = 1;
+    $Toc::currentlevel = $Toc::minlevel;
+  }
+  $Toc::parent=$tagname if exists($Toc::levels{$tagname});
+  $parser->setHandlers(Start=>\&stag);
+}
+
+sub stag {
+  my $parser=shift;
+  my $tagname=lc(shift);
+  my $element_level;
+
+  if($tagname =~ m/\Atitle(abbrev)?\Z/) {
+    # switch data collection on
+    $Toc::title = "";
+    $parser->setHandlers(Char=>\&cdata) if($Toc::levels{$Toc::parent}>-3);
+  } elsif(exists($Toc::levels{$tagname})) {
+    &tocentry if $Toc::title; # title was not yet emitted to toc
+    $Toc::parent=$tagname;
+    $Toc::parentline_no = $parser->current_line();
+    $element_level=$Toc::levels{$tagname};
+    if($element_level>=0) {
+      # look for gaps
+      while(++$Toc::currentlevel <= $element_level) {
+        $Toc::secnums[$Toc::currentlevel] = 0 
+         unless(defined($Toc::secnums[$Toc::currentlevel]));
+      }
+      # looking for overlap ist not neccessary
+      $Toc::currentlevel = $element_level;
+      $Toc::secnums[$Toc::currentlevel]++;
+    } elsif ($element_level == -1) {
+      $Toc::currentlevel ++;
+      if(defined($Toc::secnums[$Toc::currentlevel])) {
+        $Toc::secnums[$Toc::currentlevel]++;
+      } else {
+        $Toc::secnums[$Toc::currentlevel] = 1;
+      }
+    }
+    for(my $i=$Toc::currentlevel+1; $i<=$#Toc::secnums; $i++) {
+      $Toc::secnums[$i] = 0;
+    }
+  }
+}
+
+# end tag handler
+sub etag {
+  my $parser=shift;
+  my $tagname=lc(shift);
+  if($tagname =~ m/\Atitle(abbrev)?\Z/) {
+    $parser->setHandlers(Char=>0);
+  } elsif (exists($Toc::levels{$tagname})) {
+    &tocentry if $Toc::title; # title was not yet emitted to toc
+    $Toc::currentlevel=
+      max($Toc::currentlevel-1,0) if ($Toc::levels{$tagname}>=-1);
+  }
+}
+
+# character data handler
+sub cdata {
+  # translate utf8 to latin1 
+  # (solution on http://www.perldoc.com/perl5.6/pod/perlunicode.html does not work with 
+  # the cygnus perl 5.6.0 tr-operator)
+  my $u= Unicode::String::utf8( $_[1]);        # create Unicode::String
+  $Toc::title .= $u->latin1;   # convert string to latin1
+}
+
+# "main"
+my $file = '-';
+unshift(@ARGV, $file) unless @ARGV;
+
+while(defined($file = shift)) {
+  my $parser = new XML::Parser(ErrorContext => 2, NoLWP => 1);
+  $parser->setHandlers(Start => \&first_stag,
+                      End   => \&etag);
+  $parser->parsefile($file);
+}
+