---------- Forwarded message ----------
From: Ankit Dhankhar <[email protected]>
Date: Sun, Apr 29, 2018 at 12:20 AM
Subject: Re: [ft-devel] Custom search functionality & Python3 compatibility
of 'docmaker'
To: Nikhil Ramakrishnan <[email protected]>


Upon following changes it is working fine but giving more error in cross
references :/.Looking for the reasons but for rest it will work fine.



Ankit

On Sat, Apr 28, 2018 at 8:14 PM, Nikhil Ramakrishnan <
[email protected]> wrote:

>    Can you share the steps to reproduce errors, for which it is producing
>> wrong results.
>>
>
> Run `mingw32-make refdoc` (env: Windows, Python 3.6.1)
>
> Error text:
> Traceback (most recent call last):
>   File "./src/tools/docmaker/docmaker.py", line 113, in <module>
>     main( sys.argv )
>   File "./src/tools/docmaker/docmaker.py", line 94, in main
>     file_list = make_file_list( args )
>   File "F:\Nik\Freetype\Backup\freetype2\src\tools\docmaker\utils.py",
> line 110, in make_file_list
>     if string.find( pathname, '*' ) >= 0:
> AttributeError: module 'string' has no attribute 'find'
> builds/freetype.mk:296: recipe for target 'refdoc' failed
> mingw32-make: *** [refdoc] Error 1
>
> Does this work for you on Python3?
>
> --
> Nikhil
>
#
#  content.py
#
#    Parse comment blocks to build content blocks (library file).
#
#  Copyright 2002-2018 by
#  David Turner.
#
#  This file is part of the FreeType project, and may only be used,
#  modified, and distributed under the terms of the FreeType project
#  license, LICENSE.TXT.  By continuing to use, modify, or distribute
#  this file you indicate that you have read the license and
#  understand and accept it fully.

#
# This file contains routines to parse documentation comment blocks,
# building more structured objects out of them.
#


from sources import *
from utils   import *

import string, re


#
# Regular expressions to detect code sequences.  `Code sequences' are simply
# code fragments embedded in '{' and '}', as demonstrated in the following
# example.
#
#   {
#     x = y + z;
#     if ( zookoo == 2 )
#     {
#       foobar();
#     }
#   }
#
# Note that the indentation of the first opening brace and the last closing
# brace must be exactly the same.  The code sequence itself should have a
# larger indentation than the surrounding braces.
#
re_code_start = re.compile( r"(\s*){\s*$" )
re_code_end   = re.compile( r"(\s*)}\s*$" )


#
# A regular expression to isolate identifiers from other text.  Two syntax
# forms are supported:
#
#   <name>
#   <name>[<id>]
#
# where both `<name>' and `<id>' consist of alphanumeric characters, `_',
# and `-'.  Use `<id>' if there are multiple, valid `<name>' entries; in the
# index, `<id>' will be appended in parentheses.
#
# For example,
#
#   stem_darkening[autofit]
#
# becomes `stem_darkening (autofit)' in the index.
#
re_identifier = re.compile( r"""
                              ((?:\w|-)+
                               (?:\[(?:\w|-)+\])?)
                            """, re.VERBOSE )


#
# We collect macro names ending in `_H' (group 1), as defined in
# `freetype/config/ftheader.h'.  While outputting the object data, we use
# this info together with the object's file location (group 2) to emit the
# appropriate header file macro and its associated file name before the
# object itself.
#
# Example:
#
#   #define FT_FREETYPE_H <freetype.h>
#
re_header_macro = re.compile( r'^#define\s{1,}(\w{1,}_H)\s{1,}<(.*)>' )


################################################################
##
##  DOC CODE CLASS
##
##  The `DocCode' class is used to store source code lines.
##
##  `self.lines' contains a set of source code lines that will be dumped as
##  HTML in a <PRE> tag.
##
##  The object is filled line by line by the parser; it strips the leading
##  `margin' space from each input line before storing it in `self.lines'.
##
class  DocCode:

    def  __init__( self, margin, lines ):
        self.lines = []
        self.words = None

        # remove margin spaces
        for l in lines:
            if l[:margin].strip() == "":
                l = l[margin:]
            self.lines.append( l )

    def  dump( self, prefix = "", width = 60 ):
        lines = self.dump_lines( 0, width )
        for l in lines:
            print( prefix + l )

    def  dump_lines( self, margin = 0, width = 60 ):
        result = []
        for l in self.lines:
            result.append( " " * margin + l )
        return result



################################################################
##
##  DOC PARA CLASS
##
##  `Normal' text paragraphs are stored in the `DocPara' class.
##
##  `self.words' contains the list of words that make up the paragraph.
##
class  DocPara:

    def  __init__( self, lines ):
        self.lines = None
        self.words = []
        for l in lines:
            l = l.strip()
            self.words.extend( l.split( ) )

    def  dump( self, prefix = "", width = 60 ):
        lines = self.dump_lines( 0, width )
        for l in lines:
            print( prefix + l )

    def  dump_lines( self, margin = 0, width = 60 ):
        cur    = ""  # current line
        col    = 0   # current width
        result = []

        for word in self.words:
            ln = len( word )
            if col > 0:
                ln = ln + 1

            if col + ln > width:
                result.append( " " * margin + cur )
                cur = word
                col = len( word )
            else:
                if col > 0:
                    cur = cur + " "
                cur = cur + word
                col = col + ln

        if col > 0:
            result.append( " " * margin + cur )

        return result


################################################################
##
##  DOC FIELD CLASS
##
##  The `DocField' class stores a list containing either `DocPara' or
##  `DocCode' objects.  Each DocField object also has an optional `name'
##  that is used when the object corresponds to a field or value definition.
##
class  DocField:

    def  __init__( self, name, lines ):
        self.name  = name  # can be `None' for normal paragraphs/sources
        self.items = []    # list of items

        mode_none  = 0     # start parsing mode
        mode_code  = 1     # parsing code sequences
        mode_para  = 3     # parsing normal paragraph

        margin     = -1    # current code sequence indentation
        cur_lines  = []

        # analyze the markup lines to check whether they contain paragraphs,
        # code sequences, or fields definitions
        #
        start = 0
        mode  = mode_none

        for l in lines:
            # are we parsing a code sequence?
            if mode == mode_code:
                m = re_code_end.match( l )
                if m and len( m.group( 1 ) ) <= margin:
                    # that's it, we finished the code sequence
                    code = DocCode( 0, cur_lines )
                    self.items.append( code )
                    margin    = -1
                    cur_lines = []
                    mode      = mode_none
                else:
                    # otherwise continue the code sequence
                    cur_lines.append( l[margin:] )
            else:
                # start of code sequence?
                m = re_code_start.match( l )
                if m:
                    # save current lines
                    if cur_lines:
                        para = DocPara( cur_lines )
                        self.items.append( para )
                        cur_lines = []

                    # switch to code extraction mode
                    margin = len( m.group( 1 ) )
                    mode   = mode_code
                else:
                    if not l.split() and cur_lines:
                        # if the line is empty, we end the current paragraph,
                        # if any
                        para = DocPara( cur_lines )
                        self.items.append( para )
                        cur_lines = []
                    else:
                        # otherwise, simply add the line to the current
                        # paragraph
                        cur_lines.append( l )

        if mode == mode_code:
            # unexpected end of code sequence
            code = DocCode( margin, cur_lines )
            self.items.append( code )
        elif cur_lines:
            para = DocPara( cur_lines )
            self.items.append( para )

    def  dump( self, prefix = "" ):
        if self.field:
            print( prefix + self.field + " ::" )
            prefix = prefix + "----"

        first = 1
        for p in self.items:
            if not first:
                print( "" )
            p.dump( prefix )
            first = 0

    def  dump_lines( self, margin = 0, width = 60 ):
        result = []
        nl     = None

        for p in self.items:
            if nl:
                result.append( "" )

            result.extend( p.dump_lines( margin, width ) )
            nl = 1

        return result


#
# A regular expression to detect field definitions.
#
# Examples:
#
#   foo     ::
#   foo.bar ::
#
re_field = re.compile( r"""
                         \s*
                           (
                             \w*
                           |
                             \w (\w | \.)* \w
                           )
                         \s* ::
                       """, re.VERBOSE )


################################################################
##
##  DOC MARKUP CLASS
##
class  DocMarkup:

    def  __init__( self, tag, lines ):
        self.tag    = tag.lower()
        self.fields = []

        cur_lines = []
        field     = None
        mode      = 0

        for l in lines:
            m = re_field.match( l )
            if m:
                # We detected the start of a new field definition.

                # first, save the current one
                if cur_lines:
                    f = DocField( field, cur_lines )
                    self.fields.append( f )
                    cur_lines = []
                    field     = None

                field     = m.group( 1 )   # record field name
                ln        = len( m.group( 0 ) )
                l         = " " * ln + l[ln:]
                cur_lines = [l]
            else:
                cur_lines.append( l )

        if field or cur_lines:
            f = DocField( field, cur_lines )
            self.fields.append( f )

    def  get_name( self ):
        try:
            return self.fields[0].items[0].words[0]
        except:
            return None

    def  dump( self, margin ):
        print( " " * margin + "<" + self.tag + ">" )
        for f in self.fields:
            f.dump( "  " )
        print( " " * margin + "</" + self.tag + ">" )


################################################################
##
##  DOC CHAPTER CLASS
##
class  DocChapter:

    def  __init__( self, block ):
        self.block    = block
        self.sections = []
        if block:
            self.name  = block.name
            self.title = block.get_markup_words( "title" )
            self.order = block.get_markup_words( "sections" )
        else:
            self.name  = "Other"
            self.title = "Miscellaneous".split()
            self.order = []


################################################################
##
##  DOC SECTION CLASS
##
class  DocSection:

    def  __init__( self, name = "Other" ):
        self.name        = name
        self.blocks      = {}
        self.block_names = []  # ordered block names in section
        self.defs        = []
        self.abstract    = ""
        self.description = ""
        self.order       = []
        self.title       = "ERROR"
        self.chapter     = None

    def  add_def( self, block ):
        self.defs.append( block )

    def  add_block( self, block ):
        self.block_names.append( block.name )
        self.blocks[block.name] = block

    def  process( self ):
        # look up one block that contains a valid section description
        for block in self.defs:
            title = block.get_markup_text( "title" )
            if title:
                self.title       = title
                self.abstract    = block.get_markup_words( "abstract" )
                self.description = block.get_markup_items( "description" )
                self.order       = block.get_markup_words_all( "order" )
                return

    def  reorder( self ):
        self.block_names = sort_order_list( self.block_names, self.order )


################################################################
##
##  CONTENT PROCESSOR CLASS
##
class  ContentProcessor:

    def  __init__( self ):
        """Initialize a block content processor."""
        self.reset()

        self.sections = {}    # dictionary of documentation sections
        self.section  = None  # current documentation section

        self.chapters = []    # list of chapters

        self.headers  = {}    # dictionary of header macros

    def  set_section( self, section_name ):
        """Set current section during parsing."""
        if not section_name in self.sections:
            section = DocSection( section_name )
            self.sections[section_name] = section
            self.section                = section
        else:
            self.section = self.sections[section_name]

    def  add_chapter( self, block ):
        chapter = DocChapter( block )
        self.chapters.append( chapter )

    def  reset( self ):
        """Reset the content processor for a new block."""
        self.markups      = []
        self.markup       = None
        self.markup_lines = []

    def  add_markup( self ):
        """Add a new markup section."""
        if self.markup and self.markup_lines:

            # get rid of last line of markup if it's empty
            marks = self.markup_lines
            if len( marks ) > 0 and not marks[-1].strip():
                self.markup_lines = marks[:-1]

            m = DocMarkup( self.markup, self.markup_lines )

            self.markups.append( m )

            self.markup       = None
            self.markup_lines = []

    def  process_content( self, content ):
        """Process a block content and return a list of DocMarkup objects
           corresponding to it."""
        markup       = None
        markup_lines = []
        first        = 1

        margin  = -1
        in_code = 0

        for line in content:
            if in_code:
                m = re_code_end.match( line )
                if m and len( m.group( 1 ) ) <= margin:
                    in_code = 0
                    margin  = -1
            else:
                m = re_code_start.match( line )
                if m:
                    in_code = 1
                    margin  = len( m.group( 1 ) )

            found = None

            if not in_code:
                for t in re_markup_tags:
                    m = t.match( line )
                    if m:
                        found  = m.group( 1 ).lower()
                        prefix = len( m.group( 0 ) )
                        # remove markup from line
                        line   = " " * prefix + line[prefix:]
                        break

            # is it the start of a new markup section ?
            if found:
                first = 0
                self.add_markup()  # add current markup content
                self.markup = found
                if len( line.strip() ) > 0:
                    self.markup_lines.append( line )
            elif first == 0:
                self.markup_lines.append( line )

        self.add_markup()

        return self.markups

    def  parse_sources( self, source_processor ):
        blocks = source_processor.blocks
        count  = len( blocks )

        for n in range( count ):
            source = blocks[n]
            if source.content:
                # this is a documentation comment, we need to catch
                # all following normal blocks in the "follow" list
                #
                follow = []
                m = n + 1
                while m < count and not blocks[m].content:
                    follow.append( blocks[m] )
                    m = m + 1

                doc_block = DocBlock( source, follow, self )

    def  finish( self ):
        # process all sections to extract their abstract, description
        # and ordered list of items
        #
        for sec in self.sections.values():
            sec.process()

        # process chapters to check that all sections are correctly
        # listed there
        for chap in self.chapters:
            for sec in chap.order:
                if sec in self.sections:
                    section = self.sections[sec]
                    section.chapter = chap
                    section.reorder()
                    chap.sections.append( section )
                else:
                    sys.stderr.write( "WARNING: chapter '" +          \
                        chap.name + "' in " + chap.block.location() + \
                        " lists unknown section '" + sec + "'\n" )

        # check that all sections are in a chapter
        #
        others = []
        for sec in self.sections.values():
            if not sec.chapter:
                sec.reorder()
                others.append( sec )

        # create a new special chapter for all remaining sections
        # when necessary
        #
        if others:
            chap = DocChapter( None )
            chap.sections = others
            self.chapters.append( chap )


################################################################
##
##  DOC BLOCK CLASS
##
class  DocBlock:

    def  __init__( self, source, follow, processor ):
        processor.reset()

        self.source  = source
        self.code    = []
        self.type    = "ERRTYPE"
        self.name    = "ERRNAME"
        self.section = processor.section
        self.markups = processor.process_content( source.content )

        # compute block type from first markup tag
        try:
            self.type = self.markups[0].tag
        except:
            pass

        # compute block name from first markup paragraph
        try:
            markup = self.markups[0]
            para   = markup.fields[0].items[0]
            name   = para.words[0]
            m = re_identifier.match( name )
            if m:
                name = m.group( 1 )
            self.name = name
        except:
            pass

        if self.type == "section":
            # detect new section starts
            processor.set_section( self.name )
            processor.section.add_def( self )
        elif self.type == "chapter":
            # detect new chapter
            processor.add_chapter( self )
        else:
            processor.section.add_block( self )

        # now, compute the source lines relevant to this documentation
        # block. We keep normal comments in for obvious reasons (??)
        source = []
        for b in follow:
            if b.format:
                break
            for l in b.lines:
                # collect header macro definitions
                m = re_header_macro.match( l )
                if m:
                    processor.headers[m.group( 2 )] = m.group( 1 );

                # we use "/* */" as a separator
                if re_source_sep.match( l ):
                    break
                source.append( l )

        # now strip the leading and trailing empty lines from the sources
        start = 0
        end   = len( source ) - 1

        while start < end and not source[start].strip():
            start = start + 1

        while start < end and not source[end].strip():
            end = end - 1

        if start == end and not source[start].strip():
            self.code = []
        else:
            self.code = source[start:end + 1]

    def  location( self ):
        return self.source.location()

    def  get_markup( self, tag_name ):
        """Return the DocMarkup corresponding to a given tag in a block."""
        for m in self.markups:
            if m.tag == string.lower( tag_name ):
                return m
        return None

    def  get_markup_words( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            return m.fields[0].items[0].words
        except:
            return []

    def  get_markup_words_all( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            words = []
            for item in m.fields[0].items:
                # We honour empty lines in an `<Order>' section element by
                # adding the sentinel `/empty/'.  The formatter should then
                # convert it to an appropriate representation in the
                # `section_enter' function.
                words += item.words
                words.append( "/empty/" )
            return words
        except:
            return []

    def  get_markup_text( self, tag_name ):
        result = self.get_markup_words( tag_name )
        return " ".join( result )

    def  get_markup_items( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            return m.fields[0].items
        except:
            return None

# eof
#
#  formatter.py
#
#    Convert parsed content blocks to a structured document (library file).
#
#  Copyright 2002-2018 by
#  David Turner.
#
#  This file is part of the FreeType project, and may only be used,
#  modified, and distributed under the terms of the FreeType project
#  license, LICENSE.TXT.  By continuing to use, modify, or distribute
#  this file you indicate that you have read the license and
#  understand and accept it fully.

#
# This is the base Formatter class.  Its purpose is to convert a content
# processor's data into specific documents (i.e., table of contents, global
# index, and individual API reference indices).
#
# You need to sub-class it to output anything sensible.  For example, the
# file `tohtml.py' contains the definition of the `HtmlFormatter' sub-class
# to output HTML.
#


from sources import *
from content import *
from utils   import *


################################################################
##
##  FORMATTER CLASS
##
class  Formatter:

    def  __init__( self, processor ):
        self.processor   = processor
        self.identifiers = {}
        self.chapters    = processor.chapters
        self.sections    = processor.sections.values()
        self.block_index = []

        # store all blocks in a dictionary
        self.blocks = []
        for section in self.sections:
            for block in section.blocks.values():
                self.add_identifier( block.name, block )

                # add enumeration values to the index, since this is useful
                for markup in block.markups:
                    if markup.tag == 'values':
                        for field in markup.fields:
                            self.add_identifier( field.name, block )

        self.block_index = list(self.identifiers.keys())
        self.block_index.sort( key = index_key )

        # also add section names to dictionary (without making them appear
        # in the index)
        for section in self.sections:
            self.add_identifier( section.name, section )

    def  add_identifier( self, name, block ):
        if name in self.identifiers:
            # duplicate name!
            sys.stderr.write( "WARNING: duplicate definition for"
                              + " '" + name + "' "
                              + "in " + block.location() + ", "
                              + "previous definition in "
                              + self.identifiers[name].location()
                              + "\n" )
        else:
            self.identifiers[name] = block

    #
    # formatting the table of contents
    #
    def  toc_enter( self ):
        pass

    def  toc_chapter_enter( self, chapter ):
        pass

    def  toc_section_enter( self, section ):
        pass

    def  toc_section_exit( self, section ):
        pass

    def  toc_chapter_exit( self, chapter ):
        pass

    def  toc_index( self, index_filename ):
        pass

    def  toc_exit( self ):
        pass

    def  toc_dump( self, toc_filename = None, index_filename = None ):
        output = None
        if toc_filename:
            output = open_output( toc_filename )

        self.toc_enter()

        for chap in self.processor.chapters:

            self.toc_chapter_enter( chap )

            for section in chap.sections:
                self.toc_section_enter( section )
                self.toc_section_exit( section )

            self.toc_chapter_exit( chap )

        self.toc_index( index_filename )

        self.toc_exit()

        if output:
            close_output( output )

    #
    # formatting the index
    #
    def  index_enter( self ):
        pass

    def  index_name_enter( self, name ):
        pass

    def  index_name_exit( self, name ):
        pass

    def  index_exit( self ):
        pass

    def  index_dump( self, index_filename = None ):
        output = None
        if index_filename:
            output = open_output( index_filename )

        self.index_enter()

        for name in self.block_index:
            self.index_name_enter( name )
            self.index_name_exit( name )

        self.index_exit()

        if output:
            close_output( output )

    #
    # formatting a section
    #
    def  section_enter( self, section ):
        pass

    def  block_enter( self, block ):
        pass

    def  markup_enter( self, markup, block = None ):
        pass

    def  field_enter( self, field, markup = None, block = None ):
        pass

    def  field_exit( self, field, markup = None, block = None ):
        pass

    def  markup_exit( self, markup, block = None ):
        pass

    def  block_exit( self, block ):
        pass

    def  section_exit( self, section ):
        pass

    def  section_dump( self, section, section_filename = None ):
        output = None
        if section_filename:
            output = open_output( section_filename )

        self.section_enter( section )

        for name in section.block_names:
            skip_entry = 0
            try:
                block = self.identifiers[name]
                # `block_names' can contain field names also,
                # which we filter out
                for markup in block.markups:
                    if markup.tag == 'values':
                        for field in markup.fields:
                            if field.name == name:
                                skip_entry = 1
            except:
                skip_entry = 1   # this happens e.g. for `/empty/' entries

            if skip_entry:
              continue

            self.block_enter( block )

            for markup in block.markups[1:]:   # always ignore first markup!
                self.markup_enter( markup, block )

                for field in markup.fields:
                    self.field_enter( field, markup, block )
                    self.field_exit( field, markup, block )

                self.markup_exit( markup, block )

            self.block_exit( block )

        self.section_exit( section )

        if output:
            close_output( output )

    def  section_dump_all( self ):
        for section in self.sections:
            self.section_dump( section )

# eof
#
#  sources.py
#
#    Convert source code comments to multi-line blocks (library file).
#
#  Copyright 2002-2018 by
#  David Turner.
#
#  This file is part of the FreeType project, and may only be used,
#  modified, and distributed under the terms of the FreeType project
#  license, LICENSE.TXT.  By continuing to use, modify, or distribute
#  this file you indicate that you have read the license and
#  understand and accept it fully.

#
# This library file contains definitions of classes needed to decompose C
# source code files into a series of multi-line `blocks'.  There are two
# kinds of blocks.
#
#   - Normal blocks, which contain source code or ordinary comments.
#
#   - Documentation blocks, which have restricted formatting, and whose text
#     always start with a documentation markup tag like `<Function>',
#     `<Type>', etc.
#
# The routines to process the content of documentation blocks are contained
# in file `content.py'; the classes and methods found here only deal with
# text parsing and basic documentation block extraction.
#


import fileinput, re, string


################################################################
##
##  SOURCE BLOCK FORMAT CLASS
##
##  A simple class containing compiled regular expressions to detect
##  potential documentation format block comments within C source code.
##
##  The `column' pattern must contain a group to `unbox' the content of
##  documentation comment blocks.
##
##  Later on, paragraphs are converted to long lines, which simplifies the
##  regular expressions that act upon the text.
##
class  SourceBlockFormat:

    def  __init__( self, id, start, column, end ):
        """Create a block pattern, used to recognize special documentation
           blocks."""
        self.id     = id
        self.start  = re.compile( start, re.VERBOSE )
        self.column = re.compile( column, re.VERBOSE )
        self.end    = re.compile( end, re.VERBOSE )


#
# Format 1 documentation comment blocks.
#
#    /************************************/ (at least 2 asterisks)
#    /*                                  */
#    /*                                  */
#    /*                                  */
#    /************************************/ (at least 2 asterisks)
#
start = r'''
  \s*      # any number of whitespace
  /\*{2,}/ # followed by '/' and at least two asterisks then '/'
  \s*$     # probably followed by whitespace
'''

column = r'''
  \s*      # any number of whitespace
  /\*{1}   # followed by '/' and precisely one asterisk
  ([^*].*) # followed by anything (group 1)
  \*{1}/   # followed by one asterisk and a '/'
  \s*$     # probably followed by whitespace
'''

re_source_block_format1 = SourceBlockFormat( 1, start, column, start )


#
# Format 2 documentation comment blocks.
#
#    /************************************ (at least 2 asterisks)
#     *
#     *                                    (1 asterisk)
#     *
#     */                                   (1 or more asterisks)
#
start = r'''
  \s*     # any number of whitespace
  /\*{2,} # followed by '/' and at least two asterisks
  \s*$    # probably followed by whitespace
'''

column = r'''
  \s*           # any number of whitespace
  \*{1}(?![*/]) # followed by precisely one asterisk not followed by `/'
  (.*)          # then anything (group1)
'''

end = r'''
  \s*  # any number of whitespace
  \*+/ # followed by at least one asterisk, then '/'
'''

re_source_block_format2 = SourceBlockFormat( 2, start, column, end )


#
# The list of supported documentation block formats.  We could add new ones
# quite easily.
#
re_source_block_formats = [re_source_block_format1, re_source_block_format2]


#
# The following regular expressions correspond to markup tags within the
# documentation comment blocks.  They are equivalent despite their different
# syntax.
#
# A markup tag consists of letters or character `-', to be found in group 1.
#
# Notice that a markup tag _must_ begin a new paragraph.
#
re_markup_tag1 = re.compile( r'''\s*<((?:\w|-)*)>''' )  # <xxxx> format
re_markup_tag2 = re.compile( r'''\s*@((?:\w|-)*):''' )  # @xxxx: format

#
# The list of supported markup tags.  We could add new ones quite easily.
#
re_markup_tags = [re_markup_tag1, re_markup_tag2]


#
# A regular expression to detect a cross reference, after markup tags have
# been stripped off.
#
# Two syntax forms are supported:
#
#   @<name>
#   @<name>[<id>]
#
# where both `<name>' and `<id>' consist of alphanumeric characters, `_',
# and `-'.  Use `<id>' if there are multiple, valid `<name>' entries.
#
# Example: @foo[bar]
#
re_crossref = re.compile( r"""
                            @
                            (?P<name>(?:\w|-)+
                                     (?:\[(?:\w|-)+\])?)
                            (?P<rest>.*)
                          """, re.VERBOSE )

#
# Two regular expressions to detect italic and bold markup, respectively.
# Group 1 is the markup, group 2 the rest of the line.
#
# Note that the markup is limited to words consisting of letters, digits,
# the characters `_' and `-', or an apostrophe (but not as the first
# character).
#
re_italic = re.compile( r"_((?:\w|-)(?:\w|'|-)*)_(.*)" )     #  _italic_
re_bold   = re.compile( r"\*((?:\w|-)(?:\w|'|-)*)\*(.*)" )   #  *bold*

#
# This regular expression code to identify an URL has been taken from
#
#   https://mail.python.org/pipermail/tutor/2002-September/017228.html
#
# (with slight modifications).
#
urls = r'(?:https?|telnet|gopher|file|wais|ftp)'
ltrs = r'\w'
gunk = r'/#~:.?+=&%@!\-'
punc = r'.:?\-'
any  = "%(ltrs)s%(gunk)s%(punc)s" % { 'ltrs' : ltrs,
                                      'gunk' : gunk,
                                      'punc' : punc }
url  = r"""
         (
           \b                    # start at word boundary
           %(urls)s :            # need resource and a colon
           [%(any)s] +?          # followed by one or more of any valid
                                 # character, but be conservative and
                                 # take only what you need to...
           (?=                   # [look-ahead non-consumptive assertion]
             [%(punc)s]*         # either 0 or more punctuation
             (?:                 # [non-grouping parentheses]
               [^%(any)s] | $    # followed by a non-url char
                                 # or end of the string
             )
           )
         )
        """ % {'urls' : urls,
               'any'  : any,
               'punc' : punc }

re_url = re.compile( url, re.VERBOSE | re.MULTILINE )

#
# A regular expression that stops collection of comments for the current
# block.
#
re_source_sep = re.compile( r'\s*/\*\s*\*/' )   #  /* */

#
# A regular expression to find possible C identifiers while outputting
# source code verbatim, covering things like `*foo' or `(bar'.  Group 1 is
# the prefix, group 2 the identifier -- since we scan lines from left to
# right, sequentially splitting the source code into prefix and identifier
# is fully sufficient for our purposes.
#
re_source_crossref = re.compile( r'(\W*)(\w*)' )

#
# A regular expression that matches a list of reserved C source keywords.
#
re_source_keywords = re.compile( '''\\b ( typedef   |
                                          struct    |
                                          enum      |
                                          union     |
                                          const     |
                                          char      |
                                          int       |
                                          short     |
                                          long      |
                                          void      |
                                          signed    |
                                          unsigned  |
                                          \#include |
                                          \#define  |
                                          \#undef   |
                                          \#if      |
                                          \#ifdef   |
                                          \#ifndef  |
                                          \#else    |
                                          \#endif   ) \\b''', re.VERBOSE )


################################################################
##
##  SOURCE BLOCK CLASS
##
##  There are two important fields in a `SourceBlock' object.
##
##    self.lines
##      A list of text lines for the corresponding block.
##
##    self.content
##      For documentation comment blocks only, this is the block content
##      that has been `unboxed' from its decoration.  This is `None' for all
##      other blocks (i.e., sources or ordinary comments with no starting
##      markup tag)
##
class  SourceBlock:

    def  __init__( self, processor, filename, lineno, lines ):
        self.processor = processor
        self.filename  = filename
        self.lineno    = lineno
        self.lines     = lines[:]
        self.format    = processor.format
        self.content   = []

        if self.format == None:
            return

        words = []

        # extract comment lines
        lines = []

        for line0 in self.lines:
            m = self.format.column.match( line0 )
            if m:
                lines.append( m.group( 1 ) )

        # now, look for a markup tag
        for l in lines:
            l = l.strip()
            if len( l ) > 0:
                for tag in re_markup_tags:
                    if tag.match( l ):
                        self.content = lines
                        return

    def  location( self ):
        return "(" + self.filename + ":" + repr( self.lineno ) + ")"

    # debugging only -- not used in normal operations
    def  dump( self ):
        if self.content:
            print( "{{{content start---" )
            for l in self.content:
                print( l )
            print( "---content end}}}" )
            return

        fmt = ""
        if self.format:
            fmt = repr( self.format.id ) + " "

        for line in self.lines:
            print( line )


################################################################
##
##  SOURCE PROCESSOR CLASS
##
##  The `SourceProcessor' is in charge of reading a C source file and
##  decomposing it into a series of different `SourceBlock' objects.
##
##  A SourceBlock object consists of the following data.
##
##    - A documentation comment block using one of the layouts above.  Its
##      exact format will be discussed later.
##
##    - Normal sources lines, including comments.
##
##
class  SourceProcessor:

    def  __init__( self ):
        """Initialize a source processor."""
        self.blocks   = []
        self.filename = None
        self.format   = None
        self.lines    = []

    def  reset( self ):
        """Reset a block processor and clean up all its blocks."""
        self.blocks = []
        self.format = None

    def  parse_file( self, filename ):
        """Parse a C source file and add its blocks to the processor's
           list."""
        self.reset()

        self.filename = filename

        fileinput.close()
        self.format = None
        self.lineno = 0
        self.lines  = []

        for line in fileinput.input( filename ):
            # strip trailing newlines, important on Windows machines!
            if line[-1] == '\012':
                line = line[0:-1]

            if self.format == None:
                self.process_normal_line( line )
            else:
                if self.format.end.match( line ):
                    # A normal block end.  Add it to `lines' and create a
                    # new block
                    self.lines.append( line )
                    self.add_block_lines()
                elif self.format.column.match( line ):
                    # A normal column line.  Add it to `lines'.
                    self.lines.append( line )
                else:
                    # An unexpected block end.  Create a new block, but
                    # don't process the line.
                    self.add_block_lines()

                    # we need to process the line again
                    self.process_normal_line( line )

        # record the last lines
        self.add_block_lines()

    def  process_normal_line( self, line ):
        """Process a normal line and check whether it is the start of a new
           block."""
        for f in re_source_block_formats:
            if f.start.match( line ):
                self.add_block_lines()
                self.format = f
                self.lineno = fileinput.filelineno()

        self.lines.append( line )

    def  add_block_lines( self ):
        """Add the current accumulated lines and create a new block."""
        if self.lines != []:
            block = SourceBlock( self,
                                 self.filename,
                                 self.lineno,
                                 self.lines )

            self.blocks.append( block )
            self.format = None
            self.lines  = []

    # debugging only, not used in normal operations
    def  dump( self ):
        """Print all blocks in a processor."""
        for b in self.blocks:
            b.dump()

# eof
#
#  tohtml.py
#
#    A sub-class container of the `Formatter' class to produce HTML.
#
#  Copyright 2002-2018 by
#  David Turner.
#
#  This file is part of the FreeType project, and may only be used,
#  modified, and distributed under the terms of the FreeType project
#  license, LICENSE.TXT.  By continuing to use, modify, or distribute
#  this file you indicate that you have read the license and
#  understand and accept it fully.

# The parent class is contained in file `formatter.py'.


from sources import *
from content import *
from formatter import *

import time


# The following strings define the HTML header used by all generated pages.
html_header_1 = """\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"https://www.w3.org/TR/html4/loose.dtd";>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>\
"""

html_header_2 = """\
 API Reference</title>
<style type="text/css">
  a:link { color: #0000EF; }
  a:visited { color: #51188E; }
  a:hover { color: #FF0000; }

  body { font-family: Verdana, Geneva, Arial, Helvetica, serif;
         color: #000000;
         background: #FFFFFF;
         width: 87%;
         margin: auto; }

  div.section { width: 75%;
                margin: auto; }
  div.section hr { margin: 4ex 0 1ex 0; }
  div.section h4 { background-color: #EEEEFF;
                   font-size: medium;
                   font-style: oblique;
                   font-weight: bold;
                   margin: 3ex 0 1.5ex 9%;
                   padding: 0.3ex 0 0.3ex 1%; }
  div.section p { margin: 1.5ex 0 1.5ex 10%; }
  div.section pre { margin: 3ex 0 3ex 9%;
                    background-color: #D6E8FF;
                    padding: 2ex 0 2ex 1%; }
  div.section table.fields { width: 90%;
                             margin: 1.5ex 0 1.5ex 10%; }
  div.section table.toc { width: 95%;
                          margin: 1.5ex 0 1.5ex 5%; }
  div.timestamp { text-align: center;
                  font-size: 69%;
                  margin: 1.5ex 0 1.5ex 0; }

  h1 { text-align: center; }
  h3 { font-size: medium;
       margin: 4ex 0 1.5ex 0; }

  p { text-align: justify; }

  pre.colored { color: blue; }

  span.keyword { font-family: monospace;
                 text-align: left;
                 white-space: pre;
                 color: darkblue; }

  table.fields td.val { font-weight: bold;
                        text-align: right;
                        width: 30%;
                        vertical-align: baseline;
                        padding: 1ex 1em 1ex 0; }
  table.fields td.desc { vertical-align: baseline;
                         padding: 1ex 0 1ex 1em; }
  table.fields td.desc p:first-child { margin: 0; }
  table.fields td.desc p { margin: 1.5ex 0 0 0; }
  table.index { margin: 6ex auto 6ex auto;
                border: 0;
                border-collapse: separate;
                border-spacing: 1em 0.3ex; }
  table.index tr { padding: 0; }
  table.index td { padding: 0; }
  table.index-toc-link { width: 100%;
                         border: 0;
                         border-spacing: 0;
                         margin: 1ex 0 1ex 0; }
  table.index-toc-link td.left { padding: 0 0.5em 0 0.5em;
                                 font-size: 83%;
                                 text-align: left; }
  table.index-toc-link td.middle { padding: 0 0.5em 0 0.5em;
                                   font-size: 83%;
                                   text-align: center; }
  table.index-toc-link td.right { padding: 0 0.5em 0 0.5em;
                                  font-size: 83%;
                                  text-align: right; }
  table.synopsis { margin: 6ex auto 6ex auto;
                   border: 0;
                   border-collapse: separate;
                   border-spacing: 2em 0.6ex; }
  table.synopsis tr { padding: 0; }
  table.synopsis td { padding: 0; }
  table.toc td.link { width: 30%;
                      text-align: right;
                      vertical-align: baseline;
                      padding: 1ex 1em 1ex 0; }
  table.toc td.desc { vertical-align: baseline;
                      padding: 1ex 0 1ex 1em;
                      text-align: left; }
  table.toc td.desc p:first-child { margin: 0;
                                    text-align: left; }
  table.toc td.desc p { margin: 1.5ex 0 0 0;
                        text-align: left; }

</style>
</head>
<body>
"""

html_header_3l = """
<table class="index-toc-link"><tr><td class="left">[<a href="\
"""

html_header_3r = """
<table class="index-toc-link"><tr><td class="right">[<a href="\
"""

html_header_4 = """\
">Index</a>]</td><td class="right">[<a href="\
"""

html_header_5t = """\
">TOC</a>]</td></tr></table>
<h1>\
"""

html_header_5i = """\
">Index</a>]</td></tr></table>
<h1>\
"""

html_header_6 = """\
 API Reference</h1>
"""


# The HTML footer used by all generated pages.
html_footer = """\
</body>
</html>\
"""

# The header and footer used for each section.
section_title_header1 = '<h1 id="'
section_title_header2 = '">'
section_title_footer = "</h1>"

# The header and footer used for code segments.
code_header = '<pre class="colored">'
code_footer = '</pre>'

# Paragraph header and footer.
para_header = "<p>"
para_footer = "</p>"

# Block header and footer.
block_header        = '<div class="section">'
block_footer_start  = """\
<hr>
<table class="index-toc-link"><tr><td class="left">[<a href="\
"""
block_footer_middle = """\
">Index</a>]</td>\
<td class="middle">[<a href="#">Top</a>]</td>\
<td class="right">[<a href="\
"""
block_footer_end    = """\
">TOC</a>]</td></tr></table></div>
"""

# Description header/footer.
description_header = ""
description_footer = ""

# Marker header/inter/footer combination.
marker_header = "<h4>"
marker_inter  = "</h4>"
marker_footer = ""

# Header location header/footer.
header_location_header = "<p>"
header_location_footer = "</p>"

# Source code extracts header/footer.
source_header = "<pre>"
source_footer = "</pre>"

# Chapter header/inter/footer.
chapter_header = """\
<div class="section">
<h2>\
"""
chapter_inter  = '</h2>'
chapter_footer = '</div>'

# Index footer.
index_footer_start = """\
<hr>
<table class="index-toc-link"><tr><td class="right">[<a href="\
"""
index_footer_end = """\
">TOC</a>]</td></tr></table>
"""

# TOC footer.
toc_footer_start = """\
<hr>
<table class="index-toc-link"><tr><td class="left">[<a href="\
"""
toc_footer_end = """\
">Index</a>]</td></tr></table>
"""


# Source language keyword coloration and styling.
keyword_prefix = '<span class="keyword">'
keyword_suffix = '</span>'

section_synopsis_header = '<h2>Synopsis</h2>'
section_synopsis_footer = ''


# Translate a single line of source to HTML.  This converts `<', `>', and
# `&' into `&lt;',`&gt;', and `&amp;'.
#
def  html_quote( line ):
    result = line.replace( "&", "&amp;" )
    result = result.replace( "<", "&lt;"  )
    result = result.replace( ">", "&gt;"  )
    return result


################################################################
##
##  HTML FORMATTER CLASS
##
class  HtmlFormatter( Formatter ):

    def  __init__( self, processor, project_title, file_prefix ):
        Formatter.__init__( self, processor )

        global html_header_1
        global html_header_2
        global html_header_3l, html_header_3r
        global html_header_4
        global html_header_5t, html_header_5i
        global html_header_6
        global html_footer

        if file_prefix:
            file_prefix = file_prefix + "-"
        else:
            file_prefix = ""

        self.headers       = processor.headers
        self.project_title = project_title
        self.file_prefix   = file_prefix
        self.html_header   = (
          html_header_1 + project_title
          + html_header_2
          + html_header_3l + file_prefix + "index.html"
          + html_header_4 + file_prefix + "toc.html"
          + html_header_5t + project_title
          + html_header_6 )
        self.html_index_header = (
          html_header_1 + project_title
          + html_header_2
          + html_header_3r + file_prefix + "toc.html"
          + html_header_5t + project_title
          + html_header_6 )
        self.html_toc_header = (
          html_header_1 + project_title
          + html_header_2
          + html_header_3l + file_prefix + "index.html"
          + html_header_5i + project_title
          + html_header_6 )
        self.html_footer = (
          '<div class="timestamp">generated on '
          + time.asctime( time.localtime( time.time() ) )
          + "</div>" + html_footer )

        self.columns = 3

    def  make_section_url( self, section ):
        return self.file_prefix + section.name + ".html"

    def  make_block_url( self, block, name = None ):
        if name == None:
            name = block.name

        try:
            section_url = self.make_section_url( block.section )
        except:
            # we already have a section
            section_url = self.make_section_url( block )

        return section_url + "#" + name

    def  make_html_word( self, word ):
        """Analyze a simple word to detect cross-references and markup."""
        # handle cross-references
        m = re_crossref.match( word )
        if m:
            try:
                name = m.group( 'name' )
                rest = m.group( 'rest' )
                block = self.identifiers[name]
                url   = self.make_block_url( block )
                # display `foo[bar]' as `foo'
                name = re.sub( r'\[.*\]', '', name )
                # normalize url, following RFC 3986
                url = string.replace( url, "[", "(" )
                url = string.replace( url, "]", ")" )

                try:
                    # for sections, display title
                    url = ( '&lsquo;<a href="' + url + '">'
                            + block.title + '</a>&rsquo;'
                            + rest )
                except:
                    url = ( '<a href="' + url + '">'
                            + name + '</a>'
                            + rest )

                return url
            except:
                # we detected a cross-reference to an unknown item
                sys.stderr.write( "WARNING: undefined cross reference"
                                  + " '" + name + "'.\n" )
                return '?' + name + '?' + rest

        # handle markup for italic and bold
        m = re_italic.match( word )
        if m:
            name = m.group( 1 )
            rest = m.group( 2 )
            return '<i>' + name + '</i>' + rest

        m = re_bold.match( word )
        if m:
            name = m.group( 1 )
            rest = m.group( 2 )
            return '<b>' + name + '</b>' + rest

        return html_quote( word )

    def  make_html_para( self, words ):
        """Convert words of a paragraph into tagged HTML text.  Also handle
           cross references."""
        line = ""
        if words:
            line = self.make_html_word( words[0] )
            for word in words[1:]:
                line = line + " " + self.make_html_word( word )
            # handle hyperlinks
            line = re_url.sub( r'<a href="\1">\1</a>', line )
            # convert `...' quotations into real left and right single quotes
            line = re.sub( r"(^|\W)`(.*?)'(\W|$)",
                           r'\1&lsquo;\2&rsquo;\3',
                           line )
            # convert tilde into non-breakable space
            line = line.replace( "~", "&nbsp;" )

        return para_header + line + para_footer

    def  make_html_code( self, lines ):
        """Convert a code sequence to HTML."""
        line = code_header + '\n'
        for l in lines:
            line = line + html_quote( l ).rstrip() + '\n'

        return line + code_footer

    def  make_html_items( self, items ):
        """Convert a field's content into HTML."""
        lines = []
        for item in items:
            if item.lines:
                lines.append( self.make_html_code( item.lines ) )
            else:
                lines.append( self.make_html_para( item.words ) )

        return '\n'.join( lines )

    def  print_html_items( self, items ):
        print( self.make_html_items( items ) )

    def  print_html_field( self, field ):
        if field.name:
            print( '<table><tr valign="top"><td><b>'
                   + field.name
                   + "</b></td><td>" )

        print( self.make_html_items( field.items ) )

        if field.name:
            print( "</td></tr></table>" )

    def  html_source_quote( self, line, block_name = None ):
        result = ""
        while line:
            m = re_source_crossref.match( line )
            if m:
                name   = m.group( 2 )
                prefix = html_quote( m.group( 1 ) )
                length = len( m.group( 0 ) )

                if name == block_name:
                    # this is the current block name, if any
                    result = result + prefix + '<b>' + name + '</b>'
                elif re_source_keywords.match( name ):
                    # this is a C keyword
                    result = ( result + prefix
                               + keyword_prefix + name + keyword_suffix )
                elif name in self.identifiers:
                    # this is a known identifier
                    block = self.identifiers[name]
                    id = block.name

                    # link to a field ID if possible
                    try:
                      for markup in block.markups:
                          if markup.tag == 'values':
                              for field in markup.fields:
                                  if field.name:
                                      id = name

                      result = ( result + prefix
                                 + '<a href="'
                                 + self.make_block_url( block, id )
                                 + '">' + name + '</a>' )
                    except:
                      # sections don't have `markups'; however, we don't
                      # want references to sections here anyway
                      result = result + html_quote( line[:length] )

                else:
                    result = result + html_quote( line[:length] )

                line = line[length:]
            else:
                result = result + html_quote( line )
                line   = []

        return result

    def  print_html_field_list( self, fields ):
        print( '<table class="fields">' )
        for field in fields:
            print( '<tr><td class="val" id="' + field.name + '">'
                   + field.name
                   + '</td><td class="desc">' )
            self.print_html_items( field.items )
            print( "</td></tr>" )
        print( "</table>" )

    def  print_html_markup( self, markup ):
        table_fields = []
        for field in markup.fields:
            if field.name:
                # We begin a new series of field or value definitions.  We
                # record them in the `table_fields' list before outputting
                # all of them as a single table.
                table_fields.append( field )
            else:
                if table_fields:
                    self.print_html_field_list( table_fields )
                    table_fields = []

                self.print_html_items( field.items )

        if table_fields:
            self.print_html_field_list( table_fields )

    #
    # formatting the index
    #
    def  index_enter( self ):
        print( self.html_index_header )
        self.index_items = {}

    def  index_name_enter( self, name ):
        block = self.identifiers[name]
        url   = self.make_block_url( block )
        self.index_items[name] = url

    def  index_exit( self ):
        # `block_index' already contains the sorted list of index names
        count = len( self.block_index )
        rows  = ( count + self.columns - 1 ) // self.columns

        print( '<table class="index">' )
        for r in range( rows ):
            line = "<tr>"
            for c in range( self.columns ):
                i = r + c * rows
                if i < count:
                    bname = self.block_index[r + c * rows]
                    url   = self.index_items[bname]
                    # display `foo[bar]' as `foo (bar)'
                    bname = bname.replace(  "[", " (" )
                    bname = bname.replace(  "]", ")"  )
                    # normalize url, following RFC 3986
                    url = url.replace( "[", "(" )
                    url = url.replace( "]", ")" )
                    line  = ( line + '<td><a href="' + url + '">'
                              + bname + '</a></td>' )
                else:
                    line = line + '<td></td>'
            line = line + "</tr>"
            print( line )

        print( "</table>" )

        print( index_footer_start
               + self.file_prefix + "toc.html"
               + index_footer_end )

        print( self.html_footer )

        self.index_items = {}

    def  index_dump( self, index_filename = None ):
        if index_filename == None:
            index_filename = self.file_prefix + "index.html"

        Formatter.index_dump( self, index_filename )

    #
    # formatting the table of contents
    #
    def  toc_enter( self ):
        print( self.html_toc_header )
        print( "<h1>Table of Contents</h1>" )

    def  toc_chapter_enter( self, chapter ):
        print( chapter_header + " ".join( chapter.title ) + chapter_inter )
        print( '<table class="toc">' )

    def  toc_section_enter( self, section ):
        print( '<tr><td class="link">'
               + '<a href="' + self.make_section_url( section ) + '">'
               + section.title + '</a></td><td class="desc">' )
        print( self.make_html_para( section.abstract ) )

    def  toc_section_exit( self, section ):
        print( "</td></tr>" )

    def  toc_chapter_exit( self, chapter ):
        print( "</table>" )
        print( chapter_footer )

    def  toc_index( self, index_filename ):
        print( chapter_header
               + '<a href="' + index_filename + '">Global Index</a>'
               + chapter_inter + chapter_footer )

    def  toc_exit( self ):
        print( toc_footer_start
               + self.file_prefix + "index.html"
               + toc_footer_end )

        print( self.html_footer )

    def  toc_dump( self, toc_filename = None, index_filename = None ):
        if toc_filename == None:
            toc_filename = self.file_prefix + "toc.html"

        if index_filename == None:
            index_filename = self.file_prefix + "index.html"

        Formatter.toc_dump( self, toc_filename, index_filename )

    #
    # formatting sections
    #
    def  section_enter( self, section ):
        print( self.html_header )

        print( section_title_header1 + section.name + section_title_header2
               + section.title
               + section_title_footer )

        maxwidth = 0
        for b in section.blocks.values():
            if len( b.name ) > maxwidth:
                maxwidth = len( b.name )

        width = 70  # XXX magic number
        if maxwidth > 0:
            # print section synopsis
            print( section_synopsis_header )
            print( '<table class="synopsis">' )

            columns = width // maxwidth
            if columns < 1:
                columns = 1

            count = len( section.block_names )
            # don't handle last entry if it is empty
            if section.block_names[-1] == "/empty/":
                count -= 1
            rows  = ( count + columns - 1 ) // columns

            for r in range( rows ):
                line = "<tr>"
                for c in range( columns ):
                    i = r + c * rows
                    line = line + '<td>'
                    if i < count:
                        name = section.block_names[i]
                        if name == "/empty/":
                            # it can happen that a complete row is empty, and
                            # without a proper `filler' the browser might
                            # collapse the row to a much smaller height (or
                            # even omit it completely)
                            line = line + "&nbsp;"
                        else:
                            url = name
                            # display `foo[bar]' as `foo'
                            name = re.sub( r'\[.*\]', '', name )
                            # normalize url, following RFC 3986
                            url = url.replace( "[", "(" )
                            url = url.replace( "]", ")" )
                            line = ( line + '<a href="#' + url + '">'
                                     + name + '</a>' )

                    line = line + '</td>'
                line = line + "</tr>"
                print( line )

            print( "</table>" )
            print( section_synopsis_footer )

        print( description_header )
        print( self.make_html_items( section.description ) )
        print( description_footer )

    def  block_enter( self, block ):
        print( block_header )

        # place html anchor if needed
        if block.name:
            url = block.name
            # display `foo[bar]' as `foo'
            name = re.sub( r'\[.*\]', '', block.name )
            # normalize url, following RFC 3986
            url = url.replace( "[", "(" )
            url = url.replace( "]", ")" )
            print( '<h3 id="' + url + '">' + name + '</h3>' )

        # dump the block C source lines now
        if block.code:
            header = ''
            for f in self.headers.keys():
                if block.source.filename.find( f ) >= 0:
                    header = self.headers[f] + ' (' + f + ')'
                    break

#           if not header:
#               sys.stderr.write(
#                 "WARNING: No header macro for"
#                 + " '" + block.source.filename + "'.\n" )

            if header:
                print( header_location_header
                       + 'Defined in ' + header + '.'
                       + header_location_footer )

            print( source_header )
            for l in block.code:
                print( self.html_source_quote( l, block.name ) )
            print( source_footer )

    def  markup_enter( self, markup, block ):
        if markup.tag == "description":
            print( description_header )
        else:
            print( marker_header + markup.tag + marker_inter )

        self.print_html_markup( markup )

    def  markup_exit( self, markup, block ):
        if markup.tag == "description":
            print( description_footer )
        else:
            print( marker_footer )

    def  block_exit( self, block ):
        print( block_footer_start + self.file_prefix + "index.html"
               + block_footer_middle + self.file_prefix + "toc.html"
               + block_footer_end )

    def  section_exit( self, section ):
        print( html_footer )

    def  section_dump_all( self ):
        for section in self.sections:
            self.section_dump( section,
                               self.file_prefix + section.name + '.html' )

# eof
#
#  utils.py
#
#    Auxiliary functions for the `docmaker' tool (library file).
#
#  Copyright 2002-2018 by
#  David Turner.
#
#  This file is part of the FreeType project, and may only be used,
#  modified, and distributed under the terms of the FreeType project
#  license, LICENSE.TXT.  By continuing to use, modify, or distribute
#  this file you indicate that you have read the license and
#  understand and accept it fully.


import string, sys, os, glob, itertools


# current output directory
#
output_dir = None


# A function that generates a sorting key.  We want lexicographical order
# (primary key) except that capital letters are sorted before lowercase
# ones (secondary key).
#
# The primary key is implemented by lowercasing the input.  The secondary
# key is simply the original data appended, character by character.  For
# example, the sort key for `FT_x' is `fFtT__xx', while the sort key for
# `ft_X' is `fftt__xX'.  Since ASCII codes of uppercase letters are
# numerically smaller than the codes of lowercase letters, `fFtT__xx' gets
# sorted before `fftt__xX'.
#
def  index_key( s ):
    return " ".join( itertools.chain( *zip( s.lower(), s ) ) )


# Sort `input_list', placing the elements of `order_list' in front.
#
def  sort_order_list( input_list, order_list ):
    new_list = order_list[:]
    for id in input_list:
        if not id in order_list:
            new_list.append( id )
    return new_list


# Divert standard output to a given project documentation file.  Use
# `output_dir' to determine the filename location if necessary and save the
# old stdout handle in a tuple that is returned by this function.
#
def  open_output( filename ):
    global output_dir

    if output_dir and output_dir != "":
        filename = output_dir + os.sep + filename

    old_stdout = sys.stdout
    new_file   = open( filename, "w" )
    sys.stdout = new_file

    return ( new_file, old_stdout )


# Close the output that was returned by `open_output'.
#
def  close_output( output ):
    output[0].close()
    sys.stdout = output[1]


# Check output directory.
#
def  check_output():
    global output_dir
    if output_dir:
        if output_dir != "":
            if not os.path.isdir( output_dir ):
                sys.stderr.write( "argument"
                                  + " '" + output_dir + "' "
                                  + "is not a valid directory\n" )
                sys.exit( 2 )
        else:
            output_dir = None


def  file_exists( pathname ):
    """Check that a given file exists."""
    result = 1
    try:
        file = open( pathname, "r" )
        file.close()
    except:
        result = None
        sys.stderr.write( pathname + " couldn't be accessed\n" )

    return result


def  make_file_list( args = None ):
    """Build a list of input files from command-line arguments."""
    file_list = []
    # sys.stderr.write( repr( sys.argv[1 :] ) + '\n' )

    if not args:
        args = sys.argv[1:]

    for pathname in args:
        if pathname.find('*') >= 0:
            newpath = glob.glob( pathname )
            newpath.sort()  # sort files -- this is important because
                            # of the order of files
        else:
            newpath = [pathname]

        file_list.extend( newpath )

    if len( file_list ) == 0:
        file_list = None
    else:
        # now filter the file list to remove non-existing ones
        file_list = filter( file_exists, file_list )

    return file_list

# eof
_______________________________________________
Freetype-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to