[gtk-doc] mkbd: turn into module



commit 56876356432e3c2818a1e86fd7bd3686d5d27cf3
Author: Stefan Sauer <ensonic users sf net>
Date:   Thu May 25 21:20:05 2017 +0200

    mkbd: turn into module

 Makefile.am    |    2 +
 gtkdoc-mkdb.in | 5521 +-------------------------------------------------------
 gtkdoc/mkdb.py | 5513 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 5543 insertions(+), 5493 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 47ec11b..c42ec19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,6 +40,7 @@ pylibdata_DATA = \
   gtkdoc/common.py \
   gtkdoc/config.py \
   gtkdoc/fixxref.py \
+  gtkdoc/mkdb.py \
   gtkdoc/mkhtml.py \
   gtkdoc/mkman.py \
   gtkdoc/mkpdf.py \
@@ -91,6 +92,7 @@ CLEANFILES = \
   gtkdoc/common.pyc \
   gtkdoc/config.pyc \
   gtkdoc/fixxref.pyc \
+  gtkdoc/mkdb.pyc \
   gtkdoc/mkhtml.pyc \
   gtkdoc/mkman.pyc \
   gtkdoc/mkpdf.pyc \
diff --git a/gtkdoc-mkdb.in b/gtkdoc-mkdb.in
index 967feb7..ff061e6 100755
--- a/gtkdoc-mkdb.in
+++ b/gtkdoc-mkdb.in
@@ -20,5508 +20,43 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 #
 
-#
-# Script      : gtkdoc-mkdb
-# Description : This creates the DocBook files from the source comments.
-#
-
 from __future__ import print_function
 
 import argparse
-from collections import OrderedDict
-import logging
-import os
-import re
-import string
 import sys
-
 sys.path.append('@PYTHON_PACKAGE_DIR@')
-from gtkdoc import common
-
-# Options
-
-# name of documentation module
-MODULE = None
-DB_OUTPUT_DIR = None
-SOURCE_DIRS = None
-SOURCE_SUFFIXES = ''
-IGNORE_FILES = ''
-MAIN_SGML_FILE = None
-EXPAND_CONTENT_FILES = ''
-INLINE_MARKUP_MODE = None
-DEFAULT_STABILITY = None
-DEFAULT_INCLUDES = None
-OUTPUT_FORMAT = None
-NAME_SPACE = ''
-OUTPUT_ALL_SYMBOLS = None
-OUTPUT_SYMBOLS_WITHOUT_SINCE = None
-ROOT_DIR = "."
-OBJECT_TREE_FILE = None
-INTERFACES_FILE = None
-PREREQUISITES_FILE = None
-SIGNALS_FILE = None
-ARGS_FILE = None
-
-# These global arrays store information on signals. Each signal has an entry
-# in each of these arrays at the same index, like a multi-dimensional array.
-SignalObjects = []        # The GtkObject which emits the signal.
-SignalNames = []        # The signal name.
-SignalReturns = []        # The return type.
-SignalFlags = []        # Flags for the signal
-SignalPrototypes = []        # The rest of the prototype of the signal handler.
-
-# These global arrays store information on Args. Each Arg has an entry
-# in each of these arrays at the same index, like a multi-dimensional array.
-ArgObjects = []                # The GtkObject which has the Arg.
-ArgNames = []                # The Arg name.
-ArgTypes = []                # The Arg type - gint, GtkArrowType etc.
-ArgFlags = []                # How the Arg can be used - readable/writable etc.
-ArgNicks = []                # The nickname of the Arg.
-ArgBlurbs = []          # Docstring of the Arg.
-ArgDefaults = []        # Default value of the Arg.
-ArgRanges = []                # The range of the Arg type
-
-# These global hashes store declaration info keyed on a symbol name.
-Declarations = {}
-DeclarationTypes = {}
-DeclarationConditional = {}
-DeclarationOutput = {}
-Deprecated = {}
-Since = {}
-StabilityLevel = {}
-StructHasTypedef = {}
-
-# These global hashes store the existing documentation.
-SymbolDocs = {}
-SymbolParams = {}
-SymbolAnnotations = {}
-
-# These global hashes store documentation scanned from the source files.
-SourceSymbolDocs = {}
-SourceSymbolParams = {}
-SourceSymbolSourceFile = {}
-SourceSymbolSourceLine = {}
-
-# all documentation goes in here, so we can do coverage analysis
-AllSymbols = {}
-AllIncompleteSymbols = {}
-AllUnusedSymbols = {}
-AllDocumentedSymbols = {}
-
-# Undeclared yet documented symbols
-UndeclaredSymbols = {}
-
-# These global arrays store GObject, subclasses and the hierarchy (also of
-# non-object derived types).
-Objects = []
-ObjectLevels = []
-ObjectRoots = {}
-
-Interfaces = {}
-Prerequisites = {}
-
-# holds the symbols which are mentioned in <MODULE>-sections.txt and in which
-# section they are defined
-KnownSymbols = {}
-SymbolSection = {}
-SymbolSectionId = {}
-
-# collects index entries
-IndexEntriesFull = {}
-IndexEntriesSince = {}
-IndexEntriesDeprecated = {}
 
-# Standard C preprocessor directives, which we ignore for '#' abbreviations.
-PreProcessorDirectives = {
-    'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef',
-    'include', 'line', 'pragma', 'unassert', 'undef', 'warning'
-}
+from gtkdoc import common, mkdb
 
-# remember used annotation (to write minimal glossary)
-AnnotationsUsed = {}
-
-# the regexp that parses the annotation is in ScanSourceFile()
-AnnotationDefinition = {
-    # the GObjectIntrospection annotations are defined at:
-    # https://live.gnome.org/GObjectIntrospection/Annotations
-    'allow-none': "NULL is OK, both for passing and for returning.",
-    'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
-    'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
-    'optional': "NULL may be passed instead of a pointer to a location.",
-    'array': "Parameter points to an array of items.",
-    'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
-    'attributes': "Free-form key-value pairs.",
-    'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
-    'constructor': "This symbol is a constructor, not a static method.",
-    'destroy': "This parameter is a 'destroy_data', for callbacks.",
-    'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less 
parameters).",
-    'element-type': "Generics and defining elements of containers and arrays.",
-    'error-domains': "Typed errors. Similar to throws in Java.",
-    'foreign': "This is a foreign struct.",
-    'get-value-func': "The specified function is used to convert a struct from a GValue, must be a 
GTypeInstance.",
-    'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
-    'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
-    'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
-    'method': "This is a method",
-    'not-error': "A GError parameter is not to be handled like a normal GError.",
-    'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
-    'out caller-allocates': "Out parameter, where caller must allocate storage.",
-    'out callee-allocates': "Out parameter, where caller must allocate storage.",
-    'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
-    'rename-to': "Rename the original symbol's name to SYMBOL.",
-    'scope call': "The callback is valid only during the call to the method.",
-    'scope async': "The callback is valid until first called.",
-    'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
-    'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a 
GTypeInstance.",
-    'skip': "Exposed in C code, not necessarily available in other languages.",
-    'transfer container': "Free data container after the code is done.",
-    'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
-    'transfer full': "Free data after the code is done.",
-    'transfer none': "Don't free data after the code is done.",
-    'type': "Override the parsed C type with given type.",
-    'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
-    'virtual': "This is the invoker for a virtual method.",
-    'value': "The specified value overrides the evaluated value of the constant.",
-    # Stability Level definition
-    # https://bugzilla.gnome.org/show_bug.cgi?id=170860
-    'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to
-develop applications to these interfaces, release them, and have confidence that
-they will run on all minor releases of the product (after the one in which the
-interface was introduced, and within the same major release). Even at a major
-release, incompatible changes are expected to be rare, and to have strong
-justifications.
-''',
-    'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to
-give outside developers early access to new or rapidly changing technology, or
-to provide an interim solution to a problem where a more general solution is
-anticipated. No claims are made about either source or binary compatibility from
-one minor release to the next.
-
-The Unstable interface level is a warning that these interfaces are  subject to
-change without warning and should not be used in unbundled products.
-
-Given such caveats, customer impact need not be a factor when considering
-incompatible changes to an Unstable interface in a major or minor release.
-Nonetheless, when such changes are introduced, the changes should still be
-mentioned in the release notes for the affected release.
-''',
-    'Private': '''An interface that can be used within the GNOME stack itself, but that is not
-documented for end-users.  Such functions should only be used in specified and
-documented ways.
-''',
-}
-
-# Elements to consider non-block items in MarkDown parsing
-MD_TEXT_LEVEL_ELEMENTS = {
-    'emphasis', 'envar', 'filename', 'firstterm', 'footnote', 'function', 'literal',
-    'manvolnum', 'option', 'replaceable', 'structfield', 'structname', 'title',
-    'varname'
-}
-MD_ESCAPABLE_CHARS = r'\`*_{}[]()>#+-.!'
-MD_GTK_ESCAPABLE_CHARS = r'@%'
-
-# Function and other declaration output settings.
-RETURN_TYPE_FIELD_WIDTH = 20
-MAX_SYMBOL_FIELD_WIDTH = 40
-
-# XML header
-doctype_header = None
-
-# refentry template
-REFENTRY = string.Template('''${header}
-<refentry id="${section_id}">
-<refmeta>
-<refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle>
-<manvolnum>3</manvolnum>
-<refmiscinfo>${MODULE} Library${image}</refmiscinfo>
-</refmeta>
-<refnamediv>
-<refname>${title}</refname>
-<refpurpose>${short_desc}</refpurpose>
-</refnamediv>
-${stability}
-${functions_synop}${args_synop}${signals_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations}
-${include_output}
-<refsect1 id="${section_id}.description" role="desc">
-<title role="desc.title">Description</title>
-${extralinks}${long_desc}
-</refsect1>
-<refsect1 id="${section_id}.functions_details" role="details">
-<title role="details.title">Functions</title>
-${functions_details}
-</refsect1>
-<refsect1 id="${section_id}.other_details" role="details">
-<title role="details.title">Types and Values</title>
-${other_details}
-</refsect1>
-${args_desc}${signals_desc}${see_also}
-</refentry>
-''')
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument('--module', default='')
-parser.add_argument('--source-dir', action='append', dest='source_dir', default=[])
-parser.add_argument('--source-suffixes', dest='source_suffixes', default='')
-parser.add_argument('--ignore-files', dest='ignore_files', default='')
-parser.add_argument('--output-dir', dest='output_dir', default='')
-parser.add_argument('--tmpl-dir', dest='tmpl_dir', help="DEPRECATED")
-parser.add_argument('--main-sgml-file', dest='main_sgml_file', default='')
-parser.add_argument('--expand-content-files', dest='expand_content_files', default='')
-group = parser.add_mutually_exclusive_group()
-group.add_argument('--sgml-mode', action='store_true', default=False, dest='sgml_mode')
-group.add_argument('--xml-mode', action='store_true', default=False, dest='xml_mode')
-parser.add_argument('--default-stability', dest='default_stability',
-                    choices=['', 'Stable', 'Private', 'Unstable'], default='')
-parser.add_argument('--default-includes', dest='default_includes', default='')
-parser.add_argument('--output-format', dest='default_format', default='')
-parser.add_argument('--name-space', dest='name_space', default='')
-parser.add_argument('--outputallsymbols', default=False, action='store_true')
-parser.add_argument('--outputsymbolswithoutsince', default=False, action='store_true')
-
-
-def Run():
-    global MODULE, SOURCE_DIRS, SOURCE_SUFFIXES, IGNORE_FILES, MAIN_SGML_FILE, EXPAND_CONTENT_FILES, \
-        INLINE_MARKUP_MODE, DEFAULT_STABILITY, DEFAULT_INCLUDES, OUTPUT_FORMAT, NAME_SPACE, 
OUTPUT_ALL_SYMBOLS, \
-        OUTPUT_SYMBOLS_WITHOUT_SINCE, DB_OUTPUT_DIR, OBJECT_TREE_FILE, INTERFACES_FILE, PREREQUISITES_FILE, \
-        SIGNALS_FILE, ARGS_FILE, doctype_header
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--module', default='')
+    parser.add_argument('--source-dir', action='append', dest='source_dir', default=[])
+    parser.add_argument('--source-suffixes', dest='source_suffixes', default='')
+    parser.add_argument('--ignore-files', dest='ignore_files', default='')
+    parser.add_argument('--output-dir', dest='output_dir', default='')
+    parser.add_argument('--tmpl-dir', dest='tmpl_dir', help="DEPRECATED")
+    parser.add_argument('--main-sgml-file', dest='main_sgml_file', default='')
+    parser.add_argument('--expand-content-files', dest='expand_content_files', default='')
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('--sgml-mode', action='store_true', default=False, dest='sgml_mode')
+    group.add_argument('--xml-mode', action='store_true', default=False, dest='xml_mode')
+    parser.add_argument('--default-stability', dest='default_stability',
+                        choices=['', 'Stable', 'Private', 'Unstable'], default='')
+    parser.add_argument('--default-includes', dest='default_includes', default='')
+    parser.add_argument('--output-format', default='xml')  # MUST be 'xml', deprecate?
+    parser.add_argument('--name-space', dest='name_space', default='')
+    parser.add_argument('--outputallsymbols', default=False, action='store_true')
+    parser.add_argument('--outputsymbolswithoutsince', default=False, action='store_true')
 
     options = parser.parse_args()
+    if options.module == '':
+        print('Error, missing module.')
+        sys.exit(1)
 
-    common.setup_logging()
-
-    # We should pass the options variable around instead of this global variable horror
-    # but too much of the code expects these to be around. Fix this once the transition is done.
-    MODULE = options.module
-    SOURCE_DIRS = options.source_dir
-    SOURCE_SUFFIXES = options.source_suffixes
-    IGNORE_FILES = options.ignore_files
-    MAIN_SGML_FILE = options.main_sgml_file
-    EXPAND_CONTENT_FILES = options.expand_content_files
-    INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode
-    DEFAULT_STABILITY = options.default_stability
-    DEFAULT_INCLUDES = options.default_includes
-    OUTPUT_FORMAT = options.default_format
-    NAME_SPACE = options.name_space
-    OUTPUT_ALL_SYMBOLS = options.outputallsymbols
-    OUTPUT_SYMBOLS_WITHOUT_SINCE = options.outputsymbolswithoutsince
-
-    logging.info(" ignore files: " + IGNORE_FILES)
-
-    # check output format
-    if OUTPUT_FORMAT is None or OUTPUT_FORMAT == '':
-        OUTPUT_FORMAT = "xml"
-    else:
-        OUTPUT_FORMAT = OUTPUT_FORMAT.lower()
-    if OUTPUT_FORMAT != "xml":
-        sys.exit("Invalid format '%s' passed to --output.format" % OUTPUT_FORMAT)
-
-    if not MAIN_SGML_FILE:
-        # backwards compatibility
-        if os.path.exists(MODULE + "-docs.sgml"):
-            MAIN_SGML_FILE = MODULE + "-docs.sgml"
-        else:
-            MAIN_SGML_FILE = MODULE + "-docs.xml"
-
-    # extract docbook header or define default
-    if os.path.exists(MAIN_SGML_FILE):
-        INPUT = open(MAIN_SGML_FILE)
-        doctype_header = ''
-        for line in INPUT:
-            if re.search(r'^\s*<(book|chapter|article)', line):
-                # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
-                if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and not 
re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', doctype_header, flags=re.MULTILINE):
-                    doctype_header = ''
-                break
-
-            # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
-            # FIXME: not sure if we can do this now, as people already work-around the problem
-            # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">';
-            line = re.sub(
-                r'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r'<!ENTITY % gtkdocentities SYSTEM 
"../\1">', line)
-            doctype_header += line
-        INPUT.close()
-        doctype_header = doctype_header.strip()
-    else:
-        doctype_header = '''<?xml version="1.0"?>
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
-               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd";
-[
-  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
-  <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
-  %gtkdocentities;
-]>'''
-
-    # This is where we put all the DocBook output.
-    DB_OUTPUT_DIR = DB_OUTPUT_DIR if DB_OUTPUT_DIR else os.path.join(ROOT_DIR, "xml")
-
-    # This file contains the object hierarchy.
-    OBJECT_TREE_FILE = os.path.join(ROOT_DIR, MODULE + ".hierarchy")
-
-    # This file contains the interfaces.
-    INTERFACES_FILE = os.path.join(ROOT_DIR, MODULE + ".interfaces")
-
-    # This file contains the prerequisites.
-    PREREQUISITES_FILE = os.path.join(ROOT_DIR, MODULE + ".prerequisites")
-
-    # This file contains signal arguments and names.
-    SIGNALS_FILE = os.path.join(ROOT_DIR, MODULE + ".signals")
-
-    # The file containing Arg information.
-    ARGS_FILE = os.path.join(ROOT_DIR, MODULE + ".args")
-
-    # Create the root DocBook output directory if it doens't exist.
-    if not os.path.isdir(DB_OUTPUT_DIR):
-        os.mkdir(DB_OUTPUT_DIR)
-
-    ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
-    ReadSignalsFile(SIGNALS_FILE)
-    ReadArgsFile(ARGS_FILE)
-    ReadObjectHierarchy()
-    ReadInterfaces()
-    ReadPrerequisites()
-
-    ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0)
-    if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")):
-        ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1)
-
-    for sdir in SOURCE_DIRS:
-        ReadSourceDocumentation(sdir)
-
-    changed = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
-
-    # If any of the DocBook files have changed, update the timestamp file (so
-    # it can be used for Makefile dependencies).
-    if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")):
-
-        # try to detect the common prefix
-        # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
-        if NAME_SPACE == '':
-            pos = 0
-            ratio = 0.0
-            while True:
-                prefix = {}
-                letter = ''
-                for symbol in IndexEntriesFull.keys():
-                    if NAME_SPACE == '' or NAME_SPACE.lower() in symbol.lower():
-                        if len(symbol) > pos:
-                            letter = symbol[pos:pos + 1]
-                            # stop prefix scanning
-                            if letter == "_":
-                                # stop on "_"
-                                break
-                            # Should we also stop on a uppercase char, if last was lowercase
-                            #   GtkWidget, if we have the 'W' and had the 't' before
-                            # or should we count upper and lowercase, and stop one 2nd uppercase, if we 
already had a lowercase
-                            #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had 
lowercase chars before
-                            # need to recound each time as this is per symbol
-                            ul = letter.upper()
-                            if ul in prefix:
-                                prefix[ul] += 1
-                            else:
-                                prefix[ul] = 1
-
-                if letter != '' and letter != "_":
-                    maxletter = ''
-                    maxsymbols = 0
-                    for letter in prefix.keys():
-                        logging.debug("ns prefix: %s: %s", letter, prefix[letter])
-                        if prefix[letter] > maxsymbols:
-                            maxletter = letter
-                            maxsymbols = prefix[letter]
-
-                    ratio = float(len(IndexEntriesFull)) / prefix[maxletter]
-                    logging.debug('most symbols start with %s, that is %f', maxletter, (100 * ratio))
-                    if ratio > 0.9:
-                        # do another round
-                        NAME_SPACE += maxletter
-
-                    pos += 1
-
-                else:
-                    ratio = 0.0
-
-                if ratio < 0.9:
-                    break
-
-        logging.info('namespace prefix ="%s"', NAME_SPACE)
-
-        OutputIndexFull()
-        OutputDeprecatedIndex()
-        OutputSinceIndexes()
-        OutputAnnotationGlossary()
-
-        open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w').write('timestamp')
-
-
-#
-# Function    : OutputObjectList
-# Description : This outputs the alphabetical list of objects, in a columned
-#                table.
-#               FIXME: Currently this also outputs ancestor objects
-#                which may not actually be in this module.
-# Arguments   : none
-#
-
-def OutputObjectList():
-    cols = 3
-
-    # FIXME: use .xml
-    old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml")
-    new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new")
-
-    OUTPUT = open(new_object_index, 'w')
-
-    OUTPUT.write('''%s
-<informaltable pgwide="1" frame="none">
-<tgroup cols="%s">
-<colspec colwidth="1*"/>
-<colspec colwidth="1*"/>
-<colspec colwidth="1*"/>
-<tbody>
-''' % (MakeDocHeader("informaltable"), cols))
-
-    count = 0
-    object = None
-    for object in sorted(Objects):
-        xref = MakeXRef(object)
-        if count % cols == 0:
-            OUTPUT.write("<row>\n")
-        OUTPUT.write("<entry>%s</entry>\n" % xref)
-        if count % cols == cols - 1:
-            OUTPUT.write("</row>\n")
-        count += 1
-
-    if count == 0:
-        # emit an empty row, since empty tables are invalid
-        OUTPUT.write("<row><entry> </entry></row>\n")
-
-    else:
-        if count % cols > 0:
-            OUTPUT.write("</row>\n")
-
-    OUTPUT.write('''</tbody></tgroup></informaltable>\n''')
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_object_index, new_object_index, 0)
-
-
-#
-# Function    : TrimTextBlock
-# Description : Trims extra whitespace. Empty lines inside a block are
-#                preserved.
-# Arguments   : $desc - the text block to trim. May contain newlines.
-#
-
-def TrimTextBlock(desc):
-    # strip leading spaces on the block
-    desc = re.sub(r'^\s+', '', desc)
-    # strip trailing spaces on every line
-    desc = re.sub(r'\s+$', '\n', desc, flags=re.MULTILINE)
-
-    return desc
-
-
-#
-# Function    : OutputDB
-# Description : This collects the output for each section of the docs, and
-#                outputs each file when the end of the section is found.
-# Arguments   : $file - the $MODULE-sections.txt file which contains all of
-#                the functions/macros/structs etc. being documented, organised
-#                into sections and subsections.
-#
-
-def OutputDB(file):
-
-    logging.info("Reading: %s", file)
-    INPUT = open(file)
-    filename = ''
-    book_top = ''
-    book_bottom = ''
-    includes = DEFAULT_INCLUDES if DEFAULT_INCLUDES else ''
-    section_includes = ''
-    in_section = 0
-    title = ''
-    section_id = ''
-    subsection = ''
-    num_symbols = 0
-    changed = 0
-    functions_synop = ''
-    other_synop = ''
-    functions_details = ''
-    other_details = ''
-    signals_synop = ''
-    signals_desc = ''
-    args_synop = ''
-    child_args_synop = ''
-    style_args_synop = ''
-    args_desc = ''
-    child_args_desc = ''
-    style_args_desc = ''
-    hierarchy_str = ''
-    hierarchy = []
-    interfaces = ''
-    implementations = ''
-    prerequisites = ''
-    derived = ''
-    file_objects = []
-    file_def_line = {}
-    symbol_def_line = {}
-
-    MergeSourceDocumentation()
-
-    line_number = 0
-    for line in INPUT:
-        line_number += 1
-
-        if line.startswith('#'):
-            continue
-
-        logging.info("section file data: %d: %s", line_number, line)
-
-        m1 = re.search(r'^<SUBSECTION\s*(.*)>', line, re.I)
-        m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line)
-        m3 = re.search(r'^<FILE>(.*)<\/FILE>', line)
-        m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
-        m5 = re.search(r'^(\S+)', line)
-
-        if line.startswith('<SECTION>'):
-            num_symbols = 0
-            in_section = False
-            file_objects = []
-            symbol_def_line = {}
-
-        elif m1:
-            other_synop += "\n"
-            functions_synop += "\n"
-            subsection = m1.group(1)
-
-        elif line.startswith('<SUBSECTION>'):
-            continue
-        elif m2:
-            title = m2.group(1)
-            logging.info("Section: %s", title)
-
-            # We don't want warnings if object & class structs aren't used.
-            DeclarationOutput[title] = 1
-            DeclarationOutput["%sClass" % title] = 1
-            DeclarationOutput["%sIface" % title] = 1
-            DeclarationOutput["%sInterface" % title] = 1
-
-        elif m3:
-            filename = m3.group(1)
-            if not filename in file_def_line:
-                file_def_line[filename] = line_number
-            else:
-                common.LogWarning(file, line_number, "Double <FILE>%s</FILE> entry. Previous occurrence on 
line %s." %
-                                  (filename, file_def_line[filename]))
-            if title == '':
-                key = filename + ":Title"
-                if key in SourceSymbolDocs:
-                    title = SourceSymbolDocs[key].rstrip()
-
-        elif m4:
-            if in_section:
-                section_includes = m4.group(1)
-            else:
-                if DEFAULT_INCLUDES:
-                    common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line 
option.")
-                else:
-                    includes = m4.group(1)
-
-        elif re.search(r'^<\/SECTION>', line):
-            logging.info("End of section: %s", title)
-            if num_symbols > 0:
-                # collect documents
-                book_bottom += "    <xi:include href=\"xml/%s.xml\"/>\n" % filename
-
-                key = filename + ":Include"
-                if key in SourceSymbolDocs:
-                    if section_includes:
-                        common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline 
comments.")
-                    section_includes = SourceSymbolDocs[key]
-
-                if section_includes == '':
-                    section_includes = includes
-
-                signals_synop = re.sub(r'^\n*', '', signals_synop)
-                signals_synop = re.sub(r'\n+$', '\n', signals_synop)
-
-                if signals_synop != '':
-                    signals_synop = '''<refsect1 id="%s.signals" role="signal_proto">
-<title role="signal_proto.title">Signals</title>
-<informaltable frame="none">
-<tgroup cols="3">
-<colspec colname="signals_return" colwidth="150px"/>
-<colspec colname="signals_name" colwidth="300px"/>
-<colspec colname="signals_flags" colwidth="200px"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, signals_synop)
-                    signals_desc = TrimTextBlock(signals_desc)
-                    signals_desc = '''<refsect1 id="%s.signal-details" role="signals">
-<title role="signals.title">Signal Details</title>
-%s
-</refsect1>
-''' % (section_id, signals_desc)
-
-                args_synop = re.sub(r'^\n*', '', args_synop)
-                args_synop = re.sub(r'\n+$', '\n', args_synop)
-                if args_synop != '':
-                    args_synop = '''<refsect1 id="%s.properties" role="properties">
-<title role="properties.title">Properties</title>
-<informaltable frame="none">
-<tgroup cols="3">
-<colspec colname="properties_type" colwidth="150px"/>
-<colspec colname="properties_name" colwidth="300px"/>
-<colspec colname="properties_flags" colwidth="200px"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, args_synop)
-                    args_desc = TrimTextBlock(args_desc)
-                    args_desc = '''<refsect1 id="%s.property-details" role="property_details">
-<title role="property_details.title">Property Details</title>
-%s
-</refsect1>
-''' % (section_id, args_desc)
-
-                child_args_synop = re.sub(r'^\n*', '', child_args_synop)
-                child_args_synop = re.sub(r'\n+$', '\n', child_args_synop)
-                if child_args_synop != '':
-                    args_synop += '''<refsect1 id="%s.child-properties" role="child_properties">
-<title role="child_properties.title">Child Properties</title>
-<informaltable frame="none">
-<tgroup cols="3">
-<colspec colname="child_properties_type" colwidth="150px"/>
-<colspec colname="child_properties_name" colwidth="300px"/>
-<colspec colname="child_properties_flags" colwidth="200px"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, child_args_synop)
-                    child_args_desc = TrimTextBlock(child_args_desc)
-                    args_desc += '''<refsect1 id="%s.child-property-details" role="child_property_details">
-<title role="child_property_details.title">Child Property Details</title>
-%s
-</refsect1>
-''' % (section_id, child_args_desc)
-
-                style_args_synop = re.sub(r'^\n*', '', style_args_synop)
-                style_args_synop = re.sub(r'\n+$', '\n', style_args_synop)
-                if style_args_synop != '':
-                    args_synop += '''<refsect1 id="%s.style-properties" role="style_properties">
-<title role="style_properties.title">Style Properties</title>
-<informaltable frame="none">
-<tgroup cols="3">
-<colspec colname="style_properties_type" colwidth="150px"/>
-<colspec colname="style_properties_name" colwidth="300px"/>
-<colspec colname="style_properties_flags" colwidth="200px"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, style_args_synop)
-                    style_args_desc = TrimTextBlock(style_args_desc)
-                    args_desc += '''<refsect1 id="%s.style-property-details" role="style_properties_details">
-<title role="style_properties_details.title">Style Property Details</title>
-%s
-</refsect1>
-''' % (section_id, style_args_desc)
-
-                hierarchy_str = AddTreeLineArt(hierarchy)
-                if hierarchy_str != '':
-                    hierarchy_str = '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
-<title role="object_hierarchy.title">Object Hierarchy</title>
-<screen>%s
-</screen>
-</refsect1>
-''' % (section_id, hierarchy_str)
-
-                interfaces = TrimTextBlock(interfaces)
-                if interfaces != '':
-                    interfaces = '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
-<title role="impl_interfaces.title">Implemented Interfaces</title>
-%s
-</refsect1>
-''' % (section_id, interfaces)
-
-                implementations = TrimTextBlock(implementations)
-                if implementations != '':
-                    implementations = '''<refsect1 id="%s.implementations" role="implementations">
-<title role="implementations.title">Known Implementations</title>
-%s
-</refsect1>
-''' % (section_id, implementations)
-
-                prerequisites = TrimTextBlock(prerequisites)
-                if prerequisites != '':
-                    prerequisites = '''<refsect1 id="%s.prerequisites" role="prerequisites">
-<title role="prerequisites.title">Prerequisites</title>
-%s
-</refsect1>
-''' % (section_id, prerequisites)
-
-                derived = TrimTextBlock(derived)
-                if derived != '':
-                    derived = '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
-<title role="derived_interfaces.title">Known Derived Interfaces</title>
-%s
-</refsect1>
-''' % (section_id, derived)
-
-                functions_synop = re.sub(r'^\n*', '', functions_synop)
-                functions_synop = re.sub(r'\n+$', '\n', functions_synop)
-                if functions_synop != '':
-                    functions_synop = '''<refsect1 id="%s.functions" role="functions_proto">
-<title role="functions_proto.title">Functions</title>
-<informaltable pgwide="1" frame="none">
-<tgroup cols="2">
-<colspec colname="functions_return" colwidth="150px"/>
-<colspec colname="functions_name"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, functions_synop)
-
-                other_synop = re.sub(r'^\n*', '', other_synop)
-                other_synop = re.sub(r'\n+$', '\n', other_synop)
-                if other_synop != '':
-                    other_synop = '''<refsect1 id="%s.other" role="other_proto">
-<title role="other_proto.title">Types and Values</title>
-<informaltable role="enum_members_table" pgwide="1" frame="none">
-<tgroup cols="2">
-<colspec colname="name" colwidth="150px"/>
-<colspec colname="description"/>
-<tbody>
-%s
-</tbody>
-</tgroup>
-</informaltable>
-</refsect1>
-''' % (section_id, other_synop)
-
-                file_changed = OutputDBFile(filename, title, section_id,
-                                            section_includes,
-                                            functions_synop, other_synop,
-                                            functions_details, other_details,
-                                            signals_synop, signals_desc,
-                                            args_synop, args_desc,
-                                            hierarchy_str, interfaces,
-                                            implementations,
-                                            prerequisites, derived,
-                                            file_objects)
-                if file_changed:
-                    changed = True
-
-            title = ''
-            section_id = ''
-            subsection = ''
-            in_section = 0
-            section_includes = ''
-            functions_synop = ''
-            other_synop = ''
-            functions_details = ''
-            other_details = ''
-            signals_synop = ''
-            signals_desc = ''
-            args_synop = ''
-            child_args_synop = ''
-            style_args_synop = ''
-            args_desc = ''
-            child_args_desc = ''
-            style_args_desc = ''
-            hierarchy_str = ''
-            hierarchy = []
-            interfaces = ''
-            implementations = ''
-            prerequisites = ''
-            derived = ''
-
-        elif m5:
-            symbol = m5.group(1)
-            logging.info('  Symbol: "%s" in subsection: "%s"', symbol, subsection)
-
-            # check for duplicate entries
-            if symbol not in symbol_def_line:
-                declaration = Declarations.get(symbol)
-                # FIXME: with this we'll output empty declaration
-                if declaration is not None:
-                    if CheckIsObject(symbol):
-                        file_objects.append(symbol)
-
-                    # We don't want standard macros/functions of GObjects,
-                    # or private declarations.
-                    if subsection != "Standard" and subsection != "Private":
-                        synop, desc = OutputDeclaration(symbol, declaration)
-                        type = DeclarationTypes[symbol]
-
-                        if type == 'FUNCTION' or type == 'USER_FUNCTION':
-                            functions_synop += synop
-                            functions_details += desc
-                        elif type == 'MACRO' and re.search(symbol + r'\(', declaration):
-                            functions_synop += synop
-                            functions_details += desc
-                        else:
-                            other_synop += synop
-                            other_details += desc
-
-                    sig_synop, sig_desc = GetSignals(symbol)
-                    arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = 
GetArgs(
-                        symbol)
-                    ifaces = GetInterfaces(symbol)
-                    impls = GetImplementations(symbol)
-                    prereqs = GetPrerequisites(symbol)
-                    der = GetDerived(symbol)
-                    hierarchy = GetHierarchy(symbol, hierarchy)
-
-                    signals_synop += sig_synop
-                    signals_desc += sig_desc
-                    args_synop += arg_synop
-                    child_args_synop += child_arg_synop
-                    style_args_synop += style_arg_synop
-                    args_desc += arg_desc
-                    child_args_desc += child_arg_desc
-                    style_args_desc += style_arg_desc
-                    interfaces += ifaces
-                    implementations += impls
-                    prerequisites += prereqs
-                    derived += der
-
-                    # Note that the declaration has been output.
-                    DeclarationOutput[symbol] = True
-                elif subsection != "Standard" and subsection != "Private":
-                    UndeclaredSymbols[symbol] = True
-                    common.LogWarning(file, line_number, "No declaration found for %s." % symbol)
-
-                num_symbols += 1
-                symbol_def_line[symbol] = line_number
-
-                if section_id == '':
-                    if title == '' and filename == '':
-                        common.LogWarning(file, line_number, "Section has no title and no file.")
-
-                    # FIXME: one of those would be enough
-                    # filename should be an internal detail for gtk-doc
-                    if title == '':
-                        title = filename
-                    elif filename == '':
-                        filename = title
-
-                    filename = filename.replace(' ', '_')
-
-                    section_id = SourceSymbolDocs.get(filename + ":Section_Id")
-                    if section_id and section_id.strip() != '':
-                        # Remove trailing blanks and use as is
-                        section_id = section_id.rstrip()
-                    elif CheckIsObject(title):
-                        # GObjects use their class name as the ID.
-                        section_id = common.CreateValidSGMLID(title)
-                    else:
-                        section_id = common.CreateValidSGMLID(MODULE + '-' + title)
-
-                SymbolSection[symbol] = title
-                SymbolSectionId[symbol] = section_id
-
-            else:
-                common.LogWarning(file, line_number, "Double symbol entry for %s. "
-                                  "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol]))
-    INPUT.close()
-
-    OutputMissingDocumentation()
-    OutputUndeclaredSymbols()
-    OutputUnusedSymbols()
-
-    if OUTPUT_ALL_SYMBOLS:
-        OutputAllSymbols()
-
-    if OUTPUT_SYMBOLS_WITHOUT_SINCE:
-        OutputSymbolsWithoutSince()
-
-    for filename in EXPAND_CONTENT_FILES.split():
-        file_changed = OutputExtraFile(filename)
-        if file_changed:
-            changed = True
-
-    OutputBook(book_top, book_bottom)
-
-    logging.info("All files created: %d", changed)
-    return changed
-
-
-#
-# Function    : OutputIndex
-# Description : This writes an indexlist that can be included into the main-
-#               document into an <index> tag.
-#
-
-def OutputIndex(basename, apiindex):
-    old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml')
-    new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new')
-    lastletter = " "
-    divopen = 0
-    symbol = None
-    short_symbol = None
-
-    OUTPUT = open(new_index, 'w')
-
-    OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename)
-
-    logging.info("generate %s index (%d entries) with namespace %s", basename, len(apiindex), NAME_SPACE)
-
-    # do a case insensitive sort while chopping off the prefix
-    mapped_keys = [
-        {
-            'original': x,
-            'short': re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', x.upper(), flags=re.I),
-        } for x in apiindex.keys()]
-    sorted_keys = sorted(mapped_keys, key=lambda d: (d['short'], d['original']))
-
-    for key in sorted_keys:
-        symbol = key['original']
-        short = key['short']
-        if short != '':
-            short_symbol = short
-        else:
-            short_symbol = symbol
-
-        # generate a short symbol description
-        symbol_desc = ''
-        symbol_section = ''
-        symbol_section_id = ''
-        symbol_type = ''
-        if symbol in DeclarationTypes:
-            symbol_type = DeclarationTypes[symbol].lower()
-
-        if symbol_type == '':
-            logging.info("trying symbol %s", symbol)
-            m1 = re.search(r'(.*)::(.*)', symbol)
-            m2 = re.search(r'(.*):(.*)', symbol)
-            if m1:
-                oname = m1.group(1)
-                osym = m1.group(2)
-                logging.info("  trying object signal %s:%s in %d signals", oname, osym, len(SignalNames))
-                for name in SignalNames:
-                    logging.info("    " + name)
-                    if name == osym:
-                        symbol_type = "object signal"
-                        if oname in SymbolSection:
-                            symbol_section = SymbolSection[oname]
-                            symbol_section_id = SymbolSectionId[oname]
-                        break
-            elif m2:
-                oname = m2.group(1)
-                osym = m2.group(2)
-                logging.info("  trying object property %s::%s in %d properties", oname, osym, len(ArgNames))
-                for name in ArgNames:
-                    logging.info("    " + name)
-                    if name == osym:
-                        symbol_type = "object property"
-                        if oname in SymbolSection:
-                            symbol_section = SymbolSection[oname]
-                            symbol_section_id = SymbolSectionId[oname]
-                        break
-        else:
-            if symbol in SymbolSection:
-                symbol_section = SymbolSection[symbol]
-                symbol_section_id = SymbolSectionId[symbol]
-
-        if symbol_type != '':
-            symbol_desc = ", " + symbol_type
-            if symbol_section != '':
-                symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section)
-                # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section")
-
-        curletter = short_symbol[0].upper()
-        ixid = apiindex[symbol]
-
-        logging.info("  add symbol %s with %s to index in section '%s' (derived from %s)",
-                     symbol, ixid, curletter, short_symbol)
-
-        if curletter != lastletter:
-            lastletter = curletter
-
-            if divopen:
-                OUTPUT.write("</indexdiv>\n")
-
-            OUTPUT.write("<indexdiv><title>%s</title>\n" % curletter)
-            divopen = True
-
-        OUTPUT.write('<indexentry><primaryie linkends="%s"><link 
linkend="%s">%s</link>%s</primaryie></indexentry>\n' %
-                     (ixid, ixid, symbol, symbol_desc))
-
-    if divopen:
-        OUTPUT.write("</indexdiv>\n")
-
-    OUTPUT.write("</indexdiv>\n")
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_index, new_index, 0)
-
-
-#
-# Function    : OutputIndexFull
-# Description : This writes the full api indexlist that can be included into the
-#               main document into an <index> tag.
-#
-
-def OutputIndexFull():
-    OutputIndex("api-index-full", IndexEntriesFull)
-
-
-#
-# Function    : OutputDeprecatedIndex
-# Description : This writes the deprecated api indexlist that can be included
-#               into the main document into an <index> tag.
-#
-
-def OutputDeprecatedIndex():
-    OutputIndex("api-index-deprecated", IndexEntriesDeprecated)
-
-
-#
-# Function    : OutputSinceIndexes
-# Description : This writes the 'since' api indexlists that can be included into
-#               the main document into an <index> tag.
-#
-
-def OutputSinceIndexes():
-    sinces = set(Since.values())
-
-    for version in sinces:
-        logging.info("Since : [%s]", version)
-        index = {x: IndexEntriesSince[x] for x in IndexEntriesSince.keys() if Since[x] == version}
-
-        OutputIndex("api-index-" + version, index)
-
-
-#
-# Function    : OutputAnnotationGlossary
-# Description : This writes a glossary of the used annotation terms into a
-#               separate glossary file that can be included into the main
-#               document.
-#
-
-def OutputAnnotationGlossary():
-    old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml")
-    new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new")
-    lastletter = " "
-    divopen = False
-
-    # if there are no annotations used return
-    if not AnnotationsUsed:
-        return
-
-    # add acronyms that are referenced from acronym text
-    rerun = True
-    while rerun:
-        rerun = False
-        for annotation in AnnotationsUsed:
-            if annotation not in AnnotationDefinition:
-                continue
-            m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation])
-            if m and m.group(1) not in AnnotationsUsed:
-                AnnotationsUsed[m.group(1)] = 1
-                rerun = True
-                break
-
-    OUTPUT = open(new_glossary, 'w')
-
-    OUTPUT.write('''%s
-<glossary id="annotation-glossary">
-  <title>Annotation Glossary</title>
-''' % MakeDocHeader("glossary"))
-
-    for annotation in sorted(AnnotationsUsed.keys(), key=str.lower):
-        if annotation in AnnotationDefinition:
-            definition = AnnotationDefinition[annotation]
-            curletter = annotation[0].upper()
-
-            if curletter != lastletter:
-                lastletter = curletter
-
-                if divopen:
-                    OUTPUT.write("</glossdiv>\n")
-
-                OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter)
-                divopen = True
-
-            OUTPUT.write('''    <glossentry>
-      <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
-      <glossdef>
-        <para>%s</para>
-      </glossdef>
-    </glossentry>
-''' % (annotation, annotation, definition))
-
-    if divopen:
-        OUTPUT.write("</glossdiv>\n")
-
-    OUTPUT.write("</glossary>\n")
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_glossary, new_glossary, 0)
-
-
-#
-# Function    : ReadKnownSymbols
-# Description : This collects the names of non-private symbols from the
-#               $MODULE-sections.txt file.
-# Arguments   : $file - the $MODULE-sections.txt file which contains all of
-#                the functions/macros/structs etc. being documented, organised
-#                into sections and subsections.
-#
-
-def ReadKnownSymbols(file):
-
-    subsection = ''
-
-    logging.info("Reading: %s", file)
-    INPUT = open(file)
-
-    for line in INPUT:
-        if line.startswith('#'):
-            continue
-
-        if line.startswith('<SECTION>'):
-            subsection = ''
-            continue
-
-        m = re.search(r'^<SUBSECTION\s*(.*)>', line, flags=re.I)
-        if m:
-            subsection = m.group(1)
-            continue
-
-        if line.startswith('<SUBSECTION>'):
-            continue
-
-        if re.search(r'^<TITLE>(.*)<\/TITLE>', line):
-            continue
-
-        m = re.search(r'^<FILE>(.*)<\/FILE>', line)
-        if m:
-            KnownSymbols[m.group(1) + ":Long_Description"] = 1
-            KnownSymbols[m.group(1) + ":Short_Description"] = 1
-            continue
-
-        m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
-        if m:
-            continue
-
-        m = re.search(r'^<\/SECTION>', line)
-        if m:
-            continue
-
-        m = re.search(r'^(\S+)', line)
-        if m:
-            symbol = m.group(1)
-            if subsection != "Standard" and subsection != "Private":
-                KnownSymbols[symbol] = 1
-            else:
-                KnownSymbols[symbol] = 0
-    INPUT.close()
-
-
-#
-# Function    : OutputDeclaration
-# Description : Returns the synopsis and detailed description DocBook
-#                describing one function/macro etc.
-# Arguments   : $symbol - the name of the function/macro begin described.
-#                $declaration - the declaration of the function/macro.
-#
-
-def OutputDeclaration(symbol, declaration):
-    dtype = DeclarationTypes[symbol]
-    if dtype == 'MACRO':
-        return OutputMacro(symbol, declaration)
-    elif dtype == 'TYPEDEF':
-        return OutputTypedef(symbol, declaration)
-    elif dtype == 'STRUCT':
-        return OutputStruct(symbol, declaration)
-    elif dtype == 'ENUM':
-        return OutputEnum(symbol, declaration)
-    elif dtype == 'UNION':
-        return OutputUnion(symbol, declaration)
-    elif dtype == 'VARIABLE':
-        return OutputVariable(symbol, declaration)
-    elif dtype == 'FUNCTION':
-        return OutputFunction(symbol, declaration, dtype)
-    elif dtype == 'USER_FUNCTION':
-        return OutputFunction(symbol, declaration, dtype)
-    else:
-        sys.exit("Unknown symbol type " + dtype)
-
-
-#
-# Function    : OutputSymbolTraits
-# Description : Returns the Since and StabilityLevel paragraphs for a symbol.
-# Arguments   : $symbol - the name of the function/macro begin described.
-#
-
-def OutputSymbolTraits(symbol):
-    desc = ''
-
-    if symbol in Since:
-        link_id = "api-index-" + Since[symbol]
-        desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, 
Since[symbol])
-
-    if symbol in StabilityLevel:
-        stability = StabilityLevel[symbol]
-        AnnotationsUsed[stability] = True
-        desc += "<para role=\"stability\">Stability Level: <acronym>%s</acronym></para>" % stability
-    return desc
-
-
-#
-# Function    : Output{Symbol,Section}ExtraLinks
-# Description : Returns extralinks for the symbol (if enabled).
-# Arguments   : $symbol - the name of the function/macro begin described.
-#
-
-def uri_escape(text):
-    if text is None:
-        return None
-
-    # Build a char to hex map
-    escapes = {}
-    for i in range(256):
-        escapes[chr(i)] = "%%%02X" % i
-
-    # Default unsafe characters.  RFC 2732 ^(uric - reserved)
-    def do_escape(char):
-        return escapes[char]
-    text = re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text)
-
-    return text
-
-
-def OutputSymbolExtraLinks(symbol):
-    desc = ''
-
-    if False:   # NEW FEATURE: needs configurability
-        sstr = uri_escape(symbol)
-        mstr = uri_escape(MODULE)
-        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
-<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
-''' % (sstr, mstr, sstr)
-
-    return desc
-
-
-def OutputSectionExtraLinks(symbol, docsymbol):
-    desc = ''
-
-    if False:   # NEW FEATURE: needs configurability
-        sstr = uri_escape(symbol)
-        mstr = uri_escape(MODULE)
-        dsstr = uri_escape(docsymbol)
-        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
-<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
-''' % (sstr, mstr, dsstr)
-    return desc
-
-
-#
-# Function    : OutputMacro
-# Description : Returns the synopsis and detailed description of a macro.
-# Arguments   : $symbol - the macro.
-#                $declaration - the declaration of the macro.
-#
-
-def OutputMacro(symbol, declaration):
-    sid = common.CreateValidSGMLID(symbol)
-    condition = MakeConditionDescription(symbol)
-    synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link>" % (
-        sid, symbol)
-
-    fields = common.ParseMacroDeclaration(declaration, CreateValidSGML)
-    title = symbol
-    if len(fields) > 0:
-        title += '()'
-
-    desc = '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid, condition, title)
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-
-    if len(fields) > 0:
-        synop += "<phrase role=\"c_punctuation\">()</phrase>"
-
-    synop += "</entry></row>\n"
-
-    # Don't output the macro definition if is is a conditional macro or it
-    # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
-    # longer than 2 lines, otherwise we get lots of complicated macros like
-    # g_assert.
-    if symbol not in DeclarationConditional and not symbol.startswith('g_') \
-            and not re.search(r'^_?gnome_', symbol) and declaration.count('\n') < 2:
-        decl_out = CreateValidSGML(declaration)
-        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
-    else:
-        desc += "<programlisting language=\"C\">" + MakeReturnField("#define") + symbol
-        m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration)
-        if m:
-            args = m.group(1)
-            pad = ' ' * (RETURN_TYPE_FIELD_WIDTH - len("#define "))
-            # Align each line so that if should all line up OK.
-            args = args.replace('\n', '\n' + pad)
-            desc += CreateValidSGML(args)
-
-        desc += "</programlisting>\n"
-
-    desc += MakeDeprecationNote(symbol)
-
-    parameters = OutputParamDescriptions("MACRO", symbol, fields)
-
-    if symbol in SymbolDocs:
-        symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
-        desc += symbol_docs
-
-    desc += parameters
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputTypedef
-# Description : Returns the synopsis and detailed description of a typedef.
-# Arguments   : $symbol - the typedef.
-#                $declaration - the declaration of the typedef,
-#                  e.g. 'typedef unsigned int guint;'
-#
-
-def OutputTypedef(symbol, declaration):
-    sid = common.CreateValidSGMLID(symbol)
-    condition = MakeConditionDescription(symbol)
-    desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
-    synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
-        sid, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-
-    if symbol in DeclarationConditional:
-        decl_out = CreateValidSGML(declaration)
-        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
-
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputStruct
-# Description : Returns the synopsis and detailed description of a struct.
-#                We check if it is a object struct, and if so we only output
-#                parts of it that are noted as public fields.
-#                We also use a different IDs for object structs, since the
-#                original ID is used for the entire RefEntry.
-# Arguments   : $symbol - the struct.
-#                $declaration - the declaration of the struct.
-#
-
-def OutputStruct(symbol, declaration):
-
-    is_gtype = False
-    default_to_public = True
-    if CheckIsObject(symbol):
-        logging.info("Found struct gtype: %s", symbol)
-        is_gtype = True
-        default_to_public = ObjectRoots[symbol] == 'GBoxed'
-
-    sid = None
-    condition = None
-    if is_gtype:
-        sid = common.CreateValidSGMLID(symbol + "_struct")
-        condition = MakeConditionDescription(symbol + "_struct")
-    else:
-        sid = common.CreateValidSGMLID(symbol)
-        condition = MakeConditionDescription(symbol)
-
-    # Determine if it is a simple struct or it also has a typedef.
-    has_typedef = False
-    if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
-        has_typedef = True
-
-    type_output = None
-    desc = None
-    if has_typedef:
-        # For structs with typedefs we just output the struct name.
-        type_output = ''
-        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
-    else:
-        type_output = "struct"
-        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, 
symbol)
-
-    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
-        type_output, sid, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-
-    # Form a pretty-printed, private-data-removed form of the declaration
-
-    decl_out = ''
-    if re.search(r'^\s*$', declaration):
-        logging.info("Found opaque struct: %s", symbol)
-        decl_out = "typedef struct _%s %s;" % (symbol, symbol)
-    elif re.search(r'^\s*struct\s+\w+\s*;\s*$', declaration):
-        logging.info("Found opaque struct: %s", symbol)
-        decl_out = "struct %s;" % symbol
-    else:
-        m = re.search(
-            r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', declaration, 
flags=re.S)
-        if m:
-            struct_contents = m.group(2)
-
-            public = default_to_public
-            new_declaration = ''
-
-            for decl_line in struct_contents.splitlines():
-                logging.info("Struct line: %s", decl_line)
-                m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', decl_line)
-                m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line)
-                if m2:
-                    public = True
-                elif m3:
-                    public = False
-                elif public:
-                    new_declaration += decl_line + "\n"
-
-            if new_declaration:
-                # Strip any blank lines off the ends.
-                new_declaration = re.sub(r'^\s*\n', '', new_declaration)
-                new_declaration = re.sub(r'\n\s*$', r'\n', new_declaration)
-
-                if has_typedef:
-                    decl_out = "typedef struct {\n%s} %s;\n" % (new_declaration, symbol)
-                else:
-                    decl_out = "struct %s {\n%s};\n" % (symbol, new_declaration)
-
-        else:
-            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                              "Couldn't parse struct:\n%s" % declaration)
-
-        # If we couldn't parse the struct or it was all private, output an
-        # empty struct declaration.
-        if decl_out == '':
-            if has_typedef:
-                decl_out = "typedef struct _%s %s;" % (symbol, symbol)
-            else:
-                decl_out = "struct %s;" % symbol
-
-    decl_out = CreateValidSGML(decl_out)
-    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
-
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    # Create a table of fields and descriptions
-
-    # FIXME: Inserting &#160's into the produced type declarations here would
-    #        improve the output in most situations ... except for function
-    #        members of structs!
-    def pfunc(*args):
-        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
-    fields = common.ParseStructDeclaration(declaration, not default_to_public, 0, MakeXRef, pfunc)
-    params = SymbolParams.get(symbol)
-
-    # If no parameters are filled in, we don't generate the description
-    # table, for backwards compatibility.
-    found = False
-    if params:
-        found = next((True for p in params.values() if p.strip() != ''), False)
-
-    if found:
-        field_descrs = params
-        missing_parameters = ''
-        unused_parameters = ''
-        sid = common.CreateValidSGMLID(symbol + ".members")
-
-        desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
-<informaltable role="struct_members_table" pgwide="1" frame="none">
-<tgroup cols="3">
-<colspec colname="struct_members_name" colwidth="300px"/>
-<colspec colname="struct_members_description"/>
-<colspec colname="struct_members_annotations" colwidth="200px"/>
-<tbody>
-''' % sid
-
-        for field_name, text in fields.iteritems():
-            param_annotations = ''
-
-            desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % 
text
-            if field_name in field_descrs:
-                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
-                field_descr = ConvertMarkDown(symbol, field_descr)
-                # trim
-                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
-                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
-                desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry 
role=\"struct_member_annotations\">%s</entry>\n" % (
-                    field_descr, param_annotations)
-                del field_descrs[field_name]
-            else:
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
-                if missing_parameters != '':
-                    missing_parameters += ", " + field_name
-                else:
-                    missing_parameters = field_name
-
-                desc += "<entry /><entry />\n"
-
-            desc += "</row>\n"
-
-        desc += "</tbody></tgroup></informaltable>\n</refsect3>\n"
-        for field_name in field_descrs:
-            # Documenting those standard fields is not required anymore, but
-            # we don't want to warn if they are documented anyway.
-            m = re.search(r'(g_iface|parent_instance|parent_class)', field_name)
-            if m:
-                continue
-
-            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
-            if unused_parameters != '':
-                unused_parameters += ", " + field_name
-            else:
-                unused_parameters = field_name
-
-        # remember missing/unused parameters (needed in tmpl-free build)
-        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
-            AllIncompleteSymbols[symbol] = missing_parameters
-
-        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
-            AllUnusedSymbols[symbol] = unused_parameters
-    else:
-        if fields:
-            if symbol not in AllIncompleteSymbols:
-                AllIncompleteSymbols[symbol] = "<items>"
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Field descriptions for struct %s are missing in source code comment 
block." % symbol)
-                logging.info("Remaining structs fields: " + ':'.join(fields) + "\n")
-
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputUnion
-# Description : Returns the synopsis and detailed description of a union.
-# Arguments   : $symbol - the union.
-#                $declaration - the declaration of the union.
-#
-
-def OutputUnion(symbol, declaration):
-
-    is_gtype = False
-    if CheckIsObject(symbol):
-        logging.info("Found union gtype: %s", symbol)
-        is_gtype = True
-
-    sid = None
-    condition = None
-    if is_gtype:
-        sid = common.CreateValidSGMLID(symbol + "_union")
-        condition = MakeConditionDescription(symbol + "_union")
-    else:
-        sid = common.CreateValidSGMLID(symbol)
-        condition = MakeConditionDescription(symbol)
-
-    # Determine if it is a simple struct or it also has a typedef.
-    has_typedef = False
-    if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
-        has_typedef = True
-
-    type_output = None
-    desc = None
-    if has_typedef:
-        # For unions with typedefs we just output the union name.
-        type_output = ''
-        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
-    else:
-        type_output = "union"
-        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol)
-
-    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
-        type_output, sid, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    # Create a table of fields and descriptions
-
-    # FIXME: Inserting &#160's into the produced type declarations here would
-    #        improve the output in most situations ... except for function
-    #        members of structs!
-    def pfunc(*args):
-        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
-    fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc)
-    params = SymbolParams.get(symbol)
-
-    # If no parameters are filled in, we don't generate the description
-    # table, for backwards compatibility
-    found = False
-    if params:
-        found = next((True for p in params.values() if p.strip() != ''), False)
-
-    logging.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol, len(fields), found, 
has_typedef)
-
-    if found:
-        field_descrs = params
-        missing_parameters = ''
-        unused_parameters = ''
-        sid = common.CreateValidSGMLID('%s.members' % symbol)
-
-        desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
-<informaltable role="union_members_table" pgwide="1" frame="none">
-<tgroup cols="3">
-<colspec colname="union_members_name" colwidth="300px"/>
-<colspec colname="union_members_description"/>
-<colspec colname="union_members_annotations" colwidth="200px"/>
-<tbody>
-''' % sid
-
-        for field_name, text in fields.iteritems():
-            param_annotations = ''
-
-            desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
-            if field_name in field_descrs:
-                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
-                field_descr = ConvertMarkDown(symbol, field_descr)
-
-                # trim
-                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
-                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
-                desc += "<entry role=\"union_member_description\">%s</entry>\n<entry 
role=\"union_member_annotations\">%s</entry>\n" % (
-                    field_descr, param_annotations)
-                del field_descrs[field_name]
-            else:
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
-                if missing_parameters != '':
-                    missing_parameters += ", " + field_name
-                else:
-                    missing_parameters = field_name
-
-                desc += "<entry /><entry />\n"
-
-            desc += "</row>\n"
-
-        desc += "</tbody></tgroup></informaltable>\n</refsect3>"
-        for field_name in field_descrs:
-            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
-            if unused_parameters != '':
-                unused_parameters += ", " + field_name
-            else:
-                unused_parameters = field_name
-
-        # remember missing/unused parameters (needed in tmpl-free build)
-        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
-            AllIncompleteSymbols[symbol] = missing_parameters
-
-        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
-            AllUnusedSymbols[symbol] = unused_parameters
-    else:
-        if len(fields) > 0:
-            if symbol not in AllIncompleteSymbols:
-                AllIncompleteSymbols[symbol] = "<items>"
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Field descriptions for union %s are missing in source code comment 
block." % symbol)
-                logging.info("Remaining union fields: " + ':'.join(fields) + "\n")
-
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputEnum
-# Description : Returns the synopsis and detailed description of a enum.
-# Arguments   : $symbol - the enum.
-#                $declaration - the declaration of the enum.
-#
-
-def OutputEnum(symbol, declaration):
-    is_gtype = False
-    if CheckIsObject(symbol):
-        logging.info("Found enum gtype: %s", symbol)
-        is_gtype = True
-
-    sid = None
-    condition = None
-    if is_gtype:
-        sid = common.CreateValidSGMLID(symbol + "_enum")
-        condition = MakeConditionDescription(symbol + "_enum")
-    else:
-        sid = common.CreateValidSGMLID(symbol)
-        condition = MakeConditionDescription(symbol)
-
-    synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
-        sid, symbol)
-    desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    # Create a table of fields and descriptions
-
-    fields = common.ParseEnumDeclaration(declaration)
-    params = SymbolParams.get(symbol)
-
-    # If nothing at all is documented log a single summary warning at the end.
-    # Otherwise, warn about each undocumented item.
-
-    found = False
-    if params:
-        found = next((True for p in params.values() if p.strip() != ''), False)
-        field_descrs = params
-    else:
-        field_descrs = {}
-
-    missing_parameters = ''
-    unused_parameters = ''
-
-    sid = common.CreateValidSGMLID("%s.members" % symbol)
-    desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
-<informaltable role="enum_members_table" pgwide="1" frame="none">
-<tgroup cols="3">
-<colspec colname="enum_members_name" colwidth="300px"/>
-<colspec colname="enum_members_description"/>
-<colspec colname="enum_members_annotations" colwidth="200px"/>
-<tbody>
-''' % sid
-
-    for field_name in fields:
-        field_descr = field_descrs.get(field_name)
-        param_annotations = ''
-
-        sid = common.CreateValidSGMLID(field_name)
-        condition = MakeConditionDescription(field_name)
-        desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para 
id=\"%s\">%s</para></entry>\n" % (
-            sid, field_name)
-        if field_descr:
-            field_descr, param_annotations = ExpandAnnotation(symbol, field_descr)
-            field_descr = ConvertMarkDown(symbol, field_descr)
-            desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry 
role=\"enum_member_annotations\">%s</entry>\n" % (
-                field_descr, param_annotations)
-            del field_descrs[field_name]
-        else:
-            if found:
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Value description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
-                if missing_parameters != '':
-                    missing_parameters += ", " + field_name
-                else:
-                    missing_parameters = field_name
-            desc += "<entry /><entry />\n"
-        desc += "</row>\n"
-
-    desc += "</tbody></tgroup></informaltable>\n</refsect3>"
-    for field_name in field_descrs:
-        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                          "Value description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
-        if unused_parameters != '':
-            unused_parameters += ", " + field_name
-        else:
-            unused_parameters = field_name
-
-    # remember missing/unused parameters (needed in tmpl-free build)
-    if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
-        AllIncompleteSymbols[symbol] = missing_parameters
-
-    if unused_parameters != '' and (symbol not in AllUnusedSymbols):
-        AllUnusedSymbols[symbol] = unused_parameters
-
-    if not found:
-        if len(fields) > 0:
-            if symbol not in AllIncompleteSymbols:
-                AllIncompleteSymbols[symbol] = "<items>"
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "Value descriptions for %s are missing in source code comment block." % 
symbol)
-
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputVariable
-# Description : Returns the synopsis and detailed description of a variable.
-# Arguments   : $symbol - the extern'ed variable.
-#                $declaration - the declaration of the variable.
-#
-
-def OutputVariable(symbol, declaration):
-    sid = common.CreateValidSGMLID(symbol)
-    condition = MakeConditionDescription(symbol)
-
-    logging.info("ouputing variable: '%s' '%s'", symbol, declaration)
-
-    type_output = None
-    m1 = re.search(
-        
r'^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;',
 declaration)
-    m2 = re.search(
-        
r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=',
 declaration)
-    if m1:
-        mod1 = m1.group(1) or ''
-        ptr = m1.group(3) or ''
-        space = m1.group(4) or ''
-        mod2 = m1.group(5) or ''
-        type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2)
-    elif m2:
-        mod1 = m2.group(1) or ''
-        ptr = m2.group(3) or ''
-        space = m2.group(4) or ''
-        mod2 = m2.group(5) or ''
-        type_output = '%s%s%s%s' % (mod1, ptr, space, mod2)
-    else:
-        type_output = "extern"
-
-    synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
-        type_output, sid, symbol)
-
-    desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-
-    decl_out = CreateValidSGML(declaration)
-    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
-
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    if symbol in SymbolAnnotations:
-        param_desc = SymbolAnnotations[symbol]
-        param_annotations = ''
-        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
-        if param_annotations != '':
-            desc += "\n<para>%s</para>" % param_annotations
-
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputFunction
-# Description : Returns the synopsis and detailed description of a function.
-# Arguments   : $symbol - the function.
-#                $declaration - the declaration of the function.
-#
-
-def OutputFunction(symbol, declaration, symbol_type):
-    sid = common.CreateValidSGMLID(symbol)
-    condition = MakeConditionDescription(symbol)
-
-    # Take out the return type
-    #                      $1                                                                                
       $2   $3
-    regex = 
r'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n'
-    m = re.search(regex, declaration)
-    declaration = re.sub(regex, '', declaration)
-    type_modifier = m.group(1) or ''
-    type = m.group(2)
-    pointer = m.group(3)
-    # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
-    pointer = pointer.rstrip()
-    xref = MakeXRef(type, tagify(type, "returnvalue"))
-    start = ''
-    # if (symbol_type == 'USER_FUNCTION')
-    #    start = "typedef "
-    #
-
-    # We output const rather than G_CONST_RETURN.
-    type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier)
-    pointer = re.sub(r'G_CONST_RETURN', 'const', pointer)
-    pointer = re.sub(r'^\s+', '&#160;', pointer)
-
-    ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer)
-
-    indent_len = len(symbol) + 2
-    char1 = char2 = char3 = ''
-    if symbol_type == 'USER_FUNCTION':
-        indent_len += 3
-        char1 = "<phrase role=\"c_punctuation\">(</phrase>"
-        char2 = "*"
-        char3 = "<phrase role=\"c_punctuation\">)</phrase>"
-
-    symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3)
-    if indent_len < MAX_SYMBOL_FIELD_WIDTH:
-        symbol_desc_output = "%s%s%s%s " % (char1, char2, symbol, char3)
-    else:
-        indent_len = MAX_SYMBOL_FIELD_WIDTH - 8
-        symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1))
-
-    synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s&#160;<phrase 
role=\"c_punctuation\">()</phrase></entry></row>\n" % (
-        ret_type_output, symbol_output)
-
-    desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s&#160;()</title>\n" % (sid, condition, symbol)
-
-    desc += MakeIndexterms(symbol, sid)
-    desc += "\n"
-    desc += OutputSymbolExtraLinks(symbol)
-
-    desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output)
-
-    def tagfun(*args):
-        return tagify(args[0], "parameter")
-
-    fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun)
-
-    first = True
-    for field_name in fields.values():
-        if first:
-            desc += field_name
-            first = False
-        else:
-            desc += ",\n" + (' ' * indent_len) + field_name
-
-    desc += ");</programlisting>\n"
-
-    desc += MakeDeprecationNote(symbol)
-
-    if symbol in SymbolDocs:
-        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-    if symbol in SymbolAnnotations:
-        param_desc = SymbolAnnotations[symbol]
-        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
-        if param_annotations != '':
-            desc += "\n<para>%s</para>" % param_annotations
-
-    desc += OutputParamDescriptions("FUNCTION", symbol, fields.keys())
-    desc += OutputSymbolTraits(symbol)
-    desc += "</refsect2>\n"
-    return (synop, desc)
-
-
-#
-# Function    : OutputParamDescriptions
-# Description : Returns the DocBook output describing the parameters of a
-#                function, macro or signal handler.
-# Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
-#                  handlers have an implicit user_data parameter last.
-#                $symbol - the name of the function/macro being described.
-#               @fields - parsed fields from the declaration, used to determine
-#                  undocumented/unused entries
-#
-
-def OutputParamDescriptions(symbol_type, symbol, fields):
-    output = ''
-    num_params = 0
-    field_descrs = None
-
-    if fields:
-        field_descrs = [f for f in fields if f not in ['void', 'Returns']]
-    else:
-        field_descrs = []
-
-    params = SymbolParams.get(symbol)
-    logging.info("param_desc(%s, %s) = %s", symbol_type, symbol, str(params))
-    # This might be an empty dict, but for SIGNALS we append the user_data docs.
-    # TODO(ensonic): maybe create that docstring in GetSignals()
-    if params is not None:
-        returns = ''
-        params_desc = ''
-        missing_parameters = ''
-        unused_parameters = ''
-
-        for param_name, param_desc in params.iteritems():
-            (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
-            param_desc = ConvertMarkDown(symbol, param_desc)
-            # trim
-            param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M | re.S)
-            param_desc = re.sub(r'(\s|\n)+$', '', param_desc, flags=re.M | re.S)
-            if param_name == "Returns":
-                returns = param_desc
-                if param_annotations != '':
-                    returns += "\n<para>%s</para>" % param_annotations
-
-                elif param_name == "void":
-                    # FIXME: &common.LogWarning()?
-                    logging.info("!!!! void in params for %s?\n", symbol)
-            else:
-                if fields:
-                    if param_name not in field_descrs:
-                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                          "Parameter description for %s::%s is not used from source code 
comment block." % (symbol, param_name))
-                        if unused_parameters != '':
-                            unused_parameters += ", " + param_name
-                        else:
-                            unused_parameters = param_name
-                    else:
-                        field_descrs.remove(param_name)
-
-                if param_desc != '':
-                    params_desc += "<row><entry role=\"parameter_name\"><para>%s</para></entry>\n<entry 
role=\"parameter_description\">%s</entry>\n<entry role=\"parameter_annotations\">%s</entry></row>\n" % (
-                        param_name, param_desc, param_annotations)
-                    num_params += 1
-
-        for param_name in field_descrs:
-            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                              "Parameter description for %s::%s is missing in source code comment block." % 
(symbol, param_name))
-            if missing_parameters != '':
-                missing_parameters += ", " + param_name
-            else:
-                missing_parameters = param_name
-
-        # Signals have an implicit user_data parameter which we describe.
-        if symbol_type == "SIGNAL":
-            params_desc += "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry 
role=\"parameter_description\"><simpara>user data set when the signal handler was 
connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n"
-
-        # Start a table if we need one.
-        if params_desc != '':
-            sid = common.CreateValidSGMLID("%s.parameters" % symbol)
-
-            output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
-<informaltable role="parameters_table" pgwide="1" frame="none">
-<tgroup cols="3">
-<colspec colname="parameters_name" colwidth="150px"/>
-<colspec colname="parameters_description"/>
-<colspec colname="parameters_annotations" colwidth="200px"/>
-<tbody>
-''' % sid
-            output += params_desc
-            output += "</tbody></tgroup></informaltable>\n</refsect3>"
-
-        # Output the returns info last
-        if returns != '':
-            sid = common.CreateValidSGMLID("%s.returns" % symbol)
-
-            output += '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
-''' % sid
-            output += returns
-            output += "\n</refsect3>"
-
-        # remember missing/unused parameters (needed in tmpl-free build)
-        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
-            AllIncompleteSymbols[symbol] = missing_parameters
-
-        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
-            AllUnusedSymbols[symbol] = unused_parameters
-
-    if num_params == 0 and fields and field_descrs:
-        if symbol not in AllIncompleteSymbols:
-            AllIncompleteSymbols[symbol] = "<parameters>"
-    return output
-
-
-#
-# Function    : ParseStabilityLevel
-# Description : Parses a stability level and outputs a warning if it isn't
-#               valid.
-# Arguments   : $stability - the stability text.
-#                $file, $line - context for error message
-#                $message - description of where the level is from, to use in
-#               any error message.
-# Returns     : The parsed stability level string.
-#
-
-def ParseStabilityLevel(stability, file, line, message):
-
-    stability = stability.strip()
-    sl = stability.lower()
-
-    if sl == 'stable':
-        stability = "Stable"
-    elif stability == 'unstable':
-        stability = "Unstable"
-    elif stability == 'private':
-        stability = "Private"
-    else:
-        common.LogWarning(file, line, "%s is %s." % (message, stability) +
-                          "It should be one of these: Stable, Unstable, or Private.")
-    return stability
-
-
-#
-# Function    : OutputDBFile
-# Description : Outputs the final DocBook file for one section.
-# Arguments   : $file - the name of the file.
-#               $title - the title from the $MODULE-sections.txt file, which
-#                 will be overridden by the title in the template file.
-#               $section_id - the id to use for the toplevel tag.
-#               $includes - comma-separates list of include files added at top of
-#                 synopsis, with '<' '>' around them (if not already enclosed in '').
-#               $functions_synop - reference to the DocBook for the Functions Synopsis part.
-#               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
-#               $functions_details - reference to the DocBook for the Functions Details part.
-#               $other_details - reference to the DocBook for the Types and Values Details part.
-#               $signal_synop - reference to the DocBook for the Signal Synopsis part
-#               $signal_desc - reference to the DocBook for the Signal Description part
-#               $args_synop - reference to the DocBook for the Arg Synopsis part
-#               $args_desc - reference to the DocBook for the Arg Description part
-#               $hierarchy - reference to the DocBook for the Object Hierarchy part
-#               $interfaces - reference to the DocBook for the Interfaces part
-#               $implementations - reference to the DocBook for the Known Implementations part
-#               $prerequisites - reference to the DocBook for the Prerequisites part
-#               $derived - reference to the DocBook for the Derived Interfaces part
-#               $file_objects - reference to an array of objects in this file
-#
-
-def OutputDBFile(file, title, section_id, includes, functions_synop, other_synop, functions_details, 
other_details, signals_synop, signals_desc, args_synop, args_desc, hierarchy, interfaces, implementations, 
prerequisites, derived, file_objects):
-
-    logging.info("Output docbook for file %s with title '%s'", file, title)
-
-    # The edited title overrides the one from the sections file.
-    new_title = SymbolDocs.get(file + ":Title")
-    if new_title and not new_title.strip() == '':
-        title = new_title
-        logging.info("Found title: %s", title)
-
-    short_desc = SymbolDocs.get(file + ":Short_Description")
-    if not short_desc or short_desc.strip() == '':
-        short_desc = ''
-    else:
-        # Don't use ConvertMarkDown here for now since we don't want blocks
-        short_desc = ExpandAbbreviations(title + ":Short_description", short_desc)
-        logging.info("Found short_desc: %s", short_desc)
-
-    long_desc = SymbolDocs.get(file + ":Long_Description")
-    if not long_desc or long_desc.strip() == '':
-        long_desc = ''
-    else:
-        long_desc = ConvertMarkDown(title + ":Long_description", long_desc)
-        logging.info("Found long_desc: %s", long_desc)
-
-    see_also = SymbolDocs.get(file + ":See_Also")
-    if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
-        see_also = ''
-    else:
-        see_also = ConvertMarkDown(title + ":See_Also", see_also)
-        logging.info("Found see_also: %s", see_also)
-
-    if see_also:
-        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
-
-    stability = SymbolDocs.get(file + ":Stability_Level")
-    if not stability or re.search(r'^\s*$', stability):
-        stability = ''
-    else:
-        line_number = GetSymbolSourceLine(file + ":Stability_Level")
-        stability = ParseStabilityLevel(stability, file, line_number, "Section stability level")
-        logging.info("Found stability: %s", stability)
-
-    if stability:
-        AnnotationsUsed[stability] = 1
-        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
-            section_id, stability)
-    elif DEFAULT_STABILITY:
-        AnnotationsUsed[DEFAULT_STABILITY] = 1
-        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
-            section_id, DEFAULT_STABILITY)
-
-    image = SymbolDocs.get(file + ":Image")
-    if not image or re.search(r'^\s*$', image):
-        image = ''
-    else:
-        image = image.strip()
-
-        format = None
-
-        il = image.lower()
-        if re.search(r'jpe?g$', il):
-            format = "format='JPEG'"
-        elif il.endswith('png'):
-            format = "format='PNG'"
-        elif il.endswith('svg'):
-            format = "format='SVG'"
-        else:
-            format = ''
-
-        image = "  <inlinegraphic fileref='%s' %s/>\n" % (image, format)
-
-    include_output = ''
-    if includes:
-        include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
-        for include in includes.split(','):
-            if re.search(r'^\".+\"$', include):
-                include_output += "#include %s\n" % include
-            else:
-                include = re.sub(r'^\s+|\s+$', '', include, flags=re.S)
-                include_output += "#include &lt;%s&gt;\n" % include
-
-        include_output += "</synopsis></refsect1>\n"
-
-    extralinks = OutputSectionExtraLinks(title, "Section:%s" % file)
-
-    old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml')
-    new_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml.new')
-
-    OUTPUT = open(new_db_file, 'w')
-
-    object_anchors = ''
-    for fobject in file_objects:
-        if fobject == section_id:
-            continue
-        sid = common.CreateValidSGMLID(fobject)
-        logging.info("Adding anchor for %s\n", fobject)
-        object_anchors += "<anchor id=\"%s\"/>" % sid
-
-    # Make sure we produce valid docbook
-    if not functions_details:
-        functions_details = "<para />"
-
-    # We used to output this, but is messes up our common.UpdateFileIfChanged code
-    # since it changes every day (and it is only used in the man pages):
-    # "<refentry id="$section_id" revision="$mday $month $year">"
-
-    OUTPUT.write(REFENTRY.substitute({
-        'args_desc': args_desc,
-        'args_synop': args_synop,
-        'derived': derived,
-        'extralinks': extralinks,
-        'functions_details': functions_details,
-        'functions_synop': functions_synop,
-        'header': MakeDocHeader('refentry'),
-        'hierarchy': hierarchy,
-        'image': image,
-        'include_output': include_output,
-        'interfaces': interfaces,
-        'implementations': implementations,
-        'long_desc': long_desc,
-        'object_anchors': object_anchors,
-        'other_details': other_details,
-        'other_synop': other_synop,
-        'prerequisites': prerequisites,
-        'section_id': section_id,
-        'see_also': see_also,
-        'signals_desc': signals_desc,
-        'signals_synop': signals_synop,
-        'short_desc': short_desc,
-        'stability': stability,
-        'title': title,
-        'MODULE': MODULE.upper(),
-    }))
-    OUTPUT.close()
-
-    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
-
-
-#
-# Function    : OutputProgramDBFile
-# Description : Outputs the final DocBook file for one program.
-# Arguments   : $file - the name of the file.
-#               $section_id - the id to use for the toplevel tag.
-#
-
-def OutputProgramDBFile(program, section_id):
-    logging.info("Output program docbook for %s", program)
-
-    short_desc = SourceSymbolDocs.get(program + ":Short_Description")
-    if not short_desc or short_desc.strip() == '':
-        short_desc = ''
-    else:
-        # Don't use ConvertMarkDown here for now since we don't want blocks
-        short_desc = ExpandAbbreviations(program, short_desc)
-        logging.info("Found short_desc: %s", short_desc)
-
-    synopsis = SourceSymbolDocs.get(program + ":Synopsis")
-    if synopsis and synopsis.strip() != '':
-        items = synopsis.split(' ')
-        for i in range(0, len(items)):
-            parameter = items[i]
-            choice = "plain"
-            rep = ''
-
-            # first parameter is the command name
-            if i == 0:
-                synopsis = "<command>%s</command>\n" % parameter
-                continue
-
-            # square brackets indicate optional parameters, curly brackets
-            # indicate required parameters ("plain" parameters are also
-            # mandatory, but do not get extra decoration)
-            m1 = re.search(r'^\[(.+?)\]$', parameter)
-            m2 = re.search(r'^\{(.+?)\}$', parameter)
-            if m1:
-                choice = "opt"
-                parameter = m1.group(1)
-            elif m2:
-                choice = "req"
-                parameter = m2.group(1)
-
-            # parameters ending in "..." are repeatable
-            if parameter.endswith('...'):
-                rep = ' rep=\"repeat\"'
-                parameter = parameter[:-3]
-
-            # italic parameters are replaceable parameters
-            parameter = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', parameter)
-
-            synopsis += "<arg choice=\"%s\"%s>" % (choice, rep)
-            synopsis += parameter
-            synopsis += "</arg>\n"
-
-        logging.info("Found synopsis: %s", synopsis)
-    else:
-        synopsis = "<command>%s</command>" % program
-
-    long_desc = SourceSymbolDocs.get(program + ":Long_Description")
-    if not long_desc or long_desc.strip() == '':
-        long_desc = ''
-    else:
-        long_desc = ConvertMarkDown("%s:Long_description" % program, long_desc)
-        logging.info("Found long_desc: %s", long_desc)
-
-    options = ''
-    o = program + ":Options"
-    if o in SourceSymbolDocs:
-        opts = SourceSymbolDocs[o].split('\t')
-
-        logging.info('options: %d, %s', len(opts), str(opts))
-
-        options = "<refsect1>\n<title>Options</title>\n<variablelist>\n"
-        for k in range(0, len(opts), 2):
-            opt_desc = opts[k + 1]
-
-            opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_desc)
-
-            options += "<varlistentry>\n<term>"
-            opt_names = opts[k].split(',')
-            for i in range(len(opt_names)):
-                prefix = ', ' if i > 0 else ''
-                # italic parameters are replaceable parameters
-                opt_name = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_names[i])
-
-                options += "%s<option>%s</option>\n" % (prefix, opt_name)
-
-            options += "</term>\n"
-            options += "<listitem><para>%s</para></listitem>\n" % opt_desc
-            options += "</varlistentry>\n"
-
-        options += "</variablelist></refsect1>\n"
-
-    exit_status = SourceSymbolDocs.get(program + ":Returns")
-    if exit_status and exit_status != '':
-        exit_status = ConvertMarkDown("%s:Returns" % program, exit_status)
-        exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (
-            section_id, exit_status)
-    else:
-        exit_status = ''
-
-    see_also = SourceSymbolDocs.get(program + ":See_Also")
-    if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
-        see_also = ''
-    else:
-        see_also = ConvertMarkDown("%s:See_Also" % program, see_also)
-        logging.info("Found see_also: %s", see_also)
-
-    if see_also:
-        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
-
-    old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml")
-    new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new")
-
-    OUTPUT = open(new_db_file, 'w')
-
-    OUTPUT.write('''%s
-<refentry id="%s">
-<refmeta>
-<refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
-<manvolnum>1</manvolnum>
-<refmiscinfo>User Commands</refmiscinfo>
-</refmeta>
-<refnamediv>
-<refname>%s</refname>
-<refpurpose>%s</refpurpose>
-</refnamediv>
-<refsynopsisdiv>
-<cmdsynopsis>%s</cmdsynopsis>
-</refsynopsisdiv>
-<refsect1 id="%s.description" role="desc">
-<title role="desc.title">Description</title>
-%s
-</refsect1>
-%s%s%s
-</refentry>
-''' % (MakeDocHeader("refentry"), section_id, section_id, program, program, short_desc, synopsis, 
section_id, long_desc, options, exit_status, see_also))
-    OUTPUT.close()
-
-    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
-
-
-#
-# Function    : OutputExtraFile
-# Description : Copies an "extra" DocBook file into the output directory,
-#               expanding abbreviations
-# Arguments   : $file - the source file.
-#
-def OutputExtraFile(file):
-
-    basename = os.path.basename(file)
-
-    old_db_file = os.path.join(DB_OUTPUT_DIR, basename)
-    new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new")
-
-    contents = open(file).read()
-
-    OUTPUT = open(new_db_file, 'w')
-    OUTPUT.write(ExpandAbbreviations(basename + " file", contents))
-    OUTPUT.close()
-
-    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
-
-
-#
-# Function    : OutputBook
-# Description : Outputs the entities that need to be included into the
-#                main docbook file for the module.
-# Arguments   : $book_top - the declarations of the entities, which are added
-#                  at the top of the main docbook file.
-#                $book_bottom - the references to the entities, which are
-#                  added in the main docbook file at the desired position.
-#
-
-def OutputBook(book_top, book_bottom):
-
-    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top")
-    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new")
-
-    OUTPUT = open(new_file, 'w')
-    OUTPUT.write(book_top)
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_file, new_file, 0)
-
-    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom")
-    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new")
-
-    OUTPUT = open(new_file, 'w')
-    OUTPUT.write(book_bottom)
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_file, new_file, 0)
-
-    # If the main docbook file hasn't been created yet, we create it here.
-    # The user can tweak it later.
-    if MAIN_SGML_FILE and not os.path.exists(MAIN_SGML_FILE):
-        OUTPUT = open(MAIN_SGML_FILE, 'w')
-
-        OUTPUT.write('''%s
-<book id="index">
-  <bookinfo>
-    <title>&package_name; Reference Manual</title>
-    <releaseinfo>
-      for &package_string;.
-      The latest version of this documentation can be found on-line at
-      <ulink role="online-location" 
url="http://[SERVER]/&package_name;/index.html";>http://[SERVER]/&package_name;/</ulink>.
-    </releaseinfo>
-  </bookinfo>
-
-  <chapter>
-    <title>[Insert title here]</title>
-    %s
-  </chapter>
-''' % (MakeDocHeader("book"), book_bottom))
-        if os.path.exists(OBJECT_TREE_FILE):
-            OUTPUT.write('''  <chapter id="object-tree">
-    <title>Object Hierarchy</title>
-    <xi:include href="xml/tree_index.sgml"/>
-  </chapter>
-''')
-        else:
-            OUTPUT.write('''  <!-- enable this when you use gobject types
-  <chapter id="object-tree">
-    <title>Object Hierarchy</title>
-    <xi:include href="xml/tree_index.sgml"/>
-  </chapter>
-  -->
-''')
-
-        OUTPUT.write('''  <index id="api-index-full">
-    <title>API Index</title>
-    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
-  </index>
-  <index id="deprecated-api-index" role="deprecated">
-    <title>Index of deprecated API</title>
-    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
-  </index>
-''')
-        if AnnotationsUsed:
-            OUTPUT.write('''  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
-''')
-        else:
-            OUTPUT.write('''  <!-- enable this when you use gobject introspection annotations
-  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
-  -->
-''')
-
-        OUTPUT.write('''</book>
-''')
-
-        OUTPUT.close()
-
-
-#
-# Function    : CreateValidSGML
-# Description : This turns any chars which are used in SGML into entities,
-#                e.g. '<' into '&lt;'
-# Arguments   : $text - the text to turn into proper SGML.
-#
-
-def CreateValidSGML(text):
-    text = re.sub(r'&', r'&amp;', text)        # Do this first, or the others get messed up.
-    text = re.sub(r'<', r'&lt;', text)
-    text = re.sub(r'>', r'&gt;', text)
-    # browsers render single tabs inconsistently
-    text = re.sub(r'([^\s])\t([^\s])', r'\1&#160;\2', text)
-    return text
-
-
-#
-# Function    : ConvertSGMLChars
-# Description : This is used for text in source code comment blocks, to turn
-#               chars which are used in SGML into entities, e.g. '<' into
-#               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
-#               unconditionally or only if the character doesn't seem to be
-#               part of an SGML construct (tag or entity reference).
-# Arguments   : $text - the text to turn into proper SGML.
-#
-
-def ConvertSGMLChars(symbol, text):
-
-    if INLINE_MARKUP_MODE:
-        # For the XML/SGML mode only convert to entities outside CDATA sections.
-        return ModifyXMLElements(text, symbol,
-                                 "<!\\[CDATA\\[|<programlisting[^>]*>",
-                                 ConvertSGMLCharsEndTag,
-                                 ConvertSGMLCharsCallback)
-    # For the simple non-sgml mode, convert to entities everywhere.
-
-    # First, convert freestanding & to &amp
-    text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)
-    text = re.sub(r'<', r'&lt;', text)
-    # Allow '>' at beginning of string for blockquote markdown
-    text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
-
-    return text
-
-
-def ConvertSGMLCharsEndTag(t):
-    if t == '<![CDATA[':
-        return "]]>"
-    return "</programlisting>"
-
-
-def ConvertSGMLCharsCallback(text, symbol, tag):
-    if re.search(r'^<programlisting', tag):
-        # We can handle <programlisting> specially here.
-        return ModifyXMLElements(text, symbol,
-                                 "<!\\[CDATA\\[",
-                                 ConvertSGMLCharsEndTag,
-                                 ConvertSGMLCharsCallback2)
-    elif tag == '':
-        # If we're not in CDATA convert to entities.
-        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
-        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
-        # Allow '>' at beginning of string for blockquote markdown
-        text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
-
-        # Handle "#include <xxxxx>"
-        text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
-
-    return text
-
-
-def ConvertSGMLCharsCallback2(text, symbol, tag):
-
-    # If we're not in CDATA convert to entities.
-    # We could handle <programlisting> differently, though I'm not sure it helps.
-    if tag == '':
-        # replace only if its not a tag
-        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
-        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
-        text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'&gt;', text)
-        # Handle "#include <xxxxx>"
-        text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
-
-    return text
-
-
-#
-# Function    : ExpandAnnotation
-# Description : This turns annotations into acronym tags.
-# Arguments   : $symbol - the symbol being documented, for error messages.
-#                $text - the text to expand.
-#
-def ExpandAnnotation(symbol, param_desc):
-    param_annotations = ''
-
-    # look for annotations at the start of the comment part
-    # function level annotations don't end with a colon ':'
-    m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc)
-    if m:
-        param_desc = param_desc[m.end():]
-
-        annotations = re.split(r'\)\s*\(', m.group(1))
-        logging.info("annotations for %s: '%s'\n", symbol, m.group(1))
-        for annotation in annotations:
-            # need to search for the longest key-match in %AnnotationDefinition
-            match_length = 0
-            match_annotation = ''
-
-            for annotationdef in AnnotationDefinition:
-                if annotation.startswith(annotationdef):
-                    if len(annotationdef) > match_length:
-                        match_length = len(annotationdef)
-                        match_annotation = annotationdef
-
-            annotation_extra = ''
-            if match_annotation != '':
-                m = re.search(match_annotation + r'\s+(.*)', annotation)
-                if m:
-                    annotation_extra = " " + m.group(1)
-
-                AnnotationsUsed[match_annotation] = 1
-                param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra)
-            else:
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "unknown annotation \"%s\" in documentation for %s." % (annotation, 
symbol))
-                param_annotations += "[%s]" % annotation
-
-        param_desc = param_desc.strip()
-        m = re.search(r'^(.*?)\.*\s*$', param_desc, flags=re.S)
-        param_desc = m.group(1) + '. '
-
-    if param_annotations != '':
-        param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
-
-    return (param_desc, param_annotations)
-
-
-#
-# Function    : ExpandAbbreviations
-# Description : This turns the abbreviations function(), macro(), @param,
-#               %constant, and #symbol into appropriate DocBook markup.
-#               CDATA sections and <programlisting> parts are skipped.
-# Arguments   : $symbol - the symbol being documented, for error messages.
-#                $text - the text to expand.
-#
-
-def ExpandAbbreviations(symbol, text):
-    # Note: This is a fallback and normally done in the markdown parser
-
-    # Convert "|[" and "]|" into the start and end of program listing examples.
-    # Support \[<!-- language="C" --> modifiers
-    text = re.sub(r'\|\[<!-- language="([^"]+)" -->', r'<informalexample><programlisting 
language="\1"><![CDATA[', text)
-    text = re.sub(r'\|\[', r'<informalexample><programlisting><![CDATA[', text)
-    text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text)
-
-    # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
-    # as such)
-    return ModifyXMLElements(text, symbol,
-                             "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
-                             ExpandAbbreviationsEndTag,
-                             ExpandAbbreviationsCallback)
-
-
-# Returns the end tag (as a regexp) corresponding to the given start tag.
-def ExpandAbbreviationsEndTag(start_tag):
-    if start_tag == r'<!\[CDATA\[':
-        return "]]>"
-    if start_tag == "<!DOCTYPE":
-        return '>'
-    m = re.search(r'<(\w+)', start_tag)
-    if m:
-        return "</%s>" % m.group(1)
-
-    logging.warning('no end tag for "%s"', start_tag)
-    return ''
-
-
-# Called inside or outside each CDATA or <programlisting> section.
-def ExpandAbbreviationsCallback(text, symbol, tag):
-
-    if tag.startswith(r'^<programlisting'):
-        # Handle any embedded CDATA sections.
-        return ModifyXMLElements(text, symbol,
-                                 "<!\\[CDATA\\[",
-                                 ExpandAbbreviationsEndTag,
-                                 ExpandAbbreviationsCallback2)
-    elif tag == '':
-        # NOTE: this is a fallback. It is normally done by the Markdown parser.
-        # but is also used for OutputExtraFile
-
-        # We are outside any CDATA or <programlisting> sections, so we expand
-        # any gtk-doc abbreviations.
-
-        # Convert '@param()'
-        # FIXME: we could make those also links ($symbol.$2), but that would be less
-        # useful as the link target is a few lines up or down
-        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()</parameter>', text)
-
-        # Convert 'function()' or 'macro()'.
-        # if there is abc_*_def() we don't want to make a link to _def()
-        # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
-        def f1(m):
-            return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function"))
-        text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text)
-        # handle #Object.func()
-        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text)
-
-        # Convert '@param', but not '\@param'.
-        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2</parameter>', text)
-        text = re.sub(r'/\\\@', r'\@', text)
-
-        # Convert '%constant', but not '\%constant'.
-        # Also allow negative numbers, e.g. %-1.
-        def f2(m):
-            return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2), "literal"))
-        text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text)
-        text = re.sub(r'\\\%', r'\%', text)
-
-        # Convert '#symbol', but not '\#symbol'.
-        def f3(m):
-            return m.group(1) + MakeHashXRef(m.group(2), "type")
-        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text)
-        text = re.sub(r'\\#', '#', text)
-
-    return text
-
-
-# This is called inside a <programlisting>
-def ExpandAbbreviationsCallback2(text, symbol, tag):
-    if tag == '':
-        # We are inside a <programlisting> but outside any CDATA sections,
-        # so we expand any gtk-doc abbreviations.
-        # FIXME: why is this different from &ExpandAbbreviationsCallback(),
-        #        why not just call it
-        text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text)
-    elif tag == "<![CDATA[":
-        # NOTE: this is a fallback. It is normally done by the Markdown parser.
-        text = ReplaceEntities(text, symbol)
-
-    return text
-
-
-def MakeHashXRef(symbol, tag):
-    text = symbol
-
-    # Check for things like '#include', '#define', and skip them.
-    if symbol in PreProcessorDirectives:
-        return "#%s" % symbol
-
-    # Get rid of special suffixes ('-struct','-enum').
-    text = re.sub(r'-struct$', '', text)
-    text = re.sub(r'-enum$', '', text)
-
-    # If the symbol is in the form "Object::signal", then change the symbol to
-    # "Object-signal" and use "signal" as the text.
-    if '::' in symbol:
-        o, s = symbol.split('::', 1)
-        symbol = '%s-%s' % (o, s)
-        text = '“' + s + '”'
-
-    # If the symbol is in the form "Object:property", then change the symbol to
-    # "Object--property" and use "property" as the text.
-    if ':' in symbol:
-        o, p = symbol.split(':', 1)
-        symbol = '%s--%s' % (o, p)
-        text = '“' + p + '”'
-
-    if tag != '':
-        text = tagify(text, tag)
-
-    return MakeXRef(symbol, text)
-
-
-#
-# Function    : ModifyXMLElements
-# Description : Looks for given XML element tags within the text, and calls
-#               the callback on pieces of text inside & outside those elements.
-#               Used for special handling of text inside things like CDATA
-#               and <programlisting>.
-# Arguments   : $text - the text.
-#               $symbol - the symbol currently being documented (only used for
-#                      error messages).
-#               $start_tag_regexp - the regular expression to match start tags.
-#                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
-#                      CDATA sections or programlisting elements.
-#               $end_tag_func - function which is passed the matched start tag
-#                      and should return the appropriate end tag string regexp.
-#               $callback - callback called with each part of the text. It is
-#                      called with a piece of text, the symbol being
-#                      documented, and the matched start tag or '' if the text
-#                      is outside the XML elements being matched.
-#
-def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback):
-    before_tag = start_tag = end_tag_regexp = end_tag = None
-    result = ''
-
-    logging.debug('symbol: %s text: [%s]', symbol, text)
-
-    m = re.search(start_tag_regexp, text, flags=re.S)
-    while m:
-        before_tag = text[:m.start()]  # Prematch for last successful match string
-        start_tag = m.group(0)         # Last successful match
-        text = text[m.end():]          # Postmatch for last successful match string
-
-        logging.debug('symbol: %s matched start %s: text: [%s]', symbol, start_tag, text)
-
-        result += callback(before_tag, symbol, '')
-        result += start_tag
-
-        # get the matching end-tag for current tag
-        end_tag_regexp = end_tag_func(start_tag)
-
-        m2 = re.search(end_tag_regexp, text, flags=re.S)
-        if m2:
-            before_tag = text[:m2.start()]
-            end_tag = m2.group(0)
-            text = text[m2.end():]
-
-            logging.debug('symbol: %s matched end %s: text: [%s]', symbol, end_tag, text)
-
-            result += callback(before_tag, symbol, start_tag)
-            result += end_tag
-        else:
-            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                              "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol))
-            # Just assume it is all inside the tag.
-            result += callback(text, symbol, start_tag)
-            text = ''
-        m = re.search(start_tag_regexp, text, flags=re.S)
-
-    # Handle any remaining text outside the tags.
-    result += callback(text, symbol, '')
-
-    return result
-
-
-# Adds a tag around some text.
-# e.g tagify("Text", "literal") => "<literal>Text</literal>".
-def tagify(text, elem):
-    return '<' + elem + '>' + text + '</' + elem + '>'
-
-
-#
-# Function    : MakeDocHeader
-# Description : Builds a docbook header for the given tag
-# Arguments   : $tag - doctype tag
-#
-
-def MakeDocHeader(tag):
-    header = doctype_header
-    header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, header)
-
-    # fix the path for book since this is one level up
-    if tag == 'book':
-        header = re.sub(
-            r'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r'<!ENTITY % gtkdocentities SYSTEM 
"\1">', header)
-    return header
-
-
-#
-# Function    : MakeXRef
-# Description : This returns a cross-reference link to the given symbol.
-#                Though it doesn't try to do this for a few standard C types
-#                that it        knows won't be in the documentation.
-# Arguments   : $symbol - the symbol to try to create a XRef to.
-#               $text - text text to put inside the XRef, defaults to $symbol
-#
-
-def MakeXRef(symbol, text=None):
-    symbol = symbol.strip()
-
-    if not text:
-        text = symbol
-
-        # Get rid of special suffixes ('-struct','-enum').
-        text = re.sub(r'-struct$', '', text)
-        text = re.sub(r'-enum$', '', text)
-
-    if ' ' in symbol:
-        return text
-
-    logging.info("Getting type link for %s -> %s", symbol, text)
-
-    symbol_id = common.CreateValidSGMLID(symbol)
-    return "<link linkend=\"%s\">%s</link>" % (symbol_id, text)
-
-
-#
-# Function    : MakeIndexterms
-# Description : This returns a indexterm elements for the given symbol
-# Arguments   : $symbol - the symbol to create indexterms for
-#
-
-def MakeIndexterms(symbol, sid):
-    terms = ''
-    sortas = ''
-
-    # make the index useful, by ommiting the namespace when sorting
-    if NAME_SPACE != '':
-        m = re.search(r'^%s\_?(.*)' % NAME_SPACE, symbol, flags=re.I)
-        if m:
-            sortas = ' sortas="%s"' % m.group(1)
-
-    if symbol in Deprecated:
-        terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (
-            sid, sortas, symbol)
-        IndexEntriesDeprecated[symbol] = sid
-        IndexEntriesFull[symbol] = sid
-    if symbol in Since:
-        since = Since[symbol].strip()
-        if since != '':
-            terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (
-                sid, since, sortas, symbol)
-        IndexEntriesSince[symbol] = sid
-        IndexEntriesFull[symbol] = sid
-    if terms == '':
-        terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
-        IndexEntriesFull[symbol] = sid
-    return terms
-
-
-#
-# Function    : MakeDeprecationNote
-# Description : This returns a deprecation warning for the given symbol.
-# Arguments   : $symbol - the symbol to try to create a warning for.
-#
-
-def MakeDeprecationNote(symbol):
-    desc = ''
-    if symbol in Deprecated:
-        desc += "<warning><para><literal>%s</literal> " % symbol
-
-        note = Deprecated[symbol]
-
-        m = re.search(r'^\s*([0-9\.]+)\s*:?', note)
-        if m:
-            desc += "has been deprecated since version %s and should not be used in newly-written 
code.</para>" % m.group(
-                1)
-        else:
-            desc += "is deprecated and should not be used in newly-written code.</para>"
-
-        note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note)
-        note = note.strip()
-
-        if note != '':
-            note = ConvertMarkDown(symbol, note)
-            desc += " " + note
-
-        desc += "</warning>\n"
-
-    return desc
-
-
-#
-# Function    : MakeConditionDescription
-# Description : This returns a sumary of conditions for the given symbol.
-# Arguments   : $symbol - the symbol to try to create the sumary.
-#
-
-def MakeConditionDescription(symbol):
-    desc = ''
-    if symbol in Deprecated:
-        if desc != '':
-            desc += "|"
-        m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol])
-        if m:
-            desc += "deprecated:%s" % m.group(1)
-        else:
-            desc += "deprecated"
-
-    if symbol in Since:
-        if desc != '':
-            desc += "|"
-        m = re.search(r'^\s*(.*?)\s*$', Since[symbol])
-        if m:
-            desc += "since:%s" % m.group(1)
-        else:
-            desc += "since"
-
-    if symbol in StabilityLevel:
-        if desc != '':
-            desc += "|"
-
-        desc += "stability:" + StabilityLevel[symbol]
-
-    if desc != '':
-        cond = re.sub(r'"', r'&quot;', desc)
-        desc = ' condition=\"%s\"' % cond
-        logging.info("condition for '%s' = '%s'", symbol, desc)
-
-    return desc
-
-
-#
-# Function    : GetHierarchy
-# Description : Returns the DocBook output describing the ancestors and
-#               immediate children of a GObject subclass. It uses the
-#               global @Objects and @ObjectLevels arrays to walk the tree.
-#
-# Arguments   : $object - the GtkObject subclass.
-#               @hierarchy - previous hierarchy
-#
-
-def GetHierarchy(gobject, hierarchy):
-    # Find object in the objects array.
-    found = False
-    children = []
-    level = 0
-    j = 0
-    for i in range(len(Objects)):
-        if found:
-            if ObjectLevels[i] <= level:
-                break
-
-            elif ObjectLevels[i] == level + 1:
-                children.append(Objects[i])
-
-        elif Objects[i] == gobject:
-            found = True
-            j = i
-            level = ObjectLevels[i]
-
-    if not found:
-        return hierarchy
-
-    logging.info("=== Hierachy for: %s (%d existing entries) ===", gobject, len(hierarchy))
-
-    # Walk up the hierarchy, pushing ancestors onto the ancestors array.
-    ancestors = [gobject]
-    logging.info("Level: %s", level)
-    while level > 1:
-        j -= 1
-        if ObjectLevels[j] < level:
-            ancestors.append(Objects[j])
-            level = ObjectLevels[j]
-            logging.info("Level: %s", level)
-
-    # Output the ancestors, indented and with links.
-    logging.info('%d ancestors', len(ancestors))
-    last_index = 0
-    level = 1
-    for i in range(len(ancestors) - 1, -1, -1):
-        ancestor = ancestors[i]
-        ancestor_id = common.CreateValidSGMLID(ancestor)
-        indent = ' ' * (level * 4)
-        # Don't add a link to the current object, i.e. when i == 0.
-        if i > 0:
-            entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
-            alt_text = indent + ancestor
-        else:
-            entry_text = indent + ancestor
-            alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
-
-        logging.info("Checking for '%s' or '%s'", entry_text, alt_text)
-        # Check if we already have this object
-        index = -1
-        for j in range(len(hierarchy)):
-            if hierarchy[j] == entry_text or (hierarchy[j] == alt_text):
-                index = j
-                break
-        if index == -1:
-            # We have a new entry, find insert position in alphabetical order
-            found = False
-            for j in range(last_index, len(hierarchy)):
-                if not re.search(r'^' + indent, hierarchy[j]):
-                    last_index = j
-                    found = True
-                    break
-                elif re.search(r'^%s[^ ]' % indent, hierarchy[j]):
-                    stripped_text = hierarchy[j]
-                    if r'<link linkend' not in entry_text:
-                        stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text)
-                        stripped_text = re.sub(r'</link>', '', stripped_text)
-
-                    if entry_text < stripped_text:
-                        last_index = j
-                        found = True
-                        break
-
-            # Append to bottom
-            if not found:
-                last_index = len(hierarchy)
-
-            logging.debug('insert at %d: %s', last_index, entry_text)
-            hierarchy.insert(last_index, entry_text)
-            last_index += 1
-        else:
-            # Already have this one, make sure we use the not linked version
-            if r'<link linkend' not in entry_text:
-                hierarchy[j] = entry_text
-
-            # Remember index as base insert point
-            last_index = index + 1
-
-        level += 1
-
-    # Output the children, indented and with links.
-    logging.info('%d children', len(children))
-    for i in range(len(children)):
-        sid = common.CreateValidSGMLID(children[i])
-        indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i])
-        logging.debug('insert at %d: %s', last_index, indented_text)
-        hierarchy.insert(last_index, indented_text)
-        last_index += 1
-    return hierarchy
-
-
-#
-# Function    : GetInterfaces
-# Description : Returns the DocBook output describing the interfaces
-#               implemented by a class. It uses the global %Interfaces hash.
-# Arguments   : $object - the GtkObject subclass.
-#
-
-def GetInterfaces(gobject):
-    text = ''
-
-    # Find object in the objects array.
-    if gobject in Interfaces:
-        ifaces = Interfaces[gobject].split()
-        text = '''<para>
-%s implements
-''' % gobject
-        count = len(ifaces)
-        for i in range(count):
-            sid = common.CreateValidSGMLID(ifaces[i])
-            text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i])
-            if i < count - 2:
-                text += ', '
-            elif i < count - 1:
-                text += ' and '
-            else:
-                text += '.'
-        text += '</para>\n'
-    return text
-
-
-#
-# Function    : GetImplementations
-# Description : Returns the DocBook output describing the implementations
-#               of an interface. It uses the global %Interfaces hash.
-# Arguments   : $object - the GtkObject subclass.
-#
-
-def GetImplementations(gobject):
-    text = ''
-
-    impls = []
-    for key in Interfaces:
-        if re.search(r'\b%s\b' % gobject, Interfaces[key]):
-            impls.append(key)
-
-    count = len(impls)
-    if count > 0:
-        impls.sort()
-        text = '''<para>
-%s is implemented by
-''' % gobject
-        for i in range(count):
-            sid = common.CreateValidSGMLID(impls[i])
-            text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i])
-            if i < count - 2:
-                text += ', '
-            elif i < count - 1:
-                text += ' and '
-            else:
-                text += '.'
-        text += '</para>\n'
-    return text
-
-
-#
-# Function    : GetPrerequisites
-# Description : Returns the DocBook output describing the prerequisites
-#               of an interface. It uses the global %Prerequisites hash.
-# Arguments   : $iface - the interface.
-#
-
-def GetPrerequisites(iface):
-    text = ''
-
-    if iface in Prerequisites:
-        text = '''<para>
-%s requires
-''' % iface
-        prereqs = Prerequisites[iface].split()
-        count = len(prereqs)
-        for i in range(count):
-            sid = common.CreateValidSGMLID(prereqs[i])
-            text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i])
-            if i < count - 2:
-                text += ', '
-            elif i < count - 1:
-                text += ' and '
-            else:
-                text += '.'
-        text += '</para>\n'
-    return text
-
-
-#
-# Function    : GetDerived
-# Description : Returns the DocBook output describing the derived interfaces
-#               of an interface. It uses the global %Prerequisites hash.
-# Arguments   : $iface - the interface.
-#
-
-def GetDerived(iface):
-    text = ''
-
-    derived = []
-    for key in Prerequisites:
-        if re.search(r'\b%s\b' % iface, Prerequisites[key]):
-            derived.append(key)
-
-    count = len(derived)
-    if count > 0:
-        derived.sort()
-        text = '''<para>
-%s is required by
-''' % iface
-        for i in range(count):
-            sid = common.CreateValidSGMLID(derived[i])
-            text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i])
-            if i < count - 2:
-                text += ', '
-            elif i < count - 1:
-                text += ' and '
-            else:
-                text += '.'
-        text += '</para>\n'
-    return text
-
-
-#
-# Function    : GetSignals
-# Description : Returns the synopsis and detailed description DocBook output
-#                for the signal handlers of a given GtkObject subclass.
-# Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
-#
-
-def GetSignals(gobject):
-    synop = ''
-    desc = ''
-
-    for i in range(len(SignalObjects)):
-        if SignalObjects[i] == gobject:
-            logging.info("Found signal: %s", SignalNames[i])
-            name = SignalNames[i]
-            symbol = '%s::%s' % (gobject, name)
-            sid = common.CreateValidSGMLID('%s-%s' % (gobject, name))
-
-            desc += "<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> 
signal</title>\n" % (
-                sid, name)
-            desc += MakeIndexterms(symbol, sid)
-            desc += "\n"
-            desc += OutputSymbolExtraLinks(symbol)
-
-            desc += "<programlisting language=\"C\">"
-
-            m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i])
-            type_modifier = m.group(1) or ''
-            gtype = m.group(2)
-            pointer = m.group(3)
-            xref = MakeXRef(gtype, tagify(gtype, "returnvalue"))
-
-            ret_type_output = '%s%s%s' % (type_modifier, xref, pointer)
-            callback_name = "user_function"
-            desc += '%s\n%s (' % (ret_type_output, callback_name)
-
-            indentation = ' ' * (len(callback_name) + 2)
-
-            sourceparams = SourceSymbolParams.get(symbol)
-            params = SignalPrototypes[i].splitlines()
-            type_len = len("gpointer")
-            name_len = len("user_data")
-            # do two passes, the first one is to calculate padding
-            for l in range(2):
-                for j in range(len(params)):
-                    param_name = None
-                    # allow alphanumerics, '_', '[' & ']' in param names
-                    m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j])
-                    if m:
-                        gtype = m.group(1)
-                        pointer = m.group(2)
-                        if sourceparams:
-                            param_name = sourceparams.keys()[j]
-                            logging.info('from sourceparams: "%s" (%d: %s)', param_name, j, params[j])
-                        else:
-                            param_name = m.group(3)
-                            logging.info('from params: "%s" (%d: %s)', param_name, j, params[j])
-
-                        if not param_name:
-                            param_name = "arg%d" % j
-
-                        if l == 0:
-                            if len(gtype) + len(pointer) > type_len:
-                                type_len = len(gtype) + len(pointer)
-                            if len(param_name) > name_len:
-                                name_len = len(param_name)
-                        else:
-                            logging.info("signal arg[%d]: '%s'", j, param_name)
-                            xref = MakeXRef(gtype, tagify(gtype, "type"))
-                            pad = ' ' * (type_len - len(gtype) - len(pointer))
-                            desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name)
-                            desc += indentation
-
-                    else:
-                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                          "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i]))
-
-            xref = MakeXRef("gpointer", tagify("gpointer", "type"))
-            pad = ' ' * (type_len - len("gpointer"))
-            desc += '%s%s user_data)' % (xref, pad)
-            desc += "</programlisting>\n"
-
-            flags = SignalFlags[i]
-            flags_string = ''
-            if flags:
-                if 'f' in flags:
-                    flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
-
-                elif 'l' in flags:
-                    flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
-
-                elif 'c' in flags:
-                    flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
-                    flags_string = "Cleanup"
-
-                if 'r' in flags:
-                    if flags_string:
-                        flags_string += " / "
-                    flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
-
-                if 'd' in flags:
-                    if flags_string:
-                        flags_string += " / "
-                    flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
-
-                if 'a' in flags:
-                    if flags_string:
-                        flags_string += " / "
-                    flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
-
-                if 'h' in flags:
-                    if flags_string:
-                        flags_string += " / "
-                    flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
-
-            synop += "<row><entry role=\"signal_type\">%s</entry><entry role=\"signal_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"signal_flags\">%s</entry></row>\n" % (
-                ret_type_output, sid, name, flags_string)
-
-            parameters = OutputParamDescriptions("SIGNAL", symbol, None)
-            logging.info("formatted signal params: '%s' -> '%s'", symbol, parameters)
-
-            AllSymbols[symbol] = 1
-            if symbol in SymbolDocs:
-                symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
-
-                desc += symbol_docs
-
-                if not IsEmptyDoc(SymbolDocs[symbol]):
-                    AllDocumentedSymbols[symbol] = 1
-
-            if symbol in SymbolAnnotations:
-                param_desc = SymbolAnnotations[symbol]
-                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
-                if param_annotations != '':
-                    desc += "\n<para>%s</para>" % param_annotations
-
-            desc += MakeDeprecationNote(symbol)
-
-            desc += parameters
-            if flags_string:
-                desc += "<para>Flags: %s</para>\n" % flags_string
-
-            desc += OutputSymbolTraits(symbol)
-            desc += "</refsect2>"
-
-    return (synop, desc)
-
-
-#
-# Function    : GetArgs
-# Description : Returns the synopsis and detailed description DocBook output
-#                for the Args of a given GtkObject subclass.
-# Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
-#
-
-def GetArgs(gobject):
-    synop = ''
-    desc = ''
-    child_synop = ''
-    child_desc = ''
-    style_synop = ''
-    style_desc = ''
-
-    for i in range(len(ArgObjects)):
-        if ArgObjects[i] == gobject:
-            logging.info("Found arg: %s", ArgNames[i])
-            name = ArgNames[i]
-            flags = ArgFlags[i]
-            flags_string = ''
-            kind = ''
-            id_sep = ''
-
-            if 'c' in flags:
-                kind = "child property"
-                id_sep = "c-"
-            elif 's' in flags:
-                kind = "style property"
-                id_sep = "s-"
-            else:
-                kind = "property"
-
-            # Remember only one colon so we don't clash with signals.
-            symbol = '%s:%s' % (gobject, name)
-            # use two dashes and ev. an extra separator here for the same reason.
-            sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name))
-
-            atype = ArgTypes[i]
-            type_output = None
-            arange = ArgRanges[i]
-            range_output = CreateValidSGML(arange)
-            default = ArgDefaults[i]
-            default_output = CreateValidSGML(default)
-
-            if atype == "GtkString":
-                atype = "char&#160;*"
-
-            if atype == "GtkSignal":
-                atype = "GtkSignalFunc, gpointer"
-                type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
-            elif re.search(r'^(\w+)\*$', atype):
-                m = re.search(r'^(\w+)\*$', atype)
-                type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + "&#160;*"
-            else:
-                type_output = MakeXRef(atype, tagify(atype, "type"))
-
-            if 'r' in flags:
-                flags_string = "Read"
-
-            if 'w' in flags:
-                if flags_string:
-                    flags_string += " / "
-                flags_string += "Write"
-
-            if 'x' in flags:
-                if flags_string:
-                    flags_string += " / "
-                flags_string += "Construct"
-
-            if 'X' in flags:
-                if flags_string:
-                    flags_string += " / "
-                flags_string += "Construct Only"
-
-            AllSymbols[symbol] = 1
-            blurb = ''
-            if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]):
-                blurb = ConvertMarkDown(symbol, SymbolDocs[symbol])
-                logging.info(".. [%s][%s]", SymbolDocs[symbol], blurb)
-                AllDocumentedSymbols[symbol] = 1
-
-            else:
-                if ArgBlurbs[i] != '':
-                    blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>"
-                    AllDocumentedSymbols[symbol] = 1
-                else:
-                    # FIXME: print a warning?
-                    logging.info(".. no description")
-
-            pad1 = ''
-            if len(name) < 24:
-                pad1 = " " * (24 - len(name))
-
-            arg_synop = "<row><entry role=\"property_type\">%s</entry><entry role=\"property_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"property_flags\">%s</entry></row>\n" % (
-                type_output, sid, name, flags_string)
-            arg_desc = "<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> 
%s</title>\n" % (
-                sid, name, kind)
-            arg_desc += MakeIndexterms(symbol, sid)
-            arg_desc += "\n"
-            arg_desc += OutputSymbolExtraLinks(symbol)
-
-            arg_desc += "<programlisting>  “%s”%s %s</programlisting>\n" % (name, pad1, type_output)
-            arg_desc += blurb
-            if symbol in SymbolAnnotations:
-                param_desc = SymbolAnnotations[symbol]
-                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
-                if param_annotations != '':
-                    arg_desc += "\n<para>%s</para>" % param_annotations
-
-            arg_desc += MakeDeprecationNote(symbol)
-
-            if flags_string:
-                arg_desc += "<para>Flags: %s</para>\n" % flags_string
-
-            if arange != '':
-                arg_desc += "<para>Allowed values: %s</para>\n" % range_output
-
-            if default != '':
-                arg_desc += "<para>Default value: %s</para>\n" % default_output
-
-            arg_desc += OutputSymbolTraits(symbol)
-            arg_desc += "</refsect2>\n"
-
-            if 'c' in flags:
-                child_synop += arg_synop
-                child_desc += arg_desc
-
-            elif 's' in flags:
-                style_synop += arg_synop
-                style_desc += arg_desc
-
-            else:
-                synop += arg_synop
-                desc += arg_desc
-
-    return (synop, child_synop, style_synop, desc, child_desc, style_desc)
-
-
-#
-# Function    : ReadSourceDocumentation
-# Description : This reads in the documentation embedded in comment blocks
-#                in the source code (for Gnome).
-#
-#                Parameter descriptions override any in the template files.
-#                Function descriptions are placed before any description from
-#                the template files.
-#
-#                It recursively descends the source directory looking for .c
-#                files and scans them looking for specially-formatted comment
-#                blocks.
-#
-# Arguments   : $source_dir - the directory to scan.
-# m###############################################################
-
-def ReadSourceDocumentation(source_dir):
-
-    # prepend entries from @SOURCE_DIR
-    for sdir in SOURCE_DIRS:
-        # Check if the filename is in the ignore list.
-        m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), source_dir)
-        if m1:
-            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
-            if m2:
-                logging.info("Skipping source directory: %s", source_dir)
-                return
-            else:
-                logging.info("No match for: %s", m1.group(1))
-        else:
-            logging.info("No match for: %s", source_dir)
-
-    logging.info("Scanning source directory: %s", source_dir)
-
-    # This array holds any subdirectories found.
-    subdirs = []
-
-    suffix_list = SOURCE_SUFFIXES.split(',')
-
-    for ifile in os.listdir(source_dir):
-        logging.debug("... : %s", ifile)
-        if ifile.startswith('.'):
-            continue
-        fname = os.path.join(source_dir, ifile)
-        if os.path.isdir(fname):
-            subdirs.append(fname)
-        elif SOURCE_SUFFIXES:
-            for suffix in suffix_list:
-                if ifile.endswith("." + suffix):
-                    ScanSourceFile(fname)
-        elif re.search(r'\.[ch]$', ifile):
-            ScanSourceFile(fname)
-
-    # Now recursively scan the subdirectories.
-    for sdir in subdirs:
-        ReadSourceDocumentation(sdir)
-
-
-#
-# Function    : ScanSourceFile
-# Description : Scans one source file looking for specially-formatted comment
-#               blocks. Later MergeSourceDocumentation() is copying over the
-#               doc blobs that are not suppressed/ignored.
-#
-# Arguments   : $file - the file to scan.
-#
-
-def ScanSourceFile(ifile):
-
-    # prepend entries from @SOURCE_DIR
-    for idir in SOURCE_DIRS:
-        # Check if the filename is in the ignore list.
-        m1 = re.search(r'^%s/(.*)$' % re.escape(idir), ifile)
-        if m1:
-            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
-            if m2:
-                logging.info("Skipping source file: %s", ifile)
-                return
-
-    m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile)
-    if m:
-        basename = m.group(1)
-    else:
-        common.LogWarning(ifile, 1, "Can't find basename for this filename.")
-        basename = ifile
-
-    # Check if the basename is in the list of files to ignore.
-    if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), IGNORE_FILES):
-        logging.info("Skipping source file: %s", ifile)
-        return
-
-    logging.info("Scanning source file: %s", ifile)
-
-    SRCFILE = open(ifile)
-    in_comment_block = False
-    symbol = None
-    in_part = ''
-    description = ''
-    return_desc = ''
-    since_desc = stability_desc = deprecated_desc = ''
-    params = OrderedDict()
-    param_name = None
-    line_number = 0
-    for line in SRCFILE:
-        line_number += 1
-        # Look for the start of a comment block.
-        if not in_comment_block:
-            if re.search(r'^\s*/\*.*\*/', line):
-                # one-line comment - not gtkdoc
-                pass
-            elif re.search(r'^\s*/\*\*\s', line):
-                logging.info("Found comment block start")
-
-                in_comment_block = True
-
-                # Reset all the symbol data.
-                symbol = ''
-                in_part = ''
-                description = ''
-                return_desc = ''
-                since_desc = ''
-                deprecated_desc = ''
-                stability_desc = ''
-                params = OrderedDict()
-                param_name = None
-
-            continue
-
-        # We're in a comment block. Check if we've found the end of it.
-        if re.search(r'^\s*\*+/', line):
-            if not symbol:
-                # maybe its not even meant to be a gtk-doc comment?
-                common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment 
block.")
-            else:
-                # Add the return value description onto the end of the params.
-                if return_desc:
-                    # TODO(ensonic): check for duplicated Return docs
-                    # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol)
-                    params['Returns'] = return_desc
-
-                # Convert special characters
-                description = ConvertSGMLChars(symbol, description)
-                for (param_name, param_desc) in params.iteritems():
-                    params[param_name] = ConvertSGMLChars(symbol, param_desc)
-
-                # Handle Section docs
-                m = re.search(r'SECTION:\s*(.*)', symbol)
-                m2 = re.search(r'PROGRAM:\s*(.*)', symbol)
-                if m:
-                    real_symbol = m.group(1)
-                    long_descr = real_symbol + ":Long_Description"
-
-                    if long_descr not in KnownSymbols or KnownSymbols[long_descr] != 1:
-                        common.LogWarning(
-                            ifile, line_number, "Section %s is not defined in the %s-sections.txt file." % 
(real_symbol, MODULE))
-
-                    logging.info("SECTION DOCS found in source for : '%s'", real_symbol)
-                    for param_name, param_desc in params.iteritems():
-                        logging.info("   '" + param_name + "'")
-                        param_name = param_name.lower()
-                        key = None
-                        if param_name == "short_description":
-                            key = real_symbol + ":Short_Description"
-                        elif param_name == "see_also":
-                            key = real_symbol + ":See_Also"
-                        elif param_name == "title":
-                            key = real_symbol + ":Title"
-                        elif param_name == "stability":
-                            key = real_symbol + ":Stability_Level"
-                        elif param_name == "section_id":
-                            key = real_symbol + ":Section_Id"
-                        elif param_name == "include":
-                            key = real_symbol + ":Include"
-                        elif param_name == "image":
-                            key = real_symbol + ":Image"
-
-                        if key:
-                            SourceSymbolDocs[key] = param_desc
-                            SourceSymbolSourceFile[key] = ifile
-                            SourceSymbolSourceLine[key] = line_number
-
-                    SourceSymbolDocs[long_descr] = description
-                    SourceSymbolSourceFile[long_descr] = ifile
-                    SourceSymbolSourceLine[long_descr] = line_number
-                elif m2:
-                    real_symbol = m2.group(1)
-                    key = None
-                    section_id = None
-
-                    logging.info("PROGRAM DOCS found in source for '%s'", real_symbol)
-                    for param_name, param_desc in params.iteritems():
-                        logging.info("PROGRAM key %s: '%s'", real_symbol, param_name)
-                        param_name = param_name.lower()
-                        key = None
-                        if param_name == "short_description":
-                            key = real_symbol + ":Short_Description"
-                        elif param_name == "see_also":
-                            key = real_symbol + ":See_Also"
-                        elif param_name == "section_id":
-                            key = real_symbol + ":Section_Id"
-                        elif param_name == "synopsis":
-                            key = real_symbol + ":Synopsis"
-                        elif param_name == "returns":
-                            key = real_symbol + ":Returns"
-                        elif re.search(r'^(-.*)', param_name):
-                            logging.info("PROGRAM opts: '%s': '%s'", param_name, param_desc)
-                            key = real_symbol + ":Options"
-                            opts = []
-                            opts_str = SourceSymbolDocs.get(key)
-                            if opts_str:
-                                opts = opts_str.split('\t')
-                            opts.append(param_name)
-                            opts.append(param_desc)
-
-                            logging.info("Setting options for symbol: %s: '%s'", real_symbol, 
'\t'.join(opts))
-                            SourceSymbolDocs[key] = '\t'.join(opts)
-                            continue
-
-                        if key:
-                            logging.info("PROGRAM value %s: '%s'", real_symbol, param_desc.rstrip())
-                            SourceSymbolDocs[key] = param_desc.rstrip()
-                            SourceSymbolSourceFile[key] = ifile
-                            SourceSymbolSourceLine[key] = line_number
-
-                    long_descr = real_symbol + ":Long_Description"
-                    SourceSymbolDocs[long_descr] = description
-                    SourceSymbolSourceFile[long_descr] = ifile
-                    SourceSymbolSourceLine[long_descr] = line_number
-
-                    section_id = SourceSymbolDocs.get(real_symbol + ":Section_Id")
-                    if section_id and section_id.strip() != '':
-                        # Remove trailing blanks and use as is
-                        section_id = section_id.rstrip()
-                    else:
-                        section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol))
-                    OutputProgramDBFile(real_symbol, section_id)
-
-                else:
-                    logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
-                    SourceSymbolDocs[symbol] = description
-                    SourceSymbolParams[symbol] = params
-                    SourceSymbolSourceFile[symbol] = ifile
-                    SourceSymbolSourceLine[symbol] = line_number
-
-                if since_desc:
-                    arr = since_desc.splitlines()
-                    since_desc = arr[0].strip()
-                    extra_lines = arr[1:]
-                    logging.info("Since(%s) : [%s]", symbol, since_desc)
-                    Since[symbol] = ConvertSGMLChars(symbol, since_desc)
-                    if len(extra_lines) > 1:
-                        common.LogWarning(ifile, line_number, "multi-line since docs found")
-
-                if stability_desc:
-                    stability_desc = ParseStabilityLevel(
-                        stability_desc, ifile, line_number, "Stability level for %s" % symbol)
-                    StabilityLevel[symbol] = ConvertSGMLChars(symbol, stability_desc)
-
-                if deprecated_desc:
-                    if symbol not in Deprecated:
-                        # don't warn for signals and properties
-                        # if ($symbol !~ m/::?(.*)/)
-                        if symbol in DeclarationTypes:
-                            common.LogWarning(ifile, line_number,
-                                              "%s is deprecated in the inline comments, but no deprecation 
guards were found around the declaration. (See the --deprecated-guards option for gtkdoc-scan.)" % symbol)
-
-                    Deprecated[symbol] = ConvertSGMLChars(symbol, deprecated_desc)
-
-            in_comment_block = False
-            continue
-
-        # Get rid of ' * ' at start of every line in the comment block.
-        line = re.sub(r'^\s*\*\s?', '', line)
-        # But make sure we don't get rid of the newline at the end.
-        if not line.endswith('\n'):
-            line = line + "\n"
-
-        logging.info("scanning :%s", line)
-
-        # If we haven't found the symbol name yet, look for it.
-        if not symbol:
-            m1 = re.search(r'^\s*(SECTION:\s*\S+)', line)
-            m2 = re.search(r'^\s*(PROGRAM:\s*\S+)', line)
-            m3 = re.search(r'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line)
-            if m1:
-                symbol = m1.group(1)
-                logging.info("SECTION DOCS found in source for : '%s'", symbol)
-            elif m2:
-                symbol = m2.group(1)
-                logging.info("PROGRAM DOCS found in source for : '%s'", symbol)
-            elif m3:
-                symbol = m3.group(1)
-                annotation = m3.group(2)
-                logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
-                if annotation:
-                    annotation = annotation.strip()
-                    if annotation != '':
-                        SymbolAnnotations[symbol] = annotation
-                        logging.info("remaining text for %s: '%s'", symbol, annotation)
-
-            continue
-
-        if in_part == "description":
-            # Get rid of 'Description:'
-            line = re.sub(r'^\s*Description:', '', line)
-
-        m1 = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I)
-        m2 = re.search(r'^\s*since:', line, flags=re.I)
-        m3 = re.search(r'^\s*deprecated:', line, flags=re.I)
-        m4 = re.search(r'^\s*stability:', line, flags=re.I)
-
-        if m1:
-            # we're in param section and have not seen the blank line
-            if in_part != '':
-                return_desc = line[m1.end():]
-                in_part = "return"
-                continue
-
-        if m2:
-            # we're in param section and have not seen the blank line
-            if in_part != "param":
-                since_desc = line[m2.end():]
-                in_part = "since"
-                continue
-
-        elif m3:
-            # we're in param section and have not seen the blank line
-            if in_part != "param":
-                deprecated_desc = line[m3.end():]
-                in_part = "deprecated"
-                continue
-
-        elif m4:
-            stability_desc = line[m4.end():]
-            in_part = "stability"
-            continue
-
-        if in_part == "description":
-            description += line
-            continue
-        elif in_part == "return":
-            return_desc += line
-            continue
-        elif in_part == "since":
-            since_desc += line
-            continue
-        elif in_part == "stability":
-            stability_desc += line
-            continue
-        elif in_part == "deprecated":
-            deprecated_desc += line
-            continue
-
-        # We must be in the parameters. Check for the empty line below them.
-        if re.search(r'^\s*$', line):
-            in_part = "description"
-            continue
-
-        # Look for a parameter name.
-        m = re.search(r'^\s*@(.+?)\s*:\s*', line)
-        if m:
-            param_name = m.group(1)
-            param_desc = line[m.end():]
-
-            logging.info("Found parameter: %s", param_name)
-            # Allow varargs variations
-            if re.search(r'^\.\.\.$', param_name):
-                param_name = "..."
-
-            logging.info("Found param for symbol %s : '%s'= '%s'", symbol, param_name, line)
-
-            params[param_name] = param_desc
-            in_part = "param"
-            continue
-        elif in_part == '':
-            logging.info("continuation for %s annotation '%s'", symbol, line)
-            annotation = re.sub(r'^\s+|\s+$', '', line)
-            if symbol in SymbolAnnotations:
-                SymbolAnnotations[symbol] += annotation
-            else:
-                SymbolAnnotations[symbol] = annotation
-            continue
-
-        # We must be in the middle of a parameter description, so add it on
-        # to the last element in @params.
-        if not param_name:
-            common.LogWarning(file, line_number, "Parsing comment block file : parameter expected, but got 
'%s'" % line)
-        else:
-            params[param_name] += line
-
-    SRCFILE.close()
-
-
-#
-# Function    : OutputMissingDocumentation
-# Description : Outputs report of documentation coverage to a file
-#
-# Arguments   : none
-#
-
-def OutputMissingDocumentation():
-    old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.txt")
-    new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new")
-
-    n_documented = 0
-    n_incomplete = 0
-    total = 0
-    symbol = None
-    percent = None
-    buffer = ''
-    buffer_deprecated = ''
-    buffer_descriptions = ''
-
-    UNDOCUMENTED = open(new_undocumented_file, 'w')
-
-    for symbol in sorted(AllSymbols.keys()):
-        # FIXME: should we print common.LogWarnings for undocumented stuff?
-        # DEBUG
-        # location = "defined at " + GetSymbolSourceFile(symbol) + ":" + GetSymbolSourceLine(symbol) + "\n"
-        # DEBUG
-        m = re.search(
-            
r':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol)
-        m2 = re.search(r':(Long_Description|Short_Description)', symbol)
-        if not m:
-            total += 1
-            if symbol in AllDocumentedSymbols:
-                n_documented += 1
-                if symbol in AllIncompleteSymbols:
-                    n_incomplete += 1
-                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
-                    #$buffer += "\t0: ".$location
-
-            elif symbol in Deprecated:
-                if symbol in AllIncompleteSymbols:
-                    n_incomplete += 1
-                    buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
-                    #$buffer += "\t1a: ".$location
-                else:
-                    buffer_deprecated += symbol + "\n"
-                    #$buffer += "\t1b: ".$location
-
-            else:
-                if symbol in AllIncompleteSymbols:
-                    n_incomplete += 1
-                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
-                    #$buffer += "\t2a: ".$location
-                else:
-                    buffer += symbol + "\n"
-                    #$buffer += "\t2b: ".$location
-
-        elif m2:
-            total += 1
-            if (symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0)\
-               or (symbol in AllDocumentedSymbols and AllDocumentedSymbols[symbol] > 0):
-                n_documented += 1
-            else:
-                buffer_descriptions += symbol + "\n"
-
-    if total == 0:
-        percent = 100
-    else:
-        percent = (n_documented / total) * 100.0
-
-    UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n" % percent)
-    UNDOCUMENTED.write("%s symbols documented.\n" % n_documented)
-    UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete)
-    UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented))
-
-    if buffer_deprecated != '':
-        buffer += "\n" + buffer_deprecated
-
-    if buffer_descriptions != '':
-        buffer += "\n" + buffer_descriptions
-
-    if buffer != '':
-        UNDOCUMENTED.write("\n\n" + buffer)
-
-    UNDOCUMENTED.close()
-
-    return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0)
-
-
-#
-# Function    : OutputUndeclaredSymbols
-# Description : Outputs symbols that are listed in the section file, but not
-#               declaration is found in the sources
-#
-# Arguments   : none
-#
-
-def OutputUndeclaredSymbols():
-    old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt")
-    new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new")
-
-    UNDECLARED = open(new_undeclared_file, 'w')
-
-    if UndeclaredSymbols:
-        UNDECLARED.write("\n".join(sorted(UndeclaredSymbols.keys())))
-        UNDECLARED.write("\n")
-        print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE)
-
-    UNDECLARED.close()
-
-    return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0)
-
-
-#
-# Function    : OutputUnusedSymbols
-# Description : Outputs symbols that are documented in comments, but not
-#               declared in the sources
-#
-# Arguments   : none
-#
-
-def OutputUnusedSymbols():
-    num_unused = 0
-    old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt")
-    new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new")
-
-    UNUSED = open(new_unused_file, 'w')
-
-    for symbol in sorted(Declarations.keys()):
-        if not symbol in DeclarationOutput:
-            UNUSED.write("%s\n" % symbol)
-            num_unused += 1
-
-    for symbol in sorted(AllUnusedSymbols.keys()):
-        UNUSED.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n")
-        num_unused += 1
-
-    UNUSED.close()
-    if num_unused != 0:
-        common.LogWarning(
-            old_unused_file, 1, "%d unused declarations. They should be added to %s-sections.txt in the 
appropriate place." % (num_unused, MODULE))
-
-    return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0)
-
-
-#
-# Function    : OutputAllSymbols
-# Description : Outputs list of all symbols to a file
-#
-# Arguments   : none
-#
-
-def OutputAllSymbols():
-    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-symbols.txt"), 'w')
-
-    for symbol in sorted(AllSymbols.keys()):
-        SYMBOLS.write(symbol + "\n")
-    SYMBOLS.close()
-
-
-#
-# Function    : OutputSymbolsWithoutSince
-# Description : Outputs list of all symbols without a since tag to a file
-#
-# Arguments   : none
-#
-
-def OutputSymbolsWithoutSince():
-    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-nosince.txt"), 'w')
-
-    for symbol in sorted(SourceSymbolDocs.keys()):
-        if symbol in Since:
-            SYMBOLS.write(symbol + "\n")
-    SYMBOLS.close()
-
-
-def CheckParamsDocumented(symbol, params):
-    stype = DeclarationTypes.get(symbol)
-
-    item = "Parameter"
-    if stype:
-        if stype == 'STRUCT':
-            item = "Field"
-        elif stype == 'ENUM':
-            item = "Value"
-        elif stype == 'UNION':
-            item = "Field"
-    else:
-        stype = "SIGNAL"
-    logging.info("Check param docs for %s, params: %s entries, type=%s", symbol, len(params), stype)
-
-    if len(params) > 0:
-        logging.info("params: %s", str(params))
-        for (param_name, param_desc) in params.iteritems():
-            # Output a warning if the parameter is empty and remember for stats.
-            if param_name != "void" and not re.search(r'\S', param_desc):
-                if symbol in AllIncompleteSymbols:
-                    AllIncompleteSymbols[symbol] += ", " + param_name
-                else:
-                    AllIncompleteSymbols[symbol] = param_name
-
-                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                                  "%s description for %s::%s is missing in source code comment block." % 
(item, symbol, param_name))
-
-    elif len(params) == 0:
-        AllIncompleteSymbols[symbol] = "<items>"
-        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
-                          "%s descriptions for %s are missing in source code comment block." % (item, 
symbol))
-
-
-#
-# Function    : MergeSourceDocumentation
-# Description : This merges documentation read from a source file into the
-#                documentation read in from a template file.
-#
-#                Parameter descriptions override any in the template files.
-#                Function descriptions are placed before any description from
-#                the template files.
-#
-# Arguments   : none
-#
-
-def MergeSourceDocumentation():
-
-    # add whats found in the source
-    symbols = set(SourceSymbolDocs.keys())
-
-    # and add known symbols from -sections.txt
-    for symbol in KnownSymbols.keys():
-        if KnownSymbols[symbol] == 1:
-            symbols.add(symbol)
-
-    logging.info("num source entries: %d", len(symbols))
-
-    for symbol in symbols:
-        AllSymbols[symbol] = 1
-
-        if symbol in SourceSymbolDocs:
-            logging.info("merging [%s] from source", symbol)
-
-            # remove leading and training whitespaces
-            src_docs = SourceSymbolDocs[symbol].strip()
-            if src_docs != '':
-                AllDocumentedSymbols[symbol] = 1
-
-            SymbolDocs[symbol] = src_docs
-
-            # merge parameters
-            if symbol in SourceSymbolParams:
-                param_docs = SourceSymbolParams[symbol]
-                SymbolParams[symbol] = param_docs
-                # if this symbol is documented, check if docs are complete
-                # remove all xml-tags and whitespaces
-                check_docs = re.sub(r'\s', '', re.sub(r'<.*?>', '', src_docs))
-                if check_docs != '' and param_docs:
-                    CheckParamsDocumented(symbol, param_docs)
-        else:
-            logging.info("[%s] undocumented", symbol)
-
-    logging.info("num doc entries: %d", len(SymbolDocs))
-
-
-#
-# Function    : IsEmptyDoc
-# Description : Check if a doc-string is empty. Its also regarded as empty if
-#               it only consist of whitespace or e.g. FIXME.
-# Arguments   : the doc-string
-#
-def IsEmptyDoc(doc):
-    if re.search(r'^\s*$', doc):
-        return True
-    if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc):
-        return True
-    return False
-
-
-#
-# Function    : ConvertMarkDown
-# Description : Converts mark down syntax to the respective docbook.
-#               http://de.wikipedia.org/wiki/Markdown
-#               Inspired by the design of ParseDown
-#               http://parsedown.org/
-#               Copyright (c) 2013 Emanuil Rusev, erusev.com
-# Arguments   : the symbol name, the doc-string
-#
-
-def ConvertMarkDown(symbol, text):
-    text = MarkDownParse(text, symbol)
-    return text
-
-
-# SUPPORTED MARKDOWN
-# ==================
-#
-# Atx-style Headers
-# -----------------
-#
-# # Header 1
-#
-# ## Header 2 ##
-#
-# Setext-style Headers
-# --------------------
-#
-# Header 1
-# ========
-#
-# Header 2
-# --------
-#
-# Ordered (unnested) Lists
-# ------------------------
-#
-# 1. item 1
-#
-# 1. item 2 with loooong
-#    description
-#
-# 3. item 3
-#
-# Note: we require a blank line above the list items
-#
-
-# TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
-
-def MarkDownParseBlocks(lines, symbol, context):
-    md_blocks = []
-    md_block = {"type": ''}
-
-    logging.debug("parsing %s lines", len(lines))
-    for line in lines:
-        logging.info("type='%s', int='%s', parsing '%s'", md_block["type"], md_block.get('interrupted'), 
line)
-        first_char = None
-        if line:
-            first_char = line[0]
-
-        if md_block["type"] == "markup":
-            if 'closed' not in md_block:
-                if md_block["start"] in line:
-                    md_block["depth"] += 1
-
-                if md_block["end"] in line:
-                    if md_block["depth"] > 0:
-                        md_block["depth"] -= 1
-                    else:
-                        logging.info("closing tag '%s'", line)
-                        md_block["closed"] = 1
-                        # TODO(ensonic): reparse inner text with MarkDownParseLines?
-
-                md_block["text"] += "\n" + line
-                logging.info("add to markup: '%s'", line)
-                continue
-
-        deindented_line = line.lstrip()
-
-        if md_block["type"] == "heading":
-            # a heading is ended by any level less than or equal
-            if md_block["level"] == 1:
-                heading_match = re.search(r'^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
-                if re.search(r'^={4,}[ \t]*$', line):
-                    text = md_block["lines"].pop()
-                    md_block.pop("interrupted", None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "heading",
-                                'text': text,
-                                'lines': [],
-                                'level': 1,
-                                }
-                    continue
-                elif heading_match:
-                    md_block.pop("interrupted", None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "heading",
-                                'text': heading_match.group(1),
-                                'lines': [],
-                                'level': 1,
-                                }
-                    if heading_match.group(2):
-                        md_block['id'] = heading_match.group(2)
-                    continue
-                else:
-                    # push lines into the block until the end is reached
-                    md_block["lines"].append(line)
-                    continue
-
-            else:
-                heading_match = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', 
line)
-                if re.search(r'^[=]{4,}[ \t]*$', line):
-                    text = md_block["lines"].pop()
-                    md_block.pop("interrupted", None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "heading",
-                                'text': text,
-                                'lines': [],
-                                'level': 1,
-                                }
-                    continue
-                elif re.search(r'^[-]{4,}[ \t]*$', line):
-                    text = md_block["lines"].pop()
-                    md_block.pop("interrupted", None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "heading",
-                                'text': text,
-                                'lines': [],
-                                'level': 2,
-                                }
-                    continue
-                elif heading_match:
-                    md_block.pop("interrupted", None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "heading",
-                                'text': heading_match.group(2),
-                                'lines': [],
-                                'level': len(heading_match.group(1))
-                                }
-                    if heading_match.group(3):
-                        md_block['id'] = heading_match.group(3)
-                    continue
-                else:
-                    # push lines into the block until the end is reached
-                    md_block["lines"].append(line)
-                    continue
-        elif md_block["type"] == "code":
-            end_of_code_match = re.search(r'^[ \t]*\]\|(.*)', line)
-            if end_of_code_match:
-                md_blocks.append(md_block)
-                md_block = {'type': "paragraph",
-                            'text': end_of_code_match.group(1),
-                            'lines': [],
-                            }
-            else:
-                md_block["lines"].append(line)
-            continue
-
-        if deindented_line == '':
-            logging.info('setting "interrupted" due to empty line')
-            md_block["interrupted"] = 1
-            continue
-
-        if md_block["type"] == "quote":
-            if 'interrupted' not in md_block:
-                line = re.sub(r'^[ ]*>[ ]?', '', line)
-                md_block["lines"].append(line)
-                continue
-
-        elif md_block["type"] == "li":
-            marker = md_block["marker"]
-            marker_match = re.search(r'^([ ]{0,3})(%s)[ ](.*)' % marker, line)
-            if marker_match:
-                indentation = marker_match.group(1)
-                if md_block["indentation"] != indentation:
-                    md_block["lines"].append(line)
-                else:
-                    ordered = md_block["ordered"]
-                    md_block.pop('last', None)
-                    md_blocks.append(md_block)
-                    md_block = {'type': "li",
-                                'ordered': ordered,
-                                'indentation': indentation,
-                                'marker': marker,
-                                'last': 1,
-                                'lines': [re.sub(r'^[ ]{0,4}', '', marker_match.group(3))],
-                                }
-                continue
-
-            if 'interrupted' in md_block:
-                if first_char == " ":
-                    md_block["lines"].append('')
-                    line = re.sub(r'^[ ]{0,4}', '', line)
-                    md_block["lines"].append(line)
-                    md_block.pop("interrupted", None)
-                    continue
-            else:
-                line = re.sub(r'^[ ]{0,4}', '', line)
-                md_block["lines"].append(line)
-                continue
-
-        # indentation sensitive types
-        heading_match = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
-        code_match = re.search(r'^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?', line)
-        if heading_match:
-            # atx heading (#)
-            md_blocks.append(md_block)
-            md_block = {'type': "heading",
-                        'text': heading_match.group(2),
-                        'lines': [],
-                        'level': len(heading_match.group(1)),
-                        }
-            if heading_match.group(3):
-                md_block['id'] = heading_match.group(3)
-            continue
-        elif re.search(r'^={4,}[ \t]*$', line):
-            # setext heading (====)
-
-            if md_block["type"] == "paragraph" and "interrupted" in md_block:
-                md_blocks.append(md_block.copy())
-                md_block["type"] = "heading"
-                md_block["lines"] = []
-                md_block["level"] = 1
-            continue
-        elif re.search(r'^-{4,}[ \t]*$', line):
-            # setext heading (-----)
-
-            if md_block["type"] == "paragraph" and "interrupted" in md_block:
-                md_blocks.append(md_block.copy())
-                md_block["type"] = "heading"
-                md_block["lines"] = []
-                md_block["level"] = 2
-
-            continue
-        elif code_match:
-            # code
-            md_block["interrupted"] = 1
-            md_blocks.append(md_block)
-            md_block = {'type': "code",
-                        'lines': [],
-                        }
-            if code_match.group(1):
-                md_block['language'] = code_match.group(1)
-            continue
-
-        # indentation insensitive types
-        markup_match = re.search(r'^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>', line)
-        li_match = re.search(r'^([ ]*)[*+-][ ](.*)', line)
-        quote_match = re.search(r'^[ ]*>[ ]?(.*)', line)
-        if re.search(r'^[ ]*<!DOCTYPE/', line):
-            md_blocks.append(md_block)
-            md_block = {'type': "markup",
-                        'text': deindented_line,
-                        'start': '<',
-                        'end': '>',
-                        'depth': 0,
-                        }
-
-        elif markup_match:
-            # markup, including <?xml version="1.0"?>
-            tag = markup_match.group(1)
-            is_self_closing = markup_match.group(2) is not None
-
-            # skip link markdown
-            # TODO(ensonic): consider adding more uri schemes (ftp, ...)
-            if re.search(r'https?', tag):
-                logging.info("skipping link '%s'", tag)
-            else:
-                # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
-                # instead of creation a markdown block.
-                scanning_for_end_of_text_level_tag = (
-                    md_block["type"] == "paragraph" and
-                    'start' in md_block and
-                    'closed' not in md_block)
-                logging.info("markup found '%s', scanning %s ?", tag, scanning_for_end_of_text_level_tag)
-                if tag not in MD_TEXT_LEVEL_ELEMENTS and not scanning_for_end_of_text_level_tag:
-                    md_blocks.append(md_block)
-
-                    if is_self_closing:
-                        logging.info("self-closing docbook '%s'", tag)
-                        md_block = {'type': "self-closing tag",
-                                    'text': deindented_line,
-                                    }
-                        is_self_closing = 0
-                        continue
-
-                    logging.info("new markup '%s'", tag)
-                    md_block = {'type': "markup",
-                                'text': deindented_line,
-                                'start': '<' + tag + '>',
-                                'end': '</' + tag + '>',
-                                'depth': 0,
-                                }
-                    if re.search(r'<\/%s>' % tag, deindented_line):
-                        md_block["closed"] = 1
-
-                    continue
-                else:
-                    if tag in MD_TEXT_LEVEL_ELEMENTS:
-                        logging.info("text level docbook '%s' in '%s' state", tag, md_block["type"])
-                        # TODO(ensonic): handle nesting
-                        if not scanning_for_end_of_text_level_tag:
-                            if not re.search(r'<\/%s>' % tag, deindented_line):
-                                logging.info("new text level markup '%s'", tag)
-                                md_block["start"] = '<' + tag + '>'
-                                md_block["end"] = '</' + tag + '>'
-                                md_block.pop("closed", None)
-                                logging.info("scanning for end of '%s'", tag)
-
-                        else:
-                            if md_block["end"] in deindented_line:
-                                md_block["closed"] = 1
-                                logging.info("found end of '%s'", tag)
-        elif li_match:
-            # li
-            md_blocks.append(md_block)
-            indentation = li_match.group(1)
-            md_block = {'type': "li",
-                        'ordered': 0,
-                        'indentation': indentation,
-                        'marker': "[*+-]",
-                        'first': 1,
-                        'last': 1,
-                        'lines': [re.sub(r'^[ ]{0,4}', '', li_match.group(2))],
-                        }
-            continue
-        elif quote_match:
-            md_blocks.append(md_block)
-            md_block = {'type': "quote",
-                        'lines': [quote_match.group(1)],
-                        }
-            continue
-
-        # list item
-        list_item_match = re.search(r'^([ ]{0,4})\d+[.][ ]+(.*)', line)
-        if list_item_match:
-            md_blocks.append(md_block)
-            indentation = list_item_match.group(1)
-            md_block = {'type': "li",
-                        'ordered': 1,
-                        'indentation': indentation,
-                        'marker': "\\d+[.]",
-                        'first': 1,
-                        'last': 1,
-                        'lines': [re.sub(r'^[ ]{0,4}', '', list_item_match.group(2))],
-                        }
-            continue
-
-        # paragraph
-        if md_block["type"] == "paragraph":
-            if "interrupted" in md_block:
-                md_blocks.append(md_block)
-                md_block = {'type': "paragraph",
-                            'text': line,
-                            }
-                logging.info("new paragraph due to interrupted")
-            else:
-                md_block["text"] += "\n" + line
-                logging.info("add to paragraph: '%s'", line)
-
-        else:
-            md_blocks.append(md_block)
-            md_block = {'type': "paragraph",
-                        'text': line,
-                        }
-            logging.info("new paragraph due to different block type")
-
-    md_blocks.append(md_block)
-    md_blocks.pop(0)
-
-    return md_blocks
-
-
-def MarkDownParseSpanElementsInner(text, markersref):
-    markup = ''
-    markers = {i: 1 for i in markersref}
-
-    while text != '':
-        closest_marker = ''
-        closest_marker_position = -1
-        text_marker = ''
-        offset = 0
-        markers_rest = []
-
-        for marker, use in markers.items():
-            if not use:
-                continue
-
-            marker_position = text.find(marker)
-
-            if marker_position < 0:
-                markers[marker] = 0
-                continue
-
-            if closest_marker == '' or marker_position < closest_marker_position:
-                closest_marker = marker
-                closest_marker_position = marker_position
-
-        if closest_marker_position >= 0:
-            text_marker = text[closest_marker_position:]
-
-        if text_marker == '':
-            markup += text
-            text = ''
-            continue
-
-        markup += text[:closest_marker_position]
-        text = text[closest_marker_position:]
-        markers_rest = {k: v for k, v in markers.items() if v and k != closest_marker}
-
-        if closest_marker == '![' or closest_marker == '[':
-            element = None
-
-            # FIXME: '(?R)' is a recursive subpattern
-            # match a [...] block with no ][ inside or this thing again
-            # m = re.search(r'\[((?:[^][]|(?R))*)\]', text)
-            m = re.search(r'\[((?:[^][])*)\]', text)
-            if ']' in text and m:
-                element = {'!': text[0] == '!',
-                           'a': m.group(1),
-                           }
-
-                offset = len(m.group(0))
-                if element['!']:
-                    offset += 1
-                logging.debug("Recursive md-expr match: off=%d, text='%s', match='%s'", offset, text, 
m.group(1))
-
-                remaining_text = text[offset:]
-                m2 = re.search(r'''^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)''', remaining_text)
-                m3 = re.search(r'^\s*\[([^\]<]*?)\]', remaining_text)
-                if m2:
-                    element['»'] = m2.group(1)
-                    if m2.group(2):
-                        element['#'] = m2.group(2)
-                    offset += len(m2.group(0))
-                elif m3:
-                    element['ref'] = m3.group(1)
-                    offset += len(m3.group(0))
-                else:
-                    element = None
-
-            if element:
-                if '»' in element:
-                    element['»'] = element['»'].replace('&', '&amp;').replace('<', '&lt;')
-
-                if element['!']:
-                    markup += '<inlinemediaobject><imageobject><imagedata fileref="' + \
-                        element['»'] + '"></imagedata></imageobject>'
-
-                    if 'a' in element:
-                        markup += "<textobject><phrase>" + element['a'] + "</phrase></textobject>"
-
-                        markup += "</inlinemediaobject>"
-                elif 'ref' in element:
-                    element['a'] = MarkDownParseSpanElementsInner(element['a'], markers_rest)
-                    markup += '<link linkend="' + element['ref'] + '"'
-
-                    if '#' in element:
-                        # title attribute not supported
-                        pass
-
-                    markup += '>' + element['a'] + "</link>"
-                else:
-                    element['a'] = MarkDownParseSpanElementsInner(element['a'], markers_rest)
-                    markup += '<ulink url="' + element['»'] + '"'
-
-                    if '#' in element:
-                        # title attribute not supported
-                        pass
-
-                    markup += '>' + element['a'] + "</ulink>"
-
-            else:
-                markup += closest_marker
-                if closest_marker == '![':
-                    offset = 2
-                else:
-                    offset = 1
-
-        elif closest_marker == '<':
-            m4 = re.search(r'^<(https?:[\/]{2}[^\s]+?)>', text, flags=re.I)
-            m5 = re.search(r'^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>', text)
-            m6 = re.search(r'^<[^>]+?>', text)
-            if m4:
-                element_url = m4.group(1).replace('&', '&amp;').replace('<', '&lt;')
-
-                markup += '<ulink url="' + element_url + '">' + element_url + '</ulink>'
-                offset = len(m4.group(0))
-            elif m5:
-                markup += "<ulink url=\"mailto:"; + m5.group(1) + "\">" + m5.group(1) + "</ulink>"
-                offset = len(m5.group(0))
-            elif m6:
-                markup += m6.group(0)
-                offset = len(m6.group(0))
-            else:
-                markup += "&lt;"
-                offset = 1
-
-        elif closest_marker == "\\":
-            special_char = ''
-            if len(text) > 1:
-                special_char = text[1]
-            if special_char in MD_ESCAPABLE_CHARS or special_char in MD_GTK_ESCAPABLE_CHARS:
-                markup += special_char
-                offset = 2
-            else:
-                markup += "\\"
-                offset = 1
-
-        elif closest_marker == "`":
-            m7 = re.search(r'^(`+)([^`]+?)\1(?!`)', text)
-            if m7:
-                element_text = m7.group(2)
-                markup += "<literal>" + element_text + "</literal>"
-                offset = len(m7.group(0))
-            else:
-                markup += "`"
-                offset = 1
-
-        elif closest_marker == "@":
-            # Convert '@param()'
-            # FIXME: we could make those also links ($symbol.$2), but that would be less
-            # useful as the link target is a few lines up or down
-            m7 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', text)
-            m8 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)', text)
-            m9 = re.search(r'^\\\@', text)
-            if m7:
-                markup += m7.group(1) + "<parameter>" + m7.group(2) + "()</parameter>\n"
-                offset = len(m7.group(0))
-            elif m8:
-                # Convert '@param', but not '\@param'.
-                markup += m8.group(1) + "<parameter>" + m8.group(2) + "</parameter>\n"
-                offset = len(m8.group(0))
-            elif m9:
-                markup += r"\@"
-                offset = len(m9.group(0))
-            else:
-                markup += "@"
-                offset = 1
-
-        elif closest_marker == '#':
-            m10 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', text)
-            m11 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)', text)
-            m12 = re.search(r'^\\#', text)
-            if m10:
-                # handle #Object.func()
-                markup += m10.group(1) + MakeXRef(m10.group(2), tagify(m10.group(2) + "()", "function"))
-                offset = len(m10.group(0))
-            elif m11:
-                # Convert '#symbol', but not '\#symbol'.
-                markup += m11.group(1) + MakeHashXRef(m11.group(2), "type")
-                offset = len(m11.group(0))
-            elif m12:
-                markup += '#'
-                offset = len(m12.group(0))
-            else:
-                markup += '#'
-                offset = 1
-
-        elif closest_marker == "%":
-            m12 = re.search(r'^(\A|[^\\])\%(-?\w+)', text)
-            m13 = re.search(r'^\\%', text)
-            if m12:
-                # Convert '%constant', but not '\%constant'.
-                # Also allow negative numbers, e.g. %-1.
-                markup += m12.group(1) + MakeXRef(m12.group(2), tagify(m12.group(2), "literal"))
-                offset = len(m12.group(0))
-            elif m13:
-                markup += r"\%"
-                offset = len(m13.group(0))
-            else:
-                markup += "%"
-                offset = 1
-
-        if offset > 0:
-            text = text[offset:]
-
-    return markup
-
-
-def MarkDownParseSpanElements(text):
-    markers = ["\\", '<', '![', '[', "`", '%', '#', '@']
-
-    text = MarkDownParseSpanElementsInner(text, markers)
-
-    # Convert 'function()' or 'macro()'.
-    # if there is abc_*_def() we don't want to make a link to _def()
-    # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
-    def f(m):
-        return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function"))
-    text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f, text)
-    return text
-
-
-def ReplaceEntities(text, symbol):
-    entities = [["&lt;", '<'],
-                ["&gt;", '>'],
-                ["&ast;", '*'],
-                ["&num;", '#'],
-                ["&percnt;", '%'],
-                ["&colon;", ':'],
-                ["&quot;", '"'],
-                ["&apos;", "'"],
-                ["&nbsp;", ' '],
-                ["&amp;", '&'],  # Do this last, or the others get messed up.
-                ]
-
-    # Expand entities in <programlisting> even inside CDATA since
-    # we changed the definition of |[ to add CDATA
-    for i in entities:
-        text = re.sub(i[0], i[1], text)
-    return text
-
-
-def MarkDownOutputDocBook(blocksref, symbol, context):
-    output = ''
-    blocks = blocksref
-
-    for block in blocks:
-    #$output += "\n<!-- beg type='" . $block->{"type"} . "'-->\n"
-
-        if block["type"] == "paragraph":
-            text = MarkDownParseSpanElements(block["text"])
-            if context == "li" and output == '':
-                if 'interrupted' in block:
-                    output += "\n<para>%s</para>\n" % text
-                else:
-                    output += "<para>%s</para>" % text
-                    if len(blocks) > 1:
-                        output += "\n"
-            else:
-                output += "<para>%s</para>\n" % text
-
-        elif block["type"] == "heading":
-
-            title = MarkDownParseSpanElements(block["text"])
-
-            if block["level"] == 1:
-                tag = "refsect2"
-            else:
-                tag = "refsect3"
-
-            text = MarkDownParseLines(block["lines"], symbol, "heading")
-            if 'id' in block:
-                output += "<%s id=\"%s\">" % (tag, block["id"])
-            else:
-                output += "<%s>" % tag
-
-            output += "<title>%s</title>%s</%s>\n" % (title, text, tag)
-        elif block["type"] == "li":
-            tag = "itemizedlist"
-
-            if "first" in block:
-                if block["ordered"]:
-                    tag = "orderedlist"
-                output += "<%s>\n" % tag
-
-            if "interrupted" in block:
-                block["lines"].append('')
-
-            text = MarkDownParseLines(block["lines"], symbol, "li")
-            output += "<listitem>" + text + "</listitem>\n"
-            if 'last' in block:
-                if block["ordered"]:
-                    tag = "orderedlist"
-                output += "</%s>\n" % tag
-
-        elif block["type"] == "quote":
-            text = MarkDownParseLines(block["lines"], symbol, "quote")
-            output += "<blockquote>\n%s</blockquote>\n" % text
-        elif block["type"] == "code":
-            tag = "programlisting"
-
-            if "language" in block:
-                if block["language"] == "plain":
-                    output += "<informalexample><screen><![CDATA[\n"
-                    tag = "screen"
-                else:
-                    output += "<informalexample><programlisting language=\"%s\"><![CDATA[\n" % 
block['language']
-            else:
-                output += "<informalexample><programlisting><![CDATA[\n"
-
-            logging.debug('listing for %s: [%s]', symbol, '\n'.join(block['lines']))
-            for line in block["lines"]:
-                output += ReplaceEntities(line, symbol) + "\n"
-
-            output += "]]></%s></informalexample>\n" % tag
-        elif block["type"] == "markup":
-            text = ExpandAbbreviations(symbol, block["text"])
-            output += text + "\n"
-        else:
-            output += block["text"] + "\n"
-
-        #$output += "\n<!-- end type='" . $block->{"type"} . "'-->\n"
-    return output
-
-
-def MarkDownParseLines(lines, symbol, context):
-    logging.info('md parse: ctx=%s, [%s]', context, '\n'.join(lines))
-    blocks = MarkDownParseBlocks(lines, symbol, context)
-    output = MarkDownOutputDocBook(blocks, symbol, context)
-    return output
-
-
-def MarkDownParse(text, symbol):
-    return MarkDownParseLines(text.splitlines(), symbol, '')
-
-
-#
-# Function    : ReadDeclarationsFile
-# Description : This reads in a file containing the function/macro/enum etc.
-#                declarations.
-#
-#                Note that in some cases there are several declarations with
-#                the same name, e.g. for conditional macros. In this case we
-#                set a flag in the %DeclarationConditional hash so the
-#                declaration is not shown in the docs.
-#
-#                If a macro and a function have the same name, e.g. for
-#                gtk_object_ref, the function declaration takes precedence.
-#
-#                Some opaque structs are just declared with 'typedef struct
-#                _name name;' in which case the declaration may be empty.
-#                The structure may have been found later in the header, so
-#                that overrides the empty declaration.
-#
-# Arguments   : $file - the declarations file to read
-#                $override - if declarations in this file should override
-#                        any current declaration.
-#
-
-def ReadDeclarationsFile(ifile, override):
-
-    if override == 0:
-        global Declarations, DeclarationTypes, DeclarationConditional, DeclarationOutput
-        Declarations = {}
-        DeclarationTypes = {}
-        DeclarationConditional = {}
-        DeclarationOutput = {}
-
-    INPUT = open(ifile)
-    declaration_type = ''
-    declaration_name = None
-    declaration = None
-    is_deprecated = 0
-    line_number = 0
-    for line in INPUT:
-        line_number += 1
-        if not declaration_type:
-            m1 = re.search(r'^<([^>]+)>', line)
-            if m1:
-                declaration_type = m1.group(1)
-                declaration_name = ''
-                logging.info("Found declaration: %s", declaration_type)
-                declaration = ''
-
-        else:
-            m2 = re.search(r'^<NAME>(.*)</NAME>', line)
-            m3 = re.search(r'^<DEPRECATED/>', line)
-            m4 = re.search(r'^</%s>' % declaration_type, line)
-            if m2:
-                declaration_name = m2.group(1)
-            elif m3:
-                is_deprecated = True
-            elif m4:
-                logging.info("Found end of declaration: %s, %s", declaration_type, declaration_name)
-                # Check that the declaration has a name
-                if declaration_name == '':
-                    common.LogWarning(ifile, line_number, declaration_type + " has no name.\n")
-
-                # If the declaration is an empty typedef struct _XXX XXX
-                # set the flag to indicate the struct has a typedef.
-                if (declaration_type == 'STRUCT' or declaration_type == 'UNION') \
-                        and re.search(r'^\s*$', declaration):
-                    logging.info("Struct has typedef: %s", declaration_name)
-                    StructHasTypedef[declaration_name] = 1
-
-                # Check if the symbol is already defined.
-                if declaration_name in Declarations and override == 0:
-                    # Function declarations take precedence.
-                    if DeclarationTypes[declaration_name] == 'FUNCTION':
-                        # Ignore it.
-                        pass
-                    elif declaration_type == 'FUNCTION':
-                        if is_deprecated:
-                            Deprecated[declaration_name] = ''
-
-                        Declarations[declaration_name] = declaration
-                        DeclarationTypes[declaration_name] = declaration_type
-                    elif DeclarationTypes[declaration_name] == declaration_type:
-                        # If the existing declaration is empty, or is just a
-                        # forward declaration of a struct, override it.
-                        if declaration_type == 'STRUCT' or declaration_type == 'UNION':
-                            if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', 
Declarations[declaration_name]):
-                                if is_deprecated:
-                                    Deprecated[declaration_name] = ''
-                                Declarations[declaration_name] = declaration
-                            elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration):
-                                # Ignore an empty or forward declaration.
-                                pass
-                            else:
-                                common.LogWarning(
-                                    ifile, line_number, "Structure %s has multiple definitions." % 
declaration_name)
-
-                        else:
-                            # set flag in %DeclarationConditional hash for
-                            # multiply defined macros/typedefs.
-                            DeclarationConditional[declaration_name] = 1
-
-                    else:
-                        common.LogWarning(ifile, line_number, declaration_name + " has multiple 
definitions.")
-
-                else:
-                    if is_deprecated:
-                        Deprecated[declaration_name] = ''
-
-                    Declarations[declaration_name] = declaration
-                    DeclarationTypes[declaration_name] = declaration_type
-                    logging.debug("added declaration: %s, %s, [%s]", declaration_type, declaration_name, 
declaration)
-
-                declaration_type = ''
-                is_deprecated = False
-            else:
-                declaration += line
-    INPUT.close()
-
-
-#
-# Function    : ReadSignalsFile
-# Description : This reads in an existing file which contains information on
-#                all GTK signals. It creates the arrays @SignalNames and
-#                @SignalPrototypes containing info on the signals. The first
-#                line of the SignalPrototype is the return type of the signal
-#                handler. The remaining lines are the parameters passed to it.
-#                The last parameter, "gpointer user_data" is always the same
-#                so is not included.
-# Arguments   : $file - the file containing the signal handler prototype
-#                        information.
-#
-
-def ReadSignalsFile(ifile):
-
-    in_signal = 0
-    signal_object = None
-    signal_name = None
-    signal_returns = None
-    signal_flags = None
-    signal_prototype = None
-
-    # Reset the signal info.
-    global SignalObjects, SignalNames, SignalReturns, SignalFlags, SignalPrototypes
-    SignalObjects = []
-    SignalNames = []
-    SignalReturns = []
-    SignalFlags = []
-    SignalPrototypes = []
-
-    if not os.path.isfile(ifile):
-        return
-
-    INPUT = open(ifile)
-    line_number = 0
-    for line in INPUT:
-        line_number += 1
-        if not in_signal:
-            if re.search(r'^<SIGNAL>', line):
-                in_signal = 1
-                signal_object = ''
-                signal_name = ''
-                signal_returns = ''
-                signal_prototype = ''
-
-        else:
-            m = re.search(r'^<NAME>(.*)<\/NAME>', line)
-            m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line)
-            m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
-            if m:
-                signal_name = m.group(1)
-                m1_2 = re.search(r'^(.*)::(.*)$', signal_name)
-                if m1_2:
-                    signal_object = m1_2.group(1)
-                    signal_name = m1_2.group(2).replace('_', '-')
-                    logging.info("Found signal: %s", signal_name)
-                else:
-                    common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name)
-
-            elif m2:
-                signal_returns = m2.group(1)
-            elif m3:
-                signal_flags = m3.group(1)
-            elif re.search(r'^</SIGNAL>', line):
-                logging.info("Found end of signal: %s::%s\nReturns: %s\n%s",
-                             signal_object, signal_name, signal_returns, signal_prototype)
-                SignalObjects.append(signal_object)
-                SignalNames.append(signal_name)
-                SignalReturns.append(signal_returns)
-                SignalFlags.append(signal_flags)
-                SignalPrototypes.append(signal_prototype)
-                in_signal = False
-            else:
-                signal_prototype += line
-    INPUT.close()
-
-
-#
-# Function    : ReadObjectHierarchy
-# Description : This reads in the $MODULE-hierarchy.txt file containing all
-#               the GtkObject subclasses described in this module (and their
-#               ancestors).
-#               It places them in the @Objects array, and places their level
-#               in the object hierarchy in the @ObjectLevels array, at the
-#               same index. GtkObject, the root object, has a level of 1.
-#
-#               This also generates tree_index.sgml as it goes along.
-#
-# Arguments   : none
-#
-
-def ReadObjectHierarchy():
-
-    global Objects, ObjectLevels
-    Objects = []
-    ObjectLevels = []
-
-    if not os.path.isfile(OBJECT_TREE_FILE):
-        return
-
-    INPUT = open(OBJECT_TREE_FILE)
-
-    # Only emit objects if they are supposed to be documented, or if
-    # they have documented children. To implement this, we maintain a
-    # stack of pending objects which will be emitted if a documented
-    # child turns up.
-    pending_objects = []
-    pending_levels = []
-    root = None
-    tree = []
-    for line in INPUT:
-        m1 = re.search(r'\S+', line)
-        if not m1:
-            continue
-
-        gobject = m1.group(0)
-        level = len(line[:m1.start()]) / 2 + 1
-
-        if level == 1:
-            root = gobject
-
-        while pending_levels and pending_levels[-1] >= level:
-            pending_objects.pop()
-            pending_levels.pop()
-
-        pending_objects.append(gobject)
-        pending_levels.append(level)
-
-        if gobject in KnownSymbols:
-            while len(pending_levels) > 0:
-                gobject = pending_objects.pop(0)
-                level = pending_levels.pop(0)
-                xref = MakeXRef(gobject)
-
-                tree.append(' ' * (level * 4) + xref)
-                Objects.append(gobject)
-                ObjectLevels.append(level)
-                ObjectRoots[gobject] = root
-        # else
-        #    common.LogWarning(OBJECT_TREE_FILE, line_number, "unknown type %s" % object)
-        #
-
-    INPUT.close()
-
-    # FIXME: use xml
-    # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
-    old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml")
-    new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new")
-
-    logging.debug('got %d entries for hierarchy', len(tree))
-
-    OUTPUT = open(new_tree_index, 'w')
-    OUTPUT.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree) + "\n</screen>\n")
-    OUTPUT.close()
-
-    common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0)
-
-    OutputObjectList()
-
-
-#
-# Function    : ReadInterfaces
-# Description : This reads in the $MODULE.interfaces file.
-#
-# Arguments   : none
-#
-
-def ReadInterfaces():
-    global Interfaces
-    Interfaces = {}
-
-    if not os.path.isfile(INTERFACES_FILE):
-        return
-
-    INPUT = open(INTERFACES_FILE)
-
-    for line in INPUT:
-        line = line.strip()
-        ifaces = line.split()
-        gobject = ifaces.pop(0)
-        if gobject in KnownSymbols and KnownSymbols[gobject] == 1:
-            knownIfaces = []
-
-            # filter out private interfaces, but leave foreign interfaces
-            for iface in ifaces:
-                if iface not in KnownSymbols or KnownSymbols[iface] == 1:
-                    knownIfaces.append(iface)
-
-            Interfaces[gobject] = ' '.join(knownIfaces)
-            logging.info("Interfaces for %s: %s", gobject, Interfaces[gobject])
-        else:
-            logging.info("skipping interfaces for unknown symbol: %s", gobject)
-
-    INPUT.close()
-
-
-#
-# Function    : ReadPrerequisites
-# Description : This reads in the $MODULE.prerequisites file.
-#
-# Arguments   : none
-#
-
-def ReadPrerequisites():
-    global Prerequisites
-    Prerequisites = {}
-
-    if not os.path.isfile(PREREQUISITES_FILE):
-        return
-
-    INPUT = open(PREREQUISITES_FILE)
-
-    for line in INPUT:
-        line = line.strip()
-        prereqs = line.split()
-        iface = prereqs.pop(0)
-        if iface in KnownSymbols and KnownSymbols[iface] == 1:
-            knownPrereqs = []
-
-            # filter out private prerequisites, but leave foreign prerequisites
-            for prereq in prereqs:
-                if prereq not in KnownSymbols or KnownSymbols[prereq] == 1:
-                    knownPrereqs.append(prereq)
-
-            Prerequisites[iface] = ' '.join(knownPrereqs)
-
-    INPUT.close()
-
-
-#
-# Function    : ReadArgsFile
-# Description : This reads in an existing file which contains information on
-#                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
-#                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
-#               on the args.
-# Arguments   : $file - the file containing the arg information.
-#
-
-def ReadArgsFile(ifile):
-    in_arg = False
-    arg_object = None
-    arg_name = None
-    arg_type = None
-    arg_flags = None
-    arg_nick = None
-    arg_blurb = None
-    arg_default = None
-    arg_range = None
-
-    # Reset the args info.
-    global ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks, ArgBlurbs, ArgDefaults, ArgRanges
-    ArgObjects = []
-    ArgNames = []
-    ArgTypes = []
-    ArgFlags = []
-    ArgNicks = []
-    ArgBlurbs = []
-    ArgDefaults = []
-    ArgRanges = []
-
-    if not os.path.isfile(ifile):
-        return
-
-    INPUT = open(ifile)
-    line_number = 0
-    for line in INPUT:
-        line_number += 1
-        if not in_arg:
-            if line.startswith('<ARG>'):
-                in_arg = True
-                arg_object = ''
-                arg_name = ''
-                arg_type = ''
-                arg_flags = ''
-                arg_nick = ''
-                arg_blurb = ''
-                arg_default = ''
-                arg_range = ''
-
-        else:
-            m1 = re.search(r'^<NAME>(.*)</NAME>', line)
-            m2 = re.search(r'^<TYPE>(.*)</TYPE>', line)
-            m3 = re.search(r'^<RANGE>(.*)</RANGE>', line)
-            m4 = re.search(r'^<FLAGS>(.*)</FLAGS>', line)
-            m5 = re.search(r'^<NICK>(.*)</NICK>', line)
-            m6 = re.search(r'^<BLURB>(.*)</BLURB>', line)
-            m7 = re.search(r'^<DEFAULT>(.*)</DEFAULT>', line)
-            if m1:
-                arg_name = m1.group(1)
-                m1_1 = re.search(r'^(.*)::(.*)$', arg_name)
-                if m1_1:
-                    arg_object = m1_1.group(1)
-                    arg_name = m1_1.group(2).replace('_', '-')
-                    logging.info("Found arg: %s", arg_name)
-                else:
-                    common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name)
-
-            elif m2:
-                arg_type = m2.group(1)
-            elif m3:
-                arg_range = m3.group(1)
-            elif m4:
-                arg_flags = m4.group(1)
-            elif m5:
-                arg_nick = m5.group(1)
-            elif m6:
-                arg_blurb = m6.group(1)
-                if arg_blurb == "(null)":
-                    arg_blurb = ''
-                    common.LogWarning(
-                        ifile, line_number, "Property %s:%s has no documentation." % (arg_object, arg_name))
-
-            elif m7:
-                arg_default = m7.group(1)
-            elif re.search(r'^</ARG>', line):
-                logging.info("Found end of arg: %s::%s\n%s : %s", arg_object, arg_name, arg_type, arg_flags)
-                ArgObjects.append(arg_object)
-                ArgNames.append(arg_name)
-                ArgTypes.append(arg_type)
-                ArgRanges.append(arg_range)
-                ArgFlags.append(arg_flags)
-                ArgNicks.append(arg_nick)
-                ArgBlurbs.append(arg_blurb)
-                ArgDefaults.append(arg_default)
-                in_arg = False
-
-    INPUT.close()
-
-
-#
-# Function    : AddTreeLineArt
-# Description : Add unicode lineart to a pre-indented string array and returns
-#               it as as multiline string.
-# Arguments   : @tree - array of indented strings.
-#
-
-def AddTreeLineArt(tree):
-    # iterate bottom up over the tree
-    for i in range(len(tree) - 1, -1, -1):
-        # count leading spaces
-        m = re.search(r'^([^<A-Za-z]*)', tree[i])
-        indent = len(m.group(1))
-        # replace with ╰───, if place of ╰ is not space insert ├
-        if indent > 4:
-            if tree[i][indent - 4] == " ":
-                tree[i] = tree[i][:indent - 4] + "--- " + tree[i][indent:]
-            else:
-                tree[i] = tree[i][:indent - 4] + "+-- " + tree[i][indent:]
-
-            # go lines up while space and insert |
-            j = i - 1
-            while j >= 0 and tree[j][indent - 4] == ' ':
-                tree[j] = tree[j][:indent - 4] + '|' + tree[j][indent - 3:]
-                j -= 1
-
-    res = "\n".join(tree)
-    # unicode chars for: ╰──
-    res = re.sub(r'---', '<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>', res)
-    # unicde chars for: ├──
-    res = re.sub(r'\+--', '<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>', res)
-    # unicode char for: │
-    res = re.sub(r'\|', '<phrase role=\"lineart\">&#9474;</phrase>', res)
-
-    return res
-
-
-#
-# Function    : CheckIsObject
-# Description : Returns 1 if the given name is a GObject or a subclass.
-#                It uses the global @Objects array.
-#                Note that the @Objects array only contains classes in the
-#                current module and their ancestors - not all GObject classes.
-# Arguments   : $name - the name to check.
-#
-
-def CheckIsObject(name):
-    root = ObjectRoots.get(name)
-    # Let GBoxed pass as an object here to get -struct appended to the id
-    # and prevent conflicts with sections.
-    return root and root != 'GEnum' and root != 'GFlags'
-
-
-#
-# Function    : MakeReturnField
-# Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
-# Arguments   : $str - the string to pad.
-#
-
-def MakeReturnField(s):
-    return s + (' ' * (RETURN_TYPE_FIELD_WIDTH - len(s)))
-
-
-#
-# Function    : GetSymbolSourceFile
-# Description : Get the filename where the symbol docs where taken from.
-# Arguments   : $symbol - the symbol name
-#
-
-def GetSymbolSourceFile(symbol):
-    return SourceSymbolSourceFile.get(symbol, '')
-
-
-#
-# Function    : GetSymbolSourceLine
-# Description : Get the file line where the symbol docs where taken from.
-# Arguments   : $symbol - the symbol name
-#
-
-def GetSymbolSourceLine(symbol):
-    return SourceSymbolSourceLine.get(symbol, 0)
+    if options.output_format != "xml":
+        sys.exit('Invalid format "%s" passed to --output.format' % options.output_format)
 
+    common.setup_logging()
 
-if __name__ == '__main__':
-    Run()
+    mkdb.Run(options)
diff --git a/gtkdoc/mkdb.py b/gtkdoc/mkdb.py
new file mode 100644
index 0000000..6f4092d
--- /dev/null
+++ b/gtkdoc/mkdb.py
@@ -0,0 +1,5513 @@
+#!@PYTHON@
+# -*- python; coding: utf-8 -*-
+#
+# gtk-doc - GTK DocBook documentation generator.
+# Copyright (C) 1998  Damon Chaplin
+#               2007-2016  Stefan Sauer
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# Script      : gtkdoc-mkdb
+# Description : This creates the DocBook files from the source comments.
+#
+
+from __future__ import print_function
+
+import argparse
+from collections import OrderedDict
+import logging
+import os
+import re
+import string
+import sys
+
+sys.path.append('@PYTHON_PACKAGE_DIR@')
+from gtkdoc import common
+
+# Options
+
+# name of documentation module
+MODULE = None
+DB_OUTPUT_DIR = None
+SOURCE_DIRS = None
+SOURCE_SUFFIXES = ''
+IGNORE_FILES = ''
+MAIN_SGML_FILE = None
+EXPAND_CONTENT_FILES = ''
+INLINE_MARKUP_MODE = None
+DEFAULT_STABILITY = None
+DEFAULT_INCLUDES = None
+NAME_SPACE = ''
+OUTPUT_ALL_SYMBOLS = None
+OUTPUT_SYMBOLS_WITHOUT_SINCE = None
+ROOT_DIR = "."
+OBJECT_TREE_FILE = None
+INTERFACES_FILE = None
+PREREQUISITES_FILE = None
+SIGNALS_FILE = None
+ARGS_FILE = None
+
+# These global arrays store information on signals. Each signal has an entry
+# in each of these arrays at the same index, like a multi-dimensional array.
+SignalObjects = []        # The GtkObject which emits the signal.
+SignalNames = []        # The signal name.
+SignalReturns = []        # The return type.
+SignalFlags = []        # Flags for the signal
+SignalPrototypes = []        # The rest of the prototype of the signal handler.
+
+# These global arrays store information on Args. Each Arg has an entry
+# in each of these arrays at the same index, like a multi-dimensional array.
+ArgObjects = []                # The GtkObject which has the Arg.
+ArgNames = []                # The Arg name.
+ArgTypes = []                # The Arg type - gint, GtkArrowType etc.
+ArgFlags = []                # How the Arg can be used - readable/writable etc.
+ArgNicks = []                # The nickname of the Arg.
+ArgBlurbs = []          # Docstring of the Arg.
+ArgDefaults = []        # Default value of the Arg.
+ArgRanges = []                # The range of the Arg type
+
+# These global hashes store declaration info keyed on a symbol name.
+Declarations = {}
+DeclarationTypes = {}
+DeclarationConditional = {}
+DeclarationOutput = {}
+Deprecated = {}
+Since = {}
+StabilityLevel = {}
+StructHasTypedef = {}
+
+# These global hashes store the existing documentation.
+SymbolDocs = {}
+SymbolParams = {}
+SymbolAnnotations = {}
+
+# These global hashes store documentation scanned from the source files.
+SourceSymbolDocs = {}
+SourceSymbolParams = {}
+SourceSymbolSourceFile = {}
+SourceSymbolSourceLine = {}
+
+# all documentation goes in here, so we can do coverage analysis
+AllSymbols = {}
+AllIncompleteSymbols = {}
+AllUnusedSymbols = {}
+AllDocumentedSymbols = {}
+
+# Undeclared yet documented symbols
+UndeclaredSymbols = {}
+
+# These global arrays store GObject, subclasses and the hierarchy (also of
+# non-object derived types).
+Objects = []
+ObjectLevels = []
+ObjectRoots = {}
+
+Interfaces = {}
+Prerequisites = {}
+
+# holds the symbols which are mentioned in <MODULE>-sections.txt and in which
+# section they are defined
+KnownSymbols = {}
+SymbolSection = {}
+SymbolSectionId = {}
+
+# collects index entries
+IndexEntriesFull = {}
+IndexEntriesSince = {}
+IndexEntriesDeprecated = {}
+
+# Standard C preprocessor directives, which we ignore for '#' abbreviations.
+PreProcessorDirectives = {
+    'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef',
+    'include', 'line', 'pragma', 'unassert', 'undef', 'warning'
+}
+
+# remember used annotation (to write minimal glossary)
+AnnotationsUsed = {}
+
+# the regexp that parses the annotation is in ScanSourceFile()
+AnnotationDefinition = {
+    # the GObjectIntrospection annotations are defined at:
+    # https://live.gnome.org/GObjectIntrospection/Annotations
+    'allow-none': "NULL is OK, both for passing and for returning.",
+    'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
+    'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
+    'optional': "NULL may be passed instead of a pointer to a location.",
+    'array': "Parameter points to an array of items.",
+    'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
+    'attributes': "Free-form key-value pairs.",
+    'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
+    'constructor': "This symbol is a constructor, not a static method.",
+    'destroy': "This parameter is a 'destroy_data', for callbacks.",
+    'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less 
parameters).",
+    'element-type': "Generics and defining elements of containers and arrays.",
+    'error-domains': "Typed errors. Similar to throws in Java.",
+    'foreign': "This is a foreign struct.",
+    'get-value-func': "The specified function is used to convert a struct from a GValue, must be a 
GTypeInstance.",
+    'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
+    'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
+    'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
+    'method': "This is a method",
+    'not-error': "A GError parameter is not to be handled like a normal GError.",
+    'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
+    'out caller-allocates': "Out parameter, where caller must allocate storage.",
+    'out callee-allocates': "Out parameter, where caller must allocate storage.",
+    'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
+    'rename-to': "Rename the original symbol's name to SYMBOL.",
+    'scope call': "The callback is valid only during the call to the method.",
+    'scope async': "The callback is valid until first called.",
+    'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
+    'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a 
GTypeInstance.",
+    'skip': "Exposed in C code, not necessarily available in other languages.",
+    'transfer container': "Free data container after the code is done.",
+    'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
+    'transfer full': "Free data after the code is done.",
+    'transfer none': "Don't free data after the code is done.",
+    'type': "Override the parsed C type with given type.",
+    'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
+    'virtual': "This is the invoker for a virtual method.",
+    'value': "The specified value overrides the evaluated value of the constant.",
+    # Stability Level definition
+    # https://bugzilla.gnome.org/show_bug.cgi?id=170860
+    'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to
+develop applications to these interfaces, release them, and have confidence that
+they will run on all minor releases of the product (after the one in which the
+interface was introduced, and within the same major release). Even at a major
+release, incompatible changes are expected to be rare, and to have strong
+justifications.
+''',
+    'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to
+give outside developers early access to new or rapidly changing technology, or
+to provide an interim solution to a problem where a more general solution is
+anticipated. No claims are made about either source or binary compatibility from
+one minor release to the next.
+
+The Unstable interface level is a warning that these interfaces are  subject to
+change without warning and should not be used in unbundled products.
+
+Given such caveats, customer impact need not be a factor when considering
+incompatible changes to an Unstable interface in a major or minor release.
+Nonetheless, when such changes are introduced, the changes should still be
+mentioned in the release notes for the affected release.
+''',
+    'Private': '''An interface that can be used within the GNOME stack itself, but that is not
+documented for end-users.  Such functions should only be used in specified and
+documented ways.
+''',
+}
+
+# Elements to consider non-block items in MarkDown parsing
+MD_TEXT_LEVEL_ELEMENTS = {
+    'emphasis', 'envar', 'filename', 'firstterm', 'footnote', 'function', 'literal',
+    'manvolnum', 'option', 'replaceable', 'structfield', 'structname', 'title',
+    'varname'
+}
+MD_ESCAPABLE_CHARS = r'\`*_{}[]()>#+-.!'
+MD_GTK_ESCAPABLE_CHARS = r'@%'
+
+# Function and other declaration output settings.
+RETURN_TYPE_FIELD_WIDTH = 20
+MAX_SYMBOL_FIELD_WIDTH = 40
+
+# XML header
+doctype_header = None
+
+# refentry template
+REFENTRY = string.Template('''${header}
+<refentry id="${section_id}">
+<refmeta>
+<refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>${MODULE} Library${image}</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>${title}</refname>
+<refpurpose>${short_desc}</refpurpose>
+</refnamediv>
+${stability}
+${functions_synop}${args_synop}${signals_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations}
+${include_output}
+<refsect1 id="${section_id}.description" role="desc">
+<title role="desc.title">Description</title>
+${extralinks}${long_desc}
+</refsect1>
+<refsect1 id="${section_id}.functions_details" role="details">
+<title role="details.title">Functions</title>
+${functions_details}
+</refsect1>
+<refsect1 id="${section_id}.other_details" role="details">
+<title role="details.title">Types and Values</title>
+${other_details}
+</refsect1>
+${args_desc}${signals_desc}${see_also}
+</refentry>
+''')
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--module', default='')
+parser.add_argument('--source-dir', action='append', dest='source_dir', default=[])
+parser.add_argument('--source-suffixes', dest='source_suffixes', default='')
+parser.add_argument('--ignore-files', dest='ignore_files', default='')
+parser.add_argument('--output-dir', dest='output_dir', default='')
+parser.add_argument('--tmpl-dir', dest='tmpl_dir', help="DEPRECATED")
+parser.add_argument('--main-sgml-file', dest='main_sgml_file', default='')
+parser.add_argument('--expand-content-files', dest='expand_content_files', default='')
+group = parser.add_mutually_exclusive_group()
+group.add_argument('--sgml-mode', action='store_true', default=False, dest='sgml_mode')
+group.add_argument('--xml-mode', action='store_true', default=False, dest='xml_mode')
+parser.add_argument('--default-stability', dest='default_stability',
+                    choices=['', 'Stable', 'Private', 'Unstable'], default='')
+parser.add_argument('--default-includes', dest='default_includes', default='')
+parser.add_argument('--output-format', dest='default_format', default='')
+parser.add_argument('--name-space', dest='name_space', default='')
+parser.add_argument('--outputallsymbols', default=False, action='store_true')
+parser.add_argument('--outputsymbolswithoutsince', default=False, action='store_true')
+
+
+def Run(options):
+    global MODULE, SOURCE_DIRS, SOURCE_SUFFIXES, IGNORE_FILES, MAIN_SGML_FILE, EXPAND_CONTENT_FILES, \
+        INLINE_MARKUP_MODE, DEFAULT_STABILITY, DEFAULT_INCLUDES, NAME_SPACE, OUTPUT_ALL_SYMBOLS, \
+        OUTPUT_SYMBOLS_WITHOUT_SINCE, DB_OUTPUT_DIR, OBJECT_TREE_FILE, INTERFACES_FILE, PREREQUISITES_FILE, \
+        SIGNALS_FILE, ARGS_FILE, doctype_header
+
+    options = parser.parse_args()
+
+    common.setup_logging()
+
+    # We should pass the options variable around instead of this global variable horror
+    # but too much of the code expects these to be around. Fix this once the transition is done.
+    MODULE = options.module
+    SOURCE_DIRS = options.source_dir
+    SOURCE_SUFFIXES = options.source_suffixes
+    IGNORE_FILES = options.ignore_files
+    MAIN_SGML_FILE = options.main_sgml_file
+    EXPAND_CONTENT_FILES = options.expand_content_files
+    INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode
+    DEFAULT_STABILITY = options.default_stability
+    DEFAULT_INCLUDES = options.default_includes
+    NAME_SPACE = options.name_space
+    OUTPUT_ALL_SYMBOLS = options.outputallsymbols
+    OUTPUT_SYMBOLS_WITHOUT_SINCE = options.outputsymbolswithoutsince
+
+    logging.info(" ignore files: " + IGNORE_FILES)
+
+    if not MAIN_SGML_FILE:
+        # backwards compatibility
+        if os.path.exists(MODULE + "-docs.sgml"):
+            MAIN_SGML_FILE = MODULE + "-docs.sgml"
+        else:
+            MAIN_SGML_FILE = MODULE + "-docs.xml"
+
+    # extract docbook header or define default
+    if os.path.exists(MAIN_SGML_FILE):
+        INPUT = open(MAIN_SGML_FILE)
+        doctype_header = ''
+        for line in INPUT:
+            if re.search(r'^\s*<(book|chapter|article)', line):
+                # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
+                if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and not 
re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', doctype_header, flags=re.MULTILINE):
+                    doctype_header = ''
+                break
+
+            # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
+            # FIXME: not sure if we can do this now, as people already work-around the problem
+            # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">';
+            line = re.sub(
+                r'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r'<!ENTITY % gtkdocentities SYSTEM 
"../\1">', line)
+            doctype_header += line
+        INPUT.close()
+        doctype_header = doctype_header.strip()
+    else:
+        doctype_header = '''<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd";
+[
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
+  %gtkdocentities;
+]>'''
+
+    # This is where we put all the DocBook output.
+    DB_OUTPUT_DIR = DB_OUTPUT_DIR if DB_OUTPUT_DIR else os.path.join(ROOT_DIR, "xml")
+
+    # This file contains the object hierarchy.
+    OBJECT_TREE_FILE = os.path.join(ROOT_DIR, MODULE + ".hierarchy")
+
+    # This file contains the interfaces.
+    INTERFACES_FILE = os.path.join(ROOT_DIR, MODULE + ".interfaces")
+
+    # This file contains the prerequisites.
+    PREREQUISITES_FILE = os.path.join(ROOT_DIR, MODULE + ".prerequisites")
+
+    # This file contains signal arguments and names.
+    SIGNALS_FILE = os.path.join(ROOT_DIR, MODULE + ".signals")
+
+    # The file containing Arg information.
+    ARGS_FILE = os.path.join(ROOT_DIR, MODULE + ".args")
+
+    # Create the root DocBook output directory if it doens't exist.
+    if not os.path.isdir(DB_OUTPUT_DIR):
+        os.mkdir(DB_OUTPUT_DIR)
+
+    ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
+    ReadSignalsFile(SIGNALS_FILE)
+    ReadArgsFile(ARGS_FILE)
+    ReadObjectHierarchy()
+    ReadInterfaces()
+    ReadPrerequisites()
+
+    ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0)
+    if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")):
+        ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1)
+
+    for sdir in SOURCE_DIRS:
+        ReadSourceDocumentation(sdir)
+
+    changed = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
+
+    # If any of the DocBook files have changed, update the timestamp file (so
+    # it can be used for Makefile dependencies).
+    if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")):
+
+        # try to detect the common prefix
+        # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
+        if NAME_SPACE == '':
+            pos = 0
+            ratio = 0.0
+            while True:
+                prefix = {}
+                letter = ''
+                for symbol in IndexEntriesFull.keys():
+                    if NAME_SPACE == '' or NAME_SPACE.lower() in symbol.lower():
+                        if len(symbol) > pos:
+                            letter = symbol[pos:pos + 1]
+                            # stop prefix scanning
+                            if letter == "_":
+                                # stop on "_"
+                                break
+                            # Should we also stop on a uppercase char, if last was lowercase
+                            #   GtkWidget, if we have the 'W' and had the 't' before
+                            # or should we count upper and lowercase, and stop one 2nd uppercase, if we 
already had a lowercase
+                            #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had 
lowercase chars before
+                            # need to recound each time as this is per symbol
+                            ul = letter.upper()
+                            if ul in prefix:
+                                prefix[ul] += 1
+                            else:
+                                prefix[ul] = 1
+
+                if letter != '' and letter != "_":
+                    maxletter = ''
+                    maxsymbols = 0
+                    for letter in prefix.keys():
+                        logging.debug("ns prefix: %s: %s", letter, prefix[letter])
+                        if prefix[letter] > maxsymbols:
+                            maxletter = letter
+                            maxsymbols = prefix[letter]
+
+                    ratio = float(len(IndexEntriesFull)) / prefix[maxletter]
+                    logging.debug('most symbols start with %s, that is %f', maxletter, (100 * ratio))
+                    if ratio > 0.9:
+                        # do another round
+                        NAME_SPACE += maxletter
+
+                    pos += 1
+
+                else:
+                    ratio = 0.0
+
+                if ratio < 0.9:
+                    break
+
+        logging.info('namespace prefix ="%s"', NAME_SPACE)
+
+        OutputIndexFull()
+        OutputDeprecatedIndex()
+        OutputSinceIndexes()
+        OutputAnnotationGlossary()
+
+        open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w').write('timestamp')
+
+
+#
+# Function    : OutputObjectList
+# Description : This outputs the alphabetical list of objects, in a columned
+#                table.
+#               FIXME: Currently this also outputs ancestor objects
+#                which may not actually be in this module.
+# Arguments   : none
+#
+
+def OutputObjectList():
+    cols = 3
+
+    # FIXME: use .xml
+    old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml")
+    new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new")
+
+    OUTPUT = open(new_object_index, 'w')
+
+    OUTPUT.write('''%s
+<informaltable pgwide="1" frame="none">
+<tgroup cols="%s">
+<colspec colwidth="1*"/>
+<colspec colwidth="1*"/>
+<colspec colwidth="1*"/>
+<tbody>
+''' % (MakeDocHeader("informaltable"), cols))
+
+    count = 0
+    object = None
+    for object in sorted(Objects):
+        xref = MakeXRef(object)
+        if count % cols == 0:
+            OUTPUT.write("<row>\n")
+        OUTPUT.write("<entry>%s</entry>\n" % xref)
+        if count % cols == cols - 1:
+            OUTPUT.write("</row>\n")
+        count += 1
+
+    if count == 0:
+        # emit an empty row, since empty tables are invalid
+        OUTPUT.write("<row><entry> </entry></row>\n")
+
+    else:
+        if count % cols > 0:
+            OUTPUT.write("</row>\n")
+
+    OUTPUT.write('''</tbody></tgroup></informaltable>\n''')
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_object_index, new_object_index, 0)
+
+
+#
+# Function    : TrimTextBlock
+# Description : Trims extra whitespace. Empty lines inside a block are
+#                preserved.
+# Arguments   : $desc - the text block to trim. May contain newlines.
+#
+
+def TrimTextBlock(desc):
+    # strip leading spaces on the block
+    desc = re.sub(r'^\s+', '', desc)
+    # strip trailing spaces on every line
+    desc = re.sub(r'\s+$', '\n', desc, flags=re.MULTILINE)
+
+    return desc
+
+
+#
+# Function    : OutputDB
+# Description : This collects the output for each section of the docs, and
+#                outputs each file when the end of the section is found.
+# Arguments   : $file - the $MODULE-sections.txt file which contains all of
+#                the functions/macros/structs etc. being documented, organised
+#                into sections and subsections.
+#
+
+def OutputDB(file):
+
+    logging.info("Reading: %s", file)
+    INPUT = open(file)
+    filename = ''
+    book_top = ''
+    book_bottom = ''
+    includes = DEFAULT_INCLUDES if DEFAULT_INCLUDES else ''
+    section_includes = ''
+    in_section = 0
+    title = ''
+    section_id = ''
+    subsection = ''
+    num_symbols = 0
+    changed = 0
+    functions_synop = ''
+    other_synop = ''
+    functions_details = ''
+    other_details = ''
+    signals_synop = ''
+    signals_desc = ''
+    args_synop = ''
+    child_args_synop = ''
+    style_args_synop = ''
+    args_desc = ''
+    child_args_desc = ''
+    style_args_desc = ''
+    hierarchy_str = ''
+    hierarchy = []
+    interfaces = ''
+    implementations = ''
+    prerequisites = ''
+    derived = ''
+    file_objects = []
+    file_def_line = {}
+    symbol_def_line = {}
+
+    MergeSourceDocumentation()
+
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+
+        if line.startswith('#'):
+            continue
+
+        logging.info("section file data: %d: %s", line_number, line)
+
+        m1 = re.search(r'^<SUBSECTION\s*(.*)>', line, re.I)
+        m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line)
+        m3 = re.search(r'^<FILE>(.*)<\/FILE>', line)
+        m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
+        m5 = re.search(r'^(\S+)', line)
+
+        if line.startswith('<SECTION>'):
+            num_symbols = 0
+            in_section = False
+            file_objects = []
+            symbol_def_line = {}
+
+        elif m1:
+            other_synop += "\n"
+            functions_synop += "\n"
+            subsection = m1.group(1)
+
+        elif line.startswith('<SUBSECTION>'):
+            continue
+        elif m2:
+            title = m2.group(1)
+            logging.info("Section: %s", title)
+
+            # We don't want warnings if object & class structs aren't used.
+            DeclarationOutput[title] = 1
+            DeclarationOutput["%sClass" % title] = 1
+            DeclarationOutput["%sIface" % title] = 1
+            DeclarationOutput["%sInterface" % title] = 1
+
+        elif m3:
+            filename = m3.group(1)
+            if not filename in file_def_line:
+                file_def_line[filename] = line_number
+            else:
+                common.LogWarning(file, line_number, "Double <FILE>%s</FILE> entry. Previous occurrence on 
line %s." %
+                                  (filename, file_def_line[filename]))
+            if title == '':
+                key = filename + ":Title"
+                if key in SourceSymbolDocs:
+                    title = SourceSymbolDocs[key].rstrip()
+
+        elif m4:
+            if in_section:
+                section_includes = m4.group(1)
+            else:
+                if DEFAULT_INCLUDES:
+                    common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line 
option.")
+                else:
+                    includes = m4.group(1)
+
+        elif re.search(r'^<\/SECTION>', line):
+            logging.info("End of section: %s", title)
+            if num_symbols > 0:
+                # collect documents
+                book_bottom += "    <xi:include href=\"xml/%s.xml\"/>\n" % filename
+
+                key = filename + ":Include"
+                if key in SourceSymbolDocs:
+                    if section_includes:
+                        common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline 
comments.")
+                    section_includes = SourceSymbolDocs[key]
+
+                if section_includes == '':
+                    section_includes = includes
+
+                signals_synop = re.sub(r'^\n*', '', signals_synop)
+                signals_synop = re.sub(r'\n+$', '\n', signals_synop)
+
+                if signals_synop != '':
+                    signals_synop = '''<refsect1 id="%s.signals" role="signal_proto">
+<title role="signal_proto.title">Signals</title>
+<informaltable frame="none">
+<tgroup cols="3">
+<colspec colname="signals_return" colwidth="150px"/>
+<colspec colname="signals_name" colwidth="300px"/>
+<colspec colname="signals_flags" colwidth="200px"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, signals_synop)
+                    signals_desc = TrimTextBlock(signals_desc)
+                    signals_desc = '''<refsect1 id="%s.signal-details" role="signals">
+<title role="signals.title">Signal Details</title>
+%s
+</refsect1>
+''' % (section_id, signals_desc)
+
+                args_synop = re.sub(r'^\n*', '', args_synop)
+                args_synop = re.sub(r'\n+$', '\n', args_synop)
+                if args_synop != '':
+                    args_synop = '''<refsect1 id="%s.properties" role="properties">
+<title role="properties.title">Properties</title>
+<informaltable frame="none">
+<tgroup cols="3">
+<colspec colname="properties_type" colwidth="150px"/>
+<colspec colname="properties_name" colwidth="300px"/>
+<colspec colname="properties_flags" colwidth="200px"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, args_synop)
+                    args_desc = TrimTextBlock(args_desc)
+                    args_desc = '''<refsect1 id="%s.property-details" role="property_details">
+<title role="property_details.title">Property Details</title>
+%s
+</refsect1>
+''' % (section_id, args_desc)
+
+                child_args_synop = re.sub(r'^\n*', '', child_args_synop)
+                child_args_synop = re.sub(r'\n+$', '\n', child_args_synop)
+                if child_args_synop != '':
+                    args_synop += '''<refsect1 id="%s.child-properties" role="child_properties">
+<title role="child_properties.title">Child Properties</title>
+<informaltable frame="none">
+<tgroup cols="3">
+<colspec colname="child_properties_type" colwidth="150px"/>
+<colspec colname="child_properties_name" colwidth="300px"/>
+<colspec colname="child_properties_flags" colwidth="200px"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, child_args_synop)
+                    child_args_desc = TrimTextBlock(child_args_desc)
+                    args_desc += '''<refsect1 id="%s.child-property-details" role="child_property_details">
+<title role="child_property_details.title">Child Property Details</title>
+%s
+</refsect1>
+''' % (section_id, child_args_desc)
+
+                style_args_synop = re.sub(r'^\n*', '', style_args_synop)
+                style_args_synop = re.sub(r'\n+$', '\n', style_args_synop)
+                if style_args_synop != '':
+                    args_synop += '''<refsect1 id="%s.style-properties" role="style_properties">
+<title role="style_properties.title">Style Properties</title>
+<informaltable frame="none">
+<tgroup cols="3">
+<colspec colname="style_properties_type" colwidth="150px"/>
+<colspec colname="style_properties_name" colwidth="300px"/>
+<colspec colname="style_properties_flags" colwidth="200px"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, style_args_synop)
+                    style_args_desc = TrimTextBlock(style_args_desc)
+                    args_desc += '''<refsect1 id="%s.style-property-details" role="style_properties_details">
+<title role="style_properties_details.title">Style Property Details</title>
+%s
+</refsect1>
+''' % (section_id, style_args_desc)
+
+                hierarchy_str = AddTreeLineArt(hierarchy)
+                if hierarchy_str != '':
+                    hierarchy_str = '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
+<title role="object_hierarchy.title">Object Hierarchy</title>
+<screen>%s
+</screen>
+</refsect1>
+''' % (section_id, hierarchy_str)
+
+                interfaces = TrimTextBlock(interfaces)
+                if interfaces != '':
+                    interfaces = '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
+<title role="impl_interfaces.title">Implemented Interfaces</title>
+%s
+</refsect1>
+''' % (section_id, interfaces)
+
+                implementations = TrimTextBlock(implementations)
+                if implementations != '':
+                    implementations = '''<refsect1 id="%s.implementations" role="implementations">
+<title role="implementations.title">Known Implementations</title>
+%s
+</refsect1>
+''' % (section_id, implementations)
+
+                prerequisites = TrimTextBlock(prerequisites)
+                if prerequisites != '':
+                    prerequisites = '''<refsect1 id="%s.prerequisites" role="prerequisites">
+<title role="prerequisites.title">Prerequisites</title>
+%s
+</refsect1>
+''' % (section_id, prerequisites)
+
+                derived = TrimTextBlock(derived)
+                if derived != '':
+                    derived = '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
+<title role="derived_interfaces.title">Known Derived Interfaces</title>
+%s
+</refsect1>
+''' % (section_id, derived)
+
+                functions_synop = re.sub(r'^\n*', '', functions_synop)
+                functions_synop = re.sub(r'\n+$', '\n', functions_synop)
+                if functions_synop != '':
+                    functions_synop = '''<refsect1 id="%s.functions" role="functions_proto">
+<title role="functions_proto.title">Functions</title>
+<informaltable pgwide="1" frame="none">
+<tgroup cols="2">
+<colspec colname="functions_return" colwidth="150px"/>
+<colspec colname="functions_name"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, functions_synop)
+
+                other_synop = re.sub(r'^\n*', '', other_synop)
+                other_synop = re.sub(r'\n+$', '\n', other_synop)
+                if other_synop != '':
+                    other_synop = '''<refsect1 id="%s.other" role="other_proto">
+<title role="other_proto.title">Types and Values</title>
+<informaltable role="enum_members_table" pgwide="1" frame="none">
+<tgroup cols="2">
+<colspec colname="name" colwidth="150px"/>
+<colspec colname="description"/>
+<tbody>
+%s
+</tbody>
+</tgroup>
+</informaltable>
+</refsect1>
+''' % (section_id, other_synop)
+
+                file_changed = OutputDBFile(filename, title, section_id,
+                                            section_includes,
+                                            functions_synop, other_synop,
+                                            functions_details, other_details,
+                                            signals_synop, signals_desc,
+                                            args_synop, args_desc,
+                                            hierarchy_str, interfaces,
+                                            implementations,
+                                            prerequisites, derived,
+                                            file_objects)
+                if file_changed:
+                    changed = True
+
+            title = ''
+            section_id = ''
+            subsection = ''
+            in_section = 0
+            section_includes = ''
+            functions_synop = ''
+            other_synop = ''
+            functions_details = ''
+            other_details = ''
+            signals_synop = ''
+            signals_desc = ''
+            args_synop = ''
+            child_args_synop = ''
+            style_args_synop = ''
+            args_desc = ''
+            child_args_desc = ''
+            style_args_desc = ''
+            hierarchy_str = ''
+            hierarchy = []
+            interfaces = ''
+            implementations = ''
+            prerequisites = ''
+            derived = ''
+
+        elif m5:
+            symbol = m5.group(1)
+            logging.info('  Symbol: "%s" in subsection: "%s"', symbol, subsection)
+
+            # check for duplicate entries
+            if symbol not in symbol_def_line:
+                declaration = Declarations.get(symbol)
+                # FIXME: with this we'll output empty declaration
+                if declaration is not None:
+                    if CheckIsObject(symbol):
+                        file_objects.append(symbol)
+
+                    # We don't want standard macros/functions of GObjects,
+                    # or private declarations.
+                    if subsection != "Standard" and subsection != "Private":
+                        synop, desc = OutputDeclaration(symbol, declaration)
+                        type = DeclarationTypes[symbol]
+
+                        if type == 'FUNCTION' or type == 'USER_FUNCTION':
+                            functions_synop += synop
+                            functions_details += desc
+                        elif type == 'MACRO' and re.search(symbol + r'\(', declaration):
+                            functions_synop += synop
+                            functions_details += desc
+                        else:
+                            other_synop += synop
+                            other_details += desc
+
+                    sig_synop, sig_desc = GetSignals(symbol)
+                    arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = 
GetArgs(
+                        symbol)
+                    ifaces = GetInterfaces(symbol)
+                    impls = GetImplementations(symbol)
+                    prereqs = GetPrerequisites(symbol)
+                    der = GetDerived(symbol)
+                    hierarchy = GetHierarchy(symbol, hierarchy)
+
+                    signals_synop += sig_synop
+                    signals_desc += sig_desc
+                    args_synop += arg_synop
+                    child_args_synop += child_arg_synop
+                    style_args_synop += style_arg_synop
+                    args_desc += arg_desc
+                    child_args_desc += child_arg_desc
+                    style_args_desc += style_arg_desc
+                    interfaces += ifaces
+                    implementations += impls
+                    prerequisites += prereqs
+                    derived += der
+
+                    # Note that the declaration has been output.
+                    DeclarationOutput[symbol] = True
+                elif subsection != "Standard" and subsection != "Private":
+                    UndeclaredSymbols[symbol] = True
+                    common.LogWarning(file, line_number, "No declaration found for %s." % symbol)
+
+                num_symbols += 1
+                symbol_def_line[symbol] = line_number
+
+                if section_id == '':
+                    if title == '' and filename == '':
+                        common.LogWarning(file, line_number, "Section has no title and no file.")
+
+                    # FIXME: one of those would be enough
+                    # filename should be an internal detail for gtk-doc
+                    if title == '':
+                        title = filename
+                    elif filename == '':
+                        filename = title
+
+                    filename = filename.replace(' ', '_')
+
+                    section_id = SourceSymbolDocs.get(filename + ":Section_Id")
+                    if section_id and section_id.strip() != '':
+                        # Remove trailing blanks and use as is
+                        section_id = section_id.rstrip()
+                    elif CheckIsObject(title):
+                        # GObjects use their class name as the ID.
+                        section_id = common.CreateValidSGMLID(title)
+                    else:
+                        section_id = common.CreateValidSGMLID(MODULE + '-' + title)
+
+                SymbolSection[symbol] = title
+                SymbolSectionId[symbol] = section_id
+
+            else:
+                common.LogWarning(file, line_number, "Double symbol entry for %s. "
+                                  "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol]))
+    INPUT.close()
+
+    OutputMissingDocumentation()
+    OutputUndeclaredSymbols()
+    OutputUnusedSymbols()
+
+    if OUTPUT_ALL_SYMBOLS:
+        OutputAllSymbols()
+
+    if OUTPUT_SYMBOLS_WITHOUT_SINCE:
+        OutputSymbolsWithoutSince()
+
+    for filename in EXPAND_CONTENT_FILES.split():
+        file_changed = OutputExtraFile(filename)
+        if file_changed:
+            changed = True
+
+    OutputBook(book_top, book_bottom)
+
+    logging.info("All files created: %d", changed)
+    return changed
+
+
+#
+# Function    : OutputIndex
+# Description : This writes an indexlist that can be included into the main-
+#               document into an <index> tag.
+#
+
+def OutputIndex(basename, apiindex):
+    old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml')
+    new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new')
+    lastletter = " "
+    divopen = 0
+    symbol = None
+    short_symbol = None
+
+    OUTPUT = open(new_index, 'w')
+
+    OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename)
+
+    logging.info("generate %s index (%d entries) with namespace %s", basename, len(apiindex), NAME_SPACE)
+
+    # do a case insensitive sort while chopping off the prefix
+    mapped_keys = [
+        {
+            'original': x,
+            'short': re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', x.upper(), flags=re.I),
+        } for x in apiindex.keys()]
+    sorted_keys = sorted(mapped_keys, key=lambda d: (d['short'], d['original']))
+
+    for key in sorted_keys:
+        symbol = key['original']
+        short = key['short']
+        if short != '':
+            short_symbol = short
+        else:
+            short_symbol = symbol
+
+        # generate a short symbol description
+        symbol_desc = ''
+        symbol_section = ''
+        symbol_section_id = ''
+        symbol_type = ''
+        if symbol in DeclarationTypes:
+            symbol_type = DeclarationTypes[symbol].lower()
+
+        if symbol_type == '':
+            logging.info("trying symbol %s", symbol)
+            m1 = re.search(r'(.*)::(.*)', symbol)
+            m2 = re.search(r'(.*):(.*)', symbol)
+            if m1:
+                oname = m1.group(1)
+                osym = m1.group(2)
+                logging.info("  trying object signal %s:%s in %d signals", oname, osym, len(SignalNames))
+                for name in SignalNames:
+                    logging.info("    " + name)
+                    if name == osym:
+                        symbol_type = "object signal"
+                        if oname in SymbolSection:
+                            symbol_section = SymbolSection[oname]
+                            symbol_section_id = SymbolSectionId[oname]
+                        break
+            elif m2:
+                oname = m2.group(1)
+                osym = m2.group(2)
+                logging.info("  trying object property %s::%s in %d properties", oname, osym, len(ArgNames))
+                for name in ArgNames:
+                    logging.info("    " + name)
+                    if name == osym:
+                        symbol_type = "object property"
+                        if oname in SymbolSection:
+                            symbol_section = SymbolSection[oname]
+                            symbol_section_id = SymbolSectionId[oname]
+                        break
+        else:
+            if symbol in SymbolSection:
+                symbol_section = SymbolSection[symbol]
+                symbol_section_id = SymbolSectionId[symbol]
+
+        if symbol_type != '':
+            symbol_desc = ", " + symbol_type
+            if symbol_section != '':
+                symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section)
+                # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section")
+
+        curletter = short_symbol[0].upper()
+        ixid = apiindex[symbol]
+
+        logging.info("  add symbol %s with %s to index in section '%s' (derived from %s)",
+                     symbol, ixid, curletter, short_symbol)
+
+        if curletter != lastletter:
+            lastletter = curletter
+
+            if divopen:
+                OUTPUT.write("</indexdiv>\n")
+
+            OUTPUT.write("<indexdiv><title>%s</title>\n" % curletter)
+            divopen = True
+
+        OUTPUT.write('<indexentry><primaryie linkends="%s"><link 
linkend="%s">%s</link>%s</primaryie></indexentry>\n' %
+                     (ixid, ixid, symbol, symbol_desc))
+
+    if divopen:
+        OUTPUT.write("</indexdiv>\n")
+
+    OUTPUT.write("</indexdiv>\n")
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_index, new_index, 0)
+
+
+#
+# Function    : OutputIndexFull
+# Description : This writes the full api indexlist that can be included into the
+#               main document into an <index> tag.
+#
+
+def OutputIndexFull():
+    OutputIndex("api-index-full", IndexEntriesFull)
+
+
+#
+# Function    : OutputDeprecatedIndex
+# Description : This writes the deprecated api indexlist that can be included
+#               into the main document into an <index> tag.
+#
+
+def OutputDeprecatedIndex():
+    OutputIndex("api-index-deprecated", IndexEntriesDeprecated)
+
+
+#
+# Function    : OutputSinceIndexes
+# Description : This writes the 'since' api indexlists that can be included into
+#               the main document into an <index> tag.
+#
+
+def OutputSinceIndexes():
+    sinces = set(Since.values())
+
+    for version in sinces:
+        logging.info("Since : [%s]", version)
+        index = {x: IndexEntriesSince[x] for x in IndexEntriesSince.keys() if Since[x] == version}
+
+        OutputIndex("api-index-" + version, index)
+
+
+#
+# Function    : OutputAnnotationGlossary
+# Description : This writes a glossary of the used annotation terms into a
+#               separate glossary file that can be included into the main
+#               document.
+#
+
+def OutputAnnotationGlossary():
+    old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml")
+    new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new")
+    lastletter = " "
+    divopen = False
+
+    # if there are no annotations used return
+    if not AnnotationsUsed:
+        return
+
+    # add acronyms that are referenced from acronym text
+    rerun = True
+    while rerun:
+        rerun = False
+        for annotation in AnnotationsUsed:
+            if annotation not in AnnotationDefinition:
+                continue
+            m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation])
+            if m and m.group(1) not in AnnotationsUsed:
+                AnnotationsUsed[m.group(1)] = 1
+                rerun = True
+                break
+
+    OUTPUT = open(new_glossary, 'w')
+
+    OUTPUT.write('''%s
+<glossary id="annotation-glossary">
+  <title>Annotation Glossary</title>
+''' % MakeDocHeader("glossary"))
+
+    for annotation in sorted(AnnotationsUsed.keys(), key=str.lower):
+        if annotation in AnnotationDefinition:
+            definition = AnnotationDefinition[annotation]
+            curletter = annotation[0].upper()
+
+            if curletter != lastletter:
+                lastletter = curletter
+
+                if divopen:
+                    OUTPUT.write("</glossdiv>\n")
+
+                OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter)
+                divopen = True
+
+            OUTPUT.write('''    <glossentry>
+      <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
+      <glossdef>
+        <para>%s</para>
+      </glossdef>
+    </glossentry>
+''' % (annotation, annotation, definition))
+
+    if divopen:
+        OUTPUT.write("</glossdiv>\n")
+
+    OUTPUT.write("</glossary>\n")
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_glossary, new_glossary, 0)
+
+
+#
+# Function    : ReadKnownSymbols
+# Description : This collects the names of non-private symbols from the
+#               $MODULE-sections.txt file.
+# Arguments   : $file - the $MODULE-sections.txt file which contains all of
+#                the functions/macros/structs etc. being documented, organised
+#                into sections and subsections.
+#
+
+def ReadKnownSymbols(file):
+
+    subsection = ''
+
+    logging.info("Reading: %s", file)
+    INPUT = open(file)
+
+    for line in INPUT:
+        if line.startswith('#'):
+            continue
+
+        if line.startswith('<SECTION>'):
+            subsection = ''
+            continue
+
+        m = re.search(r'^<SUBSECTION\s*(.*)>', line, flags=re.I)
+        if m:
+            subsection = m.group(1)
+            continue
+
+        if line.startswith('<SUBSECTION>'):
+            continue
+
+        if re.search(r'^<TITLE>(.*)<\/TITLE>', line):
+            continue
+
+        m = re.search(r'^<FILE>(.*)<\/FILE>', line)
+        if m:
+            KnownSymbols[m.group(1) + ":Long_Description"] = 1
+            KnownSymbols[m.group(1) + ":Short_Description"] = 1
+            continue
+
+        m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
+        if m:
+            continue
+
+        m = re.search(r'^<\/SECTION>', line)
+        if m:
+            continue
+
+        m = re.search(r'^(\S+)', line)
+        if m:
+            symbol = m.group(1)
+            if subsection != "Standard" and subsection != "Private":
+                KnownSymbols[symbol] = 1
+            else:
+                KnownSymbols[symbol] = 0
+    INPUT.close()
+
+
+#
+# Function    : OutputDeclaration
+# Description : Returns the synopsis and detailed description DocBook
+#                describing one function/macro etc.
+# Arguments   : $symbol - the name of the function/macro begin described.
+#                $declaration - the declaration of the function/macro.
+#
+
+def OutputDeclaration(symbol, declaration):
+    dtype = DeclarationTypes[symbol]
+    if dtype == 'MACRO':
+        return OutputMacro(symbol, declaration)
+    elif dtype == 'TYPEDEF':
+        return OutputTypedef(symbol, declaration)
+    elif dtype == 'STRUCT':
+        return OutputStruct(symbol, declaration)
+    elif dtype == 'ENUM':
+        return OutputEnum(symbol, declaration)
+    elif dtype == 'UNION':
+        return OutputUnion(symbol, declaration)
+    elif dtype == 'VARIABLE':
+        return OutputVariable(symbol, declaration)
+    elif dtype == 'FUNCTION':
+        return OutputFunction(symbol, declaration, dtype)
+    elif dtype == 'USER_FUNCTION':
+        return OutputFunction(symbol, declaration, dtype)
+    else:
+        sys.exit("Unknown symbol type " + dtype)
+
+
+#
+# Function    : OutputSymbolTraits
+# Description : Returns the Since and StabilityLevel paragraphs for a symbol.
+# Arguments   : $symbol - the name of the function/macro begin described.
+#
+
+def OutputSymbolTraits(symbol):
+    desc = ''
+
+    if symbol in Since:
+        link_id = "api-index-" + Since[symbol]
+        desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, 
Since[symbol])
+
+    if symbol in StabilityLevel:
+        stability = StabilityLevel[symbol]
+        AnnotationsUsed[stability] = True
+        desc += "<para role=\"stability\">Stability Level: <acronym>%s</acronym></para>" % stability
+    return desc
+
+
+#
+# Function    : Output{Symbol,Section}ExtraLinks
+# Description : Returns extralinks for the symbol (if enabled).
+# Arguments   : $symbol - the name of the function/macro begin described.
+#
+
+def uri_escape(text):
+    if text is None:
+        return None
+
+    # Build a char to hex map
+    escapes = {}
+    for i in range(256):
+        escapes[chr(i)] = "%%%02X" % i
+
+    # Default unsafe characters.  RFC 2732 ^(uric - reserved)
+    def do_escape(char):
+        return escapes[char]
+    text = re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text)
+
+    return text
+
+
+def OutputSymbolExtraLinks(symbol):
+    desc = ''
+
+    if False:   # NEW FEATURE: needs configurability
+        sstr = uri_escape(symbol)
+        mstr = uri_escape(MODULE)
+        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
+<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
+''' % (sstr, mstr, sstr)
+
+    return desc
+
+
+def OutputSectionExtraLinks(symbol, docsymbol):
+    desc = ''
+
+    if False:   # NEW FEATURE: needs configurability
+        sstr = uri_escape(symbol)
+        mstr = uri_escape(MODULE)
+        dsstr = uri_escape(docsymbol)
+        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
+<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
+''' % (sstr, mstr, dsstr)
+    return desc
+
+
+#
+# Function    : OutputMacro
+# Description : Returns the synopsis and detailed description of a macro.
+# Arguments   : $symbol - the macro.
+#                $declaration - the declaration of the macro.
+#
+
+def OutputMacro(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+    synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link>" % (
+        sid, symbol)
+
+    fields = common.ParseMacroDeclaration(declaration, CreateValidSGML)
+    title = symbol
+    if len(fields) > 0:
+        title += '()'
+
+    desc = '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid, condition, title)
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    if len(fields) > 0:
+        synop += "<phrase role=\"c_punctuation\">()</phrase>"
+
+    synop += "</entry></row>\n"
+
+    # Don't output the macro definition if is is a conditional macro or it
+    # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
+    # longer than 2 lines, otherwise we get lots of complicated macros like
+    # g_assert.
+    if symbol not in DeclarationConditional and not symbol.startswith('g_') \
+            and not re.search(r'^_?gnome_', symbol) and declaration.count('\n') < 2:
+        decl_out = CreateValidSGML(declaration)
+        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+    else:
+        desc += "<programlisting language=\"C\">" + MakeReturnField("#define") + symbol
+        m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration)
+        if m:
+            args = m.group(1)
+            pad = ' ' * (RETURN_TYPE_FIELD_WIDTH - len("#define "))
+            # Align each line so that if should all line up OK.
+            args = args.replace('\n', '\n' + pad)
+            desc += CreateValidSGML(args)
+
+        desc += "</programlisting>\n"
+
+    desc += MakeDeprecationNote(symbol)
+
+    parameters = OutputParamDescriptions("MACRO", symbol, fields)
+
+    if symbol in SymbolDocs:
+        symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
+        desc += symbol_docs
+
+    desc += parameters
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputTypedef
+# Description : Returns the synopsis and detailed description of a typedef.
+# Arguments   : $symbol - the typedef.
+#                $declaration - the declaration of the typedef,
+#                  e.g. 'typedef unsigned int guint;'
+#
+
+def OutputTypedef(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+    desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
+        sid, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    if symbol in DeclarationConditional:
+        decl_out = CreateValidSGML(declaration)
+        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputStruct
+# Description : Returns the synopsis and detailed description of a struct.
+#                We check if it is a object struct, and if so we only output
+#                parts of it that are noted as public fields.
+#                We also use a different IDs for object structs, since the
+#                original ID is used for the entire RefEntry.
+# Arguments   : $symbol - the struct.
+#                $declaration - the declaration of the struct.
+#
+
+def OutputStruct(symbol, declaration):
+
+    is_gtype = False
+    default_to_public = True
+    if CheckIsObject(symbol):
+        logging.info("Found struct gtype: %s", symbol)
+        is_gtype = True
+        default_to_public = ObjectRoots[symbol] == 'GBoxed'
+
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_struct")
+        condition = MakeConditionDescription(symbol + "_struct")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
+
+    # Determine if it is a simple struct or it also has a typedef.
+    has_typedef = False
+    if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
+        has_typedef = True
+
+    type_output = None
+    desc = None
+    if has_typedef:
+        # For structs with typedefs we just output the struct name.
+        type_output = ''
+        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    else:
+        type_output = "struct"
+        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, 
symbol)
+
+    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
+        type_output, sid, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    # Form a pretty-printed, private-data-removed form of the declaration
+
+    decl_out = ''
+    if re.search(r'^\s*$', declaration):
+        logging.info("Found opaque struct: %s", symbol)
+        decl_out = "typedef struct _%s %s;" % (symbol, symbol)
+    elif re.search(r'^\s*struct\s+\w+\s*;\s*$', declaration):
+        logging.info("Found opaque struct: %s", symbol)
+        decl_out = "struct %s;" % symbol
+    else:
+        m = re.search(
+            r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', declaration, 
flags=re.S)
+        if m:
+            struct_contents = m.group(2)
+
+            public = default_to_public
+            new_declaration = ''
+
+            for decl_line in struct_contents.splitlines():
+                logging.info("Struct line: %s", decl_line)
+                m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', decl_line)
+                m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line)
+                if m2:
+                    public = True
+                elif m3:
+                    public = False
+                elif public:
+                    new_declaration += decl_line + "\n"
+
+            if new_declaration:
+                # Strip any blank lines off the ends.
+                new_declaration = re.sub(r'^\s*\n', '', new_declaration)
+                new_declaration = re.sub(r'\n\s*$', r'\n', new_declaration)
+
+                if has_typedef:
+                    decl_out = "typedef struct {\n%s} %s;\n" % (new_declaration, symbol)
+                else:
+                    decl_out = "struct %s {\n%s};\n" % (symbol, new_declaration)
+
+        else:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Couldn't parse struct:\n%s" % declaration)
+
+        # If we couldn't parse the struct or it was all private, output an
+        # empty struct declaration.
+        if decl_out == '':
+            if has_typedef:
+                decl_out = "typedef struct _%s %s;" % (symbol, symbol)
+            else:
+                decl_out = "struct %s;" % symbol
+
+    decl_out = CreateValidSGML(decl_out)
+    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    # Create a table of fields and descriptions
+
+    # FIXME: Inserting &#160's into the produced type declarations here would
+    #        improve the output in most situations ... except for function
+    #        members of structs!
+    def pfunc(*args):
+        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
+    fields = common.ParseStructDeclaration(declaration, not default_to_public, 0, MakeXRef, pfunc)
+    params = SymbolParams.get(symbol)
+
+    # If no parameters are filled in, we don't generate the description
+    # table, for backwards compatibility.
+    found = False
+    if params:
+        found = next((True for p in params.values() if p.strip() != ''), False)
+
+    if found:
+        field_descrs = params
+        missing_parameters = ''
+        unused_parameters = ''
+        sid = common.CreateValidSGMLID(symbol + ".members")
+
+        desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
+<informaltable role="struct_members_table" pgwide="1" frame="none">
+<tgroup cols="3">
+<colspec colname="struct_members_name" colwidth="300px"/>
+<colspec colname="struct_members_description"/>
+<colspec colname="struct_members_annotations" colwidth="200px"/>
+<tbody>
+''' % sid
+
+        for field_name, text in fields.iteritems():
+            param_annotations = ''
+
+            desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % 
text
+            if field_name in field_descrs:
+                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
+                field_descr = ConvertMarkDown(symbol, field_descr)
+                # trim
+                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
+                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
+                desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry 
role=\"struct_member_annotations\">%s</entry>\n" % (
+                    field_descr, param_annotations)
+                del field_descrs[field_name]
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+
+                desc += "<entry /><entry />\n"
+
+            desc += "</row>\n"
+
+        desc += "</tbody></tgroup></informaltable>\n</refsect3>\n"
+        for field_name in field_descrs:
+            # Documenting those standard fields is not required anymore, but
+            # we don't want to warn if they are documented anyway.
+            m = re.search(r'(g_iface|parent_instance|parent_class)', field_name)
+            if m:
+                continue
+
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+            if unused_parameters != '':
+                unused_parameters += ", " + field_name
+            else:
+                unused_parameters = field_name
+
+        # remember missing/unused parameters (needed in tmpl-free build)
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
+
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+    else:
+        if fields:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field descriptions for struct %s are missing in source code comment 
block." % symbol)
+                logging.info("Remaining structs fields: " + ':'.join(fields) + "\n")
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputUnion
+# Description : Returns the synopsis and detailed description of a union.
+# Arguments   : $symbol - the union.
+#                $declaration - the declaration of the union.
+#
+
+def OutputUnion(symbol, declaration):
+
+    is_gtype = False
+    if CheckIsObject(symbol):
+        logging.info("Found union gtype: %s", symbol)
+        is_gtype = True
+
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_union")
+        condition = MakeConditionDescription(symbol + "_union")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
+
+    # Determine if it is a simple struct or it also has a typedef.
+    has_typedef = False
+    if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
+        has_typedef = True
+
+    type_output = None
+    desc = None
+    if has_typedef:
+        # For unions with typedefs we just output the union name.
+        type_output = ''
+        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    else:
+        type_output = "union"
+        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol)
+
+    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
+        type_output, sid, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    # Create a table of fields and descriptions
+
+    # FIXME: Inserting &#160's into the produced type declarations here would
+    #        improve the output in most situations ... except for function
+    #        members of structs!
+    def pfunc(*args):
+        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
+    fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc)
+    params = SymbolParams.get(symbol)
+
+    # If no parameters are filled in, we don't generate the description
+    # table, for backwards compatibility
+    found = False
+    if params:
+        found = next((True for p in params.values() if p.strip() != ''), False)
+
+    logging.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol, len(fields), found, 
has_typedef)
+
+    if found:
+        field_descrs = params
+        missing_parameters = ''
+        unused_parameters = ''
+        sid = common.CreateValidSGMLID('%s.members' % symbol)
+
+        desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
+<informaltable role="union_members_table" pgwide="1" frame="none">
+<tgroup cols="3">
+<colspec colname="union_members_name" colwidth="300px"/>
+<colspec colname="union_members_description"/>
+<colspec colname="union_members_annotations" colwidth="200px"/>
+<tbody>
+''' % sid
+
+        for field_name, text in fields.iteritems():
+            param_annotations = ''
+
+            desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
+            if field_name in field_descrs:
+                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
+                field_descr = ConvertMarkDown(symbol, field_descr)
+
+                # trim
+                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
+                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
+                desc += "<entry role=\"union_member_description\">%s</entry>\n<entry 
role=\"union_member_annotations\">%s</entry>\n" % (
+                    field_descr, param_annotations)
+                del field_descrs[field_name]
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+
+                desc += "<entry /><entry />\n"
+
+            desc += "</row>\n"
+
+        desc += "</tbody></tgroup></informaltable>\n</refsect3>"
+        for field_name in field_descrs:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+            if unused_parameters != '':
+                unused_parameters += ", " + field_name
+            else:
+                unused_parameters = field_name
+
+        # remember missing/unused parameters (needed in tmpl-free build)
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
+
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+    else:
+        if len(fields) > 0:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field descriptions for union %s are missing in source code comment 
block." % symbol)
+                logging.info("Remaining union fields: " + ':'.join(fields) + "\n")
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputEnum
+# Description : Returns the synopsis and detailed description of a enum.
+# Arguments   : $symbol - the enum.
+#                $declaration - the declaration of the enum.
+#
+
+def OutputEnum(symbol, declaration):
+    is_gtype = False
+    if CheckIsObject(symbol):
+        logging.info("Found enum gtype: %s", symbol)
+        is_gtype = True
+
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_enum")
+        condition = MakeConditionDescription(symbol + "_enum")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
+
+    synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
+        sid, symbol)
+    desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    # Create a table of fields and descriptions
+
+    fields = common.ParseEnumDeclaration(declaration)
+    params = SymbolParams.get(symbol)
+
+    # If nothing at all is documented log a single summary warning at the end.
+    # Otherwise, warn about each undocumented item.
+
+    found = False
+    if params:
+        found = next((True for p in params.values() if p.strip() != ''), False)
+        field_descrs = params
+    else:
+        field_descrs = {}
+
+    missing_parameters = ''
+    unused_parameters = ''
+
+    sid = common.CreateValidSGMLID("%s.members" % symbol)
+    desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
+<informaltable role="enum_members_table" pgwide="1" frame="none">
+<tgroup cols="3">
+<colspec colname="enum_members_name" colwidth="300px"/>
+<colspec colname="enum_members_description"/>
+<colspec colname="enum_members_annotations" colwidth="200px"/>
+<tbody>
+''' % sid
+
+    for field_name in fields:
+        field_descr = field_descrs.get(field_name)
+        param_annotations = ''
+
+        sid = common.CreateValidSGMLID(field_name)
+        condition = MakeConditionDescription(field_name)
+        desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para 
id=\"%s\">%s</para></entry>\n" % (
+            sid, field_name)
+        if field_descr:
+            field_descr, param_annotations = ExpandAnnotation(symbol, field_descr)
+            field_descr = ConvertMarkDown(symbol, field_descr)
+            desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry 
role=\"enum_member_annotations\">%s</entry>\n" % (
+                field_descr, param_annotations)
+            del field_descrs[field_name]
+        else:
+            if found:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Value description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+            desc += "<entry /><entry />\n"
+        desc += "</row>\n"
+
+    desc += "</tbody></tgroup></informaltable>\n</refsect3>"
+    for field_name in field_descrs:
+        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                          "Value description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+        if unused_parameters != '':
+            unused_parameters += ", " + field_name
+        else:
+            unused_parameters = field_name
+
+    # remember missing/unused parameters (needed in tmpl-free build)
+    if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+        AllIncompleteSymbols[symbol] = missing_parameters
+
+    if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+        AllUnusedSymbols[symbol] = unused_parameters
+
+    if not found:
+        if len(fields) > 0:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Value descriptions for %s are missing in source code comment block." % 
symbol)
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputVariable
+# Description : Returns the synopsis and detailed description of a variable.
+# Arguments   : $symbol - the extern'ed variable.
+#                $declaration - the declaration of the variable.
+#
+
+def OutputVariable(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+
+    logging.info("ouputing variable: '%s' '%s'", symbol, declaration)
+
+    type_output = None
+    m1 = re.search(
+        
r'^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;',
 declaration)
+    m2 = re.search(
+        
r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=',
 declaration)
+    if m1:
+        mod1 = m1.group(1) or ''
+        ptr = m1.group(3) or ''
+        space = m1.group(4) or ''
+        mod2 = m1.group(5) or ''
+        type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2)
+    elif m2:
+        mod1 = m2.group(1) or ''
+        ptr = m2.group(3) or ''
+        space = m2.group(4) or ''
+        mod2 = m2.group(5) or ''
+        type_output = '%s%s%s%s' % (mod1, ptr, space, mod2)
+    else:
+        type_output = "extern"
+
+    synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (
+        type_output, sid, symbol)
+
+    desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    decl_out = CreateValidSGML(declaration)
+    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    if symbol in SymbolAnnotations:
+        param_desc = SymbolAnnotations[symbol]
+        param_annotations = ''
+        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+        if param_annotations != '':
+            desc += "\n<para>%s</para>" % param_annotations
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputFunction
+# Description : Returns the synopsis and detailed description of a function.
+# Arguments   : $symbol - the function.
+#                $declaration - the declaration of the function.
+#
+
+def OutputFunction(symbol, declaration, symbol_type):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+
+    # Take out the return type
+    #                      $1                                                                                
       $2   $3
+    regex = 
r'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n'
+    m = re.search(regex, declaration)
+    declaration = re.sub(regex, '', declaration)
+    type_modifier = m.group(1) or ''
+    type = m.group(2)
+    pointer = m.group(3)
+    # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
+    pointer = pointer.rstrip()
+    xref = MakeXRef(type, tagify(type, "returnvalue"))
+    start = ''
+    # if (symbol_type == 'USER_FUNCTION')
+    #    start = "typedef "
+    #
+
+    # We output const rather than G_CONST_RETURN.
+    type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier)
+    pointer = re.sub(r'G_CONST_RETURN', 'const', pointer)
+    pointer = re.sub(r'^\s+', '&#160;', pointer)
+
+    ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer)
+
+    indent_len = len(symbol) + 2
+    char1 = char2 = char3 = ''
+    if symbol_type == 'USER_FUNCTION':
+        indent_len += 3
+        char1 = "<phrase role=\"c_punctuation\">(</phrase>"
+        char2 = "*"
+        char3 = "<phrase role=\"c_punctuation\">)</phrase>"
+
+    symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3)
+    if indent_len < MAX_SYMBOL_FIELD_WIDTH:
+        symbol_desc_output = "%s%s%s%s " % (char1, char2, symbol, char3)
+    else:
+        indent_len = MAX_SYMBOL_FIELD_WIDTH - 8
+        symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1))
+
+    synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s&#160;<phrase 
role=\"c_punctuation\">()</phrase></entry></row>\n" % (
+        ret_type_output, symbol_output)
+
+    desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s&#160;()</title>\n" % (sid, condition, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output)
+
+    def tagfun(*args):
+        return tagify(args[0], "parameter")
+
+    fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun)
+
+    first = True
+    for field_name in fields.values():
+        if first:
+            desc += field_name
+            first = False
+        else:
+            desc += ",\n" + (' ' * indent_len) + field_name
+
+    desc += ");</programlisting>\n"
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    if symbol in SymbolAnnotations:
+        param_desc = SymbolAnnotations[symbol]
+        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+        if param_annotations != '':
+            desc += "\n<para>%s</para>" % param_annotations
+
+    desc += OutputParamDescriptions("FUNCTION", symbol, fields.keys())
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
+
+#
+# Function    : OutputParamDescriptions
+# Description : Returns the DocBook output describing the parameters of a
+#                function, macro or signal handler.
+# Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
+#                  handlers have an implicit user_data parameter last.
+#                $symbol - the name of the function/macro being described.
+#               @fields - parsed fields from the declaration, used to determine
+#                  undocumented/unused entries
+#
+
+def OutputParamDescriptions(symbol_type, symbol, fields):
+    output = ''
+    num_params = 0
+    field_descrs = None
+
+    if fields:
+        field_descrs = [f for f in fields if f not in ['void', 'Returns']]
+    else:
+        field_descrs = []
+
+    params = SymbolParams.get(symbol)
+    logging.info("param_desc(%s, %s) = %s", symbol_type, symbol, str(params))
+    # This might be an empty dict, but for SIGNALS we append the user_data docs.
+    # TODO(ensonic): maybe create that docstring in GetSignals()
+    if params is not None:
+        returns = ''
+        params_desc = ''
+        missing_parameters = ''
+        unused_parameters = ''
+
+        for param_name, param_desc in params.iteritems():
+            (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+            param_desc = ConvertMarkDown(symbol, param_desc)
+            # trim
+            param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M | re.S)
+            param_desc = re.sub(r'(\s|\n)+$', '', param_desc, flags=re.M | re.S)
+            if param_name == "Returns":
+                returns = param_desc
+                if param_annotations != '':
+                    returns += "\n<para>%s</para>" % param_annotations
+
+                elif param_name == "void":
+                    # FIXME: &common.LogWarning()?
+                    logging.info("!!!! void in params for %s?\n", symbol)
+            else:
+                if fields:
+                    if param_name not in field_descrs:
+                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                          "Parameter description for %s::%s is not used from source code 
comment block." % (symbol, param_name))
+                        if unused_parameters != '':
+                            unused_parameters += ", " + param_name
+                        else:
+                            unused_parameters = param_name
+                    else:
+                        field_descrs.remove(param_name)
+
+                if param_desc != '':
+                    params_desc += "<row><entry role=\"parameter_name\"><para>%s</para></entry>\n<entry 
role=\"parameter_description\">%s</entry>\n<entry role=\"parameter_annotations\">%s</entry></row>\n" % (
+                        param_name, param_desc, param_annotations)
+                    num_params += 1
+
+        for param_name in field_descrs:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Parameter description for %s::%s is missing in source code comment block." % 
(symbol, param_name))
+            if missing_parameters != '':
+                missing_parameters += ", " + param_name
+            else:
+                missing_parameters = param_name
+
+        # Signals have an implicit user_data parameter which we describe.
+        if symbol_type == "SIGNAL":
+            params_desc += "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry 
role=\"parameter_description\"><simpara>user data set when the signal handler was 
connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n"
+
+        # Start a table if we need one.
+        if params_desc != '':
+            sid = common.CreateValidSGMLID("%s.parameters" % symbol)
+
+            output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
+<informaltable role="parameters_table" pgwide="1" frame="none">
+<tgroup cols="3">
+<colspec colname="parameters_name" colwidth="150px"/>
+<colspec colname="parameters_description"/>
+<colspec colname="parameters_annotations" colwidth="200px"/>
+<tbody>
+''' % sid
+            output += params_desc
+            output += "</tbody></tgroup></informaltable>\n</refsect3>"
+
+        # Output the returns info last
+        if returns != '':
+            sid = common.CreateValidSGMLID("%s.returns" % symbol)
+
+            output += '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
+''' % sid
+            output += returns
+            output += "\n</refsect3>"
+
+        # remember missing/unused parameters (needed in tmpl-free build)
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
+
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+
+    if num_params == 0 and fields and field_descrs:
+        if symbol not in AllIncompleteSymbols:
+            AllIncompleteSymbols[symbol] = "<parameters>"
+    return output
+
+
+#
+# Function    : ParseStabilityLevel
+# Description : Parses a stability level and outputs a warning if it isn't
+#               valid.
+# Arguments   : $stability - the stability text.
+#                $file, $line - context for error message
+#                $message - description of where the level is from, to use in
+#               any error message.
+# Returns     : The parsed stability level string.
+#
+
+def ParseStabilityLevel(stability, file, line, message):
+
+    stability = stability.strip()
+    sl = stability.lower()
+
+    if sl == 'stable':
+        stability = "Stable"
+    elif stability == 'unstable':
+        stability = "Unstable"
+    elif stability == 'private':
+        stability = "Private"
+    else:
+        common.LogWarning(file, line, "%s is %s." % (message, stability) +
+                          "It should be one of these: Stable, Unstable, or Private.")
+    return stability
+
+
+#
+# Function    : OutputDBFile
+# Description : Outputs the final DocBook file for one section.
+# Arguments   : $file - the name of the file.
+#               $title - the title from the $MODULE-sections.txt file, which
+#                 will be overridden by the title in the template file.
+#               $section_id - the id to use for the toplevel tag.
+#               $includes - comma-separates list of include files added at top of
+#                 synopsis, with '<' '>' around them (if not already enclosed in '').
+#               $functions_synop - reference to the DocBook for the Functions Synopsis part.
+#               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
+#               $functions_details - reference to the DocBook for the Functions Details part.
+#               $other_details - reference to the DocBook for the Types and Values Details part.
+#               $signal_synop - reference to the DocBook for the Signal Synopsis part
+#               $signal_desc - reference to the DocBook for the Signal Description part
+#               $args_synop - reference to the DocBook for the Arg Synopsis part
+#               $args_desc - reference to the DocBook for the Arg Description part
+#               $hierarchy - reference to the DocBook for the Object Hierarchy part
+#               $interfaces - reference to the DocBook for the Interfaces part
+#               $implementations - reference to the DocBook for the Known Implementations part
+#               $prerequisites - reference to the DocBook for the Prerequisites part
+#               $derived - reference to the DocBook for the Derived Interfaces part
+#               $file_objects - reference to an array of objects in this file
+#
+
+def OutputDBFile(file, title, section_id, includes, functions_synop, other_synop, functions_details, 
other_details, signals_synop, signals_desc, args_synop, args_desc, hierarchy, interfaces, implementations, 
prerequisites, derived, file_objects):
+
+    logging.info("Output docbook for file %s with title '%s'", file, title)
+
+    # The edited title overrides the one from the sections file.
+    new_title = SymbolDocs.get(file + ":Title")
+    if new_title and not new_title.strip() == '':
+        title = new_title
+        logging.info("Found title: %s", title)
+
+    short_desc = SymbolDocs.get(file + ":Short_Description")
+    if not short_desc or short_desc.strip() == '':
+        short_desc = ''
+    else:
+        # Don't use ConvertMarkDown here for now since we don't want blocks
+        short_desc = ExpandAbbreviations(title + ":Short_description", short_desc)
+        logging.info("Found short_desc: %s", short_desc)
+
+    long_desc = SymbolDocs.get(file + ":Long_Description")
+    if not long_desc or long_desc.strip() == '':
+        long_desc = ''
+    else:
+        long_desc = ConvertMarkDown(title + ":Long_description", long_desc)
+        logging.info("Found long_desc: %s", long_desc)
+
+    see_also = SymbolDocs.get(file + ":See_Also")
+    if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
+        see_also = ''
+    else:
+        see_also = ConvertMarkDown(title + ":See_Also", see_also)
+        logging.info("Found see_also: %s", see_also)
+
+    if see_also:
+        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
+
+    stability = SymbolDocs.get(file + ":Stability_Level")
+    if not stability or re.search(r'^\s*$', stability):
+        stability = ''
+    else:
+        line_number = GetSymbolSourceLine(file + ":Stability_Level")
+        stability = ParseStabilityLevel(stability, file, line_number, "Section stability level")
+        logging.info("Found stability: %s", stability)
+
+    if stability:
+        AnnotationsUsed[stability] = 1
+        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
+            section_id, stability)
+    elif DEFAULT_STABILITY:
+        AnnotationsUsed[DEFAULT_STABILITY] = 1
+        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
+            section_id, DEFAULT_STABILITY)
+
+    image = SymbolDocs.get(file + ":Image")
+    if not image or re.search(r'^\s*$', image):
+        image = ''
+    else:
+        image = image.strip()
+
+        format = None
+
+        il = image.lower()
+        if re.search(r'jpe?g$', il):
+            format = "format='JPEG'"
+        elif il.endswith('png'):
+            format = "format='PNG'"
+        elif il.endswith('svg'):
+            format = "format='SVG'"
+        else:
+            format = ''
+
+        image = "  <inlinegraphic fileref='%s' %s/>\n" % (image, format)
+
+    include_output = ''
+    if includes:
+        include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
+        for include in includes.split(','):
+            if re.search(r'^\".+\"$', include):
+                include_output += "#include %s\n" % include
+            else:
+                include = re.sub(r'^\s+|\s+$', '', include, flags=re.S)
+                include_output += "#include &lt;%s&gt;\n" % include
+
+        include_output += "</synopsis></refsect1>\n"
+
+    extralinks = OutputSectionExtraLinks(title, "Section:%s" % file)
+
+    old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml')
+    new_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml.new')
+
+    OUTPUT = open(new_db_file, 'w')
+
+    object_anchors = ''
+    for fobject in file_objects:
+        if fobject == section_id:
+            continue
+        sid = common.CreateValidSGMLID(fobject)
+        logging.info("Adding anchor for %s\n", fobject)
+        object_anchors += "<anchor id=\"%s\"/>" % sid
+
+    # Make sure we produce valid docbook
+    if not functions_details:
+        functions_details = "<para />"
+
+    # We used to output this, but is messes up our common.UpdateFileIfChanged code
+    # since it changes every day (and it is only used in the man pages):
+    # "<refentry id="$section_id" revision="$mday $month $year">"
+
+    OUTPUT.write(REFENTRY.substitute({
+        'args_desc': args_desc,
+        'args_synop': args_synop,
+        'derived': derived,
+        'extralinks': extralinks,
+        'functions_details': functions_details,
+        'functions_synop': functions_synop,
+        'header': MakeDocHeader('refentry'),
+        'hierarchy': hierarchy,
+        'image': image,
+        'include_output': include_output,
+        'interfaces': interfaces,
+        'implementations': implementations,
+        'long_desc': long_desc,
+        'object_anchors': object_anchors,
+        'other_details': other_details,
+        'other_synop': other_synop,
+        'prerequisites': prerequisites,
+        'section_id': section_id,
+        'see_also': see_also,
+        'signals_desc': signals_desc,
+        'signals_synop': signals_synop,
+        'short_desc': short_desc,
+        'stability': stability,
+        'title': title,
+        'MODULE': MODULE.upper(),
+    }))
+    OUTPUT.close()
+
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
+
+
+#
+# Function    : OutputProgramDBFile
+# Description : Outputs the final DocBook file for one program.
+# Arguments   : $file - the name of the file.
+#               $section_id - the id to use for the toplevel tag.
+#
+
+def OutputProgramDBFile(program, section_id):
+    logging.info("Output program docbook for %s", program)
+
+    short_desc = SourceSymbolDocs.get(program + ":Short_Description")
+    if not short_desc or short_desc.strip() == '':
+        short_desc = ''
+    else:
+        # Don't use ConvertMarkDown here for now since we don't want blocks
+        short_desc = ExpandAbbreviations(program, short_desc)
+        logging.info("Found short_desc: %s", short_desc)
+
+    synopsis = SourceSymbolDocs.get(program + ":Synopsis")
+    if synopsis and synopsis.strip() != '':
+        items = synopsis.split(' ')
+        for i in range(0, len(items)):
+            parameter = items[i]
+            choice = "plain"
+            rep = ''
+
+            # first parameter is the command name
+            if i == 0:
+                synopsis = "<command>%s</command>\n" % parameter
+                continue
+
+            # square brackets indicate optional parameters, curly brackets
+            # indicate required parameters ("plain" parameters are also
+            # mandatory, but do not get extra decoration)
+            m1 = re.search(r'^\[(.+?)\]$', parameter)
+            m2 = re.search(r'^\{(.+?)\}$', parameter)
+            if m1:
+                choice = "opt"
+                parameter = m1.group(1)
+            elif m2:
+                choice = "req"
+                parameter = m2.group(1)
+
+            # parameters ending in "..." are repeatable
+            if parameter.endswith('...'):
+                rep = ' rep=\"repeat\"'
+                parameter = parameter[:-3]
+
+            # italic parameters are replaceable parameters
+            parameter = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', parameter)
+
+            synopsis += "<arg choice=\"%s\"%s>" % (choice, rep)
+            synopsis += parameter
+            synopsis += "</arg>\n"
+
+        logging.info("Found synopsis: %s", synopsis)
+    else:
+        synopsis = "<command>%s</command>" % program
+
+    long_desc = SourceSymbolDocs.get(program + ":Long_Description")
+    if not long_desc or long_desc.strip() == '':
+        long_desc = ''
+    else:
+        long_desc = ConvertMarkDown("%s:Long_description" % program, long_desc)
+        logging.info("Found long_desc: %s", long_desc)
+
+    options = ''
+    o = program + ":Options"
+    if o in SourceSymbolDocs:
+        opts = SourceSymbolDocs[o].split('\t')
+
+        logging.info('options: %d, %s', len(opts), str(opts))
+
+        options = "<refsect1>\n<title>Options</title>\n<variablelist>\n"
+        for k in range(0, len(opts), 2):
+            opt_desc = opts[k + 1]
+
+            opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_desc)
+
+            options += "<varlistentry>\n<term>"
+            opt_names = opts[k].split(',')
+            for i in range(len(opt_names)):
+                prefix = ', ' if i > 0 else ''
+                # italic parameters are replaceable parameters
+                opt_name = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_names[i])
+
+                options += "%s<option>%s</option>\n" % (prefix, opt_name)
+
+            options += "</term>\n"
+            options += "<listitem><para>%s</para></listitem>\n" % opt_desc
+            options += "</varlistentry>\n"
+
+        options += "</variablelist></refsect1>\n"
+
+    exit_status = SourceSymbolDocs.get(program + ":Returns")
+    if exit_status and exit_status != '':
+        exit_status = ConvertMarkDown("%s:Returns" % program, exit_status)
+        exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (
+            section_id, exit_status)
+    else:
+        exit_status = ''
+
+    see_also = SourceSymbolDocs.get(program + ":See_Also")
+    if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
+        see_also = ''
+    else:
+        see_also = ConvertMarkDown("%s:See_Also" % program, see_also)
+        logging.info("Found see_also: %s", see_also)
+
+    if see_also:
+        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
+
+    old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml")
+    new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new")
+
+    OUTPUT = open(new_db_file, 'w')
+
+    OUTPUT.write('''%s
+<refentry id="%s">
+<refmeta>
+<refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
+<manvolnum>1</manvolnum>
+<refmiscinfo>User Commands</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>%s</refname>
+<refpurpose>%s</refpurpose>
+</refnamediv>
+<refsynopsisdiv>
+<cmdsynopsis>%s</cmdsynopsis>
+</refsynopsisdiv>
+<refsect1 id="%s.description" role="desc">
+<title role="desc.title">Description</title>
+%s
+</refsect1>
+%s%s%s
+</refentry>
+''' % (MakeDocHeader("refentry"), section_id, section_id, program, program, short_desc, synopsis, 
section_id, long_desc, options, exit_status, see_also))
+    OUTPUT.close()
+
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
+
+
+#
+# Function    : OutputExtraFile
+# Description : Copies an "extra" DocBook file into the output directory,
+#               expanding abbreviations
+# Arguments   : $file - the source file.
+#
+def OutputExtraFile(file):
+
+    basename = os.path.basename(file)
+
+    old_db_file = os.path.join(DB_OUTPUT_DIR, basename)
+    new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new")
+
+    contents = open(file).read()
+
+    OUTPUT = open(new_db_file, 'w')
+    OUTPUT.write(ExpandAbbreviations(basename + " file", contents))
+    OUTPUT.close()
+
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
+
+
+#
+# Function    : OutputBook
+# Description : Outputs the entities that need to be included into the
+#                main docbook file for the module.
+# Arguments   : $book_top - the declarations of the entities, which are added
+#                  at the top of the main docbook file.
+#                $book_bottom - the references to the entities, which are
+#                  added in the main docbook file at the desired position.
+#
+
+def OutputBook(book_top, book_bottom):
+
+    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top")
+    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new")
+
+    OUTPUT = open(new_file, 'w')
+    OUTPUT.write(book_top)
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_file, new_file, 0)
+
+    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom")
+    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new")
+
+    OUTPUT = open(new_file, 'w')
+    OUTPUT.write(book_bottom)
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_file, new_file, 0)
+
+    # If the main docbook file hasn't been created yet, we create it here.
+    # The user can tweak it later.
+    if MAIN_SGML_FILE and not os.path.exists(MAIN_SGML_FILE):
+        OUTPUT = open(MAIN_SGML_FILE, 'w')
+
+        OUTPUT.write('''%s
+<book id="index">
+  <bookinfo>
+    <title>&package_name; Reference Manual</title>
+    <releaseinfo>
+      for &package_string;.
+      The latest version of this documentation can be found on-line at
+      <ulink role="online-location" 
url="http://[SERVER]/&package_name;/index.html";>http://[SERVER]/&package_name;/</ulink>.
+    </releaseinfo>
+  </bookinfo>
+
+  <chapter>
+    <title>[Insert title here]</title>
+    %s
+  </chapter>
+''' % (MakeDocHeader("book"), book_bottom))
+        if os.path.exists(OBJECT_TREE_FILE):
+            OUTPUT.write('''  <chapter id="object-tree">
+    <title>Object Hierarchy</title>
+    <xi:include href="xml/tree_index.sgml"/>
+  </chapter>
+''')
+        else:
+            OUTPUT.write('''  <!-- enable this when you use gobject types
+  <chapter id="object-tree">
+    <title>Object Hierarchy</title>
+    <xi:include href="xml/tree_index.sgml"/>
+  </chapter>
+  -->
+''')
+
+        OUTPUT.write('''  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+  <index id="deprecated-api-index" role="deprecated">
+    <title>Index of deprecated API</title>
+    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+  </index>
+''')
+        if AnnotationsUsed:
+            OUTPUT.write('''  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+''')
+        else:
+            OUTPUT.write('''  <!-- enable this when you use gobject introspection annotations
+  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+  -->
+''')
+
+        OUTPUT.write('''</book>
+''')
+
+        OUTPUT.close()
+
+
+#
+# Function    : CreateValidSGML
+# Description : This turns any chars which are used in SGML into entities,
+#                e.g. '<' into '&lt;'
+# Arguments   : $text - the text to turn into proper SGML.
+#
+
+def CreateValidSGML(text):
+    text = re.sub(r'&', r'&amp;', text)        # Do this first, or the others get messed up.
+    text = re.sub(r'<', r'&lt;', text)
+    text = re.sub(r'>', r'&gt;', text)
+    # browsers render single tabs inconsistently
+    text = re.sub(r'([^\s])\t([^\s])', r'\1&#160;\2', text)
+    return text
+
+
+#
+# Function    : ConvertSGMLChars
+# Description : This is used for text in source code comment blocks, to turn
+#               chars which are used in SGML into entities, e.g. '<' into
+#               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
+#               unconditionally or only if the character doesn't seem to be
+#               part of an SGML construct (tag or entity reference).
+# Arguments   : $text - the text to turn into proper SGML.
+#
+
+def ConvertSGMLChars(symbol, text):
+
+    if INLINE_MARKUP_MODE:
+        # For the XML/SGML mode only convert to entities outside CDATA sections.
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[|<programlisting[^>]*>",
+                                 ConvertSGMLCharsEndTag,
+                                 ConvertSGMLCharsCallback)
+    # For the simple non-sgml mode, convert to entities everywhere.
+
+    # First, convert freestanding & to &amp
+    text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)
+    text = re.sub(r'<', r'&lt;', text)
+    # Allow '>' at beginning of string for blockquote markdown
+    text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
+
+    return text
+
+
+def ConvertSGMLCharsEndTag(t):
+    if t == '<![CDATA[':
+        return "]]>"
+    return "</programlisting>"
+
+
+def ConvertSGMLCharsCallback(text, symbol, tag):
+    if re.search(r'^<programlisting', tag):
+        # We can handle <programlisting> specially here.
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[",
+                                 ConvertSGMLCharsEndTag,
+                                 ConvertSGMLCharsCallback2)
+    elif tag == '':
+        # If we're not in CDATA convert to entities.
+        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
+        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
+        # Allow '>' at beginning of string for blockquote markdown
+        text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
+
+        # Handle "#include <xxxxx>"
+        text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
+
+    return text
+
+
+def ConvertSGMLCharsCallback2(text, symbol, tag):
+
+    # If we're not in CDATA convert to entities.
+    # We could handle <programlisting> differently, though I'm not sure it helps.
+    if tag == '':
+        # replace only if its not a tag
+        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
+        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
+        text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'&gt;', text)
+        # Handle "#include <xxxxx>"
+        text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
+
+    return text
+
+
+#
+# Function    : ExpandAnnotation
+# Description : This turns annotations into acronym tags.
+# Arguments   : $symbol - the symbol being documented, for error messages.
+#                $text - the text to expand.
+#
+def ExpandAnnotation(symbol, param_desc):
+    param_annotations = ''
+
+    # look for annotations at the start of the comment part
+    # function level annotations don't end with a colon ':'
+    m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc)
+    if m:
+        param_desc = param_desc[m.end():]
+
+        annotations = re.split(r'\)\s*\(', m.group(1))
+        logging.info("annotations for %s: '%s'\n", symbol, m.group(1))
+        for annotation in annotations:
+            # need to search for the longest key-match in %AnnotationDefinition
+            match_length = 0
+            match_annotation = ''
+
+            for annotationdef in AnnotationDefinition:
+                if annotation.startswith(annotationdef):
+                    if len(annotationdef) > match_length:
+                        match_length = len(annotationdef)
+                        match_annotation = annotationdef
+
+            annotation_extra = ''
+            if match_annotation != '':
+                m = re.search(match_annotation + r'\s+(.*)', annotation)
+                if m:
+                    annotation_extra = " " + m.group(1)
+
+                AnnotationsUsed[match_annotation] = 1
+                param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra)
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "unknown annotation \"%s\" in documentation for %s." % (annotation, 
symbol))
+                param_annotations += "[%s]" % annotation
+
+        param_desc = param_desc.strip()
+        m = re.search(r'^(.*?)\.*\s*$', param_desc, flags=re.S)
+        param_desc = m.group(1) + '. '
+
+    if param_annotations != '':
+        param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
+
+    return (param_desc, param_annotations)
+
+
+#
+# Function    : ExpandAbbreviations
+# Description : This turns the abbreviations function(), macro(), @param,
+#               %constant, and #symbol into appropriate DocBook markup.
+#               CDATA sections and <programlisting> parts are skipped.
+# Arguments   : $symbol - the symbol being documented, for error messages.
+#                $text - the text to expand.
+#
+
+def ExpandAbbreviations(symbol, text):
+    # Note: This is a fallback and normally done in the markdown parser
+
+    # Convert "|[" and "]|" into the start and end of program listing examples.
+    # Support \[<!-- language="C" --> modifiers
+    text = re.sub(r'\|\[<!-- language="([^"]+)" -->', r'<informalexample><programlisting 
language="\1"><![CDATA[', text)
+    text = re.sub(r'\|\[', r'<informalexample><programlisting><![CDATA[', text)
+    text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text)
+
+    # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
+    # as such)
+    return ModifyXMLElements(text, symbol,
+                             "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
+                             ExpandAbbreviationsEndTag,
+                             ExpandAbbreviationsCallback)
+
+
+# Returns the end tag (as a regexp) corresponding to the given start tag.
+def ExpandAbbreviationsEndTag(start_tag):
+    if start_tag == r'<!\[CDATA\[':
+        return "]]>"
+    if start_tag == "<!DOCTYPE":
+        return '>'
+    m = re.search(r'<(\w+)', start_tag)
+    if m:
+        return "</%s>" % m.group(1)
+
+    logging.warning('no end tag for "%s"', start_tag)
+    return ''
+
+
+# Called inside or outside each CDATA or <programlisting> section.
+def ExpandAbbreviationsCallback(text, symbol, tag):
+
+    if tag.startswith(r'^<programlisting'):
+        # Handle any embedded CDATA sections.
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[",
+                                 ExpandAbbreviationsEndTag,
+                                 ExpandAbbreviationsCallback2)
+    elif tag == '':
+        # NOTE: this is a fallback. It is normally done by the Markdown parser.
+        # but is also used for OutputExtraFile
+
+        # We are outside any CDATA or <programlisting> sections, so we expand
+        # any gtk-doc abbreviations.
+
+        # Convert '@param()'
+        # FIXME: we could make those also links ($symbol.$2), but that would be less
+        # useful as the link target is a few lines up or down
+        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()</parameter>', text)
+
+        # Convert 'function()' or 'macro()'.
+        # if there is abc_*_def() we don't want to make a link to _def()
+        # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
+        def f1(m):
+            return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function"))
+        text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text)
+        # handle #Object.func()
+        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text)
+
+        # Convert '@param', but not '\@param'.
+        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2</parameter>', text)
+        text = re.sub(r'/\\\@', r'\@', text)
+
+        # Convert '%constant', but not '\%constant'.
+        # Also allow negative numbers, e.g. %-1.
+        def f2(m):
+            return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2), "literal"))
+        text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text)
+        text = re.sub(r'\\\%', r'\%', text)
+
+        # Convert '#symbol', but not '\#symbol'.
+        def f3(m):
+            return m.group(1) + MakeHashXRef(m.group(2), "type")
+        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text)
+        text = re.sub(r'\\#', '#', text)
+
+    return text
+
+
+# This is called inside a <programlisting>
+def ExpandAbbreviationsCallback2(text, symbol, tag):
+    if tag == '':
+        # We are inside a <programlisting> but outside any CDATA sections,
+        # so we expand any gtk-doc abbreviations.
+        # FIXME: why is this different from &ExpandAbbreviationsCallback(),
+        #        why not just call it
+        text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text)
+    elif tag == "<![CDATA[":
+        # NOTE: this is a fallback. It is normally done by the Markdown parser.
+        text = ReplaceEntities(text, symbol)
+
+    return text
+
+
+def MakeHashXRef(symbol, tag):
+    text = symbol
+
+    # Check for things like '#include', '#define', and skip them.
+    if symbol in PreProcessorDirectives:
+        return "#%s" % symbol
+
+    # Get rid of special suffixes ('-struct','-enum').
+    text = re.sub(r'-struct$', '', text)
+    text = re.sub(r'-enum$', '', text)
+
+    # If the symbol is in the form "Object::signal", then change the symbol to
+    # "Object-signal" and use "signal" as the text.
+    if '::' in symbol:
+        o, s = symbol.split('::', 1)
+        symbol = '%s-%s' % (o, s)
+        text = '“' + s + '”'
+
+    # If the symbol is in the form "Object:property", then change the symbol to
+    # "Object--property" and use "property" as the text.
+    if ':' in symbol:
+        o, p = symbol.split(':', 1)
+        symbol = '%s--%s' % (o, p)
+        text = '“' + p + '”'
+
+    if tag != '':
+        text = tagify(text, tag)
+
+    return MakeXRef(symbol, text)
+
+
+#
+# Function    : ModifyXMLElements
+# Description : Looks for given XML element tags within the text, and calls
+#               the callback on pieces of text inside & outside those elements.
+#               Used for special handling of text inside things like CDATA
+#               and <programlisting>.
+# Arguments   : $text - the text.
+#               $symbol - the symbol currently being documented (only used for
+#                      error messages).
+#               $start_tag_regexp - the regular expression to match start tags.
+#                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
+#                      CDATA sections or programlisting elements.
+#               $end_tag_func - function which is passed the matched start tag
+#                      and should return the appropriate end tag string regexp.
+#               $callback - callback called with each part of the text. It is
+#                      called with a piece of text, the symbol being
+#                      documented, and the matched start tag or '' if the text
+#                      is outside the XML elements being matched.
+#
+def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback):
+    before_tag = start_tag = end_tag_regexp = end_tag = None
+    result = ''
+
+    logging.debug('symbol: %s text: [%s]', symbol, text)
+
+    m = re.search(start_tag_regexp, text, flags=re.S)
+    while m:
+        before_tag = text[:m.start()]  # Prematch for last successful match string
+        start_tag = m.group(0)         # Last successful match
+        text = text[m.end():]          # Postmatch for last successful match string
+
+        logging.debug('symbol: %s matched start %s: text: [%s]', symbol, start_tag, text)
+
+        result += callback(before_tag, symbol, '')
+        result += start_tag
+
+        # get the matching end-tag for current tag
+        end_tag_regexp = end_tag_func(start_tag)
+
+        m2 = re.search(end_tag_regexp, text, flags=re.S)
+        if m2:
+            before_tag = text[:m2.start()]
+            end_tag = m2.group(0)
+            text = text[m2.end():]
+
+            logging.debug('symbol: %s matched end %s: text: [%s]', symbol, end_tag, text)
+
+            result += callback(before_tag, symbol, start_tag)
+            result += end_tag
+        else:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol))
+            # Just assume it is all inside the tag.
+            result += callback(text, symbol, start_tag)
+            text = ''
+        m = re.search(start_tag_regexp, text, flags=re.S)
+
+    # Handle any remaining text outside the tags.
+    result += callback(text, symbol, '')
+
+    return result
+
+
+# Adds a tag around some text.
+# e.g tagify("Text", "literal") => "<literal>Text</literal>".
+def tagify(text, elem):
+    return '<' + elem + '>' + text + '</' + elem + '>'
+
+
+#
+# Function    : MakeDocHeader
+# Description : Builds a docbook header for the given tag
+# Arguments   : $tag - doctype tag
+#
+
+def MakeDocHeader(tag):
+    header = doctype_header
+    header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, header)
+
+    # fix the path for book since this is one level up
+    if tag == 'book':
+        header = re.sub(
+            r'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r'<!ENTITY % gtkdocentities SYSTEM 
"\1">', header)
+    return header
+
+
+#
+# Function    : MakeXRef
+# Description : This returns a cross-reference link to the given symbol.
+#                Though it doesn't try to do this for a few standard C types
+#                that it        knows won't be in the documentation.
+# Arguments   : $symbol - the symbol to try to create a XRef to.
+#               $text - text text to put inside the XRef, defaults to $symbol
+#
+
+def MakeXRef(symbol, text=None):
+    symbol = symbol.strip()
+
+    if not text:
+        text = symbol
+
+        # Get rid of special suffixes ('-struct','-enum').
+        text = re.sub(r'-struct$', '', text)
+        text = re.sub(r'-enum$', '', text)
+
+    if ' ' in symbol:
+        return text
+
+    logging.info("Getting type link for %s -> %s", symbol, text)
+
+    symbol_id = common.CreateValidSGMLID(symbol)
+    return "<link linkend=\"%s\">%s</link>" % (symbol_id, text)
+
+
+#
+# Function    : MakeIndexterms
+# Description : This returns a indexterm elements for the given symbol
+# Arguments   : $symbol - the symbol to create indexterms for
+#
+
+def MakeIndexterms(symbol, sid):
+    terms = ''
+    sortas = ''
+
+    # make the index useful, by ommiting the namespace when sorting
+    if NAME_SPACE != '':
+        m = re.search(r'^%s\_?(.*)' % NAME_SPACE, symbol, flags=re.I)
+        if m:
+            sortas = ' sortas="%s"' % m.group(1)
+
+    if symbol in Deprecated:
+        terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (
+            sid, sortas, symbol)
+        IndexEntriesDeprecated[symbol] = sid
+        IndexEntriesFull[symbol] = sid
+    if symbol in Since:
+        since = Since[symbol].strip()
+        if since != '':
+            terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (
+                sid, since, sortas, symbol)
+        IndexEntriesSince[symbol] = sid
+        IndexEntriesFull[symbol] = sid
+    if terms == '':
+        terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
+        IndexEntriesFull[symbol] = sid
+    return terms
+
+
+#
+# Function    : MakeDeprecationNote
+# Description : This returns a deprecation warning for the given symbol.
+# Arguments   : $symbol - the symbol to try to create a warning for.
+#
+
+def MakeDeprecationNote(symbol):
+    desc = ''
+    if symbol in Deprecated:
+        desc += "<warning><para><literal>%s</literal> " % symbol
+
+        note = Deprecated[symbol]
+
+        m = re.search(r'^\s*([0-9\.]+)\s*:?', note)
+        if m:
+            desc += "has been deprecated since version %s and should not be used in newly-written 
code.</para>" % m.group(
+                1)
+        else:
+            desc += "is deprecated and should not be used in newly-written code.</para>"
+
+        note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note)
+        note = note.strip()
+
+        if note != '':
+            note = ConvertMarkDown(symbol, note)
+            desc += " " + note
+
+        desc += "</warning>\n"
+
+    return desc
+
+
+#
+# Function    : MakeConditionDescription
+# Description : This returns a sumary of conditions for the given symbol.
+# Arguments   : $symbol - the symbol to try to create the sumary.
+#
+
+def MakeConditionDescription(symbol):
+    desc = ''
+    if symbol in Deprecated:
+        if desc != '':
+            desc += "|"
+        m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol])
+        if m:
+            desc += "deprecated:%s" % m.group(1)
+        else:
+            desc += "deprecated"
+
+    if symbol in Since:
+        if desc != '':
+            desc += "|"
+        m = re.search(r'^\s*(.*?)\s*$', Since[symbol])
+        if m:
+            desc += "since:%s" % m.group(1)
+        else:
+            desc += "since"
+
+    if symbol in StabilityLevel:
+        if desc != '':
+            desc += "|"
+
+        desc += "stability:" + StabilityLevel[symbol]
+
+    if desc != '':
+        cond = re.sub(r'"', r'&quot;', desc)
+        desc = ' condition=\"%s\"' % cond
+        logging.info("condition for '%s' = '%s'", symbol, desc)
+
+    return desc
+
+
+#
+# Function    : GetHierarchy
+# Description : Returns the DocBook output describing the ancestors and
+#               immediate children of a GObject subclass. It uses the
+#               global @Objects and @ObjectLevels arrays to walk the tree.
+#
+# Arguments   : $object - the GtkObject subclass.
+#               @hierarchy - previous hierarchy
+#
+
+def GetHierarchy(gobject, hierarchy):
+    # Find object in the objects array.
+    found = False
+    children = []
+    level = 0
+    j = 0
+    for i in range(len(Objects)):
+        if found:
+            if ObjectLevels[i] <= level:
+                break
+
+            elif ObjectLevels[i] == level + 1:
+                children.append(Objects[i])
+
+        elif Objects[i] == gobject:
+            found = True
+            j = i
+            level = ObjectLevels[i]
+
+    if not found:
+        return hierarchy
+
+    logging.info("=== Hierachy for: %s (%d existing entries) ===", gobject, len(hierarchy))
+
+    # Walk up the hierarchy, pushing ancestors onto the ancestors array.
+    ancestors = [gobject]
+    logging.info("Level: %s", level)
+    while level > 1:
+        j -= 1
+        if ObjectLevels[j] < level:
+            ancestors.append(Objects[j])
+            level = ObjectLevels[j]
+            logging.info("Level: %s", level)
+
+    # Output the ancestors, indented and with links.
+    logging.info('%d ancestors', len(ancestors))
+    last_index = 0
+    level = 1
+    for i in range(len(ancestors) - 1, -1, -1):
+        ancestor = ancestors[i]
+        ancestor_id = common.CreateValidSGMLID(ancestor)
+        indent = ' ' * (level * 4)
+        # Don't add a link to the current object, i.e. when i == 0.
+        if i > 0:
+            entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
+            alt_text = indent + ancestor
+        else:
+            entry_text = indent + ancestor
+            alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
+
+        logging.info("Checking for '%s' or '%s'", entry_text, alt_text)
+        # Check if we already have this object
+        index = -1
+        for j in range(len(hierarchy)):
+            if hierarchy[j] == entry_text or (hierarchy[j] == alt_text):
+                index = j
+                break
+        if index == -1:
+            # We have a new entry, find insert position in alphabetical order
+            found = False
+            for j in range(last_index, len(hierarchy)):
+                if not re.search(r'^' + indent, hierarchy[j]):
+                    last_index = j
+                    found = True
+                    break
+                elif re.search(r'^%s[^ ]' % indent, hierarchy[j]):
+                    stripped_text = hierarchy[j]
+                    if r'<link linkend' not in entry_text:
+                        stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text)
+                        stripped_text = re.sub(r'</link>', '', stripped_text)
+
+                    if entry_text < stripped_text:
+                        last_index = j
+                        found = True
+                        break
+
+            # Append to bottom
+            if not found:
+                last_index = len(hierarchy)
+
+            logging.debug('insert at %d: %s', last_index, entry_text)
+            hierarchy.insert(last_index, entry_text)
+            last_index += 1
+        else:
+            # Already have this one, make sure we use the not linked version
+            if r'<link linkend' not in entry_text:
+                hierarchy[j] = entry_text
+
+            # Remember index as base insert point
+            last_index = index + 1
+
+        level += 1
+
+    # Output the children, indented and with links.
+    logging.info('%d children', len(children))
+    for i in range(len(children)):
+        sid = common.CreateValidSGMLID(children[i])
+        indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i])
+        logging.debug('insert at %d: %s', last_index, indented_text)
+        hierarchy.insert(last_index, indented_text)
+        last_index += 1
+    return hierarchy
+
+
+#
+# Function    : GetInterfaces
+# Description : Returns the DocBook output describing the interfaces
+#               implemented by a class. It uses the global %Interfaces hash.
+# Arguments   : $object - the GtkObject subclass.
+#
+
+def GetInterfaces(gobject):
+    text = ''
+
+    # Find object in the objects array.
+    if gobject in Interfaces:
+        ifaces = Interfaces[gobject].split()
+        text = '''<para>
+%s implements
+''' % gobject
+        count = len(ifaces)
+        for i in range(count):
+            sid = common.CreateValidSGMLID(ifaces[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i])
+            if i < count - 2:
+                text += ', '
+            elif i < count - 1:
+                text += ' and '
+            else:
+                text += '.'
+        text += '</para>\n'
+    return text
+
+
+#
+# Function    : GetImplementations
+# Description : Returns the DocBook output describing the implementations
+#               of an interface. It uses the global %Interfaces hash.
+# Arguments   : $object - the GtkObject subclass.
+#
+
+def GetImplementations(gobject):
+    text = ''
+
+    impls = []
+    for key in Interfaces:
+        if re.search(r'\b%s\b' % gobject, Interfaces[key]):
+            impls.append(key)
+
+    count = len(impls)
+    if count > 0:
+        impls.sort()
+        text = '''<para>
+%s is implemented by
+''' % gobject
+        for i in range(count):
+            sid = common.CreateValidSGMLID(impls[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i])
+            if i < count - 2:
+                text += ', '
+            elif i < count - 1:
+                text += ' and '
+            else:
+                text += '.'
+        text += '</para>\n'
+    return text
+
+
+#
+# Function    : GetPrerequisites
+# Description : Returns the DocBook output describing the prerequisites
+#               of an interface. It uses the global %Prerequisites hash.
+# Arguments   : $iface - the interface.
+#
+
+def GetPrerequisites(iface):
+    text = ''
+
+    if iface in Prerequisites:
+        text = '''<para>
+%s requires
+''' % iface
+        prereqs = Prerequisites[iface].split()
+        count = len(prereqs)
+        for i in range(count):
+            sid = common.CreateValidSGMLID(prereqs[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i])
+            if i < count - 2:
+                text += ', '
+            elif i < count - 1:
+                text += ' and '
+            else:
+                text += '.'
+        text += '</para>\n'
+    return text
+
+
+#
+# Function    : GetDerived
+# Description : Returns the DocBook output describing the derived interfaces
+#               of an interface. It uses the global %Prerequisites hash.
+# Arguments   : $iface - the interface.
+#
+
+def GetDerived(iface):
+    text = ''
+
+    derived = []
+    for key in Prerequisites:
+        if re.search(r'\b%s\b' % iface, Prerequisites[key]):
+            derived.append(key)
+
+    count = len(derived)
+    if count > 0:
+        derived.sort()
+        text = '''<para>
+%s is required by
+''' % iface
+        for i in range(count):
+            sid = common.CreateValidSGMLID(derived[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i])
+            if i < count - 2:
+                text += ', '
+            elif i < count - 1:
+                text += ' and '
+            else:
+                text += '.'
+        text += '</para>\n'
+    return text
+
+
+#
+# Function    : GetSignals
+# Description : Returns the synopsis and detailed description DocBook output
+#                for the signal handlers of a given GtkObject subclass.
+# Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
+#
+
+def GetSignals(gobject):
+    synop = ''
+    desc = ''
+
+    for i in range(len(SignalObjects)):
+        if SignalObjects[i] == gobject:
+            logging.info("Found signal: %s", SignalNames[i])
+            name = SignalNames[i]
+            symbol = '%s::%s' % (gobject, name)
+            sid = common.CreateValidSGMLID('%s-%s' % (gobject, name))
+
+            desc += "<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> 
signal</title>\n" % (
+                sid, name)
+            desc += MakeIndexterms(symbol, sid)
+            desc += "\n"
+            desc += OutputSymbolExtraLinks(symbol)
+
+            desc += "<programlisting language=\"C\">"
+
+            m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i])
+            type_modifier = m.group(1) or ''
+            gtype = m.group(2)
+            pointer = m.group(3)
+            xref = MakeXRef(gtype, tagify(gtype, "returnvalue"))
+
+            ret_type_output = '%s%s%s' % (type_modifier, xref, pointer)
+            callback_name = "user_function"
+            desc += '%s\n%s (' % (ret_type_output, callback_name)
+
+            indentation = ' ' * (len(callback_name) + 2)
+
+            sourceparams = SourceSymbolParams.get(symbol)
+            params = SignalPrototypes[i].splitlines()
+            type_len = len("gpointer")
+            name_len = len("user_data")
+            # do two passes, the first one is to calculate padding
+            for l in range(2):
+                for j in range(len(params)):
+                    param_name = None
+                    # allow alphanumerics, '_', '[' & ']' in param names
+                    m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j])
+                    if m:
+                        gtype = m.group(1)
+                        pointer = m.group(2)
+                        if sourceparams:
+                            param_name = sourceparams.keys()[j]
+                            logging.info('from sourceparams: "%s" (%d: %s)', param_name, j, params[j])
+                        else:
+                            param_name = m.group(3)
+                            logging.info('from params: "%s" (%d: %s)', param_name, j, params[j])
+
+                        if not param_name:
+                            param_name = "arg%d" % j
+
+                        if l == 0:
+                            if len(gtype) + len(pointer) > type_len:
+                                type_len = len(gtype) + len(pointer)
+                            if len(param_name) > name_len:
+                                name_len = len(param_name)
+                        else:
+                            logging.info("signal arg[%d]: '%s'", j, param_name)
+                            xref = MakeXRef(gtype, tagify(gtype, "type"))
+                            pad = ' ' * (type_len - len(gtype) - len(pointer))
+                            desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name)
+                            desc += indentation
+
+                    else:
+                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                          "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i]))
+
+            xref = MakeXRef("gpointer", tagify("gpointer", "type"))
+            pad = ' ' * (type_len - len("gpointer"))
+            desc += '%s%s user_data)' % (xref, pad)
+            desc += "</programlisting>\n"
+
+            flags = SignalFlags[i]
+            flags_string = ''
+            if flags:
+                if 'f' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
+
+                elif 'l' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
+
+                elif 'c' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
+                    flags_string = "Cleanup"
+
+                if 'r' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
+
+                if 'd' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
+
+                if 'a' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
+
+                if 'h' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
+
+            synop += "<row><entry role=\"signal_type\">%s</entry><entry role=\"signal_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"signal_flags\">%s</entry></row>\n" % (
+                ret_type_output, sid, name, flags_string)
+
+            parameters = OutputParamDescriptions("SIGNAL", symbol, None)
+            logging.info("formatted signal params: '%s' -> '%s'", symbol, parameters)
+
+            AllSymbols[symbol] = 1
+            if symbol in SymbolDocs:
+                symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+                desc += symbol_docs
+
+                if not IsEmptyDoc(SymbolDocs[symbol]):
+                    AllDocumentedSymbols[symbol] = 1
+
+            if symbol in SymbolAnnotations:
+                param_desc = SymbolAnnotations[symbol]
+                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+                if param_annotations != '':
+                    desc += "\n<para>%s</para>" % param_annotations
+
+            desc += MakeDeprecationNote(symbol)
+
+            desc += parameters
+            if flags_string:
+                desc += "<para>Flags: %s</para>\n" % flags_string
+
+            desc += OutputSymbolTraits(symbol)
+            desc += "</refsect2>"
+
+    return (synop, desc)
+
+
+#
+# Function    : GetArgs
+# Description : Returns the synopsis and detailed description DocBook output
+#                for the Args of a given GtkObject subclass.
+# Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
+#
+
+def GetArgs(gobject):
+    synop = ''
+    desc = ''
+    child_synop = ''
+    child_desc = ''
+    style_synop = ''
+    style_desc = ''
+
+    for i in range(len(ArgObjects)):
+        if ArgObjects[i] == gobject:
+            logging.info("Found arg: %s", ArgNames[i])
+            name = ArgNames[i]
+            flags = ArgFlags[i]
+            flags_string = ''
+            kind = ''
+            id_sep = ''
+
+            if 'c' in flags:
+                kind = "child property"
+                id_sep = "c-"
+            elif 's' in flags:
+                kind = "style property"
+                id_sep = "s-"
+            else:
+                kind = "property"
+
+            # Remember only one colon so we don't clash with signals.
+            symbol = '%s:%s' % (gobject, name)
+            # use two dashes and ev. an extra separator here for the same reason.
+            sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name))
+
+            atype = ArgTypes[i]
+            type_output = None
+            arange = ArgRanges[i]
+            range_output = CreateValidSGML(arange)
+            default = ArgDefaults[i]
+            default_output = CreateValidSGML(default)
+
+            if atype == "GtkString":
+                atype = "char&#160;*"
+
+            if atype == "GtkSignal":
+                atype = "GtkSignalFunc, gpointer"
+                type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
+            elif re.search(r'^(\w+)\*$', atype):
+                m = re.search(r'^(\w+)\*$', atype)
+                type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + "&#160;*"
+            else:
+                type_output = MakeXRef(atype, tagify(atype, "type"))
+
+            if 'r' in flags:
+                flags_string = "Read"
+
+            if 'w' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Write"
+
+            if 'x' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Construct"
+
+            if 'X' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Construct Only"
+
+            AllSymbols[symbol] = 1
+            blurb = ''
+            if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]):
+                blurb = ConvertMarkDown(symbol, SymbolDocs[symbol])
+                logging.info(".. [%s][%s]", SymbolDocs[symbol], blurb)
+                AllDocumentedSymbols[symbol] = 1
+
+            else:
+                if ArgBlurbs[i] != '':
+                    blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>"
+                    AllDocumentedSymbols[symbol] = 1
+                else:
+                    # FIXME: print a warning?
+                    logging.info(".. no description")
+
+            pad1 = ''
+            if len(name) < 24:
+                pad1 = " " * (24 - len(name))
+
+            arg_synop = "<row><entry role=\"property_type\">%s</entry><entry role=\"property_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"property_flags\">%s</entry></row>\n" % (
+                type_output, sid, name, flags_string)
+            arg_desc = "<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> 
%s</title>\n" % (
+                sid, name, kind)
+            arg_desc += MakeIndexterms(symbol, sid)
+            arg_desc += "\n"
+            arg_desc += OutputSymbolExtraLinks(symbol)
+
+            arg_desc += "<programlisting>  “%s”%s %s</programlisting>\n" % (name, pad1, type_output)
+            arg_desc += blurb
+            if symbol in SymbolAnnotations:
+                param_desc = SymbolAnnotations[symbol]
+                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+                if param_annotations != '':
+                    arg_desc += "\n<para>%s</para>" % param_annotations
+
+            arg_desc += MakeDeprecationNote(symbol)
+
+            if flags_string:
+                arg_desc += "<para>Flags: %s</para>\n" % flags_string
+
+            if arange != '':
+                arg_desc += "<para>Allowed values: %s</para>\n" % range_output
+
+            if default != '':
+                arg_desc += "<para>Default value: %s</para>\n" % default_output
+
+            arg_desc += OutputSymbolTraits(symbol)
+            arg_desc += "</refsect2>\n"
+
+            if 'c' in flags:
+                child_synop += arg_synop
+                child_desc += arg_desc
+
+            elif 's' in flags:
+                style_synop += arg_synop
+                style_desc += arg_desc
+
+            else:
+                synop += arg_synop
+                desc += arg_desc
+
+    return (synop, child_synop, style_synop, desc, child_desc, style_desc)
+
+
+#
+# Function    : ReadSourceDocumentation
+# Description : This reads in the documentation embedded in comment blocks
+#                in the source code (for Gnome).
+#
+#                Parameter descriptions override any in the template files.
+#                Function descriptions are placed before any description from
+#                the template files.
+#
+#                It recursively descends the source directory looking for .c
+#                files and scans them looking for specially-formatted comment
+#                blocks.
+#
+# Arguments   : $source_dir - the directory to scan.
+# m###############################################################
+
+def ReadSourceDocumentation(source_dir):
+
+    # prepend entries from @SOURCE_DIR
+    for sdir in SOURCE_DIRS:
+        # Check if the filename is in the ignore list.
+        m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), source_dir)
+        if m1:
+            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
+            if m2:
+                logging.info("Skipping source directory: %s", source_dir)
+                return
+            else:
+                logging.info("No match for: %s", m1.group(1))
+        else:
+            logging.info("No match for: %s", source_dir)
+
+    logging.info("Scanning source directory: %s", source_dir)
+
+    # This array holds any subdirectories found.
+    subdirs = []
+
+    suffix_list = SOURCE_SUFFIXES.split(',')
+
+    for ifile in os.listdir(source_dir):
+        logging.debug("... : %s", ifile)
+        if ifile.startswith('.'):
+            continue
+        fname = os.path.join(source_dir, ifile)
+        if os.path.isdir(fname):
+            subdirs.append(fname)
+        elif SOURCE_SUFFIXES:
+            for suffix in suffix_list:
+                if ifile.endswith("." + suffix):
+                    ScanSourceFile(fname)
+        elif re.search(r'\.[ch]$', ifile):
+            ScanSourceFile(fname)
+
+    # Now recursively scan the subdirectories.
+    for sdir in subdirs:
+        ReadSourceDocumentation(sdir)
+
+
+#
+# Function    : ScanSourceFile
+# Description : Scans one source file looking for specially-formatted comment
+#               blocks. Later MergeSourceDocumentation() is copying over the
+#               doc blobs that are not suppressed/ignored.
+#
+# Arguments   : $file - the file to scan.
+#
+
+def ScanSourceFile(ifile):
+
+    # prepend entries from @SOURCE_DIR
+    for idir in SOURCE_DIRS:
+        # Check if the filename is in the ignore list.
+        m1 = re.search(r'^%s/(.*)$' % re.escape(idir), ifile)
+        if m1:
+            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
+            if m2:
+                logging.info("Skipping source file: %s", ifile)
+                return
+
+    m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile)
+    if m:
+        basename = m.group(1)
+    else:
+        common.LogWarning(ifile, 1, "Can't find basename for this filename.")
+        basename = ifile
+
+    # Check if the basename is in the list of files to ignore.
+    if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), IGNORE_FILES):
+        logging.info("Skipping source file: %s", ifile)
+        return
+
+    logging.info("Scanning source file: %s", ifile)
+
+    SRCFILE = open(ifile)
+    in_comment_block = False
+    symbol = None
+    in_part = ''
+    description = ''
+    return_desc = ''
+    since_desc = stability_desc = deprecated_desc = ''
+    params = OrderedDict()
+    param_name = None
+    line_number = 0
+    for line in SRCFILE:
+        line_number += 1
+        # Look for the start of a comment block.
+        if not in_comment_block:
+            if re.search(r'^\s*/\*.*\*/', line):
+                # one-line comment - not gtkdoc
+                pass
+            elif re.search(r'^\s*/\*\*\s', line):
+                logging.info("Found comment block start")
+
+                in_comment_block = True
+
+                # Reset all the symbol data.
+                symbol = ''
+                in_part = ''
+                description = ''
+                return_desc = ''
+                since_desc = ''
+                deprecated_desc = ''
+                stability_desc = ''
+                params = OrderedDict()
+                param_name = None
+
+            continue
+
+        # We're in a comment block. Check if we've found the end of it.
+        if re.search(r'^\s*\*+/', line):
+            if not symbol:
+                # maybe its not even meant to be a gtk-doc comment?
+                common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment 
block.")
+            else:
+                # Add the return value description onto the end of the params.
+                if return_desc:
+                    # TODO(ensonic): check for duplicated Return docs
+                    # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol)
+                    params['Returns'] = return_desc
+
+                # Convert special characters
+                description = ConvertSGMLChars(symbol, description)
+                for (param_name, param_desc) in params.iteritems():
+                    params[param_name] = ConvertSGMLChars(symbol, param_desc)
+
+                # Handle Section docs
+                m = re.search(r'SECTION:\s*(.*)', symbol)
+                m2 = re.search(r'PROGRAM:\s*(.*)', symbol)
+                if m:
+                    real_symbol = m.group(1)
+                    long_descr = real_symbol + ":Long_Description"
+
+                    if long_descr not in KnownSymbols or KnownSymbols[long_descr] != 1:
+                        common.LogWarning(
+                            ifile, line_number, "Section %s is not defined in the %s-sections.txt file." % 
(real_symbol, MODULE))
+
+                    logging.info("SECTION DOCS found in source for : '%s'", real_symbol)
+                    for param_name, param_desc in params.iteritems():
+                        logging.info("   '" + param_name + "'")
+                        param_name = param_name.lower()
+                        key = None
+                        if param_name == "short_description":
+                            key = real_symbol + ":Short_Description"
+                        elif param_name == "see_also":
+                            key = real_symbol + ":See_Also"
+                        elif param_name == "title":
+                            key = real_symbol + ":Title"
+                        elif param_name == "stability":
+                            key = real_symbol + ":Stability_Level"
+                        elif param_name == "section_id":
+                            key = real_symbol + ":Section_Id"
+                        elif param_name == "include":
+                            key = real_symbol + ":Include"
+                        elif param_name == "image":
+                            key = real_symbol + ":Image"
+
+                        if key:
+                            SourceSymbolDocs[key] = param_desc
+                            SourceSymbolSourceFile[key] = ifile
+                            SourceSymbolSourceLine[key] = line_number
+
+                    SourceSymbolDocs[long_descr] = description
+                    SourceSymbolSourceFile[long_descr] = ifile
+                    SourceSymbolSourceLine[long_descr] = line_number
+                elif m2:
+                    real_symbol = m2.group(1)
+                    key = None
+                    section_id = None
+
+                    logging.info("PROGRAM DOCS found in source for '%s'", real_symbol)
+                    for param_name, param_desc in params.iteritems():
+                        logging.info("PROGRAM key %s: '%s'", real_symbol, param_name)
+                        param_name = param_name.lower()
+                        key = None
+                        if param_name == "short_description":
+                            key = real_symbol + ":Short_Description"
+                        elif param_name == "see_also":
+                            key = real_symbol + ":See_Also"
+                        elif param_name == "section_id":
+                            key = real_symbol + ":Section_Id"
+                        elif param_name == "synopsis":
+                            key = real_symbol + ":Synopsis"
+                        elif param_name == "returns":
+                            key = real_symbol + ":Returns"
+                        elif re.search(r'^(-.*)', param_name):
+                            logging.info("PROGRAM opts: '%s': '%s'", param_name, param_desc)
+                            key = real_symbol + ":Options"
+                            opts = []
+                            opts_str = SourceSymbolDocs.get(key)
+                            if opts_str:
+                                opts = opts_str.split('\t')
+                            opts.append(param_name)
+                            opts.append(param_desc)
+
+                            logging.info("Setting options for symbol: %s: '%s'", real_symbol, 
'\t'.join(opts))
+                            SourceSymbolDocs[key] = '\t'.join(opts)
+                            continue
+
+                        if key:
+                            logging.info("PROGRAM value %s: '%s'", real_symbol, param_desc.rstrip())
+                            SourceSymbolDocs[key] = param_desc.rstrip()
+                            SourceSymbolSourceFile[key] = ifile
+                            SourceSymbolSourceLine[key] = line_number
+
+                    long_descr = real_symbol + ":Long_Description"
+                    SourceSymbolDocs[long_descr] = description
+                    SourceSymbolSourceFile[long_descr] = ifile
+                    SourceSymbolSourceLine[long_descr] = line_number
+
+                    section_id = SourceSymbolDocs.get(real_symbol + ":Section_Id")
+                    if section_id and section_id.strip() != '':
+                        # Remove trailing blanks and use as is
+                        section_id = section_id.rstrip()
+                    else:
+                        section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol))
+                    OutputProgramDBFile(real_symbol, section_id)
+
+                else:
+                    logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
+                    SourceSymbolDocs[symbol] = description
+                    SourceSymbolParams[symbol] = params
+                    SourceSymbolSourceFile[symbol] = ifile
+                    SourceSymbolSourceLine[symbol] = line_number
+
+                if since_desc:
+                    arr = since_desc.splitlines()
+                    since_desc = arr[0].strip()
+                    extra_lines = arr[1:]
+                    logging.info("Since(%s) : [%s]", symbol, since_desc)
+                    Since[symbol] = ConvertSGMLChars(symbol, since_desc)
+                    if len(extra_lines) > 1:
+                        common.LogWarning(ifile, line_number, "multi-line since docs found")
+
+                if stability_desc:
+                    stability_desc = ParseStabilityLevel(
+                        stability_desc, ifile, line_number, "Stability level for %s" % symbol)
+                    StabilityLevel[symbol] = ConvertSGMLChars(symbol, stability_desc)
+
+                if deprecated_desc:
+                    if symbol not in Deprecated:
+                        # don't warn for signals and properties
+                        # if ($symbol !~ m/::?(.*)/)
+                        if symbol in DeclarationTypes:
+                            common.LogWarning(ifile, line_number,
+                                              "%s is deprecated in the inline comments, but no deprecation 
guards were found around the declaration. (See the --deprecated-guards option for gtkdoc-scan.)" % symbol)
+
+                    Deprecated[symbol] = ConvertSGMLChars(symbol, deprecated_desc)
+
+            in_comment_block = False
+            continue
+
+        # Get rid of ' * ' at start of every line in the comment block.
+        line = re.sub(r'^\s*\*\s?', '', line)
+        # But make sure we don't get rid of the newline at the end.
+        if not line.endswith('\n'):
+            line = line + "\n"
+
+        logging.info("scanning :%s", line)
+
+        # If we haven't found the symbol name yet, look for it.
+        if not symbol:
+            m1 = re.search(r'^\s*(SECTION:\s*\S+)', line)
+            m2 = re.search(r'^\s*(PROGRAM:\s*\S+)', line)
+            m3 = re.search(r'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line)
+            if m1:
+                symbol = m1.group(1)
+                logging.info("SECTION DOCS found in source for : '%s'", symbol)
+            elif m2:
+                symbol = m2.group(1)
+                logging.info("PROGRAM DOCS found in source for : '%s'", symbol)
+            elif m3:
+                symbol = m3.group(1)
+                annotation = m3.group(2)
+                logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
+                if annotation:
+                    annotation = annotation.strip()
+                    if annotation != '':
+                        SymbolAnnotations[symbol] = annotation
+                        logging.info("remaining text for %s: '%s'", symbol, annotation)
+
+            continue
+
+        if in_part == "description":
+            # Get rid of 'Description:'
+            line = re.sub(r'^\s*Description:', '', line)
+
+        m1 = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I)
+        m2 = re.search(r'^\s*since:', line, flags=re.I)
+        m3 = re.search(r'^\s*deprecated:', line, flags=re.I)
+        m4 = re.search(r'^\s*stability:', line, flags=re.I)
+
+        if m1:
+            # we're in param section and have not seen the blank line
+            if in_part != '':
+                return_desc = line[m1.end():]
+                in_part = "return"
+                continue
+
+        if m2:
+            # we're in param section and have not seen the blank line
+            if in_part != "param":
+                since_desc = line[m2.end():]
+                in_part = "since"
+                continue
+
+        elif m3:
+            # we're in param section and have not seen the blank line
+            if in_part != "param":
+                deprecated_desc = line[m3.end():]
+                in_part = "deprecated"
+                continue
+
+        elif m4:
+            stability_desc = line[m4.end():]
+            in_part = "stability"
+            continue
+
+        if in_part == "description":
+            description += line
+            continue
+        elif in_part == "return":
+            return_desc += line
+            continue
+        elif in_part == "since":
+            since_desc += line
+            continue
+        elif in_part == "stability":
+            stability_desc += line
+            continue
+        elif in_part == "deprecated":
+            deprecated_desc += line
+            continue
+
+        # We must be in the parameters. Check for the empty line below them.
+        if re.search(r'^\s*$', line):
+            in_part = "description"
+            continue
+
+        # Look for a parameter name.
+        m = re.search(r'^\s*@(.+?)\s*:\s*', line)
+        if m:
+            param_name = m.group(1)
+            param_desc = line[m.end():]
+
+            logging.info("Found parameter: %s", param_name)
+            # Allow varargs variations
+            if re.search(r'^\.\.\.$', param_name):
+                param_name = "..."
+
+            logging.info("Found param for symbol %s : '%s'= '%s'", symbol, param_name, line)
+
+            params[param_name] = param_desc
+            in_part = "param"
+            continue
+        elif in_part == '':
+            logging.info("continuation for %s annotation '%s'", symbol, line)
+            annotation = re.sub(r'^\s+|\s+$', '', line)
+            if symbol in SymbolAnnotations:
+                SymbolAnnotations[symbol] += annotation
+            else:
+                SymbolAnnotations[symbol] = annotation
+            continue
+
+        # We must be in the middle of a parameter description, so add it on
+        # to the last element in @params.
+        if not param_name:
+            common.LogWarning(file, line_number, "Parsing comment block file : parameter expected, but got 
'%s'" % line)
+        else:
+            params[param_name] += line
+
+    SRCFILE.close()
+
+
+#
+# Function    : OutputMissingDocumentation
+# Description : Outputs report of documentation coverage to a file
+#
+# Arguments   : none
+#
+
+def OutputMissingDocumentation():
+    old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.txt")
+    new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new")
+
+    n_documented = 0
+    n_incomplete = 0
+    total = 0
+    symbol = None
+    percent = None
+    buffer = ''
+    buffer_deprecated = ''
+    buffer_descriptions = ''
+
+    UNDOCUMENTED = open(new_undocumented_file, 'w')
+
+    for symbol in sorted(AllSymbols.keys()):
+        # FIXME: should we print common.LogWarnings for undocumented stuff?
+        # DEBUG
+        # location = "defined at " + GetSymbolSourceFile(symbol) + ":" + GetSymbolSourceLine(symbol) + "\n"
+        # DEBUG
+        m = re.search(
+            
r':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol)
+        m2 = re.search(r':(Long_Description|Short_Description)', symbol)
+        if not m:
+            total += 1
+            if symbol in AllDocumentedSymbols:
+                n_documented += 1
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t0: ".$location
+
+            elif symbol in Deprecated:
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t1a: ".$location
+                else:
+                    buffer_deprecated += symbol + "\n"
+                    #$buffer += "\t1b: ".$location
+
+            else:
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t2a: ".$location
+                else:
+                    buffer += symbol + "\n"
+                    #$buffer += "\t2b: ".$location
+
+        elif m2:
+            total += 1
+            if (symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0)\
+               or (symbol in AllDocumentedSymbols and AllDocumentedSymbols[symbol] > 0):
+                n_documented += 1
+            else:
+                buffer_descriptions += symbol + "\n"
+
+    if total == 0:
+        percent = 100
+    else:
+        percent = (n_documented / total) * 100.0
+
+    UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n" % percent)
+    UNDOCUMENTED.write("%s symbols documented.\n" % n_documented)
+    UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete)
+    UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented))
+
+    if buffer_deprecated != '':
+        buffer += "\n" + buffer_deprecated
+
+    if buffer_descriptions != '':
+        buffer += "\n" + buffer_descriptions
+
+    if buffer != '':
+        UNDOCUMENTED.write("\n\n" + buffer)
+
+    UNDOCUMENTED.close()
+
+    return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0)
+
+
+#
+# Function    : OutputUndeclaredSymbols
+# Description : Outputs symbols that are listed in the section file, but not
+#               declaration is found in the sources
+#
+# Arguments   : none
+#
+
+def OutputUndeclaredSymbols():
+    old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt")
+    new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new")
+
+    UNDECLARED = open(new_undeclared_file, 'w')
+
+    if UndeclaredSymbols:
+        UNDECLARED.write("\n".join(sorted(UndeclaredSymbols.keys())))
+        UNDECLARED.write("\n")
+        print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE)
+
+    UNDECLARED.close()
+
+    return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0)
+
+
+#
+# Function    : OutputUnusedSymbols
+# Description : Outputs symbols that are documented in comments, but not
+#               declared in the sources
+#
+# Arguments   : none
+#
+
+def OutputUnusedSymbols():
+    num_unused = 0
+    old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt")
+    new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new")
+
+    UNUSED = open(new_unused_file, 'w')
+
+    for symbol in sorted(Declarations.keys()):
+        if not symbol in DeclarationOutput:
+            UNUSED.write("%s\n" % symbol)
+            num_unused += 1
+
+    for symbol in sorted(AllUnusedSymbols.keys()):
+        UNUSED.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n")
+        num_unused += 1
+
+    UNUSED.close()
+    if num_unused != 0:
+        common.LogWarning(
+            old_unused_file, 1, "%d unused declarations. They should be added to %s-sections.txt in the 
appropriate place." % (num_unused, MODULE))
+
+    return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0)
+
+
+#
+# Function    : OutputAllSymbols
+# Description : Outputs list of all symbols to a file
+#
+# Arguments   : none
+#
+
+def OutputAllSymbols():
+    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-symbols.txt"), 'w')
+
+    for symbol in sorted(AllSymbols.keys()):
+        SYMBOLS.write(symbol + "\n")
+    SYMBOLS.close()
+
+
+#
+# Function    : OutputSymbolsWithoutSince
+# Description : Outputs list of all symbols without a since tag to a file
+#
+# Arguments   : none
+#
+
+def OutputSymbolsWithoutSince():
+    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-nosince.txt"), 'w')
+
+    for symbol in sorted(SourceSymbolDocs.keys()):
+        if symbol in Since:
+            SYMBOLS.write(symbol + "\n")
+    SYMBOLS.close()
+
+
+def CheckParamsDocumented(symbol, params):
+    stype = DeclarationTypes.get(symbol)
+
+    item = "Parameter"
+    if stype:
+        if stype == 'STRUCT':
+            item = "Field"
+        elif stype == 'ENUM':
+            item = "Value"
+        elif stype == 'UNION':
+            item = "Field"
+    else:
+        stype = "SIGNAL"
+    logging.info("Check param docs for %s, params: %s entries, type=%s", symbol, len(params), stype)
+
+    if len(params) > 0:
+        logging.info("params: %s", str(params))
+        for (param_name, param_desc) in params.iteritems():
+            # Output a warning if the parameter is empty and remember for stats.
+            if param_name != "void" and not re.search(r'\S', param_desc):
+                if symbol in AllIncompleteSymbols:
+                    AllIncompleteSymbols[symbol] += ", " + param_name
+                else:
+                    AllIncompleteSymbols[symbol] = param_name
+
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "%s description for %s::%s is missing in source code comment block." % 
(item, symbol, param_name))
+
+    elif len(params) == 0:
+        AllIncompleteSymbols[symbol] = "<items>"
+        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                          "%s descriptions for %s are missing in source code comment block." % (item, 
symbol))
+
+
+#
+# Function    : MergeSourceDocumentation
+# Description : This merges documentation read from a source file into the
+#                documentation read in from a template file.
+#
+#                Parameter descriptions override any in the template files.
+#                Function descriptions are placed before any description from
+#                the template files.
+#
+# Arguments   : none
+#
+
+def MergeSourceDocumentation():
+
+    # add whats found in the source
+    symbols = set(SourceSymbolDocs.keys())
+
+    # and add known symbols from -sections.txt
+    for symbol in KnownSymbols.keys():
+        if KnownSymbols[symbol] == 1:
+            symbols.add(symbol)
+
+    logging.info("num source entries: %d", len(symbols))
+
+    for symbol in symbols:
+        AllSymbols[symbol] = 1
+
+        if symbol in SourceSymbolDocs:
+            logging.info("merging [%s] from source", symbol)
+
+            # remove leading and training whitespaces
+            src_docs = SourceSymbolDocs[symbol].strip()
+            if src_docs != '':
+                AllDocumentedSymbols[symbol] = 1
+
+            SymbolDocs[symbol] = src_docs
+
+            # merge parameters
+            if symbol in SourceSymbolParams:
+                param_docs = SourceSymbolParams[symbol]
+                SymbolParams[symbol] = param_docs
+                # if this symbol is documented, check if docs are complete
+                # remove all xml-tags and whitespaces
+                check_docs = re.sub(r'\s', '', re.sub(r'<.*?>', '', src_docs))
+                if check_docs != '' and param_docs:
+                    CheckParamsDocumented(symbol, param_docs)
+        else:
+            logging.info("[%s] undocumented", symbol)
+
+    logging.info("num doc entries: %d", len(SymbolDocs))
+
+
+#
+# Function    : IsEmptyDoc
+# Description : Check if a doc-string is empty. Its also regarded as empty if
+#               it only consist of whitespace or e.g. FIXME.
+# Arguments   : the doc-string
+#
+def IsEmptyDoc(doc):
+    if re.search(r'^\s*$', doc):
+        return True
+    if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc):
+        return True
+    return False
+
+
+#
+# Function    : ConvertMarkDown
+# Description : Converts mark down syntax to the respective docbook.
+#               http://de.wikipedia.org/wiki/Markdown
+#               Inspired by the design of ParseDown
+#               http://parsedown.org/
+#               Copyright (c) 2013 Emanuil Rusev, erusev.com
+# Arguments   : the symbol name, the doc-string
+#
+
+def ConvertMarkDown(symbol, text):
+    text = MarkDownParse(text, symbol)
+    return text
+
+
+# SUPPORTED MARKDOWN
+# ==================
+#
+# Atx-style Headers
+# -----------------
+#
+# # Header 1
+#
+# ## Header 2 ##
+#
+# Setext-style Headers
+# --------------------
+#
+# Header 1
+# ========
+#
+# Header 2
+# --------
+#
+# Ordered (unnested) Lists
+# ------------------------
+#
+# 1. item 1
+#
+# 1. item 2 with loooong
+#    description
+#
+# 3. item 3
+#
+# Note: we require a blank line above the list items
+#
+
+# TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
+
+def MarkDownParseBlocks(lines, symbol, context):
+    md_blocks = []
+    md_block = {"type": ''}
+
+    logging.debug("parsing %s lines", len(lines))
+    for line in lines:
+        logging.info("type='%s', int='%s', parsing '%s'", md_block["type"], md_block.get('interrupted'), 
line)
+        first_char = None
+        if line:
+            first_char = line[0]
+
+        if md_block["type"] == "markup":
+            if 'closed' not in md_block:
+                if md_block["start"] in line:
+                    md_block["depth"] += 1
+
+                if md_block["end"] in line:
+                    if md_block["depth"] > 0:
+                        md_block["depth"] -= 1
+                    else:
+                        logging.info("closing tag '%s'", line)
+                        md_block["closed"] = 1
+                        # TODO(ensonic): reparse inner text with MarkDownParseLines?
+
+                md_block["text"] += "\n" + line
+                logging.info("add to markup: '%s'", line)
+                continue
+
+        deindented_line = line.lstrip()
+
+        if md_block["type"] == "heading":
+            # a heading is ended by any level less than or equal
+            if md_block["level"] == 1:
+                heading_match = re.search(r'^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
+                if re.search(r'^={4,}[ \t]*$', line):
+                    text = md_block["lines"].pop()
+                    md_block.pop("interrupted", None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "heading",
+                                'text': text,
+                                'lines': [],
+                                'level': 1,
+                                }
+                    continue
+                elif heading_match:
+                    md_block.pop("interrupted", None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "heading",
+                                'text': heading_match.group(1),
+                                'lines': [],
+                                'level': 1,
+                                }
+                    if heading_match.group(2):
+                        md_block['id'] = heading_match.group(2)
+                    continue
+                else:
+                    # push lines into the block until the end is reached
+                    md_block["lines"].append(line)
+                    continue
+
+            else:
+                heading_match = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', 
line)
+                if re.search(r'^[=]{4,}[ \t]*$', line):
+                    text = md_block["lines"].pop()
+                    md_block.pop("interrupted", None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "heading",
+                                'text': text,
+                                'lines': [],
+                                'level': 1,
+                                }
+                    continue
+                elif re.search(r'^[-]{4,}[ \t]*$', line):
+                    text = md_block["lines"].pop()
+                    md_block.pop("interrupted", None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "heading",
+                                'text': text,
+                                'lines': [],
+                                'level': 2,
+                                }
+                    continue
+                elif heading_match:
+                    md_block.pop("interrupted", None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "heading",
+                                'text': heading_match.group(2),
+                                'lines': [],
+                                'level': len(heading_match.group(1))
+                                }
+                    if heading_match.group(3):
+                        md_block['id'] = heading_match.group(3)
+                    continue
+                else:
+                    # push lines into the block until the end is reached
+                    md_block["lines"].append(line)
+                    continue
+        elif md_block["type"] == "code":
+            end_of_code_match = re.search(r'^[ \t]*\]\|(.*)', line)
+            if end_of_code_match:
+                md_blocks.append(md_block)
+                md_block = {'type': "paragraph",
+                            'text': end_of_code_match.group(1),
+                            'lines': [],
+                            }
+            else:
+                md_block["lines"].append(line)
+            continue
+
+        if deindented_line == '':
+            logging.info('setting "interrupted" due to empty line')
+            md_block["interrupted"] = 1
+            continue
+
+        if md_block["type"] == "quote":
+            if 'interrupted' not in md_block:
+                line = re.sub(r'^[ ]*>[ ]?', '', line)
+                md_block["lines"].append(line)
+                continue
+
+        elif md_block["type"] == "li":
+            marker = md_block["marker"]
+            marker_match = re.search(r'^([ ]{0,3})(%s)[ ](.*)' % marker, line)
+            if marker_match:
+                indentation = marker_match.group(1)
+                if md_block["indentation"] != indentation:
+                    md_block["lines"].append(line)
+                else:
+                    ordered = md_block["ordered"]
+                    md_block.pop('last', None)
+                    md_blocks.append(md_block)
+                    md_block = {'type': "li",
+                                'ordered': ordered,
+                                'indentation': indentation,
+                                'marker': marker,
+                                'last': 1,
+                                'lines': [re.sub(r'^[ ]{0,4}', '', marker_match.group(3))],
+                                }
+                continue
+
+            if 'interrupted' in md_block:
+                if first_char == " ":
+                    md_block["lines"].append('')
+                    line = re.sub(r'^[ ]{0,4}', '', line)
+                    md_block["lines"].append(line)
+                    md_block.pop("interrupted", None)
+                    continue
+            else:
+                line = re.sub(r'^[ ]{0,4}', '', line)
+                md_block["lines"].append(line)
+                continue
+
+        # indentation sensitive types
+        heading_match = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
+        code_match = re.search(r'^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?', line)
+        if heading_match:
+            # atx heading (#)
+            md_blocks.append(md_block)
+            md_block = {'type': "heading",
+                        'text': heading_match.group(2),
+                        'lines': [],
+                        'level': len(heading_match.group(1)),
+                        }
+            if heading_match.group(3):
+                md_block['id'] = heading_match.group(3)
+            continue
+        elif re.search(r'^={4,}[ \t]*$', line):
+            # setext heading (====)
+
+            if md_block["type"] == "paragraph" and "interrupted" in md_block:
+                md_blocks.append(md_block.copy())
+                md_block["type"] = "heading"
+                md_block["lines"] = []
+                md_block["level"] = 1
+            continue
+        elif re.search(r'^-{4,}[ \t]*$', line):
+            # setext heading (-----)
+
+            if md_block["type"] == "paragraph" and "interrupted" in md_block:
+                md_blocks.append(md_block.copy())
+                md_block["type"] = "heading"
+                md_block["lines"] = []
+                md_block["level"] = 2
+
+            continue
+        elif code_match:
+            # code
+            md_block["interrupted"] = 1
+            md_blocks.append(md_block)
+            md_block = {'type': "code",
+                        'lines': [],
+                        }
+            if code_match.group(1):
+                md_block['language'] = code_match.group(1)
+            continue
+
+        # indentation insensitive types
+        markup_match = re.search(r'^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>', line)
+        li_match = re.search(r'^([ ]*)[*+-][ ](.*)', line)
+        quote_match = re.search(r'^[ ]*>[ ]?(.*)', line)
+        if re.search(r'^[ ]*<!DOCTYPE/', line):
+            md_blocks.append(md_block)
+            md_block = {'type': "markup",
+                        'text': deindented_line,
+                        'start': '<',
+                        'end': '>',
+                        'depth': 0,
+                        }
+
+        elif markup_match:
+            # markup, including <?xml version="1.0"?>
+            tag = markup_match.group(1)
+            is_self_closing = markup_match.group(2) is not None
+
+            # skip link markdown
+            # TODO(ensonic): consider adding more uri schemes (ftp, ...)
+            if re.search(r'https?', tag):
+                logging.info("skipping link '%s'", tag)
+            else:
+                # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
+                # instead of creation a markdown block.
+                scanning_for_end_of_text_level_tag = (
+                    md_block["type"] == "paragraph" and
+                    'start' in md_block and
+                    'closed' not in md_block)
+                logging.info("markup found '%s', scanning %s ?", tag, scanning_for_end_of_text_level_tag)
+                if tag not in MD_TEXT_LEVEL_ELEMENTS and not scanning_for_end_of_text_level_tag:
+                    md_blocks.append(md_block)
+
+                    if is_self_closing:
+                        logging.info("self-closing docbook '%s'", tag)
+                        md_block = {'type': "self-closing tag",
+                                    'text': deindented_line,
+                                    }
+                        is_self_closing = 0
+                        continue
+
+                    logging.info("new markup '%s'", tag)
+                    md_block = {'type': "markup",
+                                'text': deindented_line,
+                                'start': '<' + tag + '>',
+                                'end': '</' + tag + '>',
+                                'depth': 0,
+                                }
+                    if re.search(r'<\/%s>' % tag, deindented_line):
+                        md_block["closed"] = 1
+
+                    continue
+                else:
+                    if tag in MD_TEXT_LEVEL_ELEMENTS:
+                        logging.info("text level docbook '%s' in '%s' state", tag, md_block["type"])
+                        # TODO(ensonic): handle nesting
+                        if not scanning_for_end_of_text_level_tag:
+                            if not re.search(r'<\/%s>' % tag, deindented_line):
+                                logging.info("new text level markup '%s'", tag)
+                                md_block["start"] = '<' + tag + '>'
+                                md_block["end"] = '</' + tag + '>'
+                                md_block.pop("closed", None)
+                                logging.info("scanning for end of '%s'", tag)
+
+                        else:
+                            if md_block["end"] in deindented_line:
+                                md_block["closed"] = 1
+                                logging.info("found end of '%s'", tag)
+        elif li_match:
+            # li
+            md_blocks.append(md_block)
+            indentation = li_match.group(1)
+            md_block = {'type': "li",
+                        'ordered': 0,
+                        'indentation': indentation,
+                        'marker': "[*+-]",
+                        'first': 1,
+                        'last': 1,
+                        'lines': [re.sub(r'^[ ]{0,4}', '', li_match.group(2))],
+                        }
+            continue
+        elif quote_match:
+            md_blocks.append(md_block)
+            md_block = {'type': "quote",
+                        'lines': [quote_match.group(1)],
+                        }
+            continue
+
+        # list item
+        list_item_match = re.search(r'^([ ]{0,4})\d+[.][ ]+(.*)', line)
+        if list_item_match:
+            md_blocks.append(md_block)
+            indentation = list_item_match.group(1)
+            md_block = {'type': "li",
+                        'ordered': 1,
+                        'indentation': indentation,
+                        'marker': "\\d+[.]",
+                        'first': 1,
+                        'last': 1,
+                        'lines': [re.sub(r'^[ ]{0,4}', '', list_item_match.group(2))],
+                        }
+            continue
+
+        # paragraph
+        if md_block["type"] == "paragraph":
+            if "interrupted" in md_block:
+                md_blocks.append(md_block)
+                md_block = {'type': "paragraph",
+                            'text': line,
+                            }
+                logging.info("new paragraph due to interrupted")
+            else:
+                md_block["text"] += "\n" + line
+                logging.info("add to paragraph: '%s'", line)
+
+        else:
+            md_blocks.append(md_block)
+            md_block = {'type': "paragraph",
+                        'text': line,
+                        }
+            logging.info("new paragraph due to different block type")
+
+    md_blocks.append(md_block)
+    md_blocks.pop(0)
+
+    return md_blocks
+
+
+def MarkDownParseSpanElementsInner(text, markersref):
+    markup = ''
+    markers = {i: 1 for i in markersref}
+
+    while text != '':
+        closest_marker = ''
+        closest_marker_position = -1
+        text_marker = ''
+        offset = 0
+        markers_rest = []
+
+        for marker, use in markers.items():
+            if not use:
+                continue
+
+            marker_position = text.find(marker)
+
+            if marker_position < 0:
+                markers[marker] = 0
+                continue
+
+            if closest_marker == '' or marker_position < closest_marker_position:
+                closest_marker = marker
+                closest_marker_position = marker_position
+
+        if closest_marker_position >= 0:
+            text_marker = text[closest_marker_position:]
+
+        if text_marker == '':
+            markup += text
+            text = ''
+            continue
+
+        markup += text[:closest_marker_position]
+        text = text[closest_marker_position:]
+        markers_rest = {k: v for k, v in markers.items() if v and k != closest_marker}
+
+        if closest_marker == '![' or closest_marker == '[':
+            element = None
+
+            # FIXME: '(?R)' is a recursive subpattern
+            # match a [...] block with no ][ inside or this thing again
+            # m = re.search(r'\[((?:[^][]|(?R))*)\]', text)
+            m = re.search(r'\[((?:[^][])*)\]', text)
+            if ']' in text and m:
+                element = {'!': text[0] == '!',
+                           'a': m.group(1),
+                           }
+
+                offset = len(m.group(0))
+                if element['!']:
+                    offset += 1
+                logging.debug("Recursive md-expr match: off=%d, text='%s', match='%s'", offset, text, 
m.group(1))
+
+                remaining_text = text[offset:]
+                m2 = re.search(r'''^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)''', remaining_text)
+                m3 = re.search(r'^\s*\[([^\]<]*?)\]', remaining_text)
+                if m2:
+                    element['»'] = m2.group(1)
+                    if m2.group(2):
+                        element['#'] = m2.group(2)
+                    offset += len(m2.group(0))
+                elif m3:
+                    element['ref'] = m3.group(1)
+                    offset += len(m3.group(0))
+                else:
+                    element = None
+
+            if element:
+                if '»' in element:
+                    element['»'] = element['»'].replace('&', '&amp;').replace('<', '&lt;')
+
+                if element['!']:
+                    markup += '<inlinemediaobject><imageobject><imagedata fileref="' + \
+                        element['»'] + '"></imagedata></imageobject>'
+
+                    if 'a' in element:
+                        markup += "<textobject><phrase>" + element['a'] + "</phrase></textobject>"
+
+                        markup += "</inlinemediaobject>"
+                elif 'ref' in element:
+                    element['a'] = MarkDownParseSpanElementsInner(element['a'], markers_rest)
+                    markup += '<link linkend="' + element['ref'] + '"'
+
+                    if '#' in element:
+                        # title attribute not supported
+                        pass
+
+                    markup += '>' + element['a'] + "</link>"
+                else:
+                    element['a'] = MarkDownParseSpanElementsInner(element['a'], markers_rest)
+                    markup += '<ulink url="' + element['»'] + '"'
+
+                    if '#' in element:
+                        # title attribute not supported
+                        pass
+
+                    markup += '>' + element['a'] + "</ulink>"
+
+            else:
+                markup += closest_marker
+                if closest_marker == '![':
+                    offset = 2
+                else:
+                    offset = 1
+
+        elif closest_marker == '<':
+            m4 = re.search(r'^<(https?:[\/]{2}[^\s]+?)>', text, flags=re.I)
+            m5 = re.search(r'^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>', text)
+            m6 = re.search(r'^<[^>]+?>', text)
+            if m4:
+                element_url = m4.group(1).replace('&', '&amp;').replace('<', '&lt;')
+
+                markup += '<ulink url="' + element_url + '">' + element_url + '</ulink>'
+                offset = len(m4.group(0))
+            elif m5:
+                markup += "<ulink url=\"mailto:"; + m5.group(1) + "\">" + m5.group(1) + "</ulink>"
+                offset = len(m5.group(0))
+            elif m6:
+                markup += m6.group(0)
+                offset = len(m6.group(0))
+            else:
+                markup += "&lt;"
+                offset = 1
+
+        elif closest_marker == "\\":
+            special_char = ''
+            if len(text) > 1:
+                special_char = text[1]
+            if special_char in MD_ESCAPABLE_CHARS or special_char in MD_GTK_ESCAPABLE_CHARS:
+                markup += special_char
+                offset = 2
+            else:
+                markup += "\\"
+                offset = 1
+
+        elif closest_marker == "`":
+            m7 = re.search(r'^(`+)([^`]+?)\1(?!`)', text)
+            if m7:
+                element_text = m7.group(2)
+                markup += "<literal>" + element_text + "</literal>"
+                offset = len(m7.group(0))
+            else:
+                markup += "`"
+                offset = 1
+
+        elif closest_marker == "@":
+            # Convert '@param()'
+            # FIXME: we could make those also links ($symbol.$2), but that would be less
+            # useful as the link target is a few lines up or down
+            m7 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', text)
+            m8 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)', text)
+            m9 = re.search(r'^\\\@', text)
+            if m7:
+                markup += m7.group(1) + "<parameter>" + m7.group(2) + "()</parameter>\n"
+                offset = len(m7.group(0))
+            elif m8:
+                # Convert '@param', but not '\@param'.
+                markup += m8.group(1) + "<parameter>" + m8.group(2) + "</parameter>\n"
+                offset = len(m8.group(0))
+            elif m9:
+                markup += r"\@"
+                offset = len(m9.group(0))
+            else:
+                markup += "@"
+                offset = 1
+
+        elif closest_marker == '#':
+            m10 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', text)
+            m11 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)', text)
+            m12 = re.search(r'^\\#', text)
+            if m10:
+                # handle #Object.func()
+                markup += m10.group(1) + MakeXRef(m10.group(2), tagify(m10.group(2) + "()", "function"))
+                offset = len(m10.group(0))
+            elif m11:
+                # Convert '#symbol', but not '\#symbol'.
+                markup += m11.group(1) + MakeHashXRef(m11.group(2), "type")
+                offset = len(m11.group(0))
+            elif m12:
+                markup += '#'
+                offset = len(m12.group(0))
+            else:
+                markup += '#'
+                offset = 1
+
+        elif closest_marker == "%":
+            m12 = re.search(r'^(\A|[^\\])\%(-?\w+)', text)
+            m13 = re.search(r'^\\%', text)
+            if m12:
+                # Convert '%constant', but not '\%constant'.
+                # Also allow negative numbers, e.g. %-1.
+                markup += m12.group(1) + MakeXRef(m12.group(2), tagify(m12.group(2), "literal"))
+                offset = len(m12.group(0))
+            elif m13:
+                markup += r"\%"
+                offset = len(m13.group(0))
+            else:
+                markup += "%"
+                offset = 1
+
+        if offset > 0:
+            text = text[offset:]
+
+    return markup
+
+
+def MarkDownParseSpanElements(text):
+    markers = ["\\", '<', '![', '[', "`", '%', '#', '@']
+
+    text = MarkDownParseSpanElementsInner(text, markers)
+
+    # Convert 'function()' or 'macro()'.
+    # if there is abc_*_def() we don't want to make a link to _def()
+    # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
+    def f(m):
+        return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function"))
+    text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f, text)
+    return text
+
+
+def ReplaceEntities(text, symbol):
+    entities = [["&lt;", '<'],
+                ["&gt;", '>'],
+                ["&ast;", '*'],
+                ["&num;", '#'],
+                ["&percnt;", '%'],
+                ["&colon;", ':'],
+                ["&quot;", '"'],
+                ["&apos;", "'"],
+                ["&nbsp;", ' '],
+                ["&amp;", '&'],  # Do this last, or the others get messed up.
+                ]
+
+    # Expand entities in <programlisting> even inside CDATA since
+    # we changed the definition of |[ to add CDATA
+    for i in entities:
+        text = re.sub(i[0], i[1], text)
+    return text
+
+
+def MarkDownOutputDocBook(blocksref, symbol, context):
+    output = ''
+    blocks = blocksref
+
+    for block in blocks:
+    #$output += "\n<!-- beg type='" . $block->{"type"} . "'-->\n"
+
+        if block["type"] == "paragraph":
+            text = MarkDownParseSpanElements(block["text"])
+            if context == "li" and output == '':
+                if 'interrupted' in block:
+                    output += "\n<para>%s</para>\n" % text
+                else:
+                    output += "<para>%s</para>" % text
+                    if len(blocks) > 1:
+                        output += "\n"
+            else:
+                output += "<para>%s</para>\n" % text
+
+        elif block["type"] == "heading":
+
+            title = MarkDownParseSpanElements(block["text"])
+
+            if block["level"] == 1:
+                tag = "refsect2"
+            else:
+                tag = "refsect3"
+
+            text = MarkDownParseLines(block["lines"], symbol, "heading")
+            if 'id' in block:
+                output += "<%s id=\"%s\">" % (tag, block["id"])
+            else:
+                output += "<%s>" % tag
+
+            output += "<title>%s</title>%s</%s>\n" % (title, text, tag)
+        elif block["type"] == "li":
+            tag = "itemizedlist"
+
+            if "first" in block:
+                if block["ordered"]:
+                    tag = "orderedlist"
+                output += "<%s>\n" % tag
+
+            if "interrupted" in block:
+                block["lines"].append('')
+
+            text = MarkDownParseLines(block["lines"], symbol, "li")
+            output += "<listitem>" + text + "</listitem>\n"
+            if 'last' in block:
+                if block["ordered"]:
+                    tag = "orderedlist"
+                output += "</%s>\n" % tag
+
+        elif block["type"] == "quote":
+            text = MarkDownParseLines(block["lines"], symbol, "quote")
+            output += "<blockquote>\n%s</blockquote>\n" % text
+        elif block["type"] == "code":
+            tag = "programlisting"
+
+            if "language" in block:
+                if block["language"] == "plain":
+                    output += "<informalexample><screen><![CDATA[\n"
+                    tag = "screen"
+                else:
+                    output += "<informalexample><programlisting language=\"%s\"><![CDATA[\n" % 
block['language']
+            else:
+                output += "<informalexample><programlisting><![CDATA[\n"
+
+            logging.debug('listing for %s: [%s]', symbol, '\n'.join(block['lines']))
+            for line in block["lines"]:
+                output += ReplaceEntities(line, symbol) + "\n"
+
+            output += "]]></%s></informalexample>\n" % tag
+        elif block["type"] == "markup":
+            text = ExpandAbbreviations(symbol, block["text"])
+            output += text + "\n"
+        else:
+            output += block["text"] + "\n"
+
+        #$output += "\n<!-- end type='" . $block->{"type"} . "'-->\n"
+    return output
+
+
+def MarkDownParseLines(lines, symbol, context):
+    logging.info('md parse: ctx=%s, [%s]', context, '\n'.join(lines))
+    blocks = MarkDownParseBlocks(lines, symbol, context)
+    output = MarkDownOutputDocBook(blocks, symbol, context)
+    return output
+
+
+def MarkDownParse(text, symbol):
+    return MarkDownParseLines(text.splitlines(), symbol, '')
+
+
+#
+# Function    : ReadDeclarationsFile
+# Description : This reads in a file containing the function/macro/enum etc.
+#                declarations.
+#
+#                Note that in some cases there are several declarations with
+#                the same name, e.g. for conditional macros. In this case we
+#                set a flag in the %DeclarationConditional hash so the
+#                declaration is not shown in the docs.
+#
+#                If a macro and a function have the same name, e.g. for
+#                gtk_object_ref, the function declaration takes precedence.
+#
+#                Some opaque structs are just declared with 'typedef struct
+#                _name name;' in which case the declaration may be empty.
+#                The structure may have been found later in the header, so
+#                that overrides the empty declaration.
+#
+# Arguments   : $file - the declarations file to read
+#                $override - if declarations in this file should override
+#                        any current declaration.
+#
+
+def ReadDeclarationsFile(ifile, override):
+
+    if override == 0:
+        global Declarations, DeclarationTypes, DeclarationConditional, DeclarationOutput
+        Declarations = {}
+        DeclarationTypes = {}
+        DeclarationConditional = {}
+        DeclarationOutput = {}
+
+    INPUT = open(ifile)
+    declaration_type = ''
+    declaration_name = None
+    declaration = None
+    is_deprecated = 0
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not declaration_type:
+            m1 = re.search(r'^<([^>]+)>', line)
+            if m1:
+                declaration_type = m1.group(1)
+                declaration_name = ''
+                logging.info("Found declaration: %s", declaration_type)
+                declaration = ''
+
+        else:
+            m2 = re.search(r'^<NAME>(.*)</NAME>', line)
+            m3 = re.search(r'^<DEPRECATED/>', line)
+            m4 = re.search(r'^</%s>' % declaration_type, line)
+            if m2:
+                declaration_name = m2.group(1)
+            elif m3:
+                is_deprecated = True
+            elif m4:
+                logging.info("Found end of declaration: %s, %s", declaration_type, declaration_name)
+                # Check that the declaration has a name
+                if declaration_name == '':
+                    common.LogWarning(ifile, line_number, declaration_type + " has no name.\n")
+
+                # If the declaration is an empty typedef struct _XXX XXX
+                # set the flag to indicate the struct has a typedef.
+                if (declaration_type == 'STRUCT' or declaration_type == 'UNION') \
+                        and re.search(r'^\s*$', declaration):
+                    logging.info("Struct has typedef: %s", declaration_name)
+                    StructHasTypedef[declaration_name] = 1
+
+                # Check if the symbol is already defined.
+                if declaration_name in Declarations and override == 0:
+                    # Function declarations take precedence.
+                    if DeclarationTypes[declaration_name] == 'FUNCTION':
+                        # Ignore it.
+                        pass
+                    elif declaration_type == 'FUNCTION':
+                        if is_deprecated:
+                            Deprecated[declaration_name] = ''
+
+                        Declarations[declaration_name] = declaration
+                        DeclarationTypes[declaration_name] = declaration_type
+                    elif DeclarationTypes[declaration_name] == declaration_type:
+                        # If the existing declaration is empty, or is just a
+                        # forward declaration of a struct, override it.
+                        if declaration_type == 'STRUCT' or declaration_type == 'UNION':
+                            if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', 
Declarations[declaration_name]):
+                                if is_deprecated:
+                                    Deprecated[declaration_name] = ''
+                                Declarations[declaration_name] = declaration
+                            elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration):
+                                # Ignore an empty or forward declaration.
+                                pass
+                            else:
+                                common.LogWarning(
+                                    ifile, line_number, "Structure %s has multiple definitions." % 
declaration_name)
+
+                        else:
+                            # set flag in %DeclarationConditional hash for
+                            # multiply defined macros/typedefs.
+                            DeclarationConditional[declaration_name] = 1
+
+                    else:
+                        common.LogWarning(ifile, line_number, declaration_name + " has multiple 
definitions.")
+
+                else:
+                    if is_deprecated:
+                        Deprecated[declaration_name] = ''
+
+                    Declarations[declaration_name] = declaration
+                    DeclarationTypes[declaration_name] = declaration_type
+                    logging.debug("added declaration: %s, %s, [%s]", declaration_type, declaration_name, 
declaration)
+
+                declaration_type = ''
+                is_deprecated = False
+            else:
+                declaration += line
+    INPUT.close()
+
+
+#
+# Function    : ReadSignalsFile
+# Description : This reads in an existing file which contains information on
+#                all GTK signals. It creates the arrays @SignalNames and
+#                @SignalPrototypes containing info on the signals. The first
+#                line of the SignalPrototype is the return type of the signal
+#                handler. The remaining lines are the parameters passed to it.
+#                The last parameter, "gpointer user_data" is always the same
+#                so is not included.
+# Arguments   : $file - the file containing the signal handler prototype
+#                        information.
+#
+
+def ReadSignalsFile(ifile):
+
+    in_signal = 0
+    signal_object = None
+    signal_name = None
+    signal_returns = None
+    signal_flags = None
+    signal_prototype = None
+
+    # Reset the signal info.
+    global SignalObjects, SignalNames, SignalReturns, SignalFlags, SignalPrototypes
+    SignalObjects = []
+    SignalNames = []
+    SignalReturns = []
+    SignalFlags = []
+    SignalPrototypes = []
+
+    if not os.path.isfile(ifile):
+        return
+
+    INPUT = open(ifile)
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not in_signal:
+            if re.search(r'^<SIGNAL>', line):
+                in_signal = 1
+                signal_object = ''
+                signal_name = ''
+                signal_returns = ''
+                signal_prototype = ''
+
+        else:
+            m = re.search(r'^<NAME>(.*)<\/NAME>', line)
+            m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line)
+            m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
+            if m:
+                signal_name = m.group(1)
+                m1_2 = re.search(r'^(.*)::(.*)$', signal_name)
+                if m1_2:
+                    signal_object = m1_2.group(1)
+                    signal_name = m1_2.group(2).replace('_', '-')
+                    logging.info("Found signal: %s", signal_name)
+                else:
+                    common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name)
+
+            elif m2:
+                signal_returns = m2.group(1)
+            elif m3:
+                signal_flags = m3.group(1)
+            elif re.search(r'^</SIGNAL>', line):
+                logging.info("Found end of signal: %s::%s\nReturns: %s\n%s",
+                             signal_object, signal_name, signal_returns, signal_prototype)
+                SignalObjects.append(signal_object)
+                SignalNames.append(signal_name)
+                SignalReturns.append(signal_returns)
+                SignalFlags.append(signal_flags)
+                SignalPrototypes.append(signal_prototype)
+                in_signal = False
+            else:
+                signal_prototype += line
+    INPUT.close()
+
+
+#
+# Function    : ReadObjectHierarchy
+# Description : This reads in the $MODULE-hierarchy.txt file containing all
+#               the GtkObject subclasses described in this module (and their
+#               ancestors).
+#               It places them in the @Objects array, and places their level
+#               in the object hierarchy in the @ObjectLevels array, at the
+#               same index. GtkObject, the root object, has a level of 1.
+#
+#               This also generates tree_index.sgml as it goes along.
+#
+# Arguments   : none
+#
+
+def ReadObjectHierarchy():
+
+    global Objects, ObjectLevels
+    Objects = []
+    ObjectLevels = []
+
+    if not os.path.isfile(OBJECT_TREE_FILE):
+        return
+
+    INPUT = open(OBJECT_TREE_FILE)
+
+    # Only emit objects if they are supposed to be documented, or if
+    # they have documented children. To implement this, we maintain a
+    # stack of pending objects which will be emitted if a documented
+    # child turns up.
+    pending_objects = []
+    pending_levels = []
+    root = None
+    tree = []
+    for line in INPUT:
+        m1 = re.search(r'\S+', line)
+        if not m1:
+            continue
+
+        gobject = m1.group(0)
+        level = len(line[:m1.start()]) / 2 + 1
+
+        if level == 1:
+            root = gobject
+
+        while pending_levels and pending_levels[-1] >= level:
+            pending_objects.pop()
+            pending_levels.pop()
+
+        pending_objects.append(gobject)
+        pending_levels.append(level)
+
+        if gobject in KnownSymbols:
+            while len(pending_levels) > 0:
+                gobject = pending_objects.pop(0)
+                level = pending_levels.pop(0)
+                xref = MakeXRef(gobject)
+
+                tree.append(' ' * (level * 4) + xref)
+                Objects.append(gobject)
+                ObjectLevels.append(level)
+                ObjectRoots[gobject] = root
+        # else
+        #    common.LogWarning(OBJECT_TREE_FILE, line_number, "unknown type %s" % object)
+        #
+
+    INPUT.close()
+
+    # FIXME: use xml
+    # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
+    old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml")
+    new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new")
+
+    logging.debug('got %d entries for hierarchy', len(tree))
+
+    OUTPUT = open(new_tree_index, 'w')
+    OUTPUT.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree) + "\n</screen>\n")
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0)
+
+    OutputObjectList()
+
+
+#
+# Function    : ReadInterfaces
+# Description : This reads in the $MODULE.interfaces file.
+#
+# Arguments   : none
+#
+
+def ReadInterfaces():
+    global Interfaces
+    Interfaces = {}
+
+    if not os.path.isfile(INTERFACES_FILE):
+        return
+
+    INPUT = open(INTERFACES_FILE)
+
+    for line in INPUT:
+        line = line.strip()
+        ifaces = line.split()
+        gobject = ifaces.pop(0)
+        if gobject in KnownSymbols and KnownSymbols[gobject] == 1:
+            knownIfaces = []
+
+            # filter out private interfaces, but leave foreign interfaces
+            for iface in ifaces:
+                if iface not in KnownSymbols or KnownSymbols[iface] == 1:
+                    knownIfaces.append(iface)
+
+            Interfaces[gobject] = ' '.join(knownIfaces)
+            logging.info("Interfaces for %s: %s", gobject, Interfaces[gobject])
+        else:
+            logging.info("skipping interfaces for unknown symbol: %s", gobject)
+
+    INPUT.close()
+
+
+#
+# Function    : ReadPrerequisites
+# Description : This reads in the $MODULE.prerequisites file.
+#
+# Arguments   : none
+#
+
+def ReadPrerequisites():
+    global Prerequisites
+    Prerequisites = {}
+
+    if not os.path.isfile(PREREQUISITES_FILE):
+        return
+
+    INPUT = open(PREREQUISITES_FILE)
+
+    for line in INPUT:
+        line = line.strip()
+        prereqs = line.split()
+        iface = prereqs.pop(0)
+        if iface in KnownSymbols and KnownSymbols[iface] == 1:
+            knownPrereqs = []
+
+            # filter out private prerequisites, but leave foreign prerequisites
+            for prereq in prereqs:
+                if prereq not in KnownSymbols or KnownSymbols[prereq] == 1:
+                    knownPrereqs.append(prereq)
+
+            Prerequisites[iface] = ' '.join(knownPrereqs)
+
+    INPUT.close()
+
+
+#
+# Function    : ReadArgsFile
+# Description : This reads in an existing file which contains information on
+#                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
+#                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
+#               on the args.
+# Arguments   : $file - the file containing the arg information.
+#
+
+def ReadArgsFile(ifile):
+    in_arg = False
+    arg_object = None
+    arg_name = None
+    arg_type = None
+    arg_flags = None
+    arg_nick = None
+    arg_blurb = None
+    arg_default = None
+    arg_range = None
+
+    # Reset the args info.
+    global ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks, ArgBlurbs, ArgDefaults, ArgRanges
+    ArgObjects = []
+    ArgNames = []
+    ArgTypes = []
+    ArgFlags = []
+    ArgNicks = []
+    ArgBlurbs = []
+    ArgDefaults = []
+    ArgRanges = []
+
+    if not os.path.isfile(ifile):
+        return
+
+    INPUT = open(ifile)
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not in_arg:
+            if line.startswith('<ARG>'):
+                in_arg = True
+                arg_object = ''
+                arg_name = ''
+                arg_type = ''
+                arg_flags = ''
+                arg_nick = ''
+                arg_blurb = ''
+                arg_default = ''
+                arg_range = ''
+
+        else:
+            m1 = re.search(r'^<NAME>(.*)</NAME>', line)
+            m2 = re.search(r'^<TYPE>(.*)</TYPE>', line)
+            m3 = re.search(r'^<RANGE>(.*)</RANGE>', line)
+            m4 = re.search(r'^<FLAGS>(.*)</FLAGS>', line)
+            m5 = re.search(r'^<NICK>(.*)</NICK>', line)
+            m6 = re.search(r'^<BLURB>(.*)</BLURB>', line)
+            m7 = re.search(r'^<DEFAULT>(.*)</DEFAULT>', line)
+            if m1:
+                arg_name = m1.group(1)
+                m1_1 = re.search(r'^(.*)::(.*)$', arg_name)
+                if m1_1:
+                    arg_object = m1_1.group(1)
+                    arg_name = m1_1.group(2).replace('_', '-')
+                    logging.info("Found arg: %s", arg_name)
+                else:
+                    common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name)
+
+            elif m2:
+                arg_type = m2.group(1)
+            elif m3:
+                arg_range = m3.group(1)
+            elif m4:
+                arg_flags = m4.group(1)
+            elif m5:
+                arg_nick = m5.group(1)
+            elif m6:
+                arg_blurb = m6.group(1)
+                if arg_blurb == "(null)":
+                    arg_blurb = ''
+                    common.LogWarning(
+                        ifile, line_number, "Property %s:%s has no documentation." % (arg_object, arg_name))
+
+            elif m7:
+                arg_default = m7.group(1)
+            elif re.search(r'^</ARG>', line):
+                logging.info("Found end of arg: %s::%s\n%s : %s", arg_object, arg_name, arg_type, arg_flags)
+                ArgObjects.append(arg_object)
+                ArgNames.append(arg_name)
+                ArgTypes.append(arg_type)
+                ArgRanges.append(arg_range)
+                ArgFlags.append(arg_flags)
+                ArgNicks.append(arg_nick)
+                ArgBlurbs.append(arg_blurb)
+                ArgDefaults.append(arg_default)
+                in_arg = False
+
+    INPUT.close()
+
+
+#
+# Function    : AddTreeLineArt
+# Description : Add unicode lineart to a pre-indented string array and returns
+#               it as as multiline string.
+# Arguments   : @tree - array of indented strings.
+#
+
+def AddTreeLineArt(tree):
+    # iterate bottom up over the tree
+    for i in range(len(tree) - 1, -1, -1):
+        # count leading spaces
+        m = re.search(r'^([^<A-Za-z]*)', tree[i])
+        indent = len(m.group(1))
+        # replace with ╰───, if place of ╰ is not space insert ├
+        if indent > 4:
+            if tree[i][indent - 4] == " ":
+                tree[i] = tree[i][:indent - 4] + "--- " + tree[i][indent:]
+            else:
+                tree[i] = tree[i][:indent - 4] + "+-- " + tree[i][indent:]
+
+            # go lines up while space and insert |
+            j = i - 1
+            while j >= 0 and tree[j][indent - 4] == ' ':
+                tree[j] = tree[j][:indent - 4] + '|' + tree[j][indent - 3:]
+                j -= 1
+
+    res = "\n".join(tree)
+    # unicode chars for: ╰──
+    res = re.sub(r'---', '<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>', res)
+    # unicde chars for: ├──
+    res = re.sub(r'\+--', '<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>', res)
+    # unicode char for: │
+    res = re.sub(r'\|', '<phrase role=\"lineart\">&#9474;</phrase>', res)
+
+    return res
+
+
+#
+# Function    : CheckIsObject
+# Description : Returns 1 if the given name is a GObject or a subclass.
+#                It uses the global @Objects array.
+#                Note that the @Objects array only contains classes in the
+#                current module and their ancestors - not all GObject classes.
+# Arguments   : $name - the name to check.
+#
+
+def CheckIsObject(name):
+    root = ObjectRoots.get(name)
+    # Let GBoxed pass as an object here to get -struct appended to the id
+    # and prevent conflicts with sections.
+    return root and root != 'GEnum' and root != 'GFlags'
+
+
+#
+# Function    : MakeReturnField
+# Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
+# Arguments   : $str - the string to pad.
+#
+
+def MakeReturnField(s):
+    return s + (' ' * (RETURN_TYPE_FIELD_WIDTH - len(s)))
+
+
+#
+# Function    : GetSymbolSourceFile
+# Description : Get the filename where the symbol docs where taken from.
+# Arguments   : $symbol - the symbol name
+#
+
+def GetSymbolSourceFile(symbol):
+    return SourceSymbolSourceFile.get(symbol, '')
+
+
+#
+# Function    : GetSymbolSourceLine
+# Description : Get the file line where the symbol docs where taken from.
+# Arguments   : $symbol - the symbol name
+#
+
+def GetSymbolSourceLine(symbol):
+    return SourceSymbolSourceLine.get(symbol, 0)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]