Edit

kc3-lang/freetype/src/tools/docmaker.py

Branch :

  • Show log

    Commit

  • Author : David Turner
    Date : 2002-01-07 12:09:51
    Hash : 32ee45e0
    Message : fixed html quoting in DocMaker

  • src/tools/docmaker.py
  • #!/usr/bin/env python
    #
    #  DocMaker 0.1 (c) 2000-2001 David Turner <david@freetype.org>
    #
    #  DocMaker is a very simple program used to generate the API Reference
    #  of programs by extracting comments from source files, and generating
    #  the equivalent HTML documentation.
    #
    #  DocMaker is very similar to other tools like Doxygen, with the
    #  following differences:
    #
    #    - It is written in Python (so it is slow, but easy to maintain and
    #      improve).
    #
    #    - The comment syntax used by DocMaker is simpler and makes for
    #      clearer comments.
    #
    #  Of course, it doesn't have all the goodies of most similar tools,
    #  (e.g. C++ class hierarchies), but hey, it is only 2000 lines of
    #  Python.
    #
    #  DocMaker is mainly used to generate the API references of several
    #  FreeType packages.
    #
    #   - David
    #
    
    import fileinput, sys, os, time, string, glob, getopt
    
    # The Project's title.  This can be overridden from the command line with
    # the options "-t" or "--title".
    #
    project_title = "Project"
    
    # The project's filename prefix.  This can be set from the command line with
    # the options "-p" or "--prefix"
    #
    project_prefix = ""
    
    # The project's documentation output directory.  This can be set from the
    # command line with the options "-o" or "--output".
    #
    output_dir = None
    
    
    # The following defines the HTML header used by all generated pages.
    #
    html_header_1 = """\
    <html>
    <header>
    <title>"""
    
    html_header_2= """ API Reference</title>
    <basefont face="Verdana,Geneva,Arial,Helvetica">
    <style content="text/css">
      P { text-align=justify }
      H1 { text-align=center }
      LI { text-align=justify }
    </style>
    </header>
    <body text=#000000
          bgcolor=#FFFFFF
          link=#0000EF
          vlink=#51188E
          alink=#FF0000>
    <center><h1>"""
    
    html_header_3=""" API Reference</h1></center>
    """
    
    # This is recomputed later when the project title changes.
    #
    html_header = html_header_1 + project_title + html_header_2 + project_title + html_header_3
    
    
    # The HTML footer used by all generated pages.
    #
    html_footer = """\
    </body>
    </html>"""
    
    # The header and footer used for each section.
    #
    section_title_header = "<center><h1>"
    section_title_footer = "</h1></center>"
    
    # The header and footer used for code segments.
    #
    code_header = "<font color=blue><pre>"
    code_footer = "</pre></font>"
    
    # Paragraph header and footer.
    #
    para_header = "<p>"
    para_footer = "</p>"
    
    # Block header and footer.
    #
    block_header = "<center><table width=75%><tr><td>"
    block_footer = "</td></tr></table><hr width=75%></center>"
    
    # Description header/footer.
    #
    description_header = "<center><table width=87%><tr><td>"
    description_footer = "</td></tr></table></center><br>"
    
    # Marker header/inter/footer combination.
    #
    marker_header = "<center><table width=87% cellpadding=5><tr bgcolor=#EEEEFF><td><em><b>"
    marker_inter  = "</b></em></td></tr><tr><td>"
    marker_footer = "</td></tr></table></center>"
    
    # Source code extracts header/footer.
    #
    source_header = "<center><table width=87%><tr bgcolor=#D6E8FF width=100%><td><pre>"
    source_footer = "</pre></table></center><br>"
    
    # Chapter header/inter/footer.
    #
    chapter_header = "<center><table width=75%><tr><td><h2>"
    chapter_inter  = "</h2><ul>"
    chapter_footer = "</ul></td></tr></table></center>"
    
    current_section = None
    
    
    # This function is used to sort the index.  It is a simple lexicographical
    # sort, except that it places capital letters before lowercase ones.
    #
    def index_sort( s1, s2 ):
        if not s1:
            return -1
    
        if not s2:
            return 1
    
        l1 = len( s1 )
        l2 = len( s2 )
        m1 = string.lower( s1 )
        m2 = string.lower( s2 )
    
        for i in range( l1 ):
            if i >= l2 or m1[i] > m2[i]:
                return 1
    
            if m1[i] < m2[i]:
                return -1
    
            if s1[i] < s2[i]:
                return -1
    
            if s1[i] > s2[i]:
                return 1
    
        if l2 > l1:
            return -1
    
        return 0
    
    
    # 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
    
    
    # Translate a single line of source to HTML.  This will convert
    # a "<" into "&lt.", ">" into "&gt.", etc.
    #
    def html_quote( line ):
        result = string.replace( line,   "&", "&amp;" )
        result = string.replace( result, "<", "&lt;" )
        result = string.replace( result, ">", "&gt;" )
        return result
    
    # same as 'html_quote', but ignores left and right brackets
    #
    def html_quote0( line ):
        return string.replace( line, "&", "&amp;" )
    
    
    # Open the standard output to a given project documentation file.  Use
    # "output_dir" to determine the filename location if necessary and save the
    # old stdout 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 "close_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" )
                    sys.exit( 2 )
            else:
                output_dir = None
    
    
    def compute_time_html( ):
        global html_footer
        time_string = time.asctime( time.localtime( time.time() ) )
        html_footer = "<p><center><font size=""-2"">generated on " + time_string + "</font></p></center>" + html_footer
    
    # The FreeType 2 reference is extracted from the source files.  These
    # contain various comment blocks that follow one of the following formats:
    #
    #  /**************************
    #   *
    #   *  FORMAT1
    #   *
    #   *
    #   *
    #   *
    #   *************************/
    #
    #  /**************************/
    #  /*                        */
    #  /*  FORMAT2               */
    #  /*                        */
    #  /*                        */
    #  /*                        */
    #  /*                        */
    #
    #  /**************************/
    #  /*                        */
    #  /*  FORMAT3               */
    #  /*                        */
    #  /*                        */
    #  /*                        */
    #  /*                        */
    #  /**************************/
    #
    # Each block contains a list of markers; each one can be followed by
    # some arbitrary text or a list of fields.  Here an example:
    #
    #    <Struct>
    #       MyStruct
    #
    #    <Description>
    #       this structure holds some data
    #
    #    <Fields>
    #       x :: horizontal coordinate
    #       y :: vertical coordinate
    #
    #
    # This example defines three markers: 'Struct', 'Description' & 'Fields'.
    # The first two markers contain arbitrary text, while the last one contains
    # a list of fields.
    #
    # Each field is simply of the format:  WORD :: TEXT...
    #
    # Note that typically each comment block is followed by some source code
    # declaration that may need to be kept in the reference.
    #
    # Note that markers can alternatively be written as "@MARKER:" instead of
    # "<MARKER>".  All marker identifiers are converted to lower case during
    # parsing in order to simply sorting.
    #
    # We associate with each block the following source lines that do not begin
    # with a comment.  For example, the following:
    #
    #   /**********************************
    #    *
    #    * <mytag>  blabla
    #    *
    #    */
    #
    #   bla_bla_bla
    #   bilip_bilip
    #
    #   /* - this comment acts as a separator - */
    #
    #   blo_blo_blo
    #
    #
    # will only keep the first two lines of sources with
    # the "blabla" block.
    #
    # However, the comment will be kept, with following source lines if it
    # contains a starting '#' or '@' as in:
    #
    #   /*@.....*/
    #   /*#.....*/
    #   /* @.....*/
    #   /* #.....*/
    #
    
    
    
    #############################################################################
    #
    # 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 = 0 ):
            self.lines  = []
            self.margin = margin
    
    
        def add( self, line ):
            # remove margin whitespace
            #
            if string.strip( line[: self.margin] ) == "":
                line = line[self.margin :]
            self.lines.append( line )
    
    
        def dump( self ):
            for line in self.lines:
                print "--" + line
            print ""
    
    
        def get_identifier( self ):
            # this function should never be called
            #
            return "UNKNOWN_CODE_IDENTIFIER!"
    
    
        def dump_html( self, identifiers = None ):
            # clean the last empty lines
            #
            l = len( self.lines ) - 1
            while l > 0 and string.strip( self.lines[l - 1] ) == "":
                l = l - 1
    
            # The code footer should be directly appended to the last code
            # line to avoid an additional blank line.
            #
            print code_header,
            for line in self.lines[0 : l+1]:
                print '\n' + html_quote(line),
            print code_footer,
    
    
    
    #############################################################################
    #
    # The DocParagraph is used to store text paragraphs.
    # 'self.words' is simply a list of words for the paragraph.
    #
    # The paragraph is filled line by line by the parser.
    #
    class DocParagraph:
    
        def __init__( self ):
            self.words = []
    
    
        def add( self, line ):
            # Get rid of unwanted spaces in the paragraph.
            #
            # The following two lines are the same as
            #
            #   self.words.extend( string.split( line ) )
            #
            # but older Python versions don't have the `extend' attribute.
            #
            last = len( self.words )
            self.words[last : last] = string.split( line )
    
    
        # This function is used to retrieve the first word of a given
        # paragraph.
        #
        def get_identifier( self ):
            if self.words:
                return self.words[0]
    
            # should never happen
            #
            return "UNKNOWN_PARA_IDENTIFIER!"
    
    
        def get_words( self ):
            return self.words[:]
    
    
        def dump( self, identifiers = None ):
            max_width = 50
            cursor    = 0
            line      = ""
            extra     = None
            alphanum  = string.lowercase + string.uppercase + string.digits + '_'
    
            for word in self.words:
                # process cross references if needed
                #
                if identifiers and word and word[0] == '@':
                    word = word[1 :]
    
                    # we need to find non-alphanumeric characters
                    #
                    l = len( word )
                    i = 0
                    while i < l and word[i] in alphanum:
                        i = i + 1
    
                    if i < l:
                        extra = word[i :]
                        word  = word[0 : i]
    
                    block = identifiers.get( word )
                    if block:
                        word = '<a href="' + block.html_address() + '">' + word + '</a>'
                    else:
                        word = '?' + word
    
                if cursor + len( word ) + 1 > max_width:
                    print html_quote0(line)
                    cursor = 0
                    line   = ""
    
                line = line + word
                if not extra:
                    line = line + " "
    
                cursor = cursor + len( word ) + 1
    
    
                # Handle trailing periods, commas, etc. at the end of cross
                # references.
                #
                if extra:
                    if cursor + len( extra ) + 1 > max_width:
                        print html_quote0(line)
                        cursor = 0
                        line   = ""
    
                    line   = line + extra + " "
                    cursor = cursor + len( extra ) + 1
                    extra  = None
    
            if cursor > 0:
                print html_quote0(line)
    
            # print "