[Zope-Checkins] SVN: Zope/trunk/lib/python/docutils/ update to docutils 0.3.9

Andreas Jung andreas at andreas-jung.com
Sun Oct 9 10:31:38 EDT 2005


Log message for revision 39013:
  update to docutils 0.3.9
  

Changed:
  U   Zope/trunk/lib/python/docutils/__init__.py
  U   Zope/trunk/lib/python/docutils/core.py
  U   Zope/trunk/lib/python/docutils/examples.py
  U   Zope/trunk/lib/python/docutils/frontend.py
  U   Zope/trunk/lib/python/docutils/io.py
  U   Zope/trunk/lib/python/docutils/languages/__init__.py
  U   Zope/trunk/lib/python/docutils/languages/af.py
  U   Zope/trunk/lib/python/docutils/languages/cs.py
  U   Zope/trunk/lib/python/docutils/languages/de.py
  U   Zope/trunk/lib/python/docutils/languages/en.py
  U   Zope/trunk/lib/python/docutils/languages/eo.py
  U   Zope/trunk/lib/python/docutils/languages/es.py
  U   Zope/trunk/lib/python/docutils/languages/fi.py
  U   Zope/trunk/lib/python/docutils/languages/fr.py
  U   Zope/trunk/lib/python/docutils/languages/it.py
  U   Zope/trunk/lib/python/docutils/languages/pt_br.py
  U   Zope/trunk/lib/python/docutils/languages/ru.py
  U   Zope/trunk/lib/python/docutils/languages/sk.py
  U   Zope/trunk/lib/python/docutils/languages/sv.py
  U   Zope/trunk/lib/python/docutils/languages/zh_tw.py
  U   Zope/trunk/lib/python/docutils/nodes.py
  U   Zope/trunk/lib/python/docutils/parsers/__init__.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/__init__.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/__init__.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/admonitions.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/body.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/html.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/images.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/misc.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/parts.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/references.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/directives/tables.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/__init__.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/af.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/cs.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/de.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/en.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/eo.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/es.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/fi.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/fr.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/it.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/pt_br.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/ru.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/sk.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/sv.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/languages/zh_tw.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/roles.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/states.py
  U   Zope/trunk/lib/python/docutils/parsers/rst/tableparser.py
  U   Zope/trunk/lib/python/docutils/readers/__init__.py
  U   Zope/trunk/lib/python/docutils/readers/pep.py
  U   Zope/trunk/lib/python/docutils/readers/python/__init__.py
  U   Zope/trunk/lib/python/docutils/readers/python/moduleparser.py
  U   Zope/trunk/lib/python/docutils/readers/python/pynodes.py
  U   Zope/trunk/lib/python/docutils/readers/standalone.py
  U   Zope/trunk/lib/python/docutils/statemachine.py
  U   Zope/trunk/lib/python/docutils/transforms/__init__.py
  U   Zope/trunk/lib/python/docutils/transforms/components.py
  U   Zope/trunk/lib/python/docutils/transforms/frontmatter.py
  U   Zope/trunk/lib/python/docutils/transforms/misc.py
  U   Zope/trunk/lib/python/docutils/transforms/parts.py
  U   Zope/trunk/lib/python/docutils/transforms/peps.py
  U   Zope/trunk/lib/python/docutils/transforms/references.py
  U   Zope/trunk/lib/python/docutils/transforms/universal.py
  U   Zope/trunk/lib/python/docutils/utils.py
  U   Zope/trunk/lib/python/docutils/writers/__init__.py
  U   Zope/trunk/lib/python/docutils/writers/docutils_xml.py
  U   Zope/trunk/lib/python/docutils/writers/html4css1.py
  U   Zope/trunk/lib/python/docutils/writers/latex2e.py
  U   Zope/trunk/lib/python/docutils/writers/pep_html.py
  U   Zope/trunk/lib/python/docutils/writers/pseudoxml.py

-=-
Modified: Zope/trunk/lib/python/docutils/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at python.org
-# Revision: $Revision: 1.2.10.9 $
-# Date: $Date: 2005/01/07 13:26:01 $
+# Revision: $Revision: 3374 $
+# Date: $Date: 2005-05-26 23:21:48 +0200 (Thu, 26 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -51,7 +51,7 @@
 
 __docformat__ = 'reStructuredText'
 
-__version__ = '0.3.7'
+__version__ = '0.3.9'
 """``major.minor.micro`` version number.  The micro number is bumped for API
 changes, for new functionality, and for interim project releases.  The minor
 number is bumped whenever there is a significant project release.  The major

Modified: Zope/trunk/lib/python/docutils/core.py
===================================================================
--- Zope/trunk/lib/python/docutils/core.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/core.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger
 # Contact: goodger at python.org
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:01 $
+# Revision: $Revision: 2987 $
+# Date: $Date: 2005-02-26 19:17:59 +0100 (Sat, 26 Feb 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -197,6 +197,7 @@
             self.writer.assemble_parts()
         except Exception, error:
             if self.settings.traceback: # propagate exceptions?
+                self.debugging_dumps(document)                
                 raise
             self.report_Exception(error)
             exit = 1
@@ -210,6 +211,8 @@
         return output
 
     def debugging_dumps(self, document):
+        if not document:
+            return
         if self.settings.dump_settings:
             print >>sys.stderr, '\n::: Runtime settings:'
             print >>sys.stderr, pprint.pformat(self.settings.__dict__)

Modified: Zope/trunk/lib/python/docutils/examples.py
===================================================================
--- Zope/trunk/lib/python/docutils/examples.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/examples.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,18 +1,19 @@
 # Authors: David Goodger
 # Contact: goodger at python.org
-# Revision: $Revision: 1.1.4.3 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 3247 $
+# Date: $Date: 2005-04-23 21:23:57 +0200 (Sat, 23 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
 This module contains practical examples of Docutils client code.
 
-Importing this module is not recommended; its contents are subject to change
-in future Docutils releases.  Instead, it is recommended that you copy and
-paste the parts you need into your own code, modifying as necessary.
+Importing this module from client code is not recommended; its contents are
+subject to change in future Docutils releases.  Instead, it is recommended
+that you copy and paste the parts you need into your own code, modifying as
+necessary.
 """
 
-from docutils import core
+from docutils import core, io
 
 
 def html_parts(input_string, source_path=None, destination_path=None,
@@ -72,3 +73,23 @@
     if output_encoding != 'unicode':
         fragment = fragment.encode(output_encoding)
     return fragment
+
+def internals(input_string, source_path=None, destination_path=None,
+              input_encoding='unicode'):
+    """
+    Return the document tree and publisher, for exploring Docutils internals.
+
+    Parameters: see `html_parts()`.
+    """
+    overrides = {'input_encoding': input_encoding}
+    output, pub = core.publish_programmatically(
+        source_class=io.StringInput, source=input_string,
+        source_path=source_path,
+        destination_class=io.NullOutput, destination=None,
+        destination_path=destination_path,
+        reader=None, reader_name='standalone',
+        parser=None, parser_name='restructuredtext',
+        writer=None, writer_name='null',
+        settings=None, settings_spec=None, settings_overrides=overrides,
+        config_section=None, enable_exit_status=None)
+    return pub.writer.document, pub

Modified: Zope/trunk/lib/python/docutils/frontend.py
===================================================================
--- Zope/trunk/lib/python/docutils/frontend.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/frontend.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.8 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 3358 $
+# Date: $Date: 2005-05-21 02:00:25 +0200 (Sat, 21 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -124,6 +124,13 @@
                    None, sys.exc_info()[2])
     return value
 
+def validate_nonnegative_int(setting, value, option_parser,
+                             config_parser=None, config_section=None):
+    value = int(value)
+    if value < 0:
+        raise ValueError('negative value; must be positive or zero')
+    return value
+
 def validate_threshold(setting, value, option_parser,
                        config_parser=None, config_section=None):
     try:
@@ -333,10 +340,10 @@
                                'validator': validate_threshold}),
          ('Report all system messages, info-level and higher.  (Same as '
           '"--report=info".)',
-          ['--verbose', '-v'], {'action': 'store_const', 'const': 'info',
+          ['--verbose', '-v'], {'action': 'store_const', 'const': 1,
                                 'dest': 'report_level'}),
          ('Do not report any system messages.  (Same as "--report=none".)',
-          ['--quiet', '-q'], {'action': 'store_const', 'const': 'none',
+          ['--quiet', '-q'], {'action': 'store_const', 'const': 5,
                               'dest': 'report_level'}),
          ('Set the threshold (<level>) at or above which system messages are '
           'converted to exceptions, halting execution immediately by '
@@ -429,6 +436,9 @@
           ['--version', '-V'], {'action': 'version'}),
          ('Show this help message and exit.',
           ['--help', '-h'], {'action': 'help'}),
+         # Typically not useful for non-programmatical use.
+         (SUPPRESS_HELP, ['--id-prefix'], {'default': ''}),
+         (SUPPRESS_HELP, ['--auto-id-prefix'], {'default': 'id'}),
          # Hidden options, for development use only:
          (SUPPRESS_HELP, ['--dump-settings'], {'action': 'store_true'}),
          (SUPPRESS_HELP, ['--dump-internals'], {'action': 'store_true'}),

Modified: Zope/trunk/lib/python/docutils/io.py
===================================================================
--- Zope/trunk/lib/python/docutils/io.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/io.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 3138 $
+# Date: $Date: 2005-03-27 17:05:34 +0200 (Sun, 27 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -70,32 +70,42 @@
         if (self.encoding and self.encoding.lower() == 'unicode'
             or isinstance(data, UnicodeType)):
             return data
-        encodings = [self.encoding, 'utf-8']
-        try:
-            encodings.append(locale.nl_langinfo(locale.CODESET))
-        except:
-            pass
-        try:
-            encodings.append(locale.getlocale()[1])
-        except:
-            pass
-        try:
-            encodings.append(locale.getdefaultlocale()[1])
-        except:
-            pass
-        encodings.append('latin-1')
+        encodings = [self.encoding]
+        if not self.encoding:
+            # Apply heuristics only if no encoding is explicitly given.
+            encodings.append('utf-8')
+            try:
+                encodings.append(locale.nl_langinfo(locale.CODESET))
+            except:
+                pass
+            try:
+                encodings.append(locale.getlocale()[1])
+            except:
+                pass
+            try:
+                encodings.append(locale.getdefaultlocale()[1])
+            except:
+                pass
+            encodings.append('latin-1')
+        error = None
+        error_details = ''
         for enc in encodings:
             if not enc:
                 continue
             try:
                 decoded = unicode(data, enc, self.error_handler)
                 self.successful_encoding = enc
-                return decoded
-            except (UnicodeError, LookupError):
+                # Return decoded, removing BOMs.
+                return decoded.replace(u'\ufeff', u'')
+            except (UnicodeError, LookupError), error:
                 pass
+        if error is not None:
+            error_details = '\n(%s: %s)' % (error.__class__.__name__, error)
         raise UnicodeError(
-            'Unable to decode input data.  Tried the following encodings: %s.'
-            % ', '.join([repr(enc) for enc in encodings if enc]))
+            'Unable to decode input data.  Tried the following encodings: '
+            '%s.%s'
+            % (', '.join([repr(enc) for enc in encodings if enc]),
+               error_details))
 
 
 class Output(TransformSpec):

Modified: Zope/trunk/lib/python/docutils/languages/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # Internationalization details are documented in

Modified: Zope/trunk/lib/python/docutils/languages/af.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/af.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/af.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Jannie Hofmeyr
 # Contact: jhsh at sun.ac.za
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/cs.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/cs.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/cs.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Marek Blaha
 # Contact: mb at dat.cz
-# Revision: $Revision: 1.1.4.4 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/de.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/de.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/de.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors:   David Goodger; Gunnar Schwant
 # Contact:   goodger at users.sourceforge.net
-# Revision:  $Revision: 1.2.10.7 $
-# Date:      $Date: 2005/01/07 13:26:02 $
+# Revision:  $Revision: 2224 $
+# Date:      $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/en.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/en.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/en.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/eo.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/eo.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/eo.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Marcelo Huerta San Martin
 # Contact: richieadler at users.sourceforge.net
-# Revision: $Revision: 1.1.2.5 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/es.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/es.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/es.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,8 +1,8 @@
 # -*- coding: iso-8859-1 -*-
 # Author: Marcelo Huerta San Martín
 # Contact: mghsm at uol.com.ar
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/fi.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/fi.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/fi.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Asko Soukka
 # Contact: asko.soukka at iki.fi
-# Revision: $Revision: 1.1.2.1 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2609 $
+# Date: $Date: 2004-09-13 21:25:33 +0200 (Mon, 13 Sep 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/fr.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/fr.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/fr.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Stefane Fermigier
 # Contact: sf at fermigier.com
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/it.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/it.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/it.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Nicola Larosa
 # Contact: docutils at tekNico.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2944 $
+# Date: $Date: 2005-01-20 13:11:50 +0100 (Thu, 20 Jan 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -45,7 +45,7 @@
       'autori': 'authors',
       'organizzazione': 'organization',
       'indirizzo': 'address',
-      'contatti': 'contact',
+      'contatto': 'contact',
       'versione': 'version',
       'revisione': 'revision',
       'status': 'status',

Modified: Zope/trunk/lib/python/docutils/languages/pt_br.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/pt_br.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/pt_br.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.1.4.4 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2333 $
+# Date: $Date: 2004-06-20 22:51:22 +0200 (Sun, 20 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/ru.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/ru.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/ru.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Roman Suzi
 # Contact: rnd at onego.ru
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2999 $
+# Date: $Date: 2005-03-03 20:35:02 +0100 (Thu, 03 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -46,21 +46,21 @@
 """Mapping of node class name to label text."""
 
 bibliographic_fields = {
-      u'\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f': u'abstract',
-      u'\u0410\u0434\u0440\u0435\u0441': u'address',
-      u'\u0410\u0432\u0442\u043e\u0440': u'author',
-      u'\u0410\u0432\u0442\u043e\u0440\u044b': u'authors',
-      u'\u041a\u043e\u043d\u0442\u0430\u043a\u0442': u'contact',
-      u'\u041f\u0440\u0430\u0432\u0430 \u043a\u043e\u043f\u0438\u0440\u043e'
+      u'\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f': u'abstract',
+      u'\u0430\u0434\u0440\u0435\u0441': u'address',
+      u'\u0430\u0432\u0442\u043e\u0440': u'author',
+      u'\u0430\u0432\u0442\u043e\u0440\u044b': u'authors',
+      u'\u043a\u043e\u043d\u0442\u0430\u043a\u0442': u'contact',
+      u'\u043f\u0440\u0430\u0432\u0430 \u043a\u043e\u043f\u0438\u0440\u043e'
       u'\u0432\u0430\u043d\u0438\u044f': u'copyright',
-      u'\u0414\u0430\u0442\u0430': u'date',
-      u'\u041f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435':
+      u'\u0434\u0430\u0442\u0430': u'date',
+      u'\u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435':
       u'dedication',
-      u'\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f':
+      u'\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f':
       u'organization',
-      u'\u0420\u0435\u0434\u0430\u043a\u0446\u0438\u044f': u'revision',
-      u'\u0421\u0442\u0430\u0442\u0443\u0441': u'status',
-      u'\u0412\u0435\u0440\u0441\u0438\u044f': u'version'}
+      u'\u0440\u0435\u0434\u0430\u043a\u0446\u0438\u044f': u'revision',
+      u'\u0441\u0442\u0430\u0442\u0443\u0441': u'status',
+      u'\u0432\u0435\u0440\u0441\u0438\u044f': u'version'}
 """Russian (lowcased) to canonical name mapping for bibliographic fields."""
 
 author_separators =  [';', ',']

Modified: Zope/trunk/lib/python/docutils/languages/sk.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/sk.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/sk.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # :Author: Miroslav Vasko
 # :Contact: zemiak at zoznam.sk
-# :Revision: $Revision: 1.2.10.7 $
-# :Date: $Date: 2005/01/07 13:26:02 $
+# :Revision: $Revision: 2224 $
+# :Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # :Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/sv.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/sv.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/sv.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author:    Adam Chodorowski
 # Contact:   chodorowski at users.sourceforge.net
-# Revision:  $Revision: 1.2.10.7 $
-# Date:      $Date: 2005/01/07 13:26:02 $
+# Revision:  $Revision: 2224 $
+# Date:      $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/languages/zh_tw.py
===================================================================
--- Zope/trunk/lib/python/docutils/languages/zh_tw.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/languages/zh_tw.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Joe YS Jaw
 # Contact: joeysj at users.sourceforge.net
-# Revision: $Revision: 1.1.2.1 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2608 $
+# Date: $Date: 2004-09-13 21:09:56 +0200 (Mon, 13 Sep 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please

Modified: Zope/trunk/lib/python/docutils/nodes.py
===================================================================
--- Zope/trunk/lib/python/docutils/nodes.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/nodes.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 3358 $
+# Date: $Date: 2005-05-21 02:00:25 +0200 (Sat, 21 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -26,6 +26,8 @@
 import sys
 import os
 import re
+import copy
+import warnings
 import xml.dom.minidom
 from types import IntType, SliceType, StringType, UnicodeType, \
      TupleType, ListType
@@ -103,7 +105,7 @@
         or replaced occurs after the current node, the old node will
         still be traversed, and any new nodes will not.
 
-        Within ``visit`` methods (and ``depart`` methods for 
+        Within ``visit`` methods (and ``depart`` methods for
         `walkabout()`), `TreePruningException` subclasses may be raised
         (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
 
@@ -111,15 +113,15 @@
         ``visit`` implementation for each `Node` subclass encountered.
         """
         visitor.document.reporter.debug(
-            'calling dispatch_visit for %s' % self.__class__.__name__,
-            category='nodes.Node.walk')
+            'docutils.nodes.Node.walk calling dispatch_visit for %s'
+            % self.__class__.__name__)
         try:
             visitor.dispatch_visit(self)
         except (SkipChildren, SkipNode):
             return
         except SkipDeparture:           # not applicable; ignore
             pass
-        children = self.get_children()
+        children = self.children
         try:
             for child in children[:]:
                 child.walk(visitor)
@@ -138,8 +140,8 @@
         """
         call_depart = 1
         visitor.document.reporter.debug(
-            'calling dispatch_visit for %s' % self.__class__.__name__,
-            category='nodes.Node.walkabout')
+            'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
+            % self.__class__.__name__)
         try:
             try:
                 visitor.dispatch_visit(self)
@@ -147,7 +149,7 @@
                 return
             except SkipDeparture:
                 call_depart = 0
-            children = self.get_children()
+            children = self.children
             try:
                 for child in children[:]:
                     child.walkabout(visitor)
@@ -157,11 +159,84 @@
             pass
         if call_depart:
             visitor.document.reporter.debug(
-                'calling dispatch_departure for %s' % self.__class__.__name__,
-                category='nodes.Node.walkabout')
+                'docutils.nodes.Node.walkabout calling dispatch_departure '
+                'for %s' % self.__class__.__name__)
             visitor.dispatch_departure(self)
 
+    def traverse(self, condition=None,
+                 include_self=1, descend=1, siblings=0, ascend=0):
+        """
+        Return an iterable containing
 
+        * self (if include_self is true)
+        * all descendants in tree traversal order (if descend is true)
+        * all siblings (if siblings is true) and their descendants (if
+          also descend is true)
+        * the siblings of the parent (if ascend is true) and their
+          descendants (if also descend is true), and so on
+
+        If ascend is true, assume siblings to be true as well.
+
+        For example, given the following tree::
+
+            <paragraph>
+                <emphasis>      <--- emphasis.traverse() and
+                    <strong>    <--- strong.traverse() are called.
+                        Foo
+                    Bar
+                <reference name="Baz" refid="baz">
+                    Baz
+
+        Then list(emphasis.traverse()) equals ::
+
+            [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
+
+        and list(strong.traverse(ascend=1)) equals ::
+
+            [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
+        """
+        r = []
+        if ascend:
+            siblings=1
+        if include_self and (condition is None or condition(self)):
+            r.append(self)
+        if descend and len(self.children):
+            for child in self:
+                r.extend(child.traverse(
+                    include_self=1, descend=1, siblings=0, ascend=0,
+                    condition=condition))
+        if siblings or ascend:
+            node = self
+            while node.parent:
+                index = node.parent.index(node)
+                for sibling in node.parent[index+1:]:
+                    r.extend(sibling.traverse(include_self=1, descend=descend,
+                                              siblings=0, ascend=0,
+                                              condition=condition))
+                if not ascend:
+                    break
+                else:
+                    node = node.parent
+        return r
+
+
+    def next_node(self, condition=None,
+                  include_self=0, descend=1, siblings=0, ascend=0):
+        """
+        Return the first node in the iterable returned by traverse(),
+        or None if the iterable is empty.
+
+        Parameter list is the same as of traverse.  Note that
+        include_self defaults to 0, though.
+        """
+        iterable = self.traverse(condition=condition,
+                                 include_self=include_self, descend=descend,
+                                 siblings=siblings, ascend=ascend)
+        try:
+            return iterable[0]
+        except IndexError:
+            return None
+
 class Text(Node, UserString):
 
     """
@@ -172,6 +247,9 @@
 
     tagname = '#text'
 
+    children = ()
+    """Text nodes have no children, and cannot have children."""
+
     def __init__(self, data, rawsource=''):
         UserString.__init__(self, data)
 
@@ -209,11 +287,7 @@
             result.append(indent + line + '\n')
         return ''.join(result)
 
-    def get_children(self):
-        """Text nodes have no children. Return []."""
-        return []
 
-
 class Element(Node):
 
     """
@@ -225,6 +299,12 @@
 
         element['att'] = 'value'
 
+    There are two special attributes: 'ids' and 'names'.  Both are
+    lists of unique identifiers, and names serve as human interfaces
+    to IDs.  Names are case- and whitespace-normalized (see the
+    fully_normalize_name() function), and IDs conform to the regular
+    expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
+
     Elements also emulate lists for child nodes (element nodes and/or text
     nodes), indexing by integer.  To get the first child node, use::
 
@@ -245,6 +325,10 @@
     This is equivalent to ``element.extend([node1, node2])``.
     """
 
+    attr_defaults = {'ids': [], 'classes': [], 'names': [],
+                     'dupnames': [], 'backrefs': []}
+    """Default attributes."""
+
     tagname = None
     """The element generic identifier. If None, it is set as an instance
     attribute to the name of the class."""
@@ -261,7 +345,7 @@
 
         self.extend(children)           # maintain parent info
 
-        self.attributes = {}
+        self.attributes = copy.deepcopy(self.attr_defaults)
         """Dictionary of attribute {name: value}."""
 
         for att, value in attributes.items():
@@ -272,7 +356,7 @@
 
     def _dom_node(self, domroot):
         element = domroot.createElement(self.tagname)
-        for attribute, value in self.attributes.items():
+        for attribute, value in self.attlist():
             if isinstance(value, ListType):
                 value = ' '.join(['%s' % v for v in value])
             element.setAttribute(attribute, '%s' % value)
@@ -287,16 +371,16 @@
             if len(data) > 60:
                 data = data[:56] + ' ...'
                 break
-        if self.hasattr('name'):
+        if self['names']:
             return '<%s "%s": %s>' % (self.__class__.__name__,
-                                      self.attributes['name'], data)
+                                      '; '.join(self['names']), data)
         else:
             return '<%s: %s>' % (self.__class__.__name__, data)
 
     def shortrepr(self):
-        if self.hasattr('name'):
+        if self['names']:
             return '<%s "%s"...>' % (self.__class__.__name__,
-                                      self.attributes['name'])
+                                     '; '.join(self['names']))
         else:
             return '<%s...>' % self.tagname
 
@@ -382,20 +466,24 @@
     def __iadd__(self, other):
         """Append a node or a list of nodes to `self.children`."""
         if isinstance(other, Node):
-            self.setup_child(other)
-            self.children.append(other)
+            self.append(other)
         elif other is not None:
-            for node in other:
-                self.setup_child(node)
-            self.children.extend(other)
+            self.extend(other)
         return self
 
     def astext(self):
         return self.child_text_separator.join(
               [child.astext() for child in self.children])
 
+    def non_default_attributes(self):
+        atts = {}
+        for key, value in self.attributes.items():
+            if self.is_not_default(key):
+                atts[key] = value
+        return atts
+
     def attlist(self):
-        attlist = self.attributes.items()
+        attlist = self.non_default_attributes().items()
         attlist.sort()
         return attlist
 
@@ -420,8 +508,7 @@
 
     def extend(self, item):
         for node in item:
-            self.setup_child(node)
-        self.children.extend(item)
+            self.append(node)
 
     def insert(self, index, item):
         if isinstance(item, Node):
@@ -439,6 +526,15 @@
     def index(self, item):
         return self.children.index(item)
 
+    def is_not_default(self, key):
+        try:
+            return self[key] != self.attr_defaults[key]
+        except KeyError:
+            return 1
+
+    def clear(self):
+        self.children = []
+
     def replace(self, old, new):
         """Replace one child `Node` with another child or children."""
         index = self.index(old)
@@ -482,12 +578,10 @@
         if not isinstance(childclass, TupleType):
             childclass = (childclass,)
         for index in range(start, min(len(self), end)):
-            match = 0
             for c in childclass:
                 if isinstance(self.children[index], c):
-                    match = 1
                     break
-            if not match:
+            else:
                 return index
         return None
 
@@ -496,25 +590,41 @@
                        [child.pformat(indent, level+1)
                         for child in self.children])
 
-    def get_children(self):
-        """Return this element's children."""
-        return self.children
-
     def copy(self):
         return self.__class__(**self.attributes)
 
     def set_class(self, name):
-        """Add a new name to the "class" attribute."""
-        self.attributes['class'] = (self.attributes.get('class', '') + ' '
-                                    + name.lower()).strip()
+        """Add a new class to the "classes" attribute."""
+        warnings.warn('docutils.nodes.Element.set_class deprecated; '
+                      "append to Element['classes'] list attribute directly",
+                      DeprecationWarning, stacklevel=2)
+        assert ' ' not in name
+        self['classes'].append(name.lower())
 
+    def note_referenced_by(self, name=None, id=None):
+        """Note that this Element has been referenced by its name
+        `name` or id `id`."""
+        self.referenced = 1
+        # Element.expect_referenced_by_* dictionaries map names or ids
+        # to nodes whose ``referenced`` attribute is set to true as
+        # soon as this node is referenced by the given name or id.
+        # Needed for target propagation.
+        by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
+        by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
+        if by_name:
+            assert name is not None
+            by_name.referenced = 1
+        if by_id:
+            assert id is not None
+            by_id.referenced = 1
 
+
 class TextElement(Element):
 
     """
     An element which directly contains text.
 
-    Its children are all `Text` or `TextElement` subclass nodes.  You can
+    Its children are all `Text` or `Inline` subclass nodes.  You can
     check whether an element's context is inline simply by checking whether
     its immediate parent is a `TextElement` instance (including subclasses).
     This is handy for nodes like `image` that can appear both inline and as
@@ -557,7 +667,7 @@
 class BackLinkable:
 
     def add_backref(self, refid):
-        self.setdefault('backrefs', []).append(refid)
+        self['backrefs'].append(refid)
 
 
 # ====================
@@ -568,15 +678,12 @@
 
 class Titular: pass
 
-class PreDecorative:
-    """Category of Node which may occur before Decorative Nodes."""
-
-class PreBibliographic(PreDecorative):
+class PreBibliographic:
     """Category of Node which may occur before Bibliographic Nodes."""
 
-class Bibliographic(PreDecorative): pass
+class Bibliographic: pass
 
-class Decorative: pass
+class Decorative(PreBibliographic): pass
 
 class Structural: pass
 
@@ -584,7 +691,8 @@
 
 class General(Body): pass
 
-class Sequential(Body): pass
+class Sequential(Body):
+    """List-like elements."""
 
 class Admonition(Body): pass
 
@@ -604,9 +712,6 @@
 
     referenced = 0
 
-    indirect_reference_name = None
-    """Holds the whitespace_normalized_name (contains mixed case) of a target"""
-
 class Labeled:
     """Contains a `label` as its first element."""
 
@@ -717,6 +822,9 @@
         self.transformer = docutils.transforms.Transformer(self)
         """Storage for transforms to be applied to this document."""
 
+        self.decoration = None
+        """Document's `decoration` node."""
+
         self.document = self
 
     def asdom(self, dom=xml.dom.minidom):
@@ -726,21 +834,23 @@
         return domroot
 
     def set_id(self, node, msgnode=None):
-        if node.has_key('id'):
-            id = node['id']
+        for id in node['ids']:
             if self.ids.has_key(id) and self.ids[id] is not node:
                 msg = self.reporter.severe('Duplicate ID: "%s".' % id)
                 if msgnode != None:
                     msgnode += msg
-        else:
-            if node.has_key('name'):
-                id = make_id(node['name'])
+        if not node['ids']:
+            for name in node['names']:
+                id = self.settings.id_prefix + make_id(name)
+                if id and not self.ids.has_key(id):
+                    break
             else:
                 id = ''
-            while not id or self.ids.has_key(id):
-                id = 'id%s' % self.id_start
-                self.id_start += 1
-            node['id'] = id
+                while not id or self.ids.has_key(id):
+                    id = (self.settings.id_prefix +
+                          self.settings.auto_id_prefix + str(self.id_start))
+                    self.id_start += 1
+            node['ids'].append(id)
         self.ids[id] = node
         return id
 
@@ -775,8 +885,7 @@
            both old and new targets are external and refer to identical URIs.
            The new target is invalidated regardless.
         """
-        if node.has_key('name'):
-            name = node['name']
+        for name in node['names']:
             if self.nameids.has_key(name):
                 self.set_duplicate_name_id(node, id, name, msgnode, explicit)
             else:
@@ -794,30 +903,30 @@
                     old_node = self.ids[old_id]
                     if node.has_key('refuri'):
                         refuri = node['refuri']
-                        if old_node.has_key('name') \
+                        if old_node['names'] \
                                and old_node.has_key('refuri') \
                                and old_node['refuri'] == refuri:
                             level = 1   # just inform if refuri's identical
                     if level > 1:
-                        dupname(old_node)
+                        dupname(old_node, name)
                         self.nameids[name] = None
                 msg = self.reporter.system_message(
                     level, 'Duplicate explicit target name: "%s".' % name,
                     backrefs=[id], base_node=node)
                 if msgnode != None:
                     msgnode += msg
-                dupname(node)
+                dupname(node, name)
             else:
                 self.nameids[name] = id
                 if old_id is not None:
                     old_node = self.ids[old_id]
-                    dupname(old_node)
+                    dupname(old_node, name)
         else:
             if old_id is not None and not old_explicit:
                 self.nameids[name] = None
                 old_node = self.ids[old_id]
-                dupname(old_node)
-            dupname(node)
+                dupname(old_node, name)
+            dupname(node, name)
         if not explicit or (not old_explicit and old_id is not None):
             msg = self.reporter.info(
                 'Duplicate implicit target name: "%s".' % name,
@@ -851,7 +960,7 @@
 
     def note_indirect_target(self, target):
         self.indirect_targets.append(target)
-        if target.has_key('name'):
+        if target['names']:
             self.note_refname(target)
 
     def note_anonymous_target(self, target):
@@ -895,7 +1004,8 @@
         self.note_refname(ref)
 
     def note_substitution_def(self, subdef, def_name, msgnode=None):
-        name = subdef['name'] = whitespace_normalize_name(def_name)
+        name = whitespace_normalize_name(def_name)
+        subdef['names'].append(name)
         if self.substitution_defs.has_key(name):
             msg = self.reporter.error(
                   'Duplicate substitution definition name: "%s".' % name,
@@ -903,7 +1013,7 @@
             if msgnode != None:
                 msgnode += msg
             oldnode = self.substitution_defs[name]
-            dupname(oldnode)
+            dupname(oldnode, name)
         # keep only the last definition:
         self.substitution_defs[name] = subdef
         # case-insensitive mapping:
@@ -933,7 +1043,17 @@
         return self.__class__(self.settings, self.reporter,
                               **self.attributes)
 
+    def get_decoration(self):
+        if not self.decoration:
+            self.decoration = decoration()
+            index = self.first_child_not_matching_class(Titular)
+            if index is None:
+                self.append(self.decoration)
+            else:
+                self.insert(index, self.decoration)
+        return self.decoration
 
+
 # ================
 #  Title Elements
 # ================
@@ -964,7 +1084,19 @@
 #  Decorative Elements
 # =====================
 
-class decoration(Decorative, Element): pass
+class decoration(Decorative, Element):
+
+    def get_header(self):
+        if not len(self.children) or not isinstance(self.children[0], header):
+            self.insert(0, header())
+        return self.children[0]
+
+    def get_footer(self):
+        if not len(self.children) or not isinstance(self.children[-1], footer):
+            self.append(footer())
+        return self.children[-1]
+
+
 class header(Decorative, Element): pass
 class footer(Decorative, Element): pass
 
@@ -1061,7 +1193,7 @@
 class line_block(General, Element): pass
 
 
-class line(General, TextElement):
+class line(Part, TextElement):
 
     indent = None
 
@@ -1081,8 +1213,8 @@
 class comment(Special, Invisible, FixedTextElement): pass
 class substitution_definition(Special, Invisible, TextElement): pass
 class target(Special, Invisible, Inline, TextElement, Targetable): pass
-class footnote(General, Element, Labeled, BackLinkable): pass
-class citation(General, Element, Labeled, BackLinkable): pass
+class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
+class citation(General, BackLinkable, Element, Labeled, Targetable): pass
 class label(Part, TextElement): pass
 class figure(General, Element): pass
 class caption(Part, TextElement): pass
@@ -1096,7 +1228,7 @@
 class entry(Part, Element): pass
 
 
-class system_message(Special, PreBibliographic, Element, BackLinkable):
+class system_message(Special, BackLinkable, PreBibliographic, Element):
 
     def __init__(self, message=None, *children, **attributes):
         if message:
@@ -1210,7 +1342,7 @@
 class subscript(Inline, TextElement): pass
 
 
-class image(General, Inline, TextElement):
+class image(General, Inline, Element):
 
     def astext(self):
         return self.get('alt', '')
@@ -1306,8 +1438,8 @@
         node_name = node.__class__.__name__
         method = getattr(self, 'visit_' + node_name, self.unknown_visit)
         self.document.reporter.debug(
-            'calling %s for %s' % (method.__name__, node_name),
-            category='nodes.NodeVisitor.dispatch_visit')
+            'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
+            % (method.__name__, node_name))
         return method(node)
 
     def dispatch_departure(self, node):
@@ -1319,8 +1451,8 @@
         node_name = node.__class__.__name__
         method = getattr(self, 'depart_' + node_name, self.unknown_departure)
         self.document.reporter.debug(
-            'calling %s for %s' % (method.__name__, node_name),
-            category='nodes.NodeVisitor.dispatch_departure')
+            'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
+            % (method.__name__, node_name))
         return method(node)
 
     def unknown_visit(self, node):
@@ -1357,6 +1489,7 @@
     subclasses), subclass `NodeVisitor` instead.
     """
 
+
 class GenericNodeVisitor(NodeVisitor):
 
     """
@@ -1398,10 +1531,11 @@
         setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
         setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
         setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
-        setattr(SparseNodeVisitor, 'depart' + _name, _nop)
+        setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
 
 _add_node_class_names(node_class_names)
 
+
 class TreeCopyVisitor(GenericNodeVisitor):
 
     """
@@ -1534,9 +1668,12 @@
 _non_id_chars = re.compile('[^a-z0-9]+')
 _non_id_at_ends = re.compile('^[-0-9]+|-+$')
 
-def dupname(node):
-    node['dupname'] = node['name']
-    del node['name']
+def dupname(node, name):
+    node['dupnames'].append(name)
+    node['names'].remove(name)
+    # Assume that this method is referenced, even though it isn't; we
+    # don't want to throw unnecessary system_messages.
+    node.referenced = 1
 
 def fully_normalize_name(name):
     """Return a case- and whitespace-normalized name."""

Modified: Zope/trunk/lib/python/docutils/parsers/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:03 $
+# Revision: $Revision: 1645 $
+# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/parsers/rst/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:03 $
+# Revision: $Revision: 3171 $
+# Date: $Date: 2005-04-05 17:26:16 +0200 (Tue, 05 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -112,7 +112,23 @@
          ('Leave spaces before footnote references.',
           ['--leave-footnote-reference-space'],
           {'action': 'store_false', 'dest': 'trim_footnote_reference_space',
-           'validator': frontend.validate_boolean}),))
+           'validator': frontend.validate_boolean}),
+         ('Disable directives that insert the contents of external file '
+          '("include" & "raw"); replaced with a "warning" system message.',
+          ['--no-file-insertion'],
+          {'action': 'store_false', 'default': 1,
+           'dest': 'file_insertion_enabled'}),
+         ('Enable directives that insert the contents of external file '
+          '("include" & "raw").  Enabled by default.',
+          ['--file-insertion-enabled'],
+          {'action': 'store_true', 'dest': 'file_insertion_enabled'}),
+         ('Disable the "raw" directives; replaced with a "warning" '
+          'system message.',
+          ['--no-raw'],
+          {'action': 'store_false', 'default': 1, 'dest': 'raw_enabled'}),
+         ('Enable the "raw" directive.  Enabled by default.',
+          ['--raw-enabled'],
+          {'action': 'store_true', 'dest': 'raw_enabled'}),))
 
     config_section = 'restructuredtext parser'
     config_section_dependencies = ('parsers',)
@@ -128,11 +144,10 @@
     def parse(self, inputstring, document):
         """Parse `inputstring` and populate `document`, a document tree."""
         self.setup_parse(inputstring, document)
-        debug = document.reporter[''].debug
         self.statemachine = states.RSTStateMachine(
               state_classes=self.state_classes,
               initial_state=self.initial_state,
-              debug=debug)
+              debug=document.reporter.debug_flag)
         inputlines = docutils.statemachine.string2lines(
               inputstring, tab_width=document.settings.tab_width,
               convert_whitespace=1)

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at python.org
-# Revision: $Revision: 1.2.10.8 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -113,10 +113,13 @@
       #'questions': ('body', 'question_list'),
       'table': ('tables', 'table'),
       'csv-table': ('tables', 'csv_table'),
+      'list-table': ('tables', 'list_table'),
       'image': ('images', 'image'),
       'figure': ('images', 'figure'),
       'contents': ('parts', 'contents'),
       'sectnum': ('parts', 'sectnum'),
+      'header': ('parts', 'header'),
+      'footer': ('parts', 'footer'),
       #'footnotes': ('parts', 'footnotes'),
       #'citations': ('parts', 'citations'),
       'target-notes': ('references', 'target_notes'),
@@ -250,18 +253,27 @@
     Return the path argument unwrapped (with newlines removed).
     (Directive option conversion function.)
 
-    Raise ``ValueError`` if no argument is found or if the path contains
-    internal whitespace.
+    Raise ``ValueError`` if no argument is found.
     """
     if argument is None:
         raise ValueError('argument required but none supplied')
     else:
         path = ''.join([s.strip() for s in argument.splitlines()])
-        if path.find(' ') == -1:
-            return path
-        else:
-            raise ValueError('path contains whitespace')
+        return path
 
+def uri(argument):
+    """
+    Return the URI argument with whitespace removed.
+    (Directive option conversion function.)
+
+    Raise ``ValueError`` if no argument is found.
+    """
+    if argument is None:
+        raise ValueError('argument required but none supplied')
+    else:
+        uri = ''.join(argument.split())
+        return uri
+
 def nonnegative_int(argument):
     """
     Check for a nonnegative integer argument; raise ``ValueError`` if not.
@@ -274,7 +286,7 @@
 
 def class_option(argument):
     """
-    Convert the argument into an ID-compatible string and return it.
+    Convert the argument into a list of ID-compatible strings and return it.
     (Directive option conversion function.)
 
     Raise ``ValueError`` if no argument is found.
@@ -288,7 +300,7 @@
         if not class_name:
             raise ValueError('cannot make "%s" into a class name' % name)
         class_names.append(class_name)
-    return ' '.join(class_names)
+    return class_names
 
 unicode_pattern = re.compile(
     r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE)
@@ -296,10 +308,13 @@
 def unicode_code(code):
     r"""
     Convert a Unicode character code to a Unicode character.
+    (Directive option conversion function.)
 
     Codes may be decimal numbers, hexadecimal numbers (prefixed by ``0x``,
     ``x``, ``\x``, ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style
     numeric character entities (e.g. ``&#x262E;``).  Other text remains as-is.
+
+    Raise ValueError for illegal Unicode code values.
     """
     try:
         if code.isdigit():                  # decimal number
@@ -315,6 +330,10 @@
         raise ValueError('code too large (%s)' % detail)
 
 def single_char_or_unicode(argument):
+    """
+    A single character is returned as-is.  Unicode characters codes are
+    converted as in `unicode_code`.  (Directive option conversion function.)
+    """
     char = unicode_code(argument)
     if len(char) > 1:
         raise ValueError('%r invalid; must be a single character or '
@@ -322,6 +341,10 @@
     return char
 
 def single_char_or_whitespace_or_unicode(argument):
+    """
+    As with `single_char_or_unicode`, but "tab" and "space" are also supported.
+    (Directive option conversion function.)
+    """
     if argument == 'tab':
         char = '\t'
     elif argument == 'space':
@@ -331,12 +354,23 @@
     return char
 
 def positive_int(argument):
+    """
+    Converts the argument into an integer.  Raises ValueError for negative,
+    zero, or non-integer values.  (Directive option conversion function.)
+    """
     value = int(argument)
     if value < 1:
         raise ValueError('negative or zero value; must be positive')
     return value
 
 def positive_int_list(argument):
+    """
+    Converts a space- or comma-separated list of values into a Python list
+    of integers.
+    (Directive option conversion function.)
+
+    Raises ValueError for non-positive-integer values.
+    """
     if ',' in argument:
         entries = argument.split(',')
     else:
@@ -344,6 +378,12 @@
     return [positive_int(entry) for entry in entries]
 
 def encoding(argument):
+    """
+    Verfies the encoding argument by lookup.
+    (Directive option conversion function.)
+
+    Raises ValueError for unknown encodings.
+    """
     try:
         codecs.lookup(argument)
     except LookupError:

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/admonitions.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/admonitions.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/admonitions.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3155 $
+# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -30,10 +30,10 @@
         admonition_node += nodes.title(title_text, '', *textnodes)
         admonition_node += messages
         if options.has_key('class'):
-            class_value = options['class']
+            classes = options['class']
         else:
-            class_value = 'admonition-' + nodes.make_id(title_text)
-        admonition_node.set_class(class_value)
+            classes = ['admonition-' + nodes.make_id(title_text)]
+        admonition_node['classes'] += classes
     state.nested_parse(content, content_offset, admonition_node)
     return [admonition_node]
 

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/body.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/body.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/body.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at python.org
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3206 $
+# Date: $Date: 2005-04-12 01:16:11 +0200 (Tue, 12 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -16,14 +16,16 @@
 import sys
 from docutils import nodes
 from docutils.parsers.rst import directives
+from docutils.parsers.rst.roles import set_classes
 
               
 def topic(name, arguments, options, content, lineno,
           content_offset, block_text, state, state_machine,
           node_class=nodes.topic):
-    if not state_machine.match_titles:
+    if not (state_machine.match_titles
+            or isinstance(state_machine.node, nodes.sidebar)):
         error = state_machine.reporter.error(
-              'The "%s" directive may not be used within topics, sidebars, '
+              'The "%s" directive may not be used within topics '
               'or body elements.' % name,
               nodes.literal_block(block_text, block_text), line=lineno)
         return [error]
@@ -44,8 +46,7 @@
         messages.extend(more_messages)
     text = '\n'.join(content)
     node = node_class(text, *(titles + messages))
-    if options.has_key('class'):
-        node.set_class(options['class'])
+    node['classes'] += options.get('class', [])
     if text:
         state.nested_parse(content, content_offset, node)
     return [node]
@@ -56,6 +57,11 @@
 
 def sidebar(name, arguments, options, content, lineno,
             content_offset, block_text, state, state_machine):
+    if isinstance(state_machine.node, nodes.sidebar):
+        error = state_machine.reporter.error(
+              'The "%s" directive may not be used within a sidebar element.'
+              % name, nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
     return topic(name, arguments, options, content, lineno,
                  content_offset, block_text, state, state_machine,
                  node_class=nodes.sidebar)
@@ -72,7 +78,7 @@
             'Content block expected for the "%s" directive; none found.'
             % name, nodes.literal_block(block_text, block_text), line=lineno)
         return [warning]
-    block = nodes.line_block()
+    block = nodes.line_block(classes=options.get('class', []))
     node_list = [block]
     for line_text in content:
         text_nodes, messages = state.inline_text(line_text.strip(),
@@ -91,6 +97,7 @@
 
 def parsed_literal(name, arguments, options, content, lineno,
                    content_offset, block_text, state, state_machine):
+    set_classes(options)
     return block(name, arguments, options, content, lineno,
                  content_offset, block_text, state, state_machine,
                  node_class=nodes.literal_block)
@@ -124,7 +131,7 @@
 def epigraph(name, arguments, options, content, lineno,
              content_offset, block_text, state, state_machine):
     block_quote, messages = state.block_quote(content, content_offset)
-    block_quote.set_class('epigraph')
+    block_quote['classes'].append('epigraph')
     return [block_quote] + messages
 
 epigraph.content = 1
@@ -132,7 +139,7 @@
 def highlights(name, arguments, options, content, lineno,
              content_offset, block_text, state, state_machine):
     block_quote, messages = state.block_quote(content, content_offset)
-    block_quote.set_class('highlights')
+    block_quote['classes'].append('highlights')
     return [block_quote] + messages
 
 highlights.content = 1
@@ -140,7 +147,7 @@
 def pull_quote(name, arguments, options, content, lineno,
              content_offset, block_text, state, state_machine):
     block_quote, messages = state.block_quote(content, content_offset)
-    block_quote.set_class('pull-quote')
+    block_quote['classes'].append('pull-quote')
     return [block_quote] + messages
 
 pull_quote.content = 1
@@ -154,8 +161,7 @@
             nodes.literal_block(block_text, block_text), line=lineno)
         return [error]
     node = nodes.compound(text)
-    if options.has_key('class'):
-        node.set_class(options['class'])
+    node['classes'] += options.get('class', [])
     state.nested_parse(content, content_offset, node)
     return [node]
 

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/html.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/html.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/html.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3038 $
+# Date: $Date: 2005-03-14 17:16:57 +0100 (Mon, 14 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -34,7 +34,7 @@
             'Empty meta directive.',
             nodes.literal_block(block_text, block_text), line=lineno)
         node += error
-    return node.get_children()
+    return node.children
 
 meta.content = 1
 

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/images.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/images.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/images.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3347 $
+# Date: $Date: 2005-05-18 20:17:33 +0200 (Wed, 18 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -14,27 +14,43 @@
 import sys
 from docutils import nodes, utils
 from docutils.parsers.rst import directives, states
-from docutils.nodes import whitespace_normalize_name
+from docutils.nodes import fully_normalize_name
+from docutils.parsers.rst.roles import set_classes
 
 try:
     import Image                        # PIL
 except ImportError:
     Image = None
 
-align_values = ('top', 'middle', 'bottom', 'left', 'center', 'right')
+align_h_values = ('left', 'center', 'right')
+align_v_values = ('top', 'middle', 'bottom')
+align_values = align_v_values + align_h_values
 
 def align(argument):
     return directives.choice(argument, align_values)
 
 def image(name, arguments, options, content, lineno,
           content_offset, block_text, state, state_machine):
+    if options.has_key('align'):
+        # check for align_v values only
+        if isinstance(state, states.SubstitutionDef):
+            if options['align'] not in align_v_values:
+                error = state_machine.reporter.error(
+                    'Error in "%s" directive: "%s" is not a valid value for '
+                    'the "align" option within a substitution definition.  '
+                    'Valid values for "align" are: "%s".'
+                    % (name, options['align'], '", "'.join(align_v_values)),
+                    nodes.literal_block(block_text, block_text), line=lineno)
+                return [error]
+        elif options['align'] not in align_h_values:
+            error = state_machine.reporter.error(
+                'Error in "%s" directive: "%s" is not a valid value for '
+                'the "align" option.  Valid values for "align" are: "%s".'
+                % (name, options['align'], '", "'.join(align_h_values)),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            return [error]
     messages = []
-    reference = ''.join(arguments[0].split('\n'))
-    if reference.find(' ') != -1:
-        error = state_machine.reporter.error(
-              'Image URI contains whitespace.',
-              nodes.literal_block(block_text, block_text), line=lineno)
-        return [error]
+    reference = directives.uri(arguments[0])
     options['uri'] = reference
     reference_node = None
     if options.has_key('target'):
@@ -44,12 +60,13 @@
         if target_type == 'refuri':
             reference_node = nodes.reference(refuri=data)
         elif target_type == 'refname':
-            reference_node = nodes.reference(
-                refname=data, name=whitespace_normalize_name(options['target']))
+            reference_node = nodes.reference(refname=data,
+                name=fully_normalize_name(options['target']))
             state.document.note_refname(reference_node)
         else:                           # malformed target
             messages.append(data)       # data is a system message
         del options['target']
+    set_classes(options)
     image_node = nodes.image(block_text, **options)
     if reference_node:
         reference_node += image_node
@@ -66,31 +83,38 @@
                  'target': directives.unchanged_required,
                  'class': directives.class_option}
 
+def figure_align(argument):
+    return directives.choice(argument, align_h_values)
+
 def figure(name, arguments, options, content, lineno,
            content_offset, block_text, state, state_machine):
     figwidth = options.setdefault('figwidth')
-    figclass = options.setdefault('figclass')
+    figclasses = options.setdefault('figclass')
+    align = options.setdefault('align')
     del options['figwidth']
     del options['figclass']
+    del options['align']
     (image_node,) = image(name, arguments, options, content, lineno,
                          content_offset, block_text, state, state_machine)
     if isinstance(image_node, nodes.system_message):
         return [image_node]
     figure_node = nodes.figure('', image_node)
     if figwidth == 'image':
-        if Image:
+        if Image and state.document.settings.file_insertion_enabled:
             # PIL doesn't like Unicode paths:
             try:
                 i = Image.open(str(image_node['uri']))
             except (IOError, UnicodeError):
                 pass
             else:
-                state.document.settings.record_dependencies.add(reference)
+                state.document.settings.record_dependencies.add(image_node['uri'])
                 figure_node['width'] = i.size[0]
     elif figwidth is not None:
         figure_node['width'] = figwidth
-    if figclass:
-        figure_node.set_class(figclass)
+    if figclasses:
+        figure_node['classes'] += figclasses
+    if align:
+        figure_node['align'] = align
     if content:
         node = nodes.Element()          # anonymous container for parsing
         state.nested_parse(content, content_offset, node)
@@ -119,4 +143,5 @@
 figure.options = {'figwidth': figwidth_value,
                   'figclass': directives.class_option}
 figure.options.update(image.options)
+figure.options['align'] = figure_align
 figure.content = 1

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/misc.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/misc.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/misc.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger, Dethe Elza
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3129 $
+# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """Miscellaneous directives."""
@@ -24,15 +24,19 @@
 def include(name, arguments, options, content, lineno,
             content_offset, block_text, state, state_machine):
     """Include a reST file as part of the content of this reST file."""
+    import pdb
+    pdb.set_trace()
+
+    print '1'
+    if not state.document.settings.file_insertion_enabled:
+        warning = state_machine.reporter.warning(
+              '"%s" directive disabled.' % name,
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [warning]
     source = state_machine.input_lines.source(
         lineno - state_machine.input_offset - 1)
     source_dir = os.path.dirname(os.path.abspath(source))
-    path = ''.join(arguments[0].splitlines())
-    if path.find(' ') != -1:
-        error = state_machine.reporter.error(
-              '"%s" directive path contains whitespace.' % name,
-              nodes.literal_block(block_text, block_text), line=lineno)
-        return [error]
+    path = directives.path(arguments[0])
     path = os.path.normpath(os.path.join(source_dir, path))
     path = utils.relative_path(None, path)
     encoding = options.get('encoding', state.document.settings.input_encoding)
@@ -48,7 +52,14 @@
               % (name, error.__class__.__name__, error),
               nodes.literal_block(block_text, block_text), line=lineno)
         return [severe]
-    include_text = include_file.read()
+    try:
+        include_text = include_file.read()
+    except UnicodeError, error:
+        severe = state_machine.reporter.severe(
+              'Problem with "%s" directive:\n%s: %s'
+              % (name, error.__class__.__name__, error),
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [severe]
     if options.has_key('literal'):
         literal_block = nodes.literal_block(include_text, include_text,
                                             source=path)
@@ -74,6 +85,14 @@
     Content may be included inline (content section of directive) or
     imported from a file or url.
     """
+    print 2
+    if ( not state.document.settings.raw_enabled
+         or (not state.document.settings.file_insertion_enabled
+             and (options.has_key('file') or options.has_key('url'))) ):
+        warning = state_machine.reporter.warning(
+              '"%s" directive disabled.' % name,
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [warning]
     attributes = {'format': ' '.join(arguments[0].lower().split())}
     encoding = options.get('encoding', state.document.settings.input_encoding)
     if content:
@@ -106,7 +125,14 @@
                   'Problems with "%s" directive path:\n%s.' % (name, error),
                   nodes.literal_block(block_text, block_text), line=lineno)
             return [severe]
-        text = raw_file.read()
+        try:
+            text = raw_file.read()
+        except UnicodeError, error:
+            severe = state_machine.reporter.severe(
+                  'Problem with "%s" directive:\n%s: %s'
+                  % (name, error.__class__.__name__, error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [severe]
         attributes['source'] = path
     elif options.has_key('url'):
         if not urllib2:
@@ -128,7 +154,14 @@
         raw_file = io.StringInput(
             source=raw_text, source_path=source, encoding=encoding,
             error_handler=state.document.settings.input_encoding_error_handler)
-        text = raw_file.read()
+        try:
+            text = raw_file.read()
+        except UnicodeError, error:
+            severe = state_machine.reporter.severe(
+                  'Problem with "%s" directive:\n%s: %s'
+                  % (name, error.__class__.__name__, error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [severe]
         attributes['source'] = source
     else:
         error = state_machine.reporter.warning(
@@ -140,7 +173,7 @@
 
 raw.arguments = (1, 0, 1)
 raw.options = {'file': directives.path,
-               'url': directives.path,
+               'url': directives.uri,
                'encoding': directives.encoding}
 raw.content = 1
 
@@ -160,8 +193,7 @@
             messages = []
             for node in element:
                 if isinstance(node, nodes.system_message):
-                    if node.has_key('backrefs'):
-                        del node['backrefs']
+                    node['backrefs'] = []
                     messages.append(node)
             error = state_machine.reporter.error(
                 'Error in "%s" directive: may contain a single paragraph '

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/parts.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/parts.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/parts.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger, Dmitry Jemerov
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3199 $
+# Date: $Date: 2005-04-09 03:32:29 +0200 (Sat, 09 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -26,10 +26,24 @@
 
 def contents(name, arguments, options, content, lineno,
              content_offset, block_text, state, state_machine):
-    """Table of contents."""
+    """
+    Table of contents.
+
+    The table of contents is generated in two passes: initial parse and
+    transform.  During the initial parse, a 'pending' element is generated
+    which acts as a placeholder, storing the TOC title and any options
+    internally.  At a later stage in the processing, the 'pending' element is
+    replaced by a 'topic' element, a title and the table of contents proper.
+    """
+    if not (state_machine.match_titles
+            or isinstance(state_machine.node, nodes.sidebar)):
+        error = state_machine.reporter.error(
+              'The "%s" directive may not be used within topics '
+              'or body elements.' % name,
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
     document = state_machine.document
     language = languages.get_language(document.settings.language_code)
-
     if arguments:
         title_text = arguments[0]
         text_nodes, messages = state.inline_text(title_text, lineno)
@@ -40,24 +54,17 @@
             title = None
         else:
             title = nodes.title('', language.labels['contents'])
-
-    topic = nodes.topic(CLASS='contents')
-
-    cls = options.get('class')
-    if cls:
-        topic.set_class(cls)
-
+    topic = nodes.topic(classes=['contents'])
+    topic['classes'] += options.get('class', [])
     if title:
         name = title.astext()
         topic += title
     else:
         name = language.labels['contents']
-
     name = nodes.fully_normalize_name(name)
     if not document.has_name(name):
-        topic['name'] = name
+        topic['names'].append(name)
     document.note_implicit_target(topic)
-
     pending = nodes.pending(parts.Contents, rawsource=block_text)
     pending.details.update(options)
     document.note_pending(pending)
@@ -82,3 +89,36 @@
                    'start': int,
                    'prefix': directives.unchanged_required,
                    'suffix': directives.unchanged_required}
+
+def header_footer(node, name, arguments, options, content, lineno,
+                  content_offset, block_text, state, state_machine):
+    """Contents of document header or footer."""
+    if not content:
+        warning = state_machine.reporter.warning(
+            'Content block expected for the "%s" directive; none found.'
+            % name, nodes.literal_block(block_text, block_text),
+            line=lineno)
+        node.append(nodes.paragraph(
+            '', 'Problem with the "%s" directive: no content supplied.' % name))
+        return [warning]
+    text = '\n'.join(content)
+    state.nested_parse(content, content_offset, node)
+    return []
+
+def header(name, arguments, options, content, lineno,
+           content_offset, block_text, state, state_machine):
+    decoration = state_machine.document.get_decoration()
+    node = decoration.get_header()
+    return header_footer(node, name, arguments, options, content, lineno,
+                         content_offset, block_text, state, state_machine)
+
+header.content = 1
+
+def footer(name, arguments, options, content, lineno,
+           content_offset, block_text, state, state_machine):
+    decoration = state_machine.document.get_decoration()
+    node = decoration.get_footer()
+    return header_footer(node, name, arguments, options, content, lineno,
+                         content_offset, block_text, state, state_machine)
+
+footer.content = 1

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/references.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/references.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/references.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger, Dmitry Jemerov
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 856 $
+# Date: $Date: 2002-10-24 03:01:53 +0200 (Thu, 24 Oct 2002) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/parsers/rst/directives/tables.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/directives/tables.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/directives/tables.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger, David Priest
 # Contact: goodger at python.org
-# Revision: $Revision: 1.1.2.3 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3165 $
+# Date: $Date: 2005-04-05 04:55:06 +0200 (Tue, 05 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -44,7 +44,6 @@
         return [warning]
     title, messages = make_title(arguments, state, lineno)
     node = nodes.Element()          # anonymous container for parsing
-    text = '\n'.join(content)
     state.nested_parse(content, content_offset, node)
     if len(node) != 1 or not isinstance(node[0], nodes.table):
         error = state_machine.reporter.error(
@@ -54,8 +53,7 @@
             line=lineno)
         return [error]
     table_node = node[0]
-    if options.has_key('class'):
-        table_node.set_class(options['class'])
+    table_node['classes'] += options.get('class', [])
     if title:
         table_node.insert(0, title)
     return [table_node] + messages
@@ -116,6 +114,12 @@
 def csv_table(name, arguments, options, content, lineno,
              content_offset, block_text, state, state_machine):
     try:
+        if ( not state.document.settings.file_insertion_enabled
+             and (options.has_key('file') or options.has_key('url')) ):
+            warning = state_machine.reporter.warning(
+                '"%s" directive disabled.' % name,
+                nodes.literal_block(block_text, block_text), line=lineno)
+            return [warning]
         check_requirements(name, lineno, block_text, state_machine)
         title, messages = make_title(arguments, state, lineno)
         csv_data, source = get_csv_data(
@@ -126,8 +130,10 @@
             csv_data, DocutilsDialect(options), source, options)
         max_cols = max(max_cols, max_header_cols)
         header_rows = options.get('header-rows', 0) # default 0
+        stub_columns = options.get('stub-columns', 0) # default 0
         check_table_dimensions(
-            rows, header_rows, name, lineno, block_text, state_machine)
+            rows, header_rows, stub_columns, name, lineno,
+            block_text, state_machine)
         table_head.extend(rows[:header_rows])
         table_body = rows[header_rows:]
         col_widths = get_column_widths(
@@ -141,19 +147,19 @@
             nodes.literal_block(block_text, block_text), line=lineno)
         return [error]
     table = (col_widths, table_head, table_body)
-    table_node = state.build_table(table, content_offset)
-    if options.has_key('class'):
-        table_node.set_class(options['class'])
+    table_node = state.build_table(table, content_offset, stub_columns)
+    table_node['classes'] += options.get('class', [])
     if title:
         table_node.insert(0, title)
     return [table_node] + messages
 
 csv_table.arguments = (0, 1, 1)
 csv_table.options = {'header-rows': directives.nonnegative_int,
+                     'stub-columns': directives.nonnegative_int,
                      'header': directives.unchanged,
                      'widths': directives.positive_int_list,
                      'file': directives.path,
-                     'url': directives.path,
+                     'url': directives.uri,
                      'encoding': directives.encoding,
                      'class': directives.class_option,
                      # field delimiter char
@@ -206,7 +212,8 @@
             state.document.settings.record_dependencies.add(source)
             csv_file = io.FileInput(
                 source_path=source, encoding=encoding,
-                error_handler=state.document.settings.input_encoding_error_handler,
+                error_handler
+                    =state.document.settings.input_encoding_error_handler,
                 handle_io_errors=None)
             csv_data = csv_file.read().splitlines()
         except IOError, error:
@@ -270,20 +277,34 @@
         max_cols = max(max_cols, len(row))
     return rows, max_cols
 
-def check_table_dimensions(rows, header_rows, name, lineno, block_text,
-                           state_machine):
+def check_table_dimensions(rows, header_rows, stub_columns, name, lineno,
+                           block_text, state_machine):
     if len(rows) < header_rows:
         error = state_machine.reporter.error(
             '%s header row(s) specified but only %s row(s) of data supplied '
             '("%s" directive).' % (header_rows, len(rows), name),
             nodes.literal_block(block_text, block_text), line=lineno)
         raise SystemMessagePropagation(error)
-    elif len(rows) == header_rows > 0:
+    if len(rows) == header_rows > 0:
         error = state_machine.reporter.error(
             'Insufficient data supplied (%s row(s)); no data remaining for '
             'table body, required by "%s" directive.' % (len(rows), name),
             nodes.literal_block(block_text, block_text), line=lineno)
         raise SystemMessagePropagation(error)
+    for row in rows:
+        if len(row) < stub_columns:
+            error = state_machine.reporter.error(
+                '%s stub column(s) specified but only %s columns(s) of data '
+                'supplied ("%s" directive).' % (stub_columns, len(row), name),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
+        if len(row) == stub_columns > 0:
+            error = state_machine.reporter.error(
+                'Insufficient data supplied (%s columns(s)); no data remaining '
+                'for table body, required by "%s" directive.'
+                % (len(row), name),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
 
 def get_column_widths(max_cols, name, options, lineno, block_text,
                       state_machine):
@@ -295,8 +316,13 @@
               % (name, max_cols),
               nodes.literal_block(block_text, block_text), line=lineno)
             raise SystemMessagePropagation(error)
+    elif max_cols:
+        col_widths = [100 / max_cols] * max_cols
     else:
-        col_widths = [100 / max_cols] * max_cols
+        error = state_machine.reporter.error(
+            'No table data detected in CSV file.',
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
     return col_widths
 
 def extend_short_rows_with_empty_cells(columns, parts):
@@ -304,3 +330,112 @@
         for row in part:
             if len(row) < columns:
                 row.extend([(0, 0, 0, [])] * (columns - len(row)))
+
+def list_table(name, arguments, options, content, lineno,
+               content_offset, block_text, state, state_machine):
+    """
+    Implement tables whose data is encoded as a uniform two-level bullet list.
+    For further ideas, see
+    http://docutils.sf.net/docs/dev/rst/alternatives.html#list-driven-tables
+    """ 
+    if not content:
+        error = state_machine.reporter.error(
+            'The "%s" directive is empty; content required.' % name,
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    title, messages = make_title(arguments, state, lineno)
+    node = nodes.Element()          # anonymous container for parsing
+    state.nested_parse(content, content_offset, node)
+    try:
+        num_cols, col_widths = check_list_content(
+            node, name, options, content, lineno, block_text, state_machine)
+        table_data = [[item.children for item in row_list[0]]
+                      for row_list in node[0]]
+        header_rows = options.get('header-rows', 0) # default 0
+        stub_columns = options.get('stub-columns', 0) # default 0
+        check_table_dimensions(
+            table_data, header_rows, stub_columns, name, lineno,
+            block_text, state_machine)
+    except SystemMessagePropagation, detail:
+        return [detail.args[0]]
+    table_node = build_table_from_list(table_data, col_widths,
+                                       header_rows, stub_columns)
+    table_node['classes'] += options.get('class', [])
+    if title:
+        table_node.insert(0, title)
+    return [table_node] + messages
+
+list_table.arguments = (0, 1, 1)
+list_table.options = {'header-rows': directives.nonnegative_int,
+                      'stub-columns': directives.nonnegative_int,
+                      'widths': directives.positive_int_list,
+                      'class': directives.class_option}
+list_table.content = 1
+
+def check_list_content(node, name, options, content, lineno, block_text,
+                       state_machine):
+    if len(node) != 1 or not isinstance(node[0], nodes.bullet_list):
+        error = state_machine.reporter.error(
+            'Error parsing content block for the "%s" directive: '
+            'exactly one bullet list expected.' % name,
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+    list_node = node[0]
+    # Check for a uniform two-level bullet list:
+    for item_index in range(len(list_node)):
+        item = list_node[item_index]
+        if len(item) != 1 or not isinstance(item[0], nodes.bullet_list):
+            error = state_machine.reporter.error(
+                'Error parsing content block for the "%s" directive: '
+                'two-level bullet list expected, but row %s does not contain '
+                'a second-level bullet list.' % (name, item_index + 1),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
+        elif item_index:
+            if len(item[0]) != num_cols:
+                error = state_machine.reporter.error(
+                    'Error parsing content block for the "%s" directive: '
+                    'uniform two-level bullet list expected, but row %s does '
+                    'not contain the same number of items as row 1 (%s vs %s).'
+                    % (name, item_index + 1, len(item[0]), num_cols),
+                    nodes.literal_block(block_text, block_text), line=lineno)
+                raise SystemMessagePropagation(error)
+        else:
+            num_cols = len(item[0])
+    col_widths = get_column_widths(
+        num_cols, name, options, lineno, block_text, state_machine)
+    if len(col_widths) != num_cols:
+        error = state_machine.reporter.error(
+            'Error parsing "widths" option of the "%s" directive: '
+            'number of columns does not match the table data (%s vs %s).'
+            % (name, len(col_widths), num_cols),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+    return num_cols, col_widths
+
+def build_table_from_list(table_data, col_widths, header_rows, stub_columns):
+    table = nodes.table()
+    tgroup = nodes.tgroup(cols=len(col_widths))
+    table += tgroup
+    for col_width in col_widths:
+        colspec = nodes.colspec(colwidth=col_width)
+        if stub_columns:
+            colspec.attributes['stub'] = 1
+            stub_columns -= 1
+        tgroup += colspec
+    rows = []
+    for row in table_data:
+        row_node = nodes.row()
+        for cell in row:
+            entry = nodes.entry()
+            entry += cell
+            row_node += entry
+        rows.append(row_node)
+    if header_rows:
+        thead = nodes.thead()
+        thead.extend(rows[:header_rows])
+        tgroup += thead
+    tbody = nodes.tbody()
+    tbody.extend(rows[header_rows:])
+    tgroup += tbody
+    return table

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 2224 $
+# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 # Internationalization details are documented in

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/af.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/af.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/af.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Jannie Hofmeyr
 # Contact: jhsh at sun.ac.za
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -42,6 +42,7 @@
       #'faq': 'questions',
       'table (translation required)': 'table',
       'csv-table (translation required)': 'csv-table',
+      'list-table (translation required)': 'list-table',
       'meta': 'meta',
       #'beeldkaart': 'imagemap',
       'beeld': 'image',
@@ -55,6 +56,8 @@
       'inhoud': 'contents',
       'sectnum': 'sectnum',
       'section-numbering': 'sectnum',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #'voetnote': 'footnotes',
       #'aanhalings': 'citations',
       'teikennotas': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/cs.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/cs.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/cs.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Marek Blaha
 # Contact: mb at dat.cz
-# Revision: $Revision: 1.1.4.4 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -43,6 +43,7 @@
       #'faq': 'questions',
       u'table (translation required)': 'table',
       u'csv-table (translation required)': 'csv-table',
+      u'list-table (translation required)': 'list-table',
       u'meta (translation required)': 'meta',
       #'imagemap': 'imagemap',
       u'image (translation required)': 'image',   # obrazek
@@ -56,6 +57,8 @@
       u'obsah': 'contents',
       u'sectnum (translation required)': 'sectnum',
       u'section-numbering (translation required)': 'sectnum',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #'footnotes': 'footnotes',
       #'citations': 'citations',
       u'target-notes (translation required)': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/de.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/de.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/de.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: Engelbert Gruber; Felix Wiemann
 # Contact: grubert at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -42,6 +42,7 @@
       #'fragen': 'questions',
       'tabelle': 'table',
       'csv-tabelle': 'csv-table',
+      'list-table (translation required)': 'list-table',
       'meta': 'meta',
       #'imagemap': 'imagemap',
       'bild': 'image',
@@ -59,6 +60,8 @@
       'kapitel-nummerierung': 'sectnum',
       'abschnitts-nummerierung': 'sectnum',
       u'linkziel-fu\xdfnoten': 'target-notes',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #u'fu\xdfnoten': 'footnotes',
       #'zitate': 'citations',
       }

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/en.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/en.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/en.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -41,6 +41,7 @@
       #'questions': 'questions',
       'table': 'table',
       'csv-table': 'csv-table',
+      'list-table': 'list-table',
       #'qa': 'questions',
       #'faq': 'questions',
       'meta': 'meta',
@@ -56,6 +57,8 @@
       'contents': 'contents',
       'sectnum': 'sectnum',
       'section-numbering': 'sectnum',
+      'header': 'header',
+      'footer': 'footer',
       #'footnotes': 'footnotes',
       #'citations': 'citations',
       'target-notes': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/eo.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/eo.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/eo.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Marcelo Huerta San Martin
 # Contact: richieadler at users.sourceforge.net
-# Revision: $Revision: 1.1.2.5 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3189 $
+# Date: $Date: 2005-04-08 05:05:45 +0200 (Fri, 08 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -48,6 +48,7 @@
       u'tabelo': 'table',
       u'tabelo-vdk': 'csv-table', # "valoroj disigitaj per komoj"
       u'tabelo-csv': 'csv-table',
+      u'tabelo-lista': 'list-table',
       u'meta': 'meta',
       #'imagemap': 'imagemap',
       u'bildo': 'image',
@@ -62,6 +63,8 @@
       u'enhavo': 'contents',
       u'seknum': 'sectnum',
       u'sekcia-numerado': 'sectnum',
+      u'kapsekcio': 'header',
+      u'piedsekcio': 'footer',
       #'footnotes': 'footnotes',
       #'citations': 'citations',
       u'celaj-notoj': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/es.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/es.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/es.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,8 +1,8 @@
 # -*- coding: iso-8859-1 -*-
 # Author: Marcelo Huerta San Martín
 # Contact: richieadler at users.sourceforge.net
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3190 $
+# Date: $Date: 2005-04-08 05:06:12 +0200 (Fri, 08 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -50,6 +50,7 @@
       u'tabla': 'table',
       u'tabla-vsc': 'csv-table',
       u'tabla-csv': 'csv-table',
+      u'tabla-lista': 'list-table',
       u'meta': 'meta',
       #'imagemap': 'imagemap',
       u'imagen': 'image',
@@ -67,6 +68,8 @@
       u'numeracion-seccion': 'sectnum',
       u'numeraci\u00f3n-secci\u00f3n': 'sectnum',
       u'notas-destino': 'target-notes',
+      u'cabecera': 'header',
+      u'pie': 'footer',
       #'footnotes': 'footnotes',
       #'citations': 'citations',
       u'restructuredtext-test-directive': 'restructuredtext-test-directive'}

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/fi.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/fi.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/fi.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Asko Soukka
 # Contact: asko.soukka at iki.fi
-# Revision: $Revision: 1.1.2.1 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -39,6 +39,7 @@
       u'lainaus': u'pull-quote',
       u'taulukko': u'table',
       u'csv-taulukko': u'csv-table',
+      u'list-table (translation required)': 'list-table',
       u'compound (translation required)': 'compound',
       #u'kysymykset': u'questions',
       u'meta': u'meta',
@@ -53,6 +54,8 @@
       u'rooli': u'role',
       u'sis\u00e4llys': u'contents',
       u'kappale': u'sectnum',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #u'alaviitteet': u'footnotes',
       #u'viitaukset': u'citations',
       u'target-notes (translation required)': u'target-notes'}

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/fr.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/fr.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/fr.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger; William Dode
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -44,6 +44,7 @@
       #u'faq': 'questions',
       u'tableau': 'table',
       u'csv-table (translation required)': 'csv-table',
+      u'list-table (translation required)': 'list-table',
       u'm\u00E9ta': 'meta',
       #u'imagemap (translation required)': 'imagemap',
       u'image': 'image',
@@ -60,6 +61,8 @@
       u'sectnum': 'sectnum',
       u'section-num\u00E9rot\u00E9e': 'sectnum',
       u'liens': 'target-notes',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #u'footnotes (translation required)': 'footnotes',
       #u'citations (translation required)': 'citations',
       }

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/it.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/it.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/it.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,13 +1,13 @@
 # Author: Nicola Larosa, Lele Gaifax
 # Contact: docutils at tekNico.net, lele at seldati.it
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
-# New language mappings are welcome.  Before doing a new translation, please
-# read <http://docutils.sf.net/docs/howto/i18n.html>.  Two files must be
-# translated for each language: one in docutils/languages, the other in
-# docutils/parsers/rst/languages.
+# Beware: the italian translation of the reStructuredText documentation
+# at http://docit.bice.dyndns.org/static/ReST, in particular
+# http://docit.bice.dyndns.org/static/ReST/ref/rst/directives.html, needs
+# to be synced with the content of this file.
 
 """
 Italian-language mappings for language-dependent features of
@@ -34,14 +34,15 @@
       'blocco-interpretato': 'parsed-literal',
       'rubrica': 'rubric',
       'epigrafe': 'epigraph',
-      'evidenzia': 'highlights',
-      'pull-quote (translation required)': 'pull-quote',
-      'compound (translation required)': 'compound',
+      'punti-salienti': 'highlights',
+      'estratto-evidenziato': 'pull-quote',
+      'composito': 'compound',
       #'questions': 'questions',
       #'qa': 'questions',
       #'faq': 'questions',
       'tabella': 'table',
-      'csv-table (translation required)': 'csv-table',
+      'tabella-csv': 'csv-table',
+      'tabella-elenco': 'list-table',
       'meta': 'meta',
       #'imagemap': 'imagemap',
       'immagine': 'image',
@@ -53,9 +54,12 @@
       'classe': 'class',
       'ruolo': 'role',
       'indice': 'contents',
+      'contenuti': 'contents',
       'seznum': 'sectnum',
       'sezioni-autonumerate': 'sectnum',
       'annota-riferimenti-esterni': 'target-notes',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #'footnotes': 'footnotes',
       #'citations': 'citations',
       'restructuredtext-test-directive': 'restructuredtext-test-directive'}

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/pt_br.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/pt_br.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/pt_br.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.1.4.4 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -43,6 +43,7 @@
       #'faq': 'questions',
       u'table (translation required)': 'table',
       u'csv-table (translation required)': 'csv-table',
+      u'list-table (translation required)': 'list-table',
       'meta': 'meta',
       #'imagemap': 'imagemap',
       'imagem': 'image',
@@ -56,6 +57,8 @@
       u'\u00EDndice': 'contents',
       'numsec': 'sectnum',
       u'numera\u00E7\u00E3o-de-se\u00E7\u00F5es': 'sectnum',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #u'notas-de-rorap\u00E9': 'footnotes',
       #u'cita\u00E7\u00F5es': 'citations',
       u'links-no-rodap\u00E9': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/ru.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/ru.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/ru.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Roman Suzi
 # Contact: rnd at onego.ru
-# Revision: $Revision: 1.1.2.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -26,6 +26,7 @@
  u'compound (translation required)': 'compound',
  u'table (translation required)': 'table',
  u'csv-table (translation required)': 'csv-table',
+ u'list-table (translation required)': 'list-table',
  u'\u0441\u044b\u0440\u043e\u0439': u'raw',
  u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace',
  u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext':
@@ -60,7 +61,9 @@
  u'\u0441\u043e\u0432\u0435\u0442': u'hint',
  u'\u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435': u'contents',
  u'\u0442\u0435\u043c\u0430': u'topic',
- u'\u044d\u043f\u0438\u0433\u0440\u0430\u0444': u'epigraph'}
+ u'\u044d\u043f\u0438\u0433\u0440\u0430\u0444': u'epigraph',
+ u'header (translation required)': 'header',
+ u'footer (translation required)': 'footer',}
 """Russian name to registered (in directives/__init__.py) directive name
 mapping."""
 

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/sk.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/sk.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/sk.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Miroslav Vasko
 # Contact: zemiak at zoznam.sk
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -42,6 +42,7 @@
       #u'faq': 'questions',
       u'table (translation required)': 'table',
       u'csv-table (translation required)': 'csv-table',
+      u'list-table (translation required)': 'list-table',
       u'meta': 'meta',
       #u'imagemap': 'imagemap',
       u'obr\xe1zok': 'image',
@@ -56,6 +57,8 @@
       u'\xe8as\x9d': 'sectnum',
       u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum',
       u'cie\xbeov\xe9-pozn\xe1mky': 'target-notes',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #u'footnotes': 'footnotes',
       #u'citations': 'citations',
       }

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/sv.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/sv.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/sv.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author:    Adam Chodorowski
 # Contact:   chodorowski at users.sourceforge.net
-# Revision:  $Revision: 1.2.10.7 $
-# Date:      $Date: 2005/01/07 13:26:04 $
+# Revision:  $Revision: 3184 $
+# Date:      $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -42,6 +42,7 @@
       # u'vanliga-fr\u00e5gor': 'questions',  
       u'table (translation required)': 'table',
       u'csv-table (translation required)': 'csv-table',
+      u'list-table (translation required)': 'list-table',
       u'meta': 'meta',
       # u'bildkarta': 'imagemap',   # FIXME: Translation might be too literal.
       u'bild': 'image',
@@ -55,6 +56,8 @@
       u'inneh\u00e5ll': 'contents',
       u'sektionsnumrering': 'sectnum',
       u'target-notes (translation required)': 'target-notes',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       # u'fotnoter': 'footnotes',
       # u'citeringar': 'citations',
       }

Modified: Zope/trunk/lib/python/docutils/parsers/rst/languages/zh_tw.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/languages/zh_tw.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/languages/zh_tw.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.1.2.1 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 3184 $
+# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 # New language mappings are welcome.  Before doing a new translation, please
@@ -41,6 +41,7 @@
       #'questions (translation required)': 'questions',
       'table (translation required)': 'table',
       'csv-table (translation required)': 'csv-table',
+      'list-table (translation required)': 'list-table',
       #'qa (translation required)': 'questions',
       #'faq (translation required)': 'questions',
       'meta (translation required)': 'meta',
@@ -56,6 +57,8 @@
       'contents (translation required)': 'contents',
       'sectnum (translation required)': 'sectnum',
       'section-numbering (translation required)': 'sectnum',
+      u'header (translation required)': 'header',
+      u'footer (translation required)': 'footer',
       #'footnotes (translation required)': 'footnotes',
       #'citations (translation required)': 'citations',
       'target-notes (translation required)': 'target-notes',

Modified: Zope/trunk/lib/python/docutils/parsers/rst/roles.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/roles.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/roles.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: Edward Loper
 # Contact: edloper at gradient.cis.upenn.edu
-# Revision: $Revision: 1.1.4.4 $
-# Date: $Date: 2005/01/07 13:26:03 $
+# Revision: $Revision: 3155 $
+# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -174,7 +174,7 @@
     if not hasattr(role_fn, 'options') or role_fn.options is None:
         role_fn.options = {'class': directives.class_option}
     elif not role_fn.options.has_key('class'):
-        role_fn.options['class'] = directives.class_option    
+        role_fn.options['class'] = directives.class_option
 
 def register_generic_role(canonical_name, node_class):
     """For roles which simply wrap a given `node_class` around the text."""
@@ -195,6 +195,7 @@
 
     def __call__(self, role, rawtext, text, lineno, inliner,
                  options={}, content=[]):
+        set_classes(options)
         return [self.node_class(rawtext, utils.unescape(text), **options)], []
 
 
@@ -233,6 +234,7 @@
     """"""
     # Once nested inline markup is implemented, this and other methods should
     # recursively call inliner.nested_parse().
+    set_classes(options)
     return [nodes.inline(rawtext, utils.unescape(text), **options)], []
 
 generic_custom_role.options = {'class': directives.class_option}
@@ -265,6 +267,7 @@
         return [prb], [msg]
     # Base URL mainly used by inliner.pep_reference; so this is correct:
     ref = inliner.document.settings.pep_base_url + inliner.pep_url % pepnum
+    set_classes(options)
     return [nodes.reference(rawtext, 'PEP ' + utils.unescape(text), refuri=ref,
                             **options)], []
 
@@ -284,6 +287,7 @@
         return [prb], [msg]
     # Base URL mainly used by inliner.rfc_reference, so this is correct:
     ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
+    set_classes(options)
     node = nodes.reference(rawtext, 'RFC ' + utils.unescape(text), refuri=ref,
                            **options)
     return [node], []
@@ -299,10 +303,11 @@
             'an associated format.' % role, line=lineno)
         prb = inliner.problematic(rawtext, rawtext, msg)
         return [prb], [msg]
+    set_classes(options)
     node = nodes.raw(rawtext, utils.unescape(text, 1), **options)
     return [node], []
 
-raw_role.options = {'format': directives.class_option}
+raw_role.options = {'format': directives.unchanged}
 
 register_canonical_role('raw', raw_role)
 
@@ -329,3 +334,14 @@
 # This should remain unimplemented, for testing purposes:
 register_canonical_role('restructuredtext-unimplemented-role',
                         unimplemented_role)
+
+
+def set_classes(options):
+    """
+    Auxiliary function to set options['classes'] and delete
+    options['class'].
+    """
+    if options.has_key('class'):
+        assert not options.has_key('classes')
+        options['classes'] = options['class']
+        del options['class']

Modified: Zope/trunk/lib/python/docutils/parsers/rst/states.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/states.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/states.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:03 $
+# Revision: $Revision: 3253 $
+# Date: $Date: 2005-04-25 17:08:01 +0200 (Mon, 25 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -365,7 +365,7 @@
         textnodes, title_messages = self.inline_text(title, lineno)
         titlenode = nodes.title(title, '', *textnodes)
         name = normalize_name(titlenode.astext())
-        section_node['name'] = name
+        section_node['names'].append(name)
         section_node += titlenode
         section_node += messages
         section_node += title_messages
@@ -533,7 +533,7 @@
     emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9\x00]"""
     email_pattern = r"""
           %(emailc)s+(?:\.%(emailc)s+)*   # name
-          @                               # at
+          (?<!\x00)@                      # at
           %(emailc)s+(?:\.%(emailc)s*)*   # host
           %(uri_end)s                     # final URI char
           """
@@ -787,7 +787,7 @@
         else:
             if target:
                 reference['refuri'] = uri
-                target['name'] = refname
+                target['names'].append(refname)
                 self.document.note_external_target(target)
                 self.document.note_explicit_target(target, self.parent)
                 node_list.append(target)
@@ -829,7 +829,7 @@
             assert len(inlines) == 1
             target = inlines[0]
             name = normalize_name(target.astext())
-            target['name'] = name
+            target['names'].append(name)
             self.document.note_explicit_target(target, self.parent)
         return before, inlines, remaining, sysmessages
 
@@ -1036,10 +1036,10 @@
     pats['alphanum'] = '[a-zA-Z0-9]'
     pats['alphanumplus'] = '[a-zA-Z0-9_-]'
     pats['enum'] = ('(%(arabic)s|%(loweralpha)s|%(upperalpha)s|%(lowerroman)s'
-                    '|%(upperroman)s)' % enum.sequencepats)
+                    '|%(upperroman)s|#)' % enum.sequencepats)
     pats['optname'] = '%(alphanum)s%(alphanumplus)s*' % pats
     # @@@ Loosen up the pattern?  Allow Unicode?
-    pats['optarg'] = '(%(alpha)s%(alphanumplus)s*|<%(alphanum)s[^ <>]+>)' % pats
+    pats['optarg'] = '(%(alpha)s%(alphanumplus)s*|<[^<>]+>)' % pats
     pats['shortopt'] = r'(-|\+)%(alphanum)s( ?%(optarg)s)?' % pats
     pats['longopt'] = r'(--|/)%(optname)s([ =]%(optarg)s)?' % pats
     pats['option'] = r'(%(shortopt)s|%(longopt)s)' % pats
@@ -1182,7 +1182,10 @@
             raise statemachine.TransitionCorrection('text')
         enumlist = nodes.enumerated_list()
         self.parent += enumlist
-        enumlist['enumtype'] = sequence
+        if sequence == '#':
+            enumlist['enumtype'] = 'arabic'
+        else:
+            enumlist['enumtype'] = sequence
         enumlist['prefix'] = self.enum.formatinfo[format].prefix
         enumlist['suffix'] = self.enum.formatinfo[format].suffix
         if ordinal != 1:
@@ -1199,7 +1202,9 @@
               input_offset=self.state_machine.abs_line_offset() + 1,
               node=enumlist, initial_state='EnumeratedList',
               blank_finish=blank_finish,
-              extra_settings={'lastordinal': ordinal, 'format': format})
+              extra_settings={'lastordinal': ordinal,
+                              'format': format,
+                              'auto': sequence == '#'})
         self.goto_line(newline_offset)
         if not blank_finish:
             self.parent += self.unindent_warning('Enumerated list')
@@ -1232,7 +1237,9 @@
             raise ParserError('enumerator format not matched')
         text = groupdict[format][self.enum.formatinfo[format].start
                                  :self.enum.formatinfo[format].end]
-        if expected_sequence:
+        if text == '#':
+            sequence = '#'
+        elif expected_sequence:
             try:
                 if self.enum.sequenceregexps[expected_sequence].match(text):
                     sequence = expected_sequence
@@ -1249,10 +1256,13 @@
                     break
             else:                       # shouldn't happen
                 raise ParserError('enumerator sequence not matched')
-        try:
-            ordinal = self.enum.converters[sequence](text)
-        except roman.InvalidRomanNumeralError:
-            ordinal = None
+        if sequence == '#':
+            ordinal = 1
+        else:
+            try:
+                ordinal = self.enum.converters[sequence](text)
+            except roman.InvalidRomanNumeralError:
+                ordinal = None
         return format, sequence, text, ordinal
 
     def is_enumerated_list_item(self, ordinal, sequence, format):
@@ -1260,7 +1270,7 @@
         Check validity based on the ordinal value and the second line.
 
         Return true iff the ordinal is valid and the second line is blank,
-        indented, or starts with the next enumerator.
+        indented, or starts with the next enumerator or an auto-enumerator.
         """
         if ordinal is None:
             return None
@@ -1273,9 +1283,11 @@
             self.state_machine.previous_line()
         if not next_line[:1].strip():   # blank or indented
             return 1
-        next_enumerator = self.make_enumerator(ordinal + 1, sequence, format)
+        next_enumerator, auto_enumerator = self.make_enumerator(
+            ordinal + 1, sequence, format)
         try:
-            if next_line.startswith(next_enumerator):
+            if ( next_line.startswith(next_enumerator) or
+                 next_line.startswith(auto_enumerator) ):
                 return 1
         except TypeError:
             pass
@@ -1283,11 +1295,14 @@
 
     def make_enumerator(self, ordinal, sequence, format):
         """
-        Construct and return an enumerated list item marker.
+        Construct and return the next enumerated list item marker, and an
+        auto-enumerator ("#" instead of the regular enumerator).
 
         Return ``None`` for invalid (out of range) ordinals.
-        """
-        if sequence == 'arabic':
+        """ #"
+        if sequence == '#':
+            enumerator = '#'
+        elif sequence == 'arabic':
             enumerator = str(ordinal)
         else:
             if sequence.endswith('alpha'):
@@ -1310,7 +1325,10 @@
                 raise ParserError('unknown enumerator sequence: "%s"'
                                   % sequence)
         formatinfo = self.enum.formatinfo[format]
-        return formatinfo.prefix + enumerator + formatinfo.suffix + ' '
+        next_enumerator = (formatinfo.prefix + enumerator + formatinfo.suffix
+                           + ' ')
+        auto_enumerator = formatinfo.prefix + '#' + formatinfo.suffix + ' '
+        return next_enumerator, auto_enumerator
 
     def field_marker(self, match, context, next_state):
         """Field list item."""
@@ -1415,14 +1433,20 @@
             delimiter = ' '
             firstopt = tokens[0].split('=')
             if len(firstopt) > 1:
+                # "--opt=value" form
                 tokens[:1] = firstopt
                 delimiter = '='
             elif (len(tokens[0]) > 2
                   and ((tokens[0].startswith('-')
                         and not tokens[0].startswith('--'))
                        or tokens[0].startswith('+'))):
+                # "-ovalue" form
                 tokens[:1] = [tokens[0][:2], tokens[0][2:]]
                 delimiter = ''
+            if len(tokens) > 1 and (tokens[1].startswith('<')
+                                    and tokens[-1].endswith('>')):
+                # "-o <value1 value2>" form; join all values into one token
+                tokens[1:] = [' '.join(tokens[1:])]
             if 0 < len(tokens) <= 2:
                 option = nodes.option(optionstring)
                 option += nodes.option_string(tokens[0], tokens[0])
@@ -1432,7 +1456,7 @@
                 optlist.append(option)
             else:
                 raise MarkupError(
-                    'wrong numer of option tokens (=%s), should be 1 or 2: '
+                    'wrong number of option tokens (=%s), should be 1 or 2: '
                     '"%s"' % (len(tokens), optionstring),
                     self.state_machine.abs_line_number() + 1)
         return optlist
@@ -1541,7 +1565,8 @@
                 table = self.build_table(tabledata, tableline)
                 nodelist = [table] + messages
             except tableparser.TableMarkupError, detail:
-                nodelist = self.malformed_table(block, str(detail)) + messages
+                nodelist = self.malformed_table(
+                    block, ' '.join(detail.args)) + messages
         else:
             nodelist = messages
         return nodelist, blank_finish
@@ -1633,13 +1658,17 @@
                                     line=lineno)
         return [error]
 
-    def build_table(self, tabledata, tableline):
-        colspecs, headrows, bodyrows = tabledata
+    def build_table(self, tabledata, tableline, stub_columns=0):
+        colwidths, headrows, bodyrows = tabledata
         table = nodes.table()
-        tgroup = nodes.tgroup(cols=len(colspecs))
+        tgroup = nodes.tgroup(cols=len(colwidths))
         table += tgroup
-        for colspec in colspecs:
-            tgroup += nodes.colspec(colwidth=colspec)
+        for colwidth in colwidths:
+            colspec = nodes.colspec(colwidth=colwidth)
+            if stub_columns:
+                colspec.attributes['stub'] = 1
+                stub_columns -= 1
+            tgroup += colspec
         if headrows:
             thead = nodes.thead()
             tgroup += thead
@@ -1727,7 +1756,7 @@
             name = name[1:]             # autonumber label
             footnote['auto'] = 1
             if name:
-                footnote['name'] = name
+                footnote['names'].append(name)
             self.document.note_autofootnote(footnote)
         elif name == '*':               # auto-symbol
             name = ''
@@ -1735,7 +1764,7 @@
             self.document.note_symbol_footnote(footnote)
         else:                           # manually numbered
             footnote += nodes.label('', label)
-            footnote['name'] = name
+            footnote['names'].append(name)
             self.document.note_footnote(footnote)
         if name:
             self.document.note_explicit_target(footnote, footnote)
@@ -1754,7 +1783,7 @@
         citation = nodes.citation('\n'.join(indented))
         citation.line = lineno
         citation += nodes.label('', label)
-        citation['name'] = name
+        citation['names'].append(name)
         self.document.note_citation(citation)
         self.document.note_explicit_target(citation, citation)
         if indented:
@@ -1790,7 +1819,6 @@
         target_type, data = self.parse_target(block, block_text, lineno)
         if target_type == 'refname':
             target = nodes.target(block_text, '', refname=normalize_name(data))
-            target.indirect_reference_name = data
             self.add_target(target_name, '', target, lineno)
             self.document.note_indirect_target(target)
             return target
@@ -1816,15 +1844,8 @@
             refname = self.is_reference(reference)
             if refname:
                 return 'refname', refname
-        reference = ''.join([line.strip() for line in block])
-        if reference.find(' ') == -1:
-            return 'refuri', unescape(reference)
-        else:
-            warning = self.reporter.warning(
-                  'Hyperlink target contains whitespace. Perhaps a footnote '
-                  'was intended?',
-                  nodes.literal_block(block_text, block_text), line=lineno)
-            return 'malformed', warning
+        reference = ''.join([''.join(line.split()) for line in block])
+        return 'refuri', unescape(reference)
 
     def is_reference(self, reference):
         match = self.explicit.patterns.reference.match(
@@ -1837,7 +1858,7 @@
         target.line = lineno
         if targetname:
             name = normalize_name(unescape(targetname))
-            target['name'] = name
+            target['names'].append(name)
             if refuri:
                 uri = self.inliner.adjust_uri(refuri)
                 if uri:
@@ -1851,6 +1872,8 @@
         else:                       # anonymous target
             if refuri:
                 target['refuri'] = refuri
+            else:
+                self.document.note_internal_target(target)
             target['anonymous'] = 1
             self.document.note_anonymous_target(target)
 
@@ -1960,7 +1983,8 @@
                                            directive_fn, option_presets))
         except MarkupError, detail:
             error = self.reporter.error(
-                'Error in "%s" directive:\n%s.' % (type_name, detail),
+                'Error in "%s" directive:\n%s.' % (type_name,
+                                                   ' '.join(detail.args)),
                 nodes.literal_block(block_text, block_text), line=lineno)
             return [error], blank_finish
         result = directive_fn(type_name, arguments, options, content, lineno,
@@ -2071,9 +2095,9 @@
         except KeyError, detail:
             return 0, ('unknown option: "%s"' % detail.args[0])
         except (ValueError, TypeError), detail:
-            return 0, ('invalid option value: %s' % detail)
+            return 0, ('invalid option value: %s' % ' '.join(detail.args))
         except utils.ExtensionOptionError, detail:
-            return 0, ('invalid option data: %s' % detail)
+            return 0, ('invalid option data: %s' % ' '.join(detail.args))
         if blank_finish:
             return 1, options
         else:
@@ -2127,13 +2151,13 @@
            re.compile(r"""
                       \.\.[ ]+          # explicit markup start
                       _                 # target indicator
-                      (?![ ])           # first char. not space
+                      (?![ ]|$)         # first char. not space or EOL
                       """, re.VERBOSE)),
           (substitution_def,
            re.compile(r"""
                       \.\.[ ]+          # explicit markup start
                       \|                # substitution indicator
-                      (?![ ])           # first char. not space
+                      (?![ ]|$)         # first char. not space or EOL
                       """, re.VERBOSE)),
           (directive,
            re.compile(r"""
@@ -2240,7 +2264,7 @@
 
     def rfc2822(self, match, context, next_state):
         """RFC2822-style field list item."""
-        fieldlist = nodes.field_list(CLASS='rfc2822')
+        fieldlist = nodes.field_list(classes=['rfc2822'])
         self.parent += fieldlist
         field, blank_finish = self.rfc2822_field(match)
         fieldlist += field
@@ -2347,12 +2371,15 @@
         """Enumerated list item."""
         format, sequence, text, ordinal = self.parse_enumerator(
               match, self.parent['enumtype'])
-        if (sequence != self.parent['enumtype'] or
-            format != self.format or
-            ordinal != (self.lastordinal + 1) or
-            not self.is_enumerated_list_item(ordinal, sequence, format)):
+        if ( format != self.format
+             or (sequence != '#' and (sequence != self.parent['enumtype']
+                                      or self.auto
+                                      or ordinal != (self.lastordinal + 1)))
+             or not self.is_enumerated_list_item(ordinal, sequence, format)):
             # different enumeration: new list
             self.invalid_input()
+        if sequence == '#':
+            self.auto = 1
         listitem, blank_finish = self.list_item(match.end())
         self.parent += listitem
         self.blank_finish = blank_finish
@@ -2475,7 +2502,7 @@
 
     def embedded_directive(self, match, context, next_state):
         nodelist, blank_finish = self.directive(match,
-                                                alt=self.parent['name'])
+                                                alt=self.parent['names'][0])
         self.parent += nodelist
         if not self.state_machine.at_eof():
             self.blank_finish = blank_finish

Modified: Zope/trunk/lib/python/docutils/parsers/rst/tableparser.py
===================================================================
--- Zope/trunk/lib/python/docutils/parsers/rst/tableparser.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/parsers/rst/tableparser.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:04 $
+# Revision: $Revision: 1574 $
+# Date: $Date: 2003-07-06 00:38:28 +0200 (Sun, 06 Jul 2003) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/readers/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger; Ueli Schlaepfer
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 1645 $
+# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/readers/pep.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/pep.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/pep.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 3129 $
+# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -31,9 +31,9 @@
     config_section_dependencies = ('readers', 'standalone reader')
 
     default_transforms = (references.Substitutions,
+                          references.PropagateTargets,
                           peps.Headers,
                           peps.Contents,
-                          references.ChainedTargets,
                           references.AnonymousHyperlinks,
                           references.IndirectHyperlinks,
                           peps.TargetNotes,

Modified: Zope/trunk/lib/python/docutils/readers/python/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/python/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/python/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.3.2.5 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 3038 $
+# Date: $Date: 2005-03-14 17:16:57 +0100 (Mon, 14 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -88,7 +88,7 @@
         node['docformat'] = docformat
         parser = self.get_parser(docformat)
         parser.parse(text, self.document)
-        for child in self.document.get_children():
+        for child in self.document.children:
             node.append(child)
         self.document.current_source = self.document.current_line = None
         del self.document[:]

Modified: Zope/trunk/lib/python/docutils/readers/python/moduleparser.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/python/moduleparser.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/python/moduleparser.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.3.2.5 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 2449 $
+# Date: $Date: 2004-07-25 03:45:27 +0200 (Sun, 25 Jul 2004) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/readers/python/pynodes.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/python/pynodes.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/python/pynodes.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -3,8 +3,8 @@
 """
 :Author: David Goodger
 :Contact: goodger at users.sourceforge.net
-:Revision: $Revision: 1.1.4.4 $
-:Date: $Date: 2005/01/07 13:26:05 $
+:Revision: $Revision: 1881 $
+:Date: $Date: 2004-03-24 00:21:11 +0100 (Wed, 24 Mar 2004) $
 :Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/readers/standalone.py
===================================================================
--- Zope/trunk/lib/python/docutils/readers/standalone.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/readers/standalone.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 3353 $
+# Date: $Date: 2005-05-19 02:49:14 +0200 (Thu, 19 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -37,15 +37,26 @@
           'default).',
           ['--no-doc-info'],
           {'dest': 'docinfo_xform', 'action': 'store_false', 'default': 1,
-           'validator': frontend.validate_boolean}),))
+           'validator': frontend.validate_boolean}),
+         ('Activate the promotion of lone subsection titles to '
+          'section subtitles (disabled by default).',
+          ['--section-subtitles'],
+          {'dest': 'sectsubtitle_xform', 'action': 'store_true', 'default': 0,
+           'validator': frontend.validate_boolean}),
+         ('Deactivate the promotion of lone subsection titles.',
+          ['--no-section-subtitles'],
+          {'dest': 'sectsubtitle_xform', 'action': 'store_false',
+           'validator': frontend.validate_boolean}),
+         ))
 
     config_section = 'standalone reader'
     config_section_dependencies = ('readers',)
 
     default_transforms = (references.Substitutions,
+                          references.PropagateTargets,
                           frontmatter.DocTitle,
+                          frontmatter.SectionSubTitle,
                           frontmatter.DocInfo,
-                          references.ChainedTargets,
                           references.AnonymousHyperlinks,
                           references.IndirectHyperlinks,
                           references.Footnotes,

Modified: Zope/trunk/lib/python/docutils/statemachine.py
===================================================================
--- Zope/trunk/lib/python/docutils/statemachine.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/statemachine.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 2299 $
+# Date: $Date: 2004-06-17 23:46:50 +0200 (Thu, 17 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/transforms/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger, Ueli Schlaepfer
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:05 $
+# Revision: $Revision: 3066 $
+# Date: $Date: 2005-03-21 18:33:42 +0100 (Mon, 21 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -59,8 +59,8 @@
         self.language = languages.get_language(
             document.settings.language_code)
         """Language module local to this document."""
-        
 
+
     def apply(self):
         """Override to apply the transform to the document tree."""
         raise NotImplementedError('subclass must override this method')
@@ -164,7 +164,6 @@
         decorated_list.sort()
         self.unknown_reference_resolvers.extend([f[1] for f in decorated_list])
 
-
     def apply_transforms(self):
         """Apply all of the stored transforms, in priority order."""
         self.document.reporter.attach_observer(

Modified: Zope/trunk/lib/python/docutils/transforms/components.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/components.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/components.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 853 $
+# Date: $Date: 2002-10-24 02:51:10 +0200 (Thu, 24 Oct 2002) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/transforms/frontmatter.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/frontmatter.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/frontmatter.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,17 +1,19 @@
 # Authors: David Goodger, Ueli Schlaepfer
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3351 $
+# Date: $Date: 2005-05-19 00:27:52 +0200 (Thu, 19 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
-Transforms related to the front matter of a document (information
-found before the main text):
+Transforms related to the front matter of a document or a section
+(information found before the main text):
 
 - `DocTitle`: Used to transform a lone top level section's title to
   the document title, and promote a remaining lone top-level section's
   title to the document subtitle.
 
+- `SectionTitle`: Used to transform a lone subsection into a subtitle.
+
 - `DocInfo`: Used to transform a bibliographic field list into docinfo
   elements.
 """
@@ -23,9 +25,102 @@
 from docutils.transforms import TransformError, Transform
 
 
-class DocTitle(Transform):
+class TitlePromoter(Transform):
 
     """
+    Abstract base class for DocTitle and SectionSubTitle transforms.
+    """
+
+    def promote_title(self, node):
+        """
+        Transform the following tree::
+
+            <node>
+                <section>
+                    <title>
+                    ...
+
+        into ::
+
+            <node>
+                <title>
+                ...
+
+        `node` is normally a document.
+        """
+        # `node` must not have a title yet.
+        assert not (len(node) and isinstance(node[0], nodes.title))
+        section, index = self.candidate_index(node)
+        if index is None:
+            return None
+        # Transfer the section's attributes to the node:
+        node.attributes.update(section.attributes)
+        # setup_child is called automatically for all nodes.
+        node[:] = (section[:1]        # section title
+                   + node[:index]     # everything that was in the
+                                      # node before the section
+                   + section[1:])     # everything that was in the section
+        assert isinstance(node[0], nodes.title)
+        return 1
+
+    def promote_subtitle(self, node):
+        """
+        Transform the following node tree::
+
+            <node>
+                <title>
+                <section>
+                    <title>
+                    ...
+
+        into ::
+
+            <node>
+                <title>
+                <subtitle>
+                ...
+        """
+        subsection, index = self.candidate_index(node)
+        if index is None:
+            return None
+        subtitle = nodes.subtitle()
+        # Transfer the subsection's attributes to the new subtitle:
+        # This causes trouble with list attributes!  To do: Write a
+        # test case which catches direct access to the `attributes`
+        # dictionary and/or write a test case which shows problems in
+        # this particular case.
+        subtitle.attributes.update(subsection.attributes)
+        # We're losing the subtitle's attributes here!  To do: Write a
+        # test case which shows this behavior.
+        # Transfer the contents of the subsection's title to the
+        # subtitle:
+        subtitle[:] = subsection[0][:]
+        node[:] = (node[:1]       # title
+                   + [subtitle]
+                   # everything that was before the section:
+                   + node[1:index]
+                   # everything that was in the subsection:
+                   + subsection[1:])
+        return 1
+
+    def candidate_index(self, node):
+        """
+        Find and return the promotion candidate and its index.
+
+        Return (None, None) if no valid candidate was found.
+        """
+        index = node.first_child_not_matching_class(
+            nodes.PreBibliographic)
+        if index is None or len(node) > (index + 1) or \
+               not isinstance(node[index], nodes.section):
+            return None, None
+        else:
+            return node[index], index
+
+
+class DocTitle(TitlePromoter):
+
+    """
     In reStructuredText_, there is no way to specify a document title
     and subtitle explicitly. Instead, we can supply the document title
     (and possibly the subtitle as well) implicitly, and use this
@@ -50,7 +145,7 @@
        Once parsed, it looks like this::
 
            <document>
-               <section name="top-level title">
+               <section names="top-level title">
                    <title>
                        Top-Level Title
                    <paragraph>
@@ -58,7 +153,7 @@
 
        After running the DocTitle transform, we have::
 
-           <document name="top-level title">
+           <document names="top-level title">
                <title>
                    Top-Level Title
                <paragraph>
@@ -85,10 +180,10 @@
        After parsing and running the Section Promotion transform, the
        result is::
 
-           <document name="top-level title">
+           <document names="top-level title">
                <title>
                    Top-Level Title
-               <subtitle name="second-level title">
+               <subtitle names="second-level title">
                    Second-Level Title
                <paragraph>
                    A paragraph.
@@ -107,56 +202,49 @@
     def apply(self):
         if not getattr(self.document.settings, 'doctitle_xform', 1):
             return
-        if self.promote_document_title():
-            self.promote_document_subtitle()
+        if self.promote_title(self.document):
+            self.promote_subtitle(self.document)
 
-    def promote_document_title(self):
-        section, index = self.candidate_index()
-        if index is None:
-            return None
-        document = self.document
-        # Transfer the section's attributes to the document element (at root):
-        document.attributes.update(section.attributes)
-        document[:] = (section[:1]        # section title
-                       + document[:index] # everything that was in the
-                                          # document before the section
-                       + section[1:])     # everything that was in the section
-        return 1
 
-    def promote_document_subtitle(self):
-        subsection, index = self.candidate_index()
-        if index is None:
-            return None
-        subtitle = nodes.subtitle()
-        # Transfer the subsection's attributes to the new subtitle:
-        subtitle.attributes.update(subsection.attributes)
-        # Transfer the contents of the subsection's title to the subtitle:
-        subtitle[:] = subsection[0][:]
-        document = self.document
-        document[:] = (document[:1]       # document title
-                       + [subtitle]
-                       # everything that was before the section:
-                       + document[1:index]
-                       # everything that was in the subsection:
-                       + subsection[1:])
-        return 1
+class SectionSubTitle(TitlePromoter):
 
-    def candidate_index(self):
-        """
-        Find and return the promotion candidate and its index.
+    """
+    This works like document subtitles, but for sections.  For example, ::
 
-        Return (None, None) if no valid candidate was found.
-        """
-        document = self.document
-        index = document.first_child_not_matching_class(
-              nodes.PreBibliographic)
-        if index is None or len(document) > (index + 1) or \
-              not isinstance(document[index], nodes.section):
-            return None, None
-        else:
-            return document[index], index
+        <section>
+            <title>
+                Title
+            <section>
+                <title>
+                    Subtitle
+                ...
 
+    is transformed into ::
 
+        <section>
+            <title>
+                Title
+            <subtitle>
+                Subtitle
+            ...
+
+    For details refer to the docstring of DocTitle.
+    """
+
+    default_priority = 350
+
+    def apply(self):
+        if not getattr(self.document.settings, 'sectsubtitle_xform', 1):
+            return
+        for section in self.document.traverse(lambda n:
+                                              isinstance(n, nodes.section)):
+            # On our way through the node tree, we are deleting
+            # sections, but we call self.promote_subtitle for those
+            # sections nonetheless.  To do: Write a test case which
+            # shows the problem and discuss on Docutils-develop.
+            self.promote_subtitle(section)
+
+
 class DocInfo(Transform):
 
     """
@@ -189,7 +277,7 @@
                         Status
                     <field_body>
                         <paragraph>
-                            $RCSfile: frontmatter.py,v $
+                            $RCSfile$
             ...
 
     After running the bibliographic field list transform, the
@@ -258,11 +346,10 @@
         candidate = document[index]
         if isinstance(candidate, nodes.field_list):
             biblioindex = document.first_child_not_matching_class(
-                  nodes.Titular)
+                  (nodes.Titular, nodes.Decorative))
             nodelist = self.extract_bibliographic(candidate)
             del document[index]         # untransformed field list (candidate)
             document[biblioindex:biblioindex] = nodelist
-        return
 
     def extract_bibliographic(self, field_list):
         docinfo = nodes.docinfo()
@@ -294,7 +381,7 @@
                         raise TransformError
                     title = nodes.title(name, labels[canonical])
                     topics[canonical] = biblioclass(
-                        '', title, CLASS=canonical, *field[1].children)
+                        '', title, classes=[canonical], *field[1].children)
                 else:
                     docinfo.append(biblioclass('', *field[1].children))
             except TransformError:

Modified: Zope/trunk/lib/python/docutils/transforms/misc.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/misc.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/misc.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3155 $
+# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -45,7 +45,6 @@
 
     def apply(self):
         pending = self.startnode
-        class_value = pending.details['class']
         parent = pending.parent
         child = pending
         while parent:
@@ -55,7 +54,7 @@
                 if (isinstance(element, nodes.Invisible) or
                     isinstance(element, nodes.system_message)):
                     continue
-                element.set_class(class_value)
+                element['classes'] += pending.details['class']
                 pending.parent.remove(pending)
                 return
             else:

Modified: Zope/trunk/lib/python/docutils/transforms/parts.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/parts.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/parts.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3199 $
+# Date: $Date: 2005-04-09 03:32:29 +0200 (Sat, 09 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -54,7 +54,7 @@
                 generated = nodes.generated(
                     '', (self.prefix + '.'.join(numbers) + self.suffix
                          +  u'\u00a0' * 3),
-                    CLASS='sectnum')
+                    classes=['sectnum'])
                 title.insert(0, generated)
                 title['auto'] = 1
                 if depth < self.maxdepth:
@@ -84,14 +84,13 @@
         details = self.startnode.details
         if details.has_key('local'):
             startnode = self.startnode.parent.parent
-            # @@@ generate an error if the startnode (directive) not at
-            # section/document top-level? Drag it up until it is?
-            while not isinstance(startnode, nodes.Structural):
+            while not (isinstance(startnode, nodes.section)
+                       or isinstance(startnode, nodes.document)):
+                # find the ToC root: a direct ancestor of startnode
                 startnode = startnode.parent
         else:
             startnode = self.document
-
-        self.toc_id = self.startnode.parent['id']
+        self.toc_id = self.startnode.parent['ids'][0]
         if details.has_key('backlinks'):
             self.backlinks = details['backlinks']
         else:
@@ -117,15 +116,17 @@
             title = section[0]
             auto = title.get('auto')    # May be set by SectNum.
             entrytext = self.copy_and_filter(title)
-            reference = nodes.reference('', '', refid=section['id'],
+            reference = nodes.reference('', '', refid=section['ids'][0],
                                         *entrytext)
             ref_id = self.document.set_id(reference)
             entry = nodes.paragraph('', '', reference)
             item = nodes.list_item('', entry)
-            if self.backlinks == 'entry':
-                title['refid'] = ref_id
-            elif self.backlinks == 'top':
-                title['refid'] = self.toc_id
+            if (self.backlinks in ('entry', 'top') and title.next_node(
+                lambda n: isinstance(n, nodes.reference)) is None):
+                if self.backlinks == 'entry':
+                    title['refid'] = ref_id
+                elif self.backlinks == 'top':
+                    title['refid'] = self.toc_id
             if level < depth:
                 subsects = self.build_contents(section, level)
                 item += subsects
@@ -133,7 +134,7 @@
         if entries:
             contents = nodes.bullet_list('', *entries)
             if auto:
-                contents.set_class('auto-toc')
+                contents['classes'].append('auto-toc')
             return contents
         else:
             return []
@@ -148,7 +149,7 @@
 class ContentsFilter(nodes.TreeCopyVisitor):
 
     def get_entry_text(self):
-        return self.get_tree_copy().get_children()
+        return self.get_tree_copy().children
 
     def visit_citation_reference(self, node):
         raise nodes.SkipNode

Modified: Zope/trunk/lib/python/docutils/transforms/peps.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/peps.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/peps.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3129 $
+# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -46,7 +46,7 @@
             raise DataError('Document tree is empty.')
         header = self.document[0]
         if not isinstance(header, nodes.field_list) or \
-              header.get('class') != 'rfc2822':
+              'rfc2822' not in header['classes']:
             raise DataError('Document does not begin with an RFC-2822 '
                             'header; it is not a PEP.')
         pep = None
@@ -149,10 +149,10 @@
         language = languages.get_language(self.document.settings.language_code)
         name = language.labels['contents']
         title = nodes.title('', name)
-        topic = nodes.topic('', title, CLASS='contents')
+        topic = nodes.topic('', title, classes=['contents'])
         name = nodes.fully_normalize_name(name)
         if not self.document.has_name(name):
-            topic['name'] = name
+            topic['names'].append(name)
         self.document.note_implicit_target(topic)
         pending = nodes.pending(parts.Contents)
         topic += pending
@@ -244,7 +244,7 @@
         node.parent.replace(node, mask_email(node))
 
     def visit_field_list(self, node):
-        if node.hasattr('class') and node['class'] == 'rfc2822':
+        if 'rfc2822' in node['classes']:
             raise nodes.SkipNode
 
     def visit_tgroup(self, node):
@@ -254,7 +254,7 @@
     def visit_colspec(self, node):
         self.entry += 1
         if self.pep_table and self.entry == 2:
-            node['class'] = 'num'
+            node['classes'].append('num')
 
     def visit_row(self, node):
         self.entry = 0
@@ -262,7 +262,7 @@
     def visit_entry(self, node):
         self.entry += 1
         if self.pep_table and self.entry == 2 and len(node) == 1:
-            node['class'] = 'num'
+            node['classes'].append('num')
             p = node[0]
             if isinstance(p, nodes.paragraph) and len(p) == 1:
                 text = p.astext()

Modified: Zope/trunk/lib/python/docutils/transforms/references.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/references.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/references.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3149 $
+# Date: $Date: 2005-03-30 22:51:06 +0200 (Wed, 30 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -16,89 +16,77 @@
 from docutils.transforms import TransformError, Transform
 
 
-indices = xrange(sys.maxint)
+class PropagateTargets(Transform):
 
-
-class ChainedTargets(Transform):
-
     """
-    Attributes "refuri" and "refname" are migrated from the final direct
-    target up the chain of contiguous adjacent internal targets, using
-    `ChainedTargetResolver`.
-    """
+    Propagate empty internal targets to the next element.
 
-    default_priority = 420
+    Given the following nodes::
 
-    def apply(self):
-        visitor = ChainedTargetResolver(self.document)
-        self.document.walk(visitor)
+        <target ids="internal1" names="internal1">
+        <target anonymous="1" ids="id1">
+        <target ids="internal2" names="internal2">
+        <paragraph>
+            This is a test.
 
+    PropagateTargets propagates the ids and names of the internal
+    targets preceding the paragraph to the paragraph itself::
 
-class ChainedTargetResolver(nodes.SparseNodeVisitor):
-
+        <target refid="internal1">
+        <target anonymous="1" refid="id1">
+        <target refid="internal2">
+        <paragraph ids="internal2 id1 internal1" names="internal2 internal1">
+            This is a test.
     """
-    Copy reference attributes up the length of a hyperlink target chain.
 
-    "Chained targets" are multiple adjacent internal hyperlink targets which
-    "point to" an external or indirect target.  After the transform, all
-    chained targets will effectively point to the same place.
+    default_priority = 260
 
-    Given the following ``document`` as input::
+    def apply(self):
+        for target in self.document.internal_targets:
+            if not (len(target) == 0 and
+                    not (target.attributes.has_key('refid') or
+                         target.attributes.has_key('refuri') or
+                         target.attributes.has_key('refname'))):
+                continue
+            next_node = target.next_node(ascend=1)
+            # Do not move names and ids into Invisibles (we'd lose the
+            # attributes) or different Targetables (e.g. footnotes).
+            if (next_node is not None and
+                ((not isinstance(next_node, nodes.Invisible) and
+                  not isinstance(next_node, nodes.Targetable)) or
+                 isinstance(next_node, nodes.target))):
+                next_node['ids'].extend(target['ids'])
+                next_node['names'].extend(target['names'])
+                # Set defaults for next_node.expect_referenced_by_name/id.
+                if not hasattr(next_node, 'expect_referenced_by_name'):
+                    next_node.expect_referenced_by_name = {}
+                if not hasattr(next_node, 'expect_referenced_by_id'):
+                    next_node.expect_referenced_by_id = {}
+                for id in target['ids']:
+                    # Update IDs to node mapping.
+                    self.document.ids[id] = next_node
+                    # If next_node is referenced by id ``id``, this
+                    # target shall be marked as referenced.
+                    next_node.expect_referenced_by_id[id] = target
+                for name in target['names']:
+                    next_node.expect_referenced_by_name[name] = target
+                # If there are any expect_referenced_by_... attributes
+                # in target set, copy them to next_node.
+                next_node.expect_referenced_by_name.update(
+                    getattr(target, 'expect_referenced_by_name', {}))
+                next_node.expect_referenced_by_id.update(
+                    getattr(target, 'expect_referenced_by_id', {}))
+                # Set refid to point to the first former ID of target
+                # which is now an ID of next_node.
+                target['refid'] = target['ids'][0]
+                # Clear ids and names; they have been moved to
+                # next_node.
+                target['ids'] = []
+                target['names'] = []
+                self.document.note_refid(target)
+                self.document.note_internal_target(next_node)
 
-        <document>
-            <target id="a" name="a">
-            <target id="b" name="b">
-            <target id="c" name="c" refuri="http://chained.external.targets">
-            <target id="d" name="d">
-            <paragraph>
-                I'm known as "d".
-            <target id="e" name="e">
-            <target id="id1">
-            <target id="f" name="f" refname="d">
 
-    ``ChainedTargetResolver(document).walk()`` will transform the above into::
-
-        <document>
-            <target id="a" name="a" refuri="http://chained.external.targets">
-            <target id="b" name="b" refuri="http://chained.external.targets">
-            <target id="c" name="c" refuri="http://chained.external.targets">
-            <target id="d" name="d">
-            <paragraph>
-                I'm known as "d".
-            <target id="e" name="e" refname="d">
-            <target id="id1" refname="d">
-            <target id="f" name="f" refname="d">
-    """
-
-    def unknown_visit(self, node):
-        pass
-
-    def visit_target(self, node):
-        if node.hasattr('refuri'):
-            attname = 'refuri'
-            call_if_named = self.document.note_external_target
-        elif node.hasattr('refname'):
-            attname = 'refname'
-            call_if_named = self.document.note_indirect_target
-        elif node.hasattr('refid'):
-            attname = 'refid'
-            call_if_named = None
-        else:
-            return
-        attval = node[attname]
-        index = node.parent.index(node)
-        for i in range(index - 1, -1, -1):
-            sibling = node.parent[i]
-            if not isinstance(sibling, nodes.target) \
-                  or sibling.hasattr('refuri') \
-                  or sibling.hasattr('refname') \
-                  or sibling.hasattr('refid'):
-                break
-            sibling[attname] = attval
-            if sibling.hasattr('name') and call_if_named:
-                call_if_named(sibling)
-
-
 class AnonymousHyperlinks(Transform):
 
     """
@@ -109,8 +97,8 @@
                 internal
             <reference anonymous="1">
                 external
-        <target anonymous="1" id="id1">
-        <target anonymous="1" id="id2" refuri="http://external">
+        <target anonymous="1" ids="id1">
+        <target anonymous="1" ids="id2" refuri="http://external">
 
     Corresponding references are linked via "refid" or resolved via "refuri"::
 
@@ -119,8 +107,8 @@
                 text
             <reference anonymous="1" refuri="http://external">
                 external
-        <target anonymous="1" id="id1">
-        <target anonymous="1" id="id2" refuri="http://external">
+        <target anonymous="1" ids="id1">
+        <target anonymous="1" ids="id2" refuri="http://external">
     """
 
     default_priority = 440
@@ -140,16 +128,28 @@
                 prbid = self.document.set_id(prb)
                 msg.add_backref(prbid)
                 ref.parent.replace(ref, prb)
+            for target in self.document.anonymous_targets:
+                # Assume that all anonymous targets have been
+                # referenced to avoid generating lots of
+                # system_messages.
+                target.referenced = 1
             return
         for ref, target in zip(self.document.anonymous_refs,
                                self.document.anonymous_targets):
-            if target.hasattr('refuri'):
-                ref['refuri'] = target['refuri']
-                ref.resolved = 1
-            else:
-                ref['refid'] = target['id']
-                self.document.note_refid(ref)
             target.referenced = 1
+            while 1:
+                if target.hasattr('refuri'):
+                    ref['refuri'] = target['refuri']
+                    ref.resolved = 1
+                    break
+                else:
+                    if not target['ids']:
+                        # Propagated target.
+                        target = self.document.ids[target['refid']]
+                        continue
+                    ref['refid'] = target['ids'][0]
+                    self.document.note_refid(ref)
+                    break
 
 
 class IndirectHyperlinks(Transform):
@@ -213,20 +213,24 @@
             self.resolve_indirect_references(target)
 
     def resolve_indirect_target(self, target):
-        refname = target['refname']
-        reftarget_id = self.document.nameids.get(refname)
-        if not reftarget_id:
-            # Check the unknown_reference_resolvers
-            for resolver_function in (self.document.transformer
-                                      .unknown_reference_resolvers):
-                if resolver_function(target):
-                    break
-            else:
-                self.nonexistent_indirect_target(target)
-            return
+        refname = target.get('refname')
+        if refname is None:
+            reftarget_id = target['refid']
+        else:
+            reftarget_id = self.document.nameids.get(refname)
+            if not reftarget_id:
+                # Check the unknown_reference_resolvers
+                for resolver_function in \
+                        self.document.transformer.unknown_reference_resolvers:
+                    if resolver_function(target):
+                        break
+                else:
+                    self.nonexistent_indirect_target(target)
+                return
         reftarget = self.document.ids[reftarget_id]
+        reftarget.note_referenced_by(id=reftarget_id)
         if isinstance(reftarget, nodes.target) \
-              and not reftarget.resolved and reftarget.hasattr('refname'):
+               and not reftarget.resolved and reftarget.hasattr('refname'):
             if hasattr(target, 'multiply_indirect'):
                 #and target.multiply_indirect):
                 #del target.multiply_indirect
@@ -237,21 +241,23 @@
             del target.multiply_indirect
         if reftarget.hasattr('refuri'):
             target['refuri'] = reftarget['refuri']
-            if target.hasattr('name'):
+            if target['names']:
                 self.document.note_external_target(target)
+            if target.has_key('refid'):
+                del target['refid']
         elif reftarget.hasattr('refid'):
             target['refid'] = reftarget['refid']
             self.document.note_refid(target)
         else:
-            try:
-                target['refid'] = reftarget['id']
+            if reftarget['ids']:
+                target['refid'] = reftarget_id
                 self.document.note_refid(target)
-            except KeyError:
+            else:
                 self.nonexistent_indirect_target(target)
                 return
-        del target['refname']
+        if refname is not None:
+            del target['refname']
         target.resolved = 1
-        reftarget.referenced = 1
 
     def nonexistent_indirect_target(self, target):
         if self.document.nameids.has_key(target['refname']):
@@ -265,18 +271,19 @@
 
     def indirect_target_error(self, target, explanation):
         naming = ''
-        if target.hasattr('name'):
-            naming = '"%s" ' % target['name']
-            reflist = self.document.refnames.get(target['name'], [])
-        else:
-            reflist = self.document.refids.get(target['id'], [])
-        naming += '(id="%s")' % target['id']
+        reflist = []
+        if target['names']:
+            naming = '"%s" ' % target['names'][0]
+        for name in target['names']:
+            reflist.extend(self.document.refnames.get(name, []))
+        for id in target['ids']:
+            reflist.extend(self.document.refids.get(id, []))
+        naming += '(id="%s")' % target['ids'][0]
         msg = self.document.reporter.error(
               'Indirect hyperlink target %s refers to target "%s", %s.'
-              % (naming, target['refname'], explanation),
-              base_node=target)
+              % (naming, target['refname'], explanation), base_node=target)
         msgid = self.document.set_id(msg)
-        for ref in reflist:
+        for ref in uniq(reflist):
             prb = nodes.problematic(
                   ref.rawsource, ref.rawsource, refid=msgid)
             prbid = self.document.set_id(prb)
@@ -296,43 +303,34 @@
         else:
             return
         attval = target[attname]
-        if target.hasattr('name'):
-            name = target['name']
-            try:
-                reflist = self.document.refnames[name]
-            except KeyError, instance:
-                if target.referenced:
-                    return
-                msg = self.document.reporter.info(
-                      'Indirect hyperlink target "%s" is not referenced.'
-                      % name, base_node=target)
-                target.referenced = 1
-                return
-            delatt = 'refname'
-        else:
-            id = target['id']
-            try:
-                reflist = self.document.refids[id]
-            except KeyError, instance:
-                if target.referenced:
-                    return
-                msg = self.document.reporter.info(
-                      'Indirect hyperlink target id="%s" is not referenced.'
-                      % id, base_node=target)
-                target.referenced = 1
-                return
-            delatt = 'refid'
-        for ref in reflist:
-            if ref.resolved:
-                continue
-            del ref[delatt]
-            ref[attname] = attval
-            if not call_if_named or ref.hasattr('name'):
-                call_method(ref)
-            ref.resolved = 1
-            if isinstance(ref, nodes.target):
-                self.resolve_indirect_references(ref)
-        target.referenced = 1
+        for name in target['names']:
+            reflist = self.document.refnames.get(name, [])
+            if reflist:
+                target.note_referenced_by(name=name)
+            for ref in reflist:
+                if ref.resolved:
+                    continue
+                del ref['refname']
+                ref[attname] = attval
+                if not call_if_named or ref['names']:
+                    call_method(ref)
+                ref.resolved = 1
+                if isinstance(ref, nodes.target):
+                    self.resolve_indirect_references(ref)
+        for id in target['ids']:
+            reflist = self.document.refids.get(id, [])
+            if reflist:
+                target.note_referenced_by(id=id)
+            for ref in reflist:
+                if ref.resolved:
+                    continue
+                del ref['refid']
+                ref[attname] = attval
+                if not call_if_named or ref['names']:
+                    call_method(ref)
+                ref.resolved = 1
+                if isinstance(ref, nodes.target):
+                    self.resolve_indirect_references(ref)
 
 
 class ExternalTargets(Transform):
@@ -357,74 +355,59 @@
 
     def apply(self):
         for target in self.document.external_targets:
-            if target.hasattr('refuri') and target.hasattr('name'):
-                name = target['name']
+            if target.hasattr('refuri'):
                 refuri = target['refuri']
-                try:
-                    reflist = self.document.refnames[name]
-                except KeyError, instance:
-                    # @@@ First clause correct???
-                    if not isinstance(target, nodes.target) or target.referenced:
-                        continue
-                    msg = self.document.reporter.info(
-                          'External hyperlink target "%s" is not referenced.'
-                          % name, base_node=target)
-                    target.referenced = 1
-                    continue
-                for ref in reflist:
-                    if ref.resolved:
-                        continue
-                    del ref['refname']
-                    ref['refuri'] = refuri
-                    ref.resolved = 1
-                target.referenced = 1
+                for name in target['names']:
+                    reflist = self.document.refnames.get(name, [])
+                    if reflist:
+                        target.note_referenced_by(name=name)
+                    for ref in reflist:
+                        if ref.resolved:
+                            continue
+                        del ref['refname']
+                        ref['refuri'] = refuri
+                        ref.resolved = 1
 
 
 class InternalTargets(Transform):
 
-    """
-    Given::
+    default_priority = 660
 
-        <paragraph>
-            <reference refname="direct internal">
-                direct internal
-        <target id="id1" name="direct internal">
+    def apply(self):
+        for target in self.document.internal_targets:
+            self.resolve_reference_ids(target)
 
-    The "refname" attribute is replaced by "refid" linking to the target's
-    "id"::
+    def resolve_reference_ids(self, target):
+        """
+        Given::
 
-        <paragraph>
-            <reference refid="id1">
-                direct internal
-        <target id="id1" name="direct internal">
-    """
+            <paragraph>
+                <reference refname="direct internal">
+                    direct internal
+            <target id="id1" name="direct internal">
 
-    default_priority = 660
+        The "refname" attribute is replaced by "refid" linking to the target's
+        "id"::
 
-    def apply(self):
-        for target in self.document.internal_targets:
-            if target.hasattr('refuri') or target.hasattr('refid') \
-                  or not target.hasattr('name'):
-                continue
-            name = target['name']
-            refid = target['id']
-            try:
-                reflist = self.document.refnames[name]
-            except KeyError, instance:
-                if target.referenced:
-                    continue
-                msg = self.document.reporter.info(
-                      'Internal hyperlink target "%s" is not referenced.'
-                      % name, base_node=target)
-                target.referenced = 1
-                continue
+            <paragraph>
+                <reference refid="id1">
+                    direct internal
+            <target id="id1" name="direct internal">
+        """
+        if target.hasattr('refuri') or target.hasattr('refid') \
+              or not target['names']:
+            return
+        for name in target['names']:
+            refid = self.document.nameids[name]
+            reflist = self.document.refnames.get(name, [])
+            if reflist:
+                target.note_referenced_by(name=name)
             for ref in reflist:
                 if ref.resolved:
                     continue
                 del ref['refname']
                 ref['refid'] = refid
                 ref.resolved = 1
-            target.referenced = 1
 
 
 class Footnotes(Transform):
@@ -532,19 +515,17 @@
                 if not self.document.nameids.has_key(label):
                     break
             footnote.insert(0, nodes.label('', label))
-            if footnote.hasattr('dupname'):
-                continue
-            if footnote.hasattr('name'):
-                name = footnote['name']
+            for name in footnote['names']:
                 for ref in self.document.footnote_refs.get(name, []):
                     ref += nodes.Text(label)
                     ref.delattr('refname')
-                    ref['refid'] = footnote['id']
-                    footnote.add_backref(ref['id'])
+                    assert len(footnote['ids']) == len(ref['ids']) == 1
+                    ref['refid'] = footnote['ids'][0]
+                    footnote.add_backref(ref['ids'][0])
                     self.document.note_refid(ref)
                     ref.resolved = 1
-            else:
-                footnote['name'] = label
+            if not footnote['names'] and not footnote['dupnames']:
+                footnote['names'].append(label)
                 self.document.note_explicit_target(footnote, footnote)
                 self.autofootnote_labels.append(label)
         return startnum
@@ -577,7 +558,8 @@
             footnote = self.document.ids[id]
             ref['refid'] = id
             self.document.note_refid(ref)
-            footnote.add_backref(ref['id'])
+            assert len(ref['ids']) == 1
+            footnote.add_backref(ref['ids'][0])
             ref.resolved = 1
             i += 1
 
@@ -612,9 +594,10 @@
                     ref.parent.replace(ref, prb)
                 break
             footnote = self.document.symbol_footnotes[i]
-            ref['refid'] = footnote['id']
+            assert len(footnote['ids']) == 1
+            ref['refid'] = footnote['ids'][0]
             self.document.note_refid(ref)
-            footnote.add_backref(ref['id'])
+            footnote.add_backref(ref['ids'][0])
             i += 1
 
     def resolve_footnotes_and_citations(self):
@@ -623,24 +606,26 @@
         references.
         """
         for footnote in self.document.footnotes:
-            label = footnote['name']
-            if self.document.footnote_refs.has_key(label):
-                reflist = self.document.footnote_refs[label]
-                self.resolve_references(footnote, reflist)
+            for label in footnote['names']:
+                if self.document.footnote_refs.has_key(label):
+                    reflist = self.document.footnote_refs[label]
+                    self.resolve_references(footnote, reflist)
         for citation in self.document.citations:
-            label = citation['name']
-            if self.document.citation_refs.has_key(label):
-                reflist = self.document.citation_refs[label]
-                self.resolve_references(citation, reflist)
+            for label in citation['names']:
+                if self.document.citation_refs.has_key(label):
+                    reflist = self.document.citation_refs[label]
+                    self.resolve_references(citation, reflist)
 
     def resolve_references(self, note, reflist):
-        id = note['id']
+        assert len(note['ids']) == 1
+        id = note['ids'][0]
         for ref in reflist:
             if ref.resolved:
                 continue
             ref.delattr('refname')
             ref['refid'] = id
-            note.add_backref(ref['id'])
+            assert len(ref['ids']) == 1
+            note.add_backref(ref['ids'][0])
             ref.resolved = 1
         note.resolved = 1
 
@@ -680,7 +665,9 @@
     def apply(self):
         defs = self.document.substitution_defs
         normed = self.document.substitution_names
-        for refname, refs in self.document.substitution_refs.items():
+        subreflist = self.document.substitution_refs.items()
+        subreflist.sort()
+        for refname, refs in subreflist:
             for ref in refs:
                 key = None
                 if defs.has_key(refname):
@@ -715,7 +702,7 @@
                              and isinstance(parent[index + 1], nodes.Text)):
                             parent.replace(parent[index + 1],
                                            parent[index + 1].lstrip())
-                    parent.replace(ref, subdef.get_children())
+                    parent.replace(ref, subdef.children)
         self.document.substitution_refs = None  # release replaced references
 
 
@@ -734,11 +721,12 @@
         notes = {}
         nodelist = []
         for target in self.document.external_targets:
-            name = target.get('name')
-            if not name:
-                print >>sys.stderr, 'no name on target: %r' % target
-                continue
-            refs = self.document.refnames.get(name, [])
+            names = target['names']
+            # Only named targets.
+            assert names
+            refs = []
+            for name in names:
+                refs.extend(self.document.refnames.get(name, []))
             if not refs:
                 continue
             footnote = self.make_target_footnote(target, refs, notes)
@@ -760,14 +748,16 @@
         refuri = target['refuri']
         if notes.has_key(refuri):  # duplicate?
             footnote = notes[refuri]
-            footnote_name = footnote['name']
+            assert len(footnote['names']) == 1
+            footnote_name = footnote['names'][0]
         else:                           # original
             footnote = nodes.footnote()
             footnote_id = self.document.set_id(footnote)
-            # Use a colon; they can't be produced inside names by the parser:
-            footnote_name = 'target_note: ' + footnote_id
+            # Use uppercase letters and a colon; they can't be
+            # produced inside names by the parser.
+            footnote_name = 'TARGET_NOTE: ' + footnote_id
             footnote['auto'] = 1
-            footnote['name'] = footnote_name
+            footnote['names'] = [footnote_name]
             footnote_paragraph = nodes.paragraph()
             footnote_paragraph += nodes.reference('', refuri, refuri=refuri)
             footnote += footnote_paragraph
@@ -786,3 +776,11 @@
                 reflist.insert(0, nodes.Text(' '))
             ref.parent.insert(index, reflist)
         return footnote
+
+
+def uniq(L):
+     r = []
+     for item in L:
+         if not item in r:
+             r.append(item)
+     return r

Modified: Zope/trunk/lib/python/docutils/transforms/universal.py
===================================================================
--- Zope/trunk/lib/python/docutils/transforms/universal.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/transforms/universal.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger, Ueli Schlaepfer
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3186 $
+# Date: $Date: 2005-04-07 21:51:45 +0200 (Thu, 07 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -32,19 +32,16 @@
     default_priority = 820
 
     def apply(self):
-        header = self.generate_header()
-        footer = self.generate_footer()
-        if header or footer:
-            decoration = nodes.decoration()
-            decoration += header
-            decoration += footer
-            document = self.document
-            index = document.first_child_not_matching_class(
-                nodes.PreDecorative)
-            if index is None:
-                document += decoration
-            else:
-                document[index:index] = [decoration]
+        header_nodes = self.generate_header()
+        if header_nodes:
+            decoration = self.document.get_decoration()
+            header = decoration.get_header()
+            header.extend(header_nodes)
+        footer_nodes = self.generate_footer()
+        if footer_nodes:
+            decoration = self.document.get_decoration()
+            footer = decoration.get_footer()
+            footer.extend(footer_nodes)
 
     def generate_header(self):
         return None
@@ -79,9 +76,7 @@
                     nodes.reference('', 'reStructuredText', refuri='http://'
                                     'docutils.sourceforge.net/rst.html'),
                     nodes.Text(' source.\n')])
-            footer = nodes.footer()
-            footer += nodes.paragraph('', '', *text)
-            return footer
+            return [nodes.paragraph('', '', *text)]
         else:
             return None
 
@@ -97,13 +92,13 @@
 
     def apply(self):
         unfiltered = self.document.transform_messages
-        threshold = self.document.reporter['writer'].report_level
+        threshold = self.document.reporter.report_level
         messages = []
         for msg in unfiltered:
             if msg['level'] >= threshold and not msg.parent:
                 messages.append(msg)
         if messages:
-            section = nodes.section(CLASS='system-messages')
+            section = nodes.section(classes=['system-messages'])
             # @@@ get this from the language module?
             section += nodes.title('', 'Docutils System Messages')
             section += messages
@@ -130,7 +125,7 @@
         pass
 
     def visit_system_message(self, node):
-        if node['level'] < self.document.reporter['writer'].report_level:
+        if node['level'] < self.document.reporter.report_level:
             node.parent.remove(node)
 
 
@@ -167,6 +162,21 @@
         if self.document.settings.expose_internals:
             visitor = InternalAttributeExposer(self.document)
             self.document.walk(visitor)
+        # *After* resolving all references, check for unreferenced
+        # targets:
+        for target in self.document.traverse():
+            if isinstance(target, nodes.target) and not target.referenced:
+                if target['names']:
+                    naming = target['names'][0]
+                elif target['ids']:
+                    naming = target['ids'][0]
+                else:
+                    # Hack: Propagated targets always have their refid
+                    # attribute set.
+                    naming = target['refid']
+                self.document.reporter.info(
+                    'Hyperlink target "%s" is not referenced.'
+                    % naming, base_node=target)
 
 
 class FinalCheckVisitor(nodes.SparseNodeVisitor):
@@ -206,7 +216,7 @@
         else:
             del node['refname']
             node['refid'] = id
-            self.document.ids[id].referenced = 1
+            self.document.ids[id].note_referenced_by(id=id)
             node.resolved = 1
 
     visit_footnote_reference = visit_citation_reference = visit_reference

Modified: Zope/trunk/lib/python/docutils/utils.py
===================================================================
--- Zope/trunk/lib/python/docutils/utils.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/utils.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:02 $
+# Revision: $Revision: 3253 $
+# Date: $Date: 2005-04-25 17:08:01 +0200 (Mon, 25 Apr 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -13,6 +13,7 @@
 import sys
 import os
 import os.path
+import warnings
 from types import StringType, UnicodeType
 from docutils import ApplicationError, DataError
 from docutils import frontend, nodes
@@ -39,37 +40,21 @@
     There is typically one Reporter object per process.  A Reporter object is
     instantiated with thresholds for reporting (generating warnings) and
     halting processing (raising exceptions), a switch to turn debug output on
-    or off, and an I/O stream for warnings.  These are stored in the default
-    reporting category, '' (zero-length string).
+    or off, and an I/O stream for warnings.  These are stored as instance
+    attributes.
 
-    Multiple reporting categories [#]_ may be set, each with its own reporting
-    and halting thresholds, debugging switch, and warning stream
-    (collectively a `ConditionSet`).  Categories are hierarchical dotted-name
-    strings that look like attribute references: 'spam', 'spam.eggs',
-    'neeeow.wum.ping'.  The 'spam' category is the ancestor of
-    'spam.bacon.eggs'.  Unset categories inherit stored conditions from their
-    closest ancestor category that has been set.
+    When a system message is generated, its level is compared to the stored
+    thresholds, and a warning or error is generated as appropriate.  Debug
+    messages are produced iff the stored debug switch is on, independently of
+    other thresholds.  Message output is sent to the stored warning stream if
+    not set to ''.
 
-    When a system message is generated, the stored conditions from its
-    category (or ancestor if unset) are retrieved.  The system message level
-    is compared to the thresholds stored in the category, and a warning or
-    error is generated as appropriate.  Debug messages are produced iff the
-    stored debug switch is on.  Message output is sent to the stored warning
-    stream if not set to ''.
-
-    The default category is '' (empty string).  By convention, Writers should
-    retrieve reporting conditions from the 'writer' category (which, unless
-    explicitly set, defaults to the conditions of the default category).
-
     The Reporter class also employs a modified form of the "Observer" pattern
     [GoF95]_ to track system messages generated.  The `attach_observer` method
     should be called before parsing, with a bound method or function which
     accepts system messages.  The observer can be removed with
     `detach_observer`, and another added in its place.
 
-    .. [#] The concept of "categories" was inspired by the log4j project:
-       http://jakarta.apache.org/log4j/.
-
     .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
        Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
        1995.
@@ -81,10 +66,7 @@
     def __init__(self, source, report_level, halt_level, stream=None,
                  debug=0, encoding='ascii', error_handler='replace'):
         """
-        Initialize the `ConditionSet` forthe `Reporter`'s default category.
-
         :Parameters:
-
             - `source`: The path to or description of the source data.
             - `report_level`: The level at or above which warning output will
               be sent to `stream`.
@@ -101,6 +83,23 @@
         self.source = source
         """The path to or description of the source data."""
 
+        self.encoding = encoding
+        """The character encoding for the stderr output."""
+
+        self.error_handler = error_handler
+        """The character encoding error handler."""
+
+        self.debug_flag = debug
+        """Show debug (level=0) system messages?"""
+
+        self.report_level = report_level
+        """The level at or above which warning output will be sent
+        to `self.stream`."""
+
+        self.halt_level = halt_level
+        """The level at or above which `SystemMessage` exceptions
+        will be raised, halting execution."""
+
         if stream is None:
             stream = sys.stderr
         elif type(stream) in (StringType, UnicodeType):
@@ -111,16 +110,9 @@
                 elif type(stream) == UnicodeType:
                     stream = open(stream.encode(), 'w')
 
-        self.encoding = encoding
-        """The character encoding for the stderr output."""
+        self.stream = stream
+        """Where warning output is sent."""
 
-        self.error_handler = error_handler
-        """The character encoding error handler."""
-
-        self.categories = {'': ConditionSet(debug, report_level, halt_level,
-                                            stream)}
-        """Mapping of category names to conditions. Default category is ''."""
-
         self.observers = []
         """List of bound methods or functions to call with each system_message
         created."""
@@ -130,24 +122,16 @@
 
     def set_conditions(self, category, report_level, halt_level,
                        stream=None, debug=0):
+        warnings.warn('docutils.utils.Reporter.set_conditions deprecated; '
+                      'set attributes via configuration settings or directly',
+                      DeprecationWarning, stacklevel=2)
+        self.report_level = report_level
+        self.halt_level = halt_level
         if stream is None:
             stream = sys.stderr
-        self.categories[category] = ConditionSet(debug, report_level,
-                                                 halt_level, stream)
+        self.stream = stream
+        self.debug = debug
 
-    def unset_conditions(self, category):
-        if category and self.categories.has_key(category):
-            del self.categories[category]
-
-    __delitem__ = unset_conditions
-
-    def get_conditions(self, category):
-        while not self.categories.has_key(category):
-            category = category[:category.rfind('.') + 1][:-1]
-        return self.categories[category]
-
-    __getitem__ = get_conditions
-
     def attach_observer(self, observer):
         """
         The `observer` parameter is a function or bound method which takes one
@@ -169,9 +153,6 @@
         Raise an exception or generate a warning if appropriate.
         """
         attributes = kwargs.copy()
-        category = kwargs.get('category', '')
-        if kwargs.has_key('category'):
-            del attributes['category']
         if kwargs.has_key('base_node'):
             source, line = get_source_line(kwargs['base_node'])
             del attributes['base_node']
@@ -183,16 +164,13 @@
         msg = nodes.system_message(message, level=level,
                                    type=self.levels[level],
                                    *children, **attributes)
-        debug, report_level, halt_level, stream = self[category].astuple()
-        if (level >= report_level or debug and level == 0) and stream:
+        if self.stream and (level >= self.report_level
+                            or self.debug_flag and level == 0):
             msgtext = msg.astext().encode(self.encoding, self.error_handler)
-            if category:
-                print >>stream, msgtext, '[%s]' % category
-            else:
-                print >>stream, msgtext
-        if level >= halt_level:
+            print >>self.stream, msgtext
+        if level >= self.halt_level:
             raise SystemMessage(msg, level)
-        if level > 0 or debug:
+        if level > 0 or self.debug_flag:
             self.notify_observers(msg)
         self.max_level = max(level, self.max_level)
         return msg
@@ -203,7 +181,8 @@
         effect on the processing. Level-0 system messages are handled
         separately from the others.
         """
-        return self.system_message(0, *args, **kwargs)
+        if self.debug_flag:
+            return self.system_message(0, *args, **kwargs)
 
     def info(self, *args, **kwargs):
         """
@@ -235,25 +214,6 @@
         return self.system_message(4, *args, **kwargs)
 
 
-class ConditionSet:
-
-    """
-    A set of two thresholds (`report_level` & `halt_level`), a switch
-    (`debug`), and an I/O stream (`stream`), corresponding to one `Reporter`
-    category.
-    """
-
-    def __init__(self, debug, report_level, halt_level, stream):
-        self.debug = debug
-        self.report_level = report_level
-        self.halt_level = halt_level
-        self.stream = stream
-
-    def astuple(self):
-        return (self.debug, self.report_level, self.halt_level,
-                self.stream)
-
-
 class ExtensionOptionError(DataError): pass
 class BadOptionError(ExtensionOptionError): pass
 class BadOptionDataError(ExtensionOptionError): pass
@@ -346,7 +306,7 @@
             options[name] = convertor(value)
         except (ValueError, TypeError), detail:
             raise detail.__class__('(option: "%s"; value: %r)\n%s'
-                                   % (name, value, detail))
+                                   % (name, value, ' '.join(detail.args)))
     return options
 
 

Modified: Zope/trunk/lib/python/docutils/writers/__init__.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/__init__.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/__init__.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 2321 $
+# Date: $Date: 2004-06-20 14:28:08 +0200 (Sun, 20 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/writers/docutils_xml.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/docutils_xml.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/docutils_xml.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 2223 $
+# Date: $Date: 2004-06-05 21:32:15 +0200 (Sat, 05 Jun 2004) $
 # Copyright: This module has been placed in the public domain.
 
 """

Modified: Zope/trunk/lib/python/docutils/writers/html4css1.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/html4css1.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/html4css1.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.7 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3367 $
+# Date: $Date: 2005-05-26 02:44:13 +0200 (Thu, 26 May 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -63,6 +63,20 @@
           ['--initial-header-level'],
           {'choices': '1 2 3 4 5 6'.split(), 'default': '1',
            'metavar': '<level>'}),
+         ('Specify the maximum width (in characters) for one-column field '
+          'names.  Longer field names will span an entire row of the table '
+          'used to render the field list.  Default is 14 characters.  '
+          'Use 0 for "no limit".',
+          ['--field-name-limit'],
+          {'default': 14, 'metavar': '<level>',
+           'validator': frontend.validate_nonnegative_int}),
+         ('Specify the maximum width (in characters) for options in option '
+          'lists.  Longer options will span an entire row of the table used '
+          'to render the option list.  Default is 14 characters.  '
+          'Use 0 for "no limit".',
+          ['--option-limit'],
+          {'default': 14, 'metavar': '<level>',
+           'validator': frontend.validate_nonnegative_int}),
          ('Format for footnote references: one of "superscript" or '
           '"brackets".  Default is "brackets".',
           ['--footnote-references'],
@@ -87,7 +101,12 @@
          ('Omit the XML declaration.  Use with caution.',
           ['--no-xml-declaration'],
           {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
-           'validator': frontend.validate_boolean}),))
+           'validator': frontend.validate_boolean}),
+         ('Scramble email addresses to confuse harvesters.  '
+          'For example, "abc at example.org" will become '
+          '``<a href="mailto:%61%62%63%40...">abc at example dot org</a>``.',
+          ['--cloak-email-addresses'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),))
 
     relative_path_settings = ('stylesheet_path',)
 
@@ -99,10 +118,9 @@
         self.translator_class = HTMLTranslator
 
     def translate(self):
-        visitor = self.translator_class(self.document)
+        self.visitor = visitor = self.translator_class(self.document)
         self.document.walkabout(visitor)
         self.output = visitor.astext()
-        self.visitor = visitor
         for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
                      'body_pre_docinfo', 'docinfo', 'body', 'fragment',
                      'body_suffix'):
@@ -111,7 +129,9 @@
     def assemble_parts(self):
         writers.Writer.assemble_parts(self)
         for part in ('title', 'subtitle', 'docinfo', 'body', 'header',
-                     'footer', 'meta', 'stylesheet', 'fragment'):
+                     'footer', 'meta', 'stylesheet', 'fragment',
+                     'html_prolog', 'html_head', 'html_title', 'html_subtitle',
+                     'html_body'):
             self.parts[part] = ''.join(getattr(self.visitor, part))
 
 
@@ -163,16 +183,15 @@
                ' PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
                ' "http://www.w3.org/TR/xhtml1/DTD/'
                'xhtml1-transitional.dtd">\n')
-    html_head = ('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="%s" '
-                 'lang="%s">\n<head>\n')
-    content_type = ('<meta http-equiv="Content-Type" content="text/html; '
-                    'charset=%s" />\n')
+    head_prefix_template = ('<html xmlns="http://www.w3.org/1999/xhtml"'
+                            ' xml:lang="%s" lang="%s">\n<head>\n')
+    content_type = ('<meta http-equiv="Content-Type"'
+                    ' content="text/html; charset=%s" />\n')
     generator = ('<meta name="generator" content="Docutils %s: '
                  'http://docutils.sourceforge.net/" />\n')
     stylesheet_link = '<link rel="stylesheet" href="%s" type="text/css" />\n'
     embedded_stylesheet = '<style type="text/css">\n\n%s\n</style>\n'
-    named_tags = {'a': 1, 'applet': 1, 'form': 1, 'frame': 1, 'iframe': 1,
-                  'img': 1, 'map': 1}
+    named_tags = ['a', 'applet', 'form', 'frame', 'iframe', 'img', 'map']
     words_and_spaces = re.compile(r'\S+| +|\n')
 
     def __init__(self, document):
@@ -182,14 +201,17 @@
         self.language = languages.get_language(lcode)
         self.meta = [self.content_type % settings.output_encoding,
                      self.generator % docutils.__version__]
-        self.head_prefix = [
-              self.doctype,
-              self.html_head % (lcode, lcode)]
-        self.head_prefix.extend(self.meta)
+        self.head_prefix = []
+        self.html_prolog = []
         if settings.xml_declaration:
-            self.head_prefix.insert(0, self.xml_declaration
+            self.head_prefix.append(self.xml_declaration
                                     % settings.output_encoding)
-        self.head = []
+            # encoding not interpolated:
+            self.html_prolog.append(self.xml_declaration)
+        self.head_prefix.extend([self.doctype,
+                                 self.head_prefix_template % (lcode, lcode)])
+        self.html_prolog.append(self.doctype)
+        self.head = self.meta[:]
         if settings.embed_stylesheet:
             stylesheet = utils.get_stylesheet_reference(settings,
                 os.path.join(os.getcwd(), 'dummy'))
@@ -199,7 +221,8 @@
         else:
             stylesheet = utils.get_stylesheet_reference(settings)
             if stylesheet:
-                self.stylesheet = [self.stylesheet_link % stylesheet]
+                self.stylesheet = [self.stylesheet_link
+                                   % self.encode(stylesheet)]
             else:
                 self.stylesheet = []
         self.body_prefix = ['</head>\n<body>\n']
@@ -215,7 +238,7 @@
         # A heterogenous stack used in conjunction with the tree traversal.
         # Make sure that the pops correspond to the pushes:
         self.context = []
-        self.topic_class = ''
+        self.topic_classes = []
         self.colspecs = []
         self.compact_p = 1
         self.compact_simple = None
@@ -225,7 +248,12 @@
         self.subtitle = []
         self.header = []
         self.footer = []
+        self.html_head = [self.content_type] # charset not interpolated
+        self.html_title = []
+        self.html_subtitle = []
+        self.html_body = []
         self.in_document_title = 0
+        self.in_mailto = 0
 
     def astext(self):
         return ''.join(self.head_prefix + self.head
@@ -245,6 +273,20 @@
         text = text.replace(u'\u00a0', "&nbsp;")
         return text
 
+    def cloak_mailto(self, uri):
+        """Try to hide a mailto: URL from harvesters."""
+        addr = uri.split(':', 1)[1]
+        if '?' in addr:
+            addr, query = addr.split('?', 1)
+            query = '?' + query
+        else:
+            query = ''
+        escaped = ['%%%02X' % ord(c) for c in addr]
+        return 'mailto:%s%s' % (''.join(escaped), query)
+
+    def cloak_email(self, addr):
+        return addr.replace('@', ' at ').replace('.', ' dot ')
+
     def attval(self, text,
                whitespace=re.compile('[\n\r\t\v\f]')):
         """Cleanse, HTML encode, and return attribute value text."""
@@ -256,17 +298,21 @@
         are extracted), tag name, and optional attributes.
         """
         tagname = tagname.lower()
+        prefix = []
         atts = {}
         for (name, value) in attributes.items():
             atts[name.lower()] = value
-        for att in ('class',):          # append to node attribute
-            if node.has_key(att) or atts.has_key(att):
-                atts[att] = \
-                      (node.get(att, '') + ' ' + atts.get(att, '')).strip()
-        for att in ('id',):             # node attribute overrides
-            if node.has_key(att):
-                atts[att] = node[att]
-        if atts.has_key('id') and self.named_tags.has_key(tagname):
+        classes = node.get('classes', [])
+        if atts.has_key('class'):
+            classes.append(atts['class'])
+        if classes:
+            atts['class'] = ' '.join(classes)
+        assert not atts.has_key('id')
+        if node.get('ids'):
+            atts['id'] = node['ids'][0]
+            for id in node['ids'][1:]:
+                prefix.append('<span id="%s"></span>' % id)
+        if atts.has_key('id') and tagname in self.named_tags:
             atts['name'] = atts['id']   # for compatibility with old browsers
         attlist = atts.items()
         attlist.sort()
@@ -285,19 +331,23 @@
                 except TypeError:       # for Python 2.1 compatibility:
                     uval = unicode(str(value))
                 parts.append('%s="%s"' % (name.lower(), self.attval(uval)))
-        return '<%s%s>%s' % (' '.join(parts), infix, suffix)
+        return ''.join(prefix) + '<%s%s>' % (' '.join(parts), infix) + suffix
 
     def emptytag(self, node, tagname, suffix='\n', **attributes):
         """Construct and return an XML-compatible empty tag."""
         return self.starttag(node, tagname, suffix, infix=' /', **attributes)
 
     def set_first_last(self, node):
-        if len(node):
-            node[0].set_class('first')
-            node[-1].set_class('last')
+        children = [n for n in node if not isinstance(n, nodes.Invisible)]
+        if children:
+            children[0]['classes'].append('first')
+            children[-1]['classes'].append('last')
 
     def visit_Text(self, node):
-        self.body.append(self.encode(node.astext()))
+        text = node.astext()
+        if self.in_mailto and self.settings.cloak_email_addresses:
+            text = self.cloak_email(text)
+        self.body.append(self.encode(text))
 
     def depart_Text(self, node):
         pass
@@ -389,7 +439,7 @@
         self.compact_p = None
         self.compact_simple = (self.settings.compact_lists and
                                (self.compact_simple
-                                or self.topic_class == 'contents'
+                                or self.topic_classes == ['contents']
                                 or self.check_simple_list(node)))
         if self.compact_simple and not old_compact_simple:
             atts['class'] = 'simple'
@@ -425,14 +475,9 @@
                          '</tbody>\n</table>\n')
 
     def visit_citation_reference(self, node):
-        href = ''
-        if node.has_key('refid'):
-            href = '#' + node['refid']
-        elif node.has_key('refname'):
-            href = '#' + self.document.nameids[node['refname']]
-        self.body.append(self.starttag(node, 'a', '[',
-                                       CLASS='citation-reference',
-                                       **(href and {'href': href} or {})))
+        href = '#' + node['refid']
+        self.body.append(self.starttag(
+            node, 'a', '[', CLASS='citation-reference', href=href))
 
     def depart_citation_reference(self, node):
         self.body.append(']</a>')
@@ -446,6 +491,8 @@
 
     def visit_colspec(self, node):
         self.colspecs.append(node)
+        # "stubs" list is an attribute of the tgroup element:
+        node.parent.stubs.append(node.attributes.get('stub'))
 
     def depart_colspec(self, node):
         pass
@@ -470,10 +517,10 @@
     def visit_compound(self, node):
         self.body.append(self.starttag(node, 'div', CLASS='compound'))
         if len(node) > 1:
-            node[0].set_class('compound-first')
-            node[-1].set_class('compound-last')
+            node[0]['classes'].append('compound-first')
+            node[-1]['classes'].append('compound-last')
             for child in node[1:-1]:
-                child.set_class('compound-middle')
+                child['classes'].append('compound-middle')
 
     def depart_compound(self, node):
         self.body.append('</div>\n')
@@ -562,9 +609,9 @@
                          % self.language.labels[name])
         if len(node):
             if isinstance(node[0], nodes.Element):
-                node[0].set_class('first')
+                node[0]['classes'].append('first')
             if isinstance(node[-1], nodes.Element):
-                node[-1].set_class('last')
+                node[-1]['classes'].append('last')
 
     def depart_docinfo_item(self):
         self.body.append('</td></tr>\n')
@@ -579,12 +626,17 @@
         # empty or untitled document?
         if not len(node) or not isinstance(node[0], nodes.title):
             # for XHTML conformance, modulo IE6 appeasement:
-            self.head.insert(0, '<title></title>\n')
+            self.head.append('<title></title>\n')
 
     def depart_document(self, node):
         self.fragment.extend(self.body)
         self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
         self.body_suffix.insert(0, '</div>\n')
+        # skip content-type meta tag with interpolated charset value:
+        self.html_head.extend(self.head[1:])
+        self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+                              + self.docinfo + self.body
+                              + self.body_suffix[:-1])
 
     def visit_emphasis(self, node):
         self.body.append('<em>')
@@ -593,15 +645,24 @@
         self.body.append('</em>')
 
     def visit_entry(self, node):
+        atts = {'class': []}
         if isinstance(node.parent.parent, nodes.thead):
+            atts['class'].append('head')
+        if node.parent.parent.parent.stubs[node.parent.column]:
+            # "stubs" list is an attribute of the tgroup element
+            atts['class'].append('stub')
+        if atts['class']:
             tagname = 'th'
+            atts['class'] = ' '.join(atts['class'])
         else:
             tagname = 'td'
-        atts = {}
+            del atts['class']
+        node.parent.column += 1
         if node.has_key('morerows'):
             atts['rowspan'] = node['morerows'] + 1
         if node.has_key('morecols'):
             atts['colspan'] = node['morecols'] + 1
+            node.parent.column += node['morecols']
         self.body.append(self.starttag(node, tagname, '', **atts))
         self.context.append('</%s>\n' % tagname.lower())
         if len(node) == 0:              # empty cell
@@ -629,7 +690,7 @@
         self.compact_p = None
         self.compact_simple = (self.settings.compact_lists and
                                (self.compact_simple
-                                or self.topic_class == 'contents'
+                                or self.topic_classes == ['contents']
                                 or self.check_simple_list(node)))
         if self.compact_simple and not old_compact_simple:
             atts['class'] = (atts.get('class', '') + ' simple').strip()
@@ -675,7 +736,8 @@
             atts['class'] = 'docinfo-name'
         else:
             atts['class'] = 'field-name'
-        if len(node.astext()) > 14:
+        if ( self.settings.field_name_limit
+             and len(node.astext()) > self.settings.field_name_limit):
             atts['colspan'] = 2
             self.context.append('</tr>\n<tr><td>&nbsp;</td>')
         else:
@@ -690,6 +752,8 @@
         atts = {'class': 'figure'}
         if node.get('width'):
             atts['style'] = 'width: %spx' % node['width']
+        if node.get('align'):
+            atts['align'] = node['align']
         self.body.append(self.starttag(node, 'div', **atts))
 
     def depart_figure(self, node):
@@ -700,9 +764,10 @@
 
     def depart_footer(self, node):
         start = self.context.pop()
-        footer = (['<hr class="docutils footer" />\n',
-                   self.starttag(node, 'div', CLASS='footer')]
-                  + self.body[start:] + ['</div>\n'])
+        footer = [self.starttag(node, 'div', CLASS='footer'),
+                  '<hr class="footer" />\n']
+        footer.extend(self.body[start:])
+        footer.append('\n</div>\n')
         self.footer.extend(footer)
         self.body_suffix[:0] = footer
         del self.body[start:]
@@ -718,12 +783,13 @@
 
     def footnote_backrefs(self, node):
         backlinks = []
-        if self.settings.footnote_backlinks and node.hasattr('backrefs'):
-            backrefs = node['backrefs']
+        backrefs = node['backrefs']
+        if self.settings.footnote_backlinks and backrefs:
             if len(backrefs) == 1:
                 self.context.append('')
-                self.context.append('<a class="fn-backref" href="#%s" '
-                                    'name="%s">' % (backrefs[0], node['id']))
+                self.context.append(
+                    '<a class="fn-backref" href="#%s" name="%s">'
+                    % (backrefs[0], node['ids'][0]))
             else:
                 i = 1
                 for backref in backrefs:
@@ -731,41 +797,34 @@
                                      % (backref, i))
                     i += 1
                 self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
-                self.context.append('<a name="%s">' % node['id'])
+                self.context.append('<a name="%s">' % node['ids'][0])
         else:
             self.context.append('')
-            self.context.append('<a name="%s">' % node['id'])
+            self.context.append('<a name="%s">' % node['ids'][0])
         # If the node does not only consist of a label.
         if len(node) > 1:
             # If there are preceding backlinks, we do not set class
             # 'first', because we need to retain the top-margin.
             if not backlinks:
-                node[1].set_class('first')
-            node[-1].set_class('last')
+                node[1]['classes'].append('first')
+            node[-1]['classes'].append('last')
 
     def depart_footnote(self, node):
         self.body.append('</td></tr>\n'
                          '</tbody>\n</table>\n')
 
     def visit_footnote_reference(self, node):
-        href = ''
-        if node.has_key('refid'):
-            href = '#' + node['refid']
-        elif node.has_key('refname'):
-            href = '#' + self.document.nameids[node['refname']]
+        href = '#' + node['refid']
         format = self.settings.footnote_references
         if format == 'brackets':
             suffix = '['
             self.context.append(']')
-        elif format == 'superscript':
+        else:
+            assert format == 'superscript'
             suffix = '<sup>'
             self.context.append('</sup>')
-        else:                           # shouldn't happen
-            suffix = '???'
-            self.content.append('???')
         self.body.append(self.starttag(node, 'a', suffix,
-                                       CLASS='footnote-reference',
-                                       **(href and {'href': href} or {})))
+                                       CLASS='footnote-reference', href=href))
 
     def depart_footnote_reference(self, node):
         self.body.append(self.context.pop() + '</a>')
@@ -783,9 +842,9 @@
         start = self.context.pop()
         header = [self.starttag(node, 'div', CLASS='header')]
         header.extend(self.body[start:])
-        header.append('<hr class="docutils header"/>\n</div>\n')
+        header.append('\n<hr class="header"/>\n</div>\n')
         self.body_prefix.extend(header)
-        self.header = header
+        self.header.extend(header)
         del self.body[start:]
 
     def visit_hint(self, node):
@@ -795,9 +854,9 @@
         self.depart_admonition()
 
     def visit_image(self, node):
-        atts = node.attributes.copy()
-        if atts.has_key('class'):
-            del atts['class']           # prevent duplication with node attrs
+        atts = node.non_default_attributes()
+        if atts.has_key('classes'):
+            del atts['classes']         # prevent duplication with node attrs
         atts['src'] = atts['uri']
         del atts['uri']
         if atts.has_key('scale'):
@@ -832,9 +891,8 @@
         self.body.append(self.emptytag(node, 'img', '', **atts))
 
     def image_div_atts(self, image_node):
-        div_atts = {'class': 'image'}
-        if image_node.attributes.has_key('class'):
-            div_atts['class'] += ' ' + image_node.attributes['class']
+        div_atts = {}
+        div_atts['class'] = ' '.join(['image'] + image_node['classes'])
         if image_node.attributes.has_key('align'):
             div_atts['align'] = self.attval(image_node.attributes['align'])
             div_atts['class'] += ' align-%s' % div_atts['align']
@@ -885,14 +943,15 @@
     def visit_list_item(self, node):
         self.body.append(self.starttag(node, 'li', ''))
         if len(node):
-            node[0].set_class('first')
+            node[0]['classes'].append('first')
 
     def depart_list_item(self, node):
         self.body.append('</li>\n')
 
     def visit_literal(self, node):
         """Process text to prevent tokens from wrapping."""
-        self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal'))
+        self.body.append(
+            self.starttag(node, 'tt', '', CLASS='docutils literal'))
         text = node.astext()
         for token in self.words_and_spaces.findall(text):
             if token.strip():
@@ -916,7 +975,7 @@
         self.body.append('\n</pre>\n')
 
     def visit_meta(self, node):
-        meta = self.emptytag(node, 'meta', **node.attributes)
+        meta = self.emptytag(node, 'meta', **node.non_default_attributes())
         self.add_meta(meta)
 
     def depart_meta(self, node):
@@ -935,8 +994,10 @@
     def visit_option(self, node):
         if self.context[-1]:
             self.body.append(', ')
+        self.body.append(self.starttag(node, 'span', '', CLASS='option'))
 
     def depart_option(self, node):
+        self.body.append('</span>')
         self.context[-1] += 1
 
     def visit_option_argument(self, node):
@@ -948,12 +1009,14 @@
 
     def visit_option_group(self, node):
         atts = {}
-        if len(node.astext()) > 14:
+        if ( self.settings.option_limit
+             and len(node.astext()) > self.settings.option_limit):
             atts['colspan'] = 2
             self.context.append('</tr>\n<tr><td>&nbsp;</td>')
         else:
             self.context.append('')
-        self.body.append(self.starttag(node, 'td', **atts))
+        self.body.append(
+            self.starttag(node, 'td', CLASS='option-group', **atts))
         self.body.append('<kbd>')
         self.context.append(0)          # count number of options
 
@@ -980,10 +1043,10 @@
         self.body.append('</tr>\n')
 
     def visit_option_string(self, node):
-        self.body.append(self.starttag(node, 'span', '', CLASS='option'))
+        pass
 
     def depart_option_string(self, node):
-        self.body.append('</span>')
+        pass
 
     def visit_organization(self, node):
         self.visit_docinfo_item(node, 'organization')
@@ -999,12 +1062,16 @@
             isinstance(node.parent, nodes.compound)):
             # Never compact paragraphs in document or compound.
             return 0
-        if ((node.attributes in ({}, {'class': 'first'}, {'class': 'last'},
-                                 {'class': 'first last'})) and
-            (self.compact_simple or
-             self.compact_p and (len(node.parent) == 1 or
-                                 len(node.parent) == 2 and
-                                 isinstance(node.parent[0], nodes.label)))):
+        for key, value in node.attlist():
+            if (node.is_not_default(key) and
+                not (key == 'classes' and value in
+                     ([], ['first'], ['last'], ['first', 'last']))):
+                # Attribute which needs to survive.
+                return 0
+        if (self.compact_simple or
+            self.compact_p and (len(node.parent) == 1 or
+                                len(node.parent) == 2 and
+                                isinstance(node.parent[0], nodes.label))):
             return 1
         return 0
 
@@ -1021,7 +1088,7 @@
     def visit_problematic(self, node):
         if node.hasattr('refid'):
             self.body.append('<a href="#%s" name="%s">' % (node['refid'],
-                                                           node['id']))
+                                                           node['ids'][0]))
             self.context.append('</a>')
         else:
             self.context.append('')
@@ -1033,12 +1100,11 @@
 
     def visit_raw(self, node):
         if 'html' in node.get('format', '').split():
-            add_class = node.attributes.get('class') is not None
             t = isinstance(node.parent, nodes.TextElement) and 'span' or 'div'
-            if add_class:
+            if node['classes']:
                 self.body.append(self.starttag(node, t, suffix=''))
             self.body.append(node.astext())
-            if add_class:
+            if node['classes']:
                 self.body.append('</%s>' % t)
         # Keep non-HTML raw text out of output:
         raise nodes.SkipNode
@@ -1052,19 +1118,23 @@
             div_atts['class'] += ' image-reference'
             self.body.append(self.starttag({}, 'div', '', **div_atts))
             self.context.append('</div>\n')
-        href = ''
         if node.has_key('refuri'):
             href = node['refuri']
-        elif node.has_key('refid'):
+            if ( self.settings.cloak_email_addresses
+                 and href.startswith('mailto:')):
+                href = self.cloak_mailto(href)
+                self.in_mailto = 1
+        else:
+            assert node.has_key('refid'), \
+                   'References must have "refuri" or "refid" attribute.'
             href = '#' + node['refid']
-        elif node.has_key('refname'):
-            href = '#' + self.document.nameids[node['refname']]
         self.body.append(self.starttag(node, 'a', '', CLASS='reference',
-                                       **(href and {'href': href} or {})))
+                                       href=href))
 
     def depart_reference(self, node):
         self.body.append('</a>')
         self.body.append(self.context.pop())
+        self.in_mailto = 0
 
     def visit_revision(self, node):
         self.visit_docinfo_item(node, 'revision', meta=None)
@@ -1074,6 +1144,7 @@
 
     def visit_row(self, node):
         self.body.append(self.starttag(node, 'tr', ''))
+        node.column = 0
 
     def depart_row(self, node):
         self.body.append('</tr>\n')
@@ -1135,6 +1206,12 @@
             self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
             self.context.append('</h2>\n')
             self.in_document_title = len(self.body)
+        elif isinstance(node.parent, nodes.section):
+            tag = 'h%s' % (self.section_level + self.initial_header_level - 1)
+            self.body.append(
+                self.starttag(node, tag, '', CLASS='section-subtitle') +
+                self.starttag({}, 'span', '', CLASS='section-subtitle'))
+            self.context.append('</span></%s>\n' % tag)
 
     def depart_subtitle(self, node):
         self.body.append(self.context.pop())
@@ -1142,6 +1219,7 @@
             self.subtitle = self.body[self.in_document_title:-1]
             self.in_document_title = 0
             self.body_pre_docinfo.extend(self.body)
+            self.html_subtitle.extend(self.body)
             del self.body[:]
 
     def visit_superscript(self, node):
@@ -1151,16 +1229,16 @@
         self.body.append('</sup>')
 
     def visit_system_message(self, node):
-        if node['level'] < self.document.reporter['writer'].report_level:
+        if node['level'] < self.document.reporter.report_level:
             # Level is too low to display:
             raise nodes.SkipNode
         self.body.append(self.starttag(node, 'div', CLASS='system-message'))
         self.body.append('<p class="system-message-title">')
         attr = {}
         backref_text = ''
-        if node.hasattr('id'):
-            attr['name'] = node['id']
-        if node.hasattr('backrefs'):
+        if node['ids']:
+            attr['name'] = node['ids'][0]
+        if len(node['backrefs']):
             backrefs = node['backrefs']
             if len(backrefs) == 1:
                 backref_text = ('; <em><a href="#%s">backlink</a></em>'
@@ -1200,8 +1278,8 @@
     def visit_target(self, node):
         if not (node.has_key('refuri') or node.has_key('refid')
                 or node.has_key('refname')):
-            self.body.append(self.starttag(node, 'a', '', CLASS='target'))
-            self.context.append('</a>')
+            self.body.append(self.starttag(node, 'span', '', CLASS='target'))
+            self.context.append('</span>')
         else:
             self.context.append('')
 
@@ -1231,6 +1309,7 @@
         self.body.append(self.starttag(node, 'colgroup'))
         # Appended by thead or tbody:
         self.context.append('</colgroup>\n')
+        node.stubs = []
 
     def depart_tgroup(self, node):
         pass
@@ -1273,6 +1352,7 @@
             check_id = 1
             close_tag = '</caption>\n'
         elif self.section_level == 0:
+            assert node.parent is self.document
             # document title
             self.head.append('<title>%s</title>\n'
                              % self.encode(node.astext()))
@@ -1280,21 +1360,26 @@
             self.context.append('</h1>\n')
             self.in_document_title = len(self.body)
         else:
+            assert isinstance(node.parent, nodes.section)
             h_level = self.section_level + self.initial_header_level - 1
+            atts = {}
+            if (len(node.parent) >= 2 and
+                isinstance(node.parent[1], nodes.subtitle)):
+                atts['CLASS'] = 'with-subtitle'
             self.body.append(
-                  self.starttag(node, 'h%s' % h_level, ''))
+                  self.starttag(node, 'h%s' % h_level, '', **atts))
             atts = {}
-            if node.parent.hasattr('id'):
-                atts['name'] = node.parent['id']
+            if node.parent['ids']:
+                atts['name'] = node.parent['ids'][0]
             if node.hasattr('refid'):
                 atts['class'] = 'toc-backref'
                 atts['href'] = '#' + node['refid']
             self.body.append(self.starttag({}, 'a', '', **atts))
             self.context.append('</a></h%s>\n' % (h_level))
         if check_id:
-            if node.parent.hasattr('id'):
+            if node.parent['ids']:
                 self.body.append(
-                    self.starttag({}, 'a', '', name=node.parent['id']))
+                    self.starttag({}, 'a', '', name=node.parent['ids'][0]))
                 self.context.append('</a>' + close_tag)
             else:
                 self.context.append(close_tag)
@@ -1305,6 +1390,7 @@
             self.title = self.body[self.in_document_title:-1]
             self.in_document_title = 0
             self.body_pre_docinfo.extend(self.body)
+            self.html_title.extend(self.body)
             del self.body[:]
 
     def visit_title_reference(self, node):
@@ -1315,11 +1401,11 @@
 
     def visit_topic(self, node):
         self.body.append(self.starttag(node, 'div', CLASS='topic'))
-        self.topic_class = node.get('class')
+        self.topic_classes = node['classes']
 
     def depart_topic(self, node):
         self.body.append('</div>\n')
-        self.topic_class = ''
+        self.topic_classes = []
 
     def visit_transition(self, node):
         self.body.append(self.emptytag(node, 'hr', CLASS='docutils'))
@@ -1364,7 +1450,7 @@
 
     def visit_list_item(self, node):
         children = []
-        for child in node.get_children():
+        for child in node.children:
             if not isinstance(child, nodes.Invisible):
                 children.append(child)
         if (children and isinstance(children[0], nodes.paragraph)

Modified: Zope/trunk/lib/python/docutils/writers/latex2e.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/latex2e.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/latex2e.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,8 +1,8 @@
 """
 :Author: Engelbert Gruber
 :Contact: grubert at users.sourceforge.net
-:Revision: $Revision: 1.1.2.7 $
-:Date: $Date: 2005/01/07 13:26:06 $
+:Revision: $Revision: 3367 $
+:Date: $Date: 2005-05-26 02:44:13 +0200 (Thu, 26 May 2005) $
 :Copyright: This module has been placed in the public domain.
 
 LaTeX2e document tree Writer.
@@ -349,7 +349,7 @@
             return self._deepest_section
 
 class Table:
-    """ Manage a table while traversing. 
+    """ Manage a table while traversing.
         Maybe change to a mixin defining the visit/departs, but then
         class Table internal variables are in the Translator.
     """
@@ -381,7 +381,7 @@
         return ''
     def get_latex_type(self):
         return self._latex_type
-    
+
     def set(self,attr,value):
         self._attrs[attr] = value
     def get(self,attr):
@@ -442,7 +442,7 @@
         return latex_table_spec+bar
 
     def get_column_width(self):
-        """ return columnwidth for current cell (not multicell) 
+        """ return columnwidth for current cell (not multicell)
         """
         return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
 
@@ -471,7 +471,7 @@
         for i in range(len(self._rowspan)):
             if (self._rowspan[i]>0):
                 self._rowspan[i] -= 1
-        
+
         if self._table_style == 'standard':
             rowspans = []
             for i in range(len(self._rowspan)):
@@ -507,7 +507,7 @@
     def visit_entry(self):
         self._cell_in_row += 1
 
-        
+
 class LaTeXTranslator(nodes.NodeVisitor):
 
     # When options are given to the documentclass, latex will pass them
@@ -664,21 +664,24 @@
         # NOTE: Latex wants a date and an author, rst puts this into
         #   docinfo, so normally we donot want latex author/date handling.
         # latex article has its own handling of date and author, deactivate.
+        # So we always emit \title{...} \author{...} \date{...}, even if the
+        # "..." are empty strings.
         self.head = [ ]
-        if not self.use_latex_docinfo:
-            self.head.extend( [ '\\author{}\n', '\\date{}\n' ] )
+        # separate title, so we can appen subtitle.
+        self.title = ''
+        # if use_latex_docinfo: collects lists of author/organization/contact/address lines
+        self.author_stack = []
+        self.date = ''
+
         self.body_prefix = ['\\raggedbottom\n']
-        # separate title, so we can appen subtitle.
-        self.title = ""
         self.body = []
         self.body_suffix = ['\n']
         self.section_level = 0
         self.context = []
-        self.topic_class = ''
+        self.topic_classes = []
         # column specification for tables
         self.table_caption = None
-        # do we have one or more authors
-        self.author_stack = None
+        
         # Flags to encode
         # ---------------
         # verbatim: to tell encode not to encode.
@@ -878,15 +881,19 @@
         return self.encode(whitespace.sub(' ', text))
 
     def astext(self):
-        if self.pdfinfo:
+        if self.pdfinfo is not None:
             if self.pdfauthor:
                 self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
+        if self.pdfinfo:
             pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
         else:
             pdfinfo = ''
-        title = '\\title{%s}\n' % self.title
-        return ''.join(self.head_prefix + [title]
-                        + self.head + [pdfinfo]
+        head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
+               (self.title,
+                ' \\and\n'.join(['~\\\\\n'.join(author_lines)
+                                 for author_lines in self.author_stack]),
+                self.date)
+        return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
                         + self.body_prefix  + self.body + self.body_suffix)
 
     def visit_Text(self, node):
@@ -927,14 +934,10 @@
 
     def visit_authors(self, node):
         # not used: visit_author is called anyway for each author.
-        if self.use_latex_docinfo:
-            self.author_stack = []
+        pass
 
     def depart_authors(self, node):
-        if self.use_latex_docinfo:
-            self.head.append('\\author{%s}\n' % \
-                ' \\and '.join(self.author_stack) )
-            self.author_stack = None
+        pass
 
     def visit_block_quote(self, node):
         self.body.append( '\\begin{quote}\n')
@@ -943,14 +946,14 @@
         self.body.append( '\\end{quote}\n')
 
     def visit_bullet_list(self, node):
-        if self.topic_class == 'contents':
+        if self.topic_classes == ['contents']:
             if not self.use_latex_toc:
                 self.body.append( '\\begin{list}{}{}\n' )
         else:
             self.body.append( '\\begin{itemize}\n' )
 
     def depart_bullet_list(self, node):
-        if self.topic_class == 'contents':
+        if self.topic_classes == ['contents']:
             if not self.use_latex_toc:
                 self.body.append( '\\end{list}\n' )
         else:
@@ -998,7 +1001,8 @@
             self.context.append(len(self.body))
         else:
             self.body.append('\\begin{figure}[b]')
-            self.body.append('\\hypertarget{%s}' % node['id'])
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}' % id)
 
     def depart_citation(self, node):
         if self._use_latex_citations:
@@ -1128,15 +1132,23 @@
                     self.pdfauthor = self.attval(node.astext())
                 else:
                     self.pdfauthor += self.author_separator + self.attval(node.astext())
-            if self.use_latex_docinfo:
-                if self.author_stack == None:
-                    self.head.append('\\author{%s}\n' % self.attval(node.astext()))
+        if self.use_latex_docinfo:
+            if name in ('author', 'organization', 'contact', 'address'):
+                # We attach these to the last author.  If any of them precedes
+                # the first author, put them in a separate "author" group (for
+                # no better semantics).
+                if name == 'author' or not self.author_stack:
+                    self.author_stack.append([])
+                if name == 'address':   # newlines are meaningful
+                    self.insert_newline = 1
+                    text = self.encode(node.astext())
+                    self.insert_newline = 0
                 else:
-                    self.author_stack.append( self.attval(node.astext()) )
+                    text = self.attval(node.astext())
+                self.author_stack[-1].append(text)
                 raise nodes.SkipNode
-        elif name == 'date':
-            if self.use_latex_docinfo:
-                self.head.append('\\date{%s}\n' % self.attval(node.astext()))
+            elif name == 'date':
+                self.date = self.attval(node.astext())
                 raise nodes.SkipNode
         self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
         if name == 'address':
@@ -1169,7 +1181,7 @@
     def visit_document(self, node):
         self.body_prefix.append('\\begin{document}\n')
         # titled document?
-        if len(node) and isinstance(node[0], nodes.title):
+        if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
             self.body_prefix.append('\\maketitle\n\n')
             # alternative use titlepage environment.
             # \begin{titlepage}
@@ -1186,7 +1198,7 @@
             for bi in self._bibitems:
                 self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], bi[0], bi[1]))
             self.body.append('\\end{thebibliography}\n')
-            
+
         self.body_suffix.append('\\end{document}\n')
 
     def visit_emphasis(self, node):
@@ -1204,7 +1216,10 @@
             # if the firstrow is a multirow, this actually is the second row.
             # this gets hairy if rowspans follow each other.
             if self.active_table.get_rowspan(0):
-                self.body.append(' & ')
+                count = 0
+                while self.active_table.get_rowspan(count):
+                    count += 1
+                    self.body.append(' & ')
                 self.active_table.visit_entry() # increment cell count
         else:
             self.body.append(' & ')
@@ -1384,7 +1399,8 @@
             self.body.append('{')
         else:
             self.body.append('\\begin{figure}[b]')
-            self.body.append('\\hypertarget{%s}' % node['id'])
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}' % id)
 
     def depart_footnote(self, node):
         if self.use_latex_footnotes:
@@ -1692,7 +1708,7 @@
 
     def visit_paragraph(self, node):
         index = node.parent.index(node)
-        if not (self.topic_class == 'contents' or
+        if not (self.topic_classes == ['contents'] or
                 (isinstance(node.parent, nodes.compound) and
                  index > 0 and
                  not isinstance(node.parent[index - 1], nodes.paragraph) and
@@ -1799,17 +1815,19 @@
         if isinstance(node.parent, nodes.sidebar):
             self.body.append('~\\\\\n\\textbf{')
             self.context.append('}\n\\smallskip\n')
-        else:
+        elif isinstance(node.parent, nodes.document):
             self.title = self.title + \
                 '\\\\\n\\large{%s}\n' % self.encode(node.astext())
             raise nodes.SkipNode
+        elif isinstance(node.parent, nodes.section):
+            self.body.append('\\textbf{')
+            self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
 
     def depart_subtitle(self, node):
-        if isinstance(node.parent, nodes.sidebar):
-            self.body.append(self.context.pop())
+        self.body.append(self.context.pop())
 
     def visit_system_message(self, node):
-        if node['level'] < self.document.reporter['writer'].report_level:
+        if node['level'] < self.document.reporter.report_level:
             raise nodes.SkipNode
 
     def depart_system_message(self, node):
@@ -1830,8 +1848,9 @@
         # BUG: why not (refuri or refid or refname) means not footnote ?
         if not (node.has_key('refuri') or node.has_key('refid')
                 or node.has_key('refname')):
-            self.body.append('\\hypertarget{%s}{' % node['id'])
-            self.context.append('}')
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}{' % id)
+            self.context.append('}' * len(node['ids']))
         else:
             self.context.append('')
 
@@ -1849,11 +1868,11 @@
         pass
 
     def visit_term(self, node):
-        self.body.append('\\item[')
+        self.body.append('\\item[{')
 
     def depart_term(self, node):
         # definition list term.
-        self.body.append('] ')
+        self.body.append('}] ')
 
     def visit_tgroup(self, node):
         #self.body.append(self.starttag(node, 'colgroup'))
@@ -1895,8 +1914,9 @@
     def bookmark(self, node):
         """Append latex href and pdfbookmarks for titles.
         """
-        if node.parent.hasattr('id'):
-            self.body.append('\\hypertarget{%s}{}\n' % node.parent['id'])
+        if node.parent['ids']:
+            for id in node.parent['ids']:
+                self.body.append('\\hypertarget{%s}{}\n' % id)
             if not self.use_latex_toc:
                 # BUG level depends on style. pdflatex allows level 0 to 3
                 # ToC would be the only on level 0 so i choose to decrement the rest.
@@ -1907,8 +1927,9 @@
                     l = l-1
                 # pdftex does not like "_" subscripts in titles
                 text = self.encode(node.astext())
-                self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
-                        (l,text,node.parent['id']))
+                for id in node.parent['ids']:
+                    self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
+                                     (l, text, id))
 
     def visit_title(self, node):
         """Only 3 section levels are supported by LaTeX article (AFAIR)."""
@@ -1957,10 +1978,10 @@
         self.body.append(self.context.pop())
 
     def visit_topic(self, node):
-        self.topic_class = node.get('class')
-        if self.use_latex_toc:
+        self.topic_classes = node['classes']
+        if 'contents' in node['classes'] and self.use_latex_toc:
             self.body.append('\\tableofcontents\n\n\\bigskip\n')
-            self.topic_class = ''
+            self.topic_classes = []
             raise nodes.SkipNode
 
     def visit_inline(self, node): # titlereference
@@ -1970,7 +1991,7 @@
         self.body.append( '}' )
 
     def depart_topic(self, node):
-        self.topic_class = ''
+        self.topic_classes = []
         self.body.append('\n')
 
     def visit_rubric(self, node):

Modified: Zope/trunk/lib/python/docutils/writers/pep_html.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/pep_html.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/pep_html.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Author: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 3129 $
+# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
 # Copyright: This module has been placed in the public domain.
 
 """
@@ -11,7 +11,6 @@
 __docformat__ = 'reStructuredText'
 
 
-import random
 import sys
 import docutils
 from docutils import frontend, nodes, utils
@@ -22,8 +21,7 @@
 
     settings_spec = html4css1.Writer.settings_spec + (
         'PEP/HTML-Specific Options',
-        """The HTML --footnote-references option's default is set to """
-        '"brackets".',
+        None,
         (('Specify a template file.  Default is "pep-html-template".',
           ['--template'],
           {'default': 'pep-html-template', 'metavar': '<file>'}),
@@ -32,10 +30,12 @@
           {'default': '..', 'metavar': '<URL>'}),
          ('Home URL prefix for PEPs.  Default is "." (current directory).',
           ['--pep-home'],
-          {'default': '.', 'metavar': '<URL>'}),))
+          {'default': '.', 'metavar': '<URL>'}),
+         # For testing.
+         (frontend.SUPPRESS_HELP,
+          ['--no-random'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),))
 
-    settings_default_overrides = {'footnote_references': 'brackets'}
-
     relative_path_settings = (html4css1.Writer.relative_path_settings
                               + ('template',))
 
@@ -66,7 +66,11 @@
         header = self.document[index]
         pepnum = header[0][1].astext()
         subs['pep'] = pepnum
-        subs['banner'] = random.randrange(64)
+        if settings.no_random:
+            subs['banner'] = 0
+        else:
+            import random
+            subs['banner'] = random.randrange(64)
         try:
             subs['pepnum'] = '%04i' % int(pepnum)
         except ValueError:
@@ -82,5 +86,5 @@
 
     def depart_field_list(self, node):
         html4css1.HTMLTranslator.depart_field_list(self, node)
-        if node.get('class') == 'rfc2822':
+        if 'rfc2822' in node['classes']:
              self.body.append('<hr />\n')

Modified: Zope/trunk/lib/python/docutils/writers/pseudoxml.py
===================================================================
--- Zope/trunk/lib/python/docutils/writers/pseudoxml.py	2005-10-09 14:31:06 UTC (rev 39012)
+++ Zope/trunk/lib/python/docutils/writers/pseudoxml.py	2005-10-09 14:31:37 UTC (rev 39013)
@@ -1,7 +1,7 @@
 # Authors: David Goodger
 # Contact: goodger at users.sourceforge.net
-# Revision: $Revision: 1.2.10.6 $
-# Date: $Date: 2005/01/07 13:26:06 $
+# Revision: $Revision: 1645 $
+# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
 # Copyright: This module has been placed in the public domain.
 
 """



More information about the Zope-Checkins mailing list