[docmaker] Formatting, copyright, improved documentation.

* src/tools/docmaker/*: No code changes besides trivial
modifications.
This commit is contained in:
Werner Lemberg 2014-11-24 06:44:45 +01:00
parent f1094c0951
commit a7a4207d10
7 changed files with 422 additions and 271 deletions

View File

@ -1,3 +1,10 @@
2014-11-24 Werner Lemberg <wl@gnu.org>
[docmaker] Formatting, copyright, improved documentation.
* src/tools/docmaker/*: No code changes besides trivial
modifications.
2014-11-22 Werner Lemberg <wl@gnu.org> 2014-11-22 Werner Lemberg <wl@gnu.org>
[bdf] Fix Savannah bug #43660. [bdf] Fix Savannah bug #43660.

View File

@ -1,57 +1,81 @@
# Content (c) 2002, 2004, 2006-2009, 2012, 2013
# David Turner <david@freetype.org>
# #
# This file contains routines used to parse the content of documentation # content.py
# comment blocks and build more structured objects out of them. #
# Parse comment blocks to build content blocks (library file).
#
# Copyright 2002, 2004, 2006-2009, 2012-2014 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 sources import *
from utils import * from utils import *
import string, re import string, re
# this regular expression is used to detect code sequences. these
# are simply code fragments embedded in '{' and '}' like in:
# #
# { # Regular expressions to detect code sequences. `Code sequences' are simply
# x = y + z; # code fragments embedded in '{' and '}', as demonstrated in the following
# if ( zookoo == 2 ) # example.
# {
# foobar();
# }
# }
# #
# note that indentation of the starting and ending accolades must be # {
# exactly the same. the code sequence can contain accolades at greater # x = y + z;
# indentation # 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_start = re.compile( r"(\s*){\s*$" )
re_code_end = re.compile( r"(\s*)}\s*$" ) re_code_end = re.compile( r"(\s*)}\s*$" )
# this regular expression is used to isolate identifiers from #
# other text # A regular expression to isolate identifiers from other text.
# #
re_identifier = re.compile( r'((?:\w|-)*)' ) re_identifier = re.compile( r'((?:\w|-)*)' )
# we collect macros ending in `_H'; while outputting the object data, we use #
# this info together with the object's file location to emit the appropriate # We collect macro names ending in `_H' (group 1), as defined in
# header file macro and name before the object itself # `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,}<(.*)>' ) re_header_macro = re.compile( r'^#define\s{1,}(\w{1,}_H)\s{1,}<(.*)>' )
############################################################################# ################################################################
# ##
# The DocCode class is used to store source code lines. ## DOC CODE CLASS
# ##
# 'self.lines' contains a set of source code lines that will be dumped as ## The `DocCode' class is used to store source code lines.
# HTML in a <PRE> tag. ##
# ## `self.lines' contains a set of source code lines that will be dumped as
# The object is filled line by line by the parser; it strips the leading ## HTML in a <PRE> tag.
# "margin" space from each input line before storing it in 'self.lines'. ##
# ## 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: class DocCode:
def __init__( self, margin, lines ): def __init__( self, margin, lines ):
@ -77,12 +101,14 @@ class DocCode:
############################################################################# ################################################################
# ##
# The DocPara class is used to store "normal" text paragraph. ## DOC PARA CLASS
# ##
# 'self.words' contains the list of words that make up the paragraph ## `Normal' text paragraphs are stored in the `DocPara' class.
# ##
## `self.words' contains the list of words that make up the paragraph.
##
class DocPara: class DocPara:
def __init__( self, lines ): def __init__( self, lines ):
@ -123,17 +149,18 @@ class DocPara:
return result return result
################################################################
############################################################################# ##
# ## DOC FIELD CLASS
# The DocField class is used to store a list containing either DocPara or ##
# DocCode objects. Each DocField also has an optional "name" which is used ## The `DocField' class stores a list containing either `DocPara' or
# when the object corresponds to a field or value definition ## `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: class DocField:
def __init__( self, name, lines ): def __init__( self, name, lines ):
self.name = name # can be None for normal paragraphs/sources self.name = name # can be `None' for normal paragraphs/sources
self.items = [] # list of items self.items = [] # list of items
mode_none = 0 # start parsing mode mode_none = 0 # start parsing mode
@ -143,14 +170,14 @@ class DocField:
margin = -1 # current code sequence indentation margin = -1 # current code sequence indentation
cur_lines = [] cur_lines = []
# now analyze the markup lines to see if they contain paragraphs, # analyze the markup lines to check whether they contain paragraphs,
# code sequences or fields definitions # code sequences, or fields definitions
# #
start = 0 start = 0
mode = mode_none mode = mode_none
for l in lines: for l in lines:
# are we parsing a code sequence ? # are we parsing a code sequence?
if mode == mode_code: if mode == mode_code:
m = re_code_end.match( l ) m = re_code_end.match( l )
if m and len( m.group( 1 ) ) <= margin: if m and len( m.group( 1 ) ) <= margin:
@ -161,10 +188,10 @@ class DocField:
cur_lines = [] cur_lines = []
mode = mode_none mode = mode_none
else: else:
# nope, continue the code sequence # otherwise continue the code sequence
cur_lines.append( l[margin:] ) cur_lines.append( l[margin:] )
else: else:
# start of code sequence ? # start of code sequence?
m = re_code_start.match( l ) m = re_code_start.match( l )
if m: if m:
# save current lines # save current lines
@ -222,13 +249,29 @@ class DocField:
return result return result
# this regular expression is used to detect field definitions
# #
re_field = re.compile( r"\s*(\w*|\w(\w|\.)*\w)\s*::" ) # 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: class DocMarkup:
def __init__( self, tag, lines ): def __init__( self, tag, lines ):
@ -242,7 +285,7 @@ class DocMarkup:
for l in lines: for l in lines:
m = re_field.match( l ) m = re_field.match( l )
if m: if m:
# we detected the start of a new field definition # We detected the start of a new field definition.
# first, save the current one # first, save the current one
if cur_lines: if cur_lines:
@ -275,7 +318,10 @@ class DocMarkup:
print " " * margin + "</" + self.tag + ">" print " " * margin + "</" + self.tag + ">"
################################################################
##
## DOC CHAPTER CLASS
##
class DocChapter: class DocChapter:
def __init__( self, block ): def __init__( self, block ):
@ -291,7 +337,10 @@ class DocChapter:
self.order = [] self.order = []
################################################################
##
## DOC SECTION CLASS
##
class DocSection: class DocSection:
def __init__( self, name = "Other" ): def __init__( self, name = "Other" ):
@ -327,7 +376,10 @@ class DocSection:
self.block_names = sort_order_list( self.block_names, self.order ) self.block_names = sort_order_list( self.block_names, self.order )
################################################################
##
## CONTENT PROCESSOR CLASS
##
class ContentProcessor: class ContentProcessor:
def __init__( self ): def __init__( self ):
@ -463,7 +515,10 @@ class ContentProcessor:
self.chapters.append( chap ) self.chapters.append( chap )
################################################################
##
## DOC BLOCK CLASS
##
class DocBlock: class DocBlock:
def __init__( self, source, follow, processor ): def __init__( self, source, follow, processor ):

View File

@ -1,16 +1,26 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# DocMaker (c) 2002, 2004, 2008, 2013 David Turner <david@freetype.org> # docmaker.py
# #
# This program is a re-write of the original DocMaker tool used # Convert source code markup to HTML documentation.
# to generate the API Reference of the FreeType font engine
# by converting in-source comments into structured HTML.
# #
# This new version is capable of outputting XML data, as well # Copyright 2002, 2004, 2008, 2013, 2014 by
# as accepts more liberal formatting options. # David Turner.
# #
# It also uses regular expression matching and substitution # This file is part of the FreeType project, and may only be used,
# to speed things significantly. # 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 program is a re-write of the original DocMaker tool used to generate
# the API Reference of the FreeType font rendering engine by converting
# in-source comments into structured HTML.
#
# This new version is capable of outputting XML data as well as accepting
# more liberal formatting options. It also uses regular expression matching
# and substitution to speed up operation significantly.
# #
from sources import * from sources import *
@ -44,8 +54,8 @@ def main( argv ):
global output_dir global output_dir
try: try:
opts, args = getopt.getopt( sys.argv[1:], \ opts, args = getopt.getopt( sys.argv[1:],
"ht:o:p:", \ "ht:o:p:",
["help", "title=", "output=", "prefix="] ) ["help", "title=", "output=", "prefix="] )
except getopt.GetoptError: except getopt.GetoptError:
usage() usage()
@ -56,7 +66,6 @@ def main( argv ):
sys.exit( 1 ) sys.exit( 1 )
# process options # process options
#
project_title = "Project" project_title = "Project"
project_prefix = None project_prefix = None
output_dir = None output_dir = None
@ -90,7 +99,9 @@ def main( argv ):
# process sections # process sections
content_processor.finish() content_processor.finish()
formatter = HtmlFormatter( content_processor, project_title, project_prefix ) formatter = HtmlFormatter( content_processor,
project_title,
project_prefix )
formatter.toc_dump() formatter.toc_dump()
formatter.index_dump() formatter.index_dump()
@ -98,9 +109,7 @@ def main( argv ):
# if called from the command line # if called from the command line
#
if __name__ == '__main__': if __name__ == '__main__':
main( sys.argv ) main( sys.argv )
# eof # eof

View File

@ -1,19 +1,37 @@
# Formatter (c) 2002, 2004, 2007, 2008 David Turner <david@freetype.org>
# #
# formatter.py
#
# Convert parsed content blocks to a structured document (library file).
#
# Copyright 2002, 2004, 2007, 2008, 2014 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 sources import *
from content import * from content import *
from utils import * from utils import *
# 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
# used to output -- you guessed it -- HTML.
#
################################################################
##
## FORMATTER CLASS
##
class Formatter: class Formatter:
def __init__( self, processor ): def __init__( self, processor ):
@ -41,15 +59,17 @@ class Formatter:
def add_identifier( self, name, block ): def add_identifier( self, name, block ):
if self.identifiers.has_key( name ): if self.identifiers.has_key( name ):
# duplicate name! # duplicate name!
sys.stderr.write( \ sys.stderr.write( "WARNING: duplicate definition for"
"WARNING: duplicate definition for '" + name + "' in " + \ + " '" + name + "' "
block.location() + ", previous definition in " + \ + "in " + block.location() + ", "
self.identifiers[name].location() + "\n" ) + "previous definition in "
+ self.identifiers[name].location()
+ "\n" )
else: else:
self.identifiers[name] = block self.identifiers[name] = block
# #
# Formatting the table of contents # formatting the table of contents
# #
def toc_enter( self ): def toc_enter( self ):
pass pass
@ -97,7 +117,7 @@ class Formatter:
close_output( output ) close_output( output )
# #
# Formatting the index # formatting the index
# #
def index_enter( self ): def index_enter( self ):
pass pass
@ -128,7 +148,7 @@ class Formatter:
close_output( output ) close_output( output )
# #
# Formatting a section # formatting a section
# #
def section_enter( self, section ): def section_enter( self, section ):
pass pass

View File

@ -1,62 +1,70 @@
# Sources (c) 2002-2004, 2006-2009, 2012, 2013
# David Turner <david@freetype.org>
# #
# sources.py
# #
# this file contains definitions of classes needed to decompose # Convert source code comments to multi-line blocks (library file).
# C sources files into a series of multi-line "blocks". There are
# two kinds of blocks:
# #
# - normal blocks, which contain source code or ordinary comments # Copyright 2002-2004, 2006-2009, 2012-2014 by
# David Turner.
# #
# - documentation blocks, which have restricted formatting, and # This file is part of the FreeType project, and may only be used,
# whose text always start with a documentation markup tag like # modified, and distributed under the terms of the FreeType project
# "<Function>", "<Type>", etc.. # 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 routines used to process the content of documentation blocks # This library file contains definitions of classes needed to decompose C
# are not contained here, but in "content.py" # source code files into a series of multi-line `blocks'. There are two
# kinds of blocks.
# #
# the classes and methods found here only deal with text parsing # - Normal blocks, which contain source code or ordinary comments.
# and basic documentation block extraction
# #
# - 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, sys, os, string import fileinput, re, sys, os, string
################################################################ ################################################################
## ##
## BLOCK FORMAT PATTERN ## SOURCE BLOCK FORMAT CLASS
## ##
## A simple class containing compiled regular expressions used ## A simple class containing compiled regular expressions to detect
## to detect potential documentation format block comments within ## potential documentation format block comments within C source code.
## C source code
## ##
## note that the 'column' pattern must contain a group that will ## The `column' pattern must contain a group to `unbox' the content of
## be used to "unbox" the content of documentation comment blocks ## documentation comment blocks.
##
## Later on, paragraphs are converted to long lines, which simplifies the
## regular expressions that act upon the text.
## ##
class SourceBlockFormat: class SourceBlockFormat:
def __init__( self, id, start, column, end ): def __init__( self, id, start, column, end ):
"""create a block pattern, used to recognize special documentation blocks""" """Create a block pattern, used to recognize special documentation
blocks."""
self.id = id self.id = id
self.start = re.compile( start, re.VERBOSE ) self.start = re.compile( start, re.VERBOSE )
self.column = re.compile( column, re.VERBOSE ) self.column = re.compile( column, re.VERBOSE )
self.end = re.compile( end, re.VERBOSE ) self.end = re.compile( end, re.VERBOSE )
# #
# format 1 documentation comment blocks look like the following: # Format 1 documentation comment blocks.
# #
# /************************************/ # /************************************/ (at least 2 asterisks)
# /* */ # /* */
# /* */ # /* */
# /* */ # /* */
# /************************************/ # /************************************/ (at least 2 asterisks)
# #
# we define a few regular expressions here to detect them
#
start = r''' start = r'''
\s* # any number of whitespace \s* # any number of whitespace
/\*{2,}/ # followed by '/' and at least two asterisks then '/' /\*{2,}/ # followed by '/' and at least two asterisks then '/'
@ -75,16 +83,13 @@ re_source_block_format1 = SourceBlockFormat( 1, start, column, start )
# #
# format 2 documentation comment blocks look like the following: # Format 2 documentation comment blocks.
# #
# /************************************ (at least 2 asterisks) # /************************************ (at least 2 asterisks)
# * # *
# * (1 asterisk)
# * # *
# * # */ (1 or more asterisks)
# *
# **/ (1 or more asterisks at the end)
#
# we define a few regular expressions here to detect them
# #
start = r''' start = r'''
\s* # any number of whitespace \s* # any number of whitespace
@ -107,47 +112,56 @@ re_source_block_format2 = SourceBlockFormat( 2, start, column, end )
# #
# the list of supported documentation block formats, we could add new ones # The list of supported documentation block formats. We could add new ones
# relatively easily # quite easily.
# #
re_source_block_formats = [re_source_block_format1, re_source_block_format2] re_source_block_formats = [re_source_block_format1, re_source_block_format2]
# #
# the following regular expressions corresponds to markup tags # The following regular expressions correspond to markup tags within the
# within the documentation comment blocks. they're equivalent # documentation comment blocks. They are equivalent despite their different
# despite their different syntax # syntax.
# #
# notice how each markup tag _must_ begin a new line # 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_tag1 = re.compile( r'''\s*<((?:\w|-)*)>''' ) # <xxxx> format
re_markup_tag2 = 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 relatively # The list of supported markup tags. We could add new ones quite easily.
# easily
# #
re_markup_tags = [re_markup_tag1, re_markup_tag2] re_markup_tags = [re_markup_tag1, re_markup_tag2]
# #
# used to detect a cross-reference, after markup tags have been stripped # A regular expression to detect a cross reference, after markup tags have
# been stripped off. Group 1 is the reference, group 2 the rest of the
# line.
#
# A cross reference consists of letters, digits, or characters `-' and `_'.
# #
re_crossref = re.compile( r'@((?:\w|-)*)(.*)' ) # @foo re_crossref = re.compile( r'@((?:\w|-)*)(.*)' ) # @foo
# #
# used to detect italic and bold styles in paragraph text # Two regular expressions to detect italic and bold markup, respectively.
# Group 1 is the markup, group 3 the rest of the line.
#
# Note that the markup is limited to words consisting of letters, digits,
# the character `_', or an apostrophe (but not as the first character).
# #
re_italic = re.compile( r"_(\w(\w|')*)_(.*)" ) # _italic_ re_italic = re.compile( r"_(\w(\w|')*)_(.*)" ) # _italic_
re_bold = re.compile( r"\*(\w(\w|')*)\*(.*)" ) # *bold* re_bold = re.compile( r"\*(\w(\w|')*)\*(.*)" ) # *bold*
# #
# this regular expression code to identify an URL has been taken from # This regular expression code to identify an URL has been taken from
# #
# http://mail.python.org/pipermail/tutor/2002-September/017228.html # http://mail.python.org/pipermail/tutor/2002-September/017228.html
# #
# (with slight modifications) # (with slight modifications).
# #
urls = r'(?:https?|telnet|gopher|file|wais|ftp)' urls = r'(?:https?|telnet|gopher|file|wais|ftp)'
ltrs = r'\w' ltrs = r'\w'
gunk = r'/#~:.?+=&%@!\-' gunk = r'/#~:.?+=&%@!\-'
@ -177,17 +191,22 @@ url = r"""
re_url = re.compile( url, re.VERBOSE | re.MULTILINE ) re_url = re.compile( url, re.VERBOSE | re.MULTILINE )
# #
# used to detect the end of commented source lines # A regular expression that stops collection of comments for the current
# block.
# #
re_source_sep = re.compile( r'\s*/\*\s*\*/' ) re_source_sep = re.compile( r'\s*/\*\s*\*/' ) # /* */
# #
# used to perform cross-reference within source output # 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*)' ) re_source_crossref = re.compile( r'(\W*)(\w*)' )
# #
# a list of reserved source keywords # A regular expression that matches a list of reserved C source keywords.
# #
re_source_keywords = re.compile( '''\\b ( typedef | re_source_keywords = re.compile( '''\\b ( typedef |
struct | struct |
@ -215,24 +234,16 @@ re_source_keywords = re.compile( '''\\b ( typedef |
## ##
## SOURCE BLOCK CLASS ## SOURCE BLOCK CLASS
## ##
## A SourceProcessor is in charge of reading a C source file ## There are two important fields in a `SourceBlock' object.
## and decomposing it into a series of different "SourceBlocks".
## each one of these blocks can be made of the following data:
## ##
## - A documentation comment block that starts with "/**" and ## self.lines
## whose exact format will be discussed later ## A list of text lines for the corresponding block.
## ##
## - normal sources lines, including comments ## self.content
## ## For documentation comment blocks only, this is the block content
## the important fields in a text block are the following ones: ## that has been `unboxed' from its decoration. This is `None' for all
## ## other blocks (i.e., sources or ordinary comments with no starting
## self.lines : a list of text lines for the corresponding block ## markup tag)
##
## 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: class SourceBlock:
@ -269,7 +280,7 @@ class SourceBlock:
def location( self ): def location( self ):
return "(" + self.filename + ":" + repr( self.lineno ) + ")" return "(" + self.filename + ":" + repr( self.lineno ) + ")"
# debugging only - not used in normal operations # debugging only -- not used in normal operations
def dump( self ): def dump( self ):
if self.content: if self.content:
print "{{{content start---" print "{{{content start---"
@ -286,39 +297,38 @@ class SourceBlock:
print line print line
################################################################ ################################################################
## ##
## SOURCE PROCESSOR CLASS ## SOURCE PROCESSOR CLASS
## ##
## The SourceProcessor is in charge of reading a C source file ## The `SourceProcessor' is in charge of reading a C source file and
## and decomposing it into a series of different "SourceBlock" ## decomposing it into a series of different `SourceBlock' objects.
## objects.
## ##
## each one of these blocks can be made of the following data: ## A SourceBlock object consists of the following data.
## ##
## - A documentation comment block that starts with "/**" and ## - A documentation comment block using one of the layouts above. Its
## whose exact format will be discussed later ## exact format will be discussed later.
## ##
## - normal sources lines, include comments ## - Normal sources lines, including comments.
## ##
## ##
class SourceProcessor: class SourceProcessor:
def __init__( self ): def __init__( self ):
"""initialize a source processor""" """Initialize a source processor."""
self.blocks = [] self.blocks = []
self.filename = None self.filename = None
self.format = None self.format = None
self.lines = [] self.lines = []
def reset( self ): def reset( self ):
"""reset a block processor, clean all its blocks""" """Reset a block processor and clean up all its blocks."""
self.blocks = [] self.blocks = []
self.format = None self.format = None
def parse_file( self, filename ): def parse_file( self, filename ):
"""parse a C source file, and add its blocks to the processor's list""" """Parse a C source file and add its blocks to the processor's
list."""
self.reset() self.reset()
self.filename = filename self.filename = filename
@ -337,16 +347,16 @@ class SourceProcessor:
self.process_normal_line( line ) self.process_normal_line( line )
else: else:
if self.format.end.match( line ): if self.format.end.match( line ):
# that's a normal block end, add it to 'lines' and # A normal block end. Add it to `lines' and create a
# create a new block # new block
self.lines.append( line ) self.lines.append( line )
self.add_block_lines() self.add_block_lines()
elif self.format.column.match( line ): elif self.format.column.match( line ):
# that's a normal column line, add it to 'lines' # A normal column line. Add it to `lines'.
self.lines.append( line ) self.lines.append( line )
else: else:
# humm.. this is an unexpected block end, # An unexpected block end. Create a new block, but
# create a new block, but don't process the line # don't process the line.
self.add_block_lines() self.add_block_lines()
# we need to process the line again # we need to process the line again
@ -356,7 +366,8 @@ class SourceProcessor:
self.add_block_lines() self.add_block_lines()
def process_normal_line( self, line ): def process_normal_line( self, line ):
"""process a normal line and check whether it is the start of a new block""" """Process a normal line and check whether it is the start of a new
block."""
for f in re_source_block_formats: for f in re_source_block_formats:
if f.start.match( line ): if f.start.match( line ):
self.add_block_lines() self.add_block_lines()
@ -366,9 +377,12 @@ class SourceProcessor:
self.lines.append( line ) self.lines.append( line )
def add_block_lines( self ): def add_block_lines( self ):
"""add the current accumulated lines and create a new block""" """Add the current accumulated lines and create a new block."""
if self.lines != []: if self.lines != []:
block = SourceBlock( self, self.filename, self.lineno, self.lines ) block = SourceBlock( self,
self.filename,
self.lineno,
self.lines )
self.blocks.append( block ) self.blocks.append( block )
self.format = None self.format = None
@ -376,7 +390,7 @@ class SourceProcessor:
# debugging only, not used in normal operations # debugging only, not used in normal operations
def dump( self ): def dump( self ):
"""print all blocks in a processor""" """Print all blocks in a processor."""
for b in self.blocks: for b in self.blocks:
b.dump() b.dump()

View File

@ -1,5 +1,19 @@
# ToHTML (c) 2002, 2003, 2005-2008, 2013 #
# David Turner <david@freetype.org> # tohtml.py
#
# A sub-class container of the `Formatter' class to produce HTML.
#
# Copyright 2002, 2003, 2005-2008, 2013, 2014 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 sources import *
from content import * from content import *
@ -8,7 +22,7 @@ from formatter import *
import time import time
# The following defines the HTML header used by all generated pages. # The following strings define the HTML header used by all generated pages.
html_header_1 = """\ html_header_1 = """\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"> "http://www.w3.org/TR/html4/loose.dtd">
@ -158,7 +172,7 @@ toc_footer_end = """\
""" """
# source language keyword coloration/styling # Source language keyword coloration and styling.
keyword_prefix = '<span class="keyword">' keyword_prefix = '<span class="keyword">'
keyword_suffix = '</span>' keyword_suffix = '</span>'
@ -166,16 +180,20 @@ section_synopsis_header = '<h2>Synopsis</h2>'
section_synopsis_footer = '' section_synopsis_footer = ''
# Translate a single line of source to HTML. This will convert # Translate a single line of source to HTML. This converts `<', `>', and
# a "<" into "&lt.", ">" into "&gt.", etc. # `&' into `&lt;',`&gt;', and `&amp;'.
#
def html_quote( line ): def html_quote( line ):
result = string.replace( line, "&", "&amp;" ) result = string.replace( line, "&", "&amp;" )
result = string.replace( result, "<", "&lt;" ) result = string.replace( result, "<", "&lt;" )
result = string.replace( result, ">", "&gt;" ) result = string.replace( result, ">", "&gt;" )
return result return result
################################################################
##
## HTML FORMATTER CLASS
##
class HtmlFormatter( Formatter ): class HtmlFormatter( Formatter ):
def __init__( self, processor, project_title, file_prefix ): def __init__( self, processor, project_title, file_prefix ):
@ -189,31 +207,32 @@ class HtmlFormatter( Formatter ):
else: else:
file_prefix = "" file_prefix = ""
self.headers = processor.headers self.headers = processor.headers
self.project_title = project_title self.project_title = project_title
self.file_prefix = file_prefix self.file_prefix = file_prefix
self.html_header = html_header_1 + project_title + \ self.html_header = (
html_header_2 + \ html_header_1 + project_title
html_header_3 + file_prefix + "index.html" + \ + html_header_2
html_header_4 + file_prefix + "toc.html" + \ + html_header_3 + file_prefix + "index.html"
html_header_5 + project_title + \ + html_header_4 + file_prefix + "toc.html"
html_header_6 + html_header_5 + project_title
+ html_header_6 )
self.html_index_header = html_header_1 + project_title + \ self.html_index_header = (
html_header_2 + \ html_header_1 + project_title
html_header_3i + file_prefix + "toc.html" + \ + html_header_2
html_header_5 + project_title + \ + html_header_3i + file_prefix + "toc.html"
html_header_6 + html_header_5 + project_title
+ html_header_6 )
self.html_toc_header = html_header_1 + project_title + \ self.html_toc_header = (
html_header_2 + \ html_header_1 + project_title
html_header_3 + file_prefix + "index.html" + \ + html_header_2
html_header_5t + project_title + \ + html_header_3 + file_prefix + "index.html"
html_header_6 + html_header_5t + project_title
+ html_header_6 )
self.html_footer = "<center><font size=""-2"">generated on " + \ self.html_footer = (
time.asctime( time.localtime( time.time() ) ) + \ "<center><font size=""-2"">generated on "
"</font></center>" + html_footer + time.asctime( time.localtime( time.time() ) )
+ "</font></center>" + html_footer )
self.columns = 3 self.columns = 3
@ -224,8 +243,8 @@ class HtmlFormatter( Formatter ):
return self.make_section_url( block.section ) + "#" + block.name return self.make_section_url( block.section ) + "#" + block.name
def make_html_word( self, word ): def make_html_word( self, word ):
"""analyze a simple word to detect cross-references and styling""" """Analyze a simple word to detect cross-references and markup."""
# look for cross-references # handle cross-references
m = re_crossref.match( word ) m = re_crossref.match( word )
if m: if m:
try: try:
@ -236,11 +255,11 @@ class HtmlFormatter( Formatter ):
return '<a href="' + url + '">' + name + '</a>' + rest return '<a href="' + url + '">' + name + '</a>' + rest
except: except:
# we detected a cross-reference to an unknown item # we detected a cross-reference to an unknown item
sys.stderr.write( \ sys.stderr.write( "WARNING: undefined cross reference"
"WARNING: undefined cross reference '" + name + "'.\n" ) + " '" + name + "'.\n" )
return '?' + name + '?' + rest return '?' + name + '?' + rest
# look for italics and bolds # handle markup for italic and bold
m = re_italic.match( word ) m = re_italic.match( word )
if m: if m:
name = m.group( 1 ) name = m.group( 1 )
@ -256,7 +275,8 @@ class HtmlFormatter( Formatter ):
return html_quote( word ) return html_quote( word )
def make_html_para( self, words ): def make_html_para( self, words ):
""" convert words of a paragraph into tagged HTML text, handle xrefs """ """Convert words of a paragraph into tagged HTML text. Also handle
cross references."""
line = "" line = ""
if words: if words:
line = self.make_html_word( words[0] ) line = self.make_html_word( words[0] )
@ -265,8 +285,8 @@ class HtmlFormatter( Formatter ):
# handle hyperlinks # handle hyperlinks
line = re_url.sub( r'<a href="\1">\1</a>', line ) line = re_url.sub( r'<a href="\1">\1</a>', line )
# convert `...' quotations into real left and right single quotes # convert `...' quotations into real left and right single quotes
line = re.sub( r"(^|\W)`(.*?)'(\W|$)", \ line = re.sub( r"(^|\W)`(.*?)'(\W|$)",
r'\1&lsquo;\2&rsquo;\3', \ r'\1&lsquo;\2&rsquo;\3',
line ) line )
# convert tilde into non-breakable space # convert tilde into non-breakable space
line = string.replace( line, "~", "&nbsp;" ) line = string.replace( line, "~", "&nbsp;" )
@ -274,7 +294,7 @@ class HtmlFormatter( Formatter ):
return para_header + line + para_footer return para_header + line + para_footer
def make_html_code( self, lines ): def make_html_code( self, lines ):
""" convert a code sequence to HTML """ """Convert a code sequence to HTML."""
line = code_header + '\n' line = code_header + '\n'
for l in lines: for l in lines:
line = line + html_quote( l ) + '\n' line = line + html_quote( l ) + '\n'
@ -282,7 +302,7 @@ class HtmlFormatter( Formatter ):
return line + code_footer return line + code_footer
def make_html_items( self, items ): def make_html_items( self, items ):
""" convert a field's content into some valid HTML """ """Convert a field's content into HTML."""
lines = [] lines = []
for item in items: for item in items:
if item.lines: if item.lines:
@ -297,7 +317,9 @@ class HtmlFormatter( Formatter ):
def print_html_field( self, field ): def print_html_field( self, field ):
if field.name: if field.name:
print "<table><tr valign=top><td><b>" + field.name + "</b></td><td>" print( "<table><tr valign=top><td><b>"
+ field.name
+ "</b></td><td>" )
print self.make_html_items( field.items ) print self.make_html_items( field.items )
@ -318,12 +340,14 @@ class HtmlFormatter( Formatter ):
result = result + prefix + '<b>' + name + '</b>' result = result + prefix + '<b>' + name + '</b>'
elif re_source_keywords.match( name ): elif re_source_keywords.match( name ):
# this is a C keyword # this is a C keyword
result = result + prefix + keyword_prefix + name + keyword_suffix result = ( result + prefix
+ keyword_prefix + name + keyword_suffix )
elif self.identifiers.has_key( name ): elif self.identifiers.has_key( name ):
# this is a known identifier # this is a known identifier
block = self.identifiers[name] block = self.identifiers[name]
result = result + prefix + '<a href="' + \ result = ( result + prefix
self.make_block_url( block ) + '">' + name + '</a>' + '<a href="' + self.make_block_url( block )
+ '">' + name + '</a>' )
else: else:
result = result + html_quote( line[:length] ) result = result + html_quote( line[:length] )
@ -339,10 +363,14 @@ class HtmlFormatter( Formatter ):
print "<table cellpadding=3 border=0>" print "<table cellpadding=3 border=0>"
for field in fields: for field in fields:
if len( field.name ) > 22: if len( field.name ) > 22:
print "<tr valign=top><td colspan=0><b>" + field.name + "</b></td></tr>" print( "<tr valign=top><td colspan=0><b>"
+ field.name
+ "</b></td></tr>" )
print "<tr valign=top><td></td><td>" print "<tr valign=top><td></td><td>"
else: else:
print "<tr valign=top><td><b>" + field.name + "</b></td><td>" print( "<tr valign=top><td><b>"
+ field.name
+ "</b></td><td>" )
self.print_html_items( field.items ) self.print_html_items( field.items )
print "</td></tr>" print "</td></tr>"
@ -352,10 +380,9 @@ class HtmlFormatter( Formatter ):
table_fields = [] table_fields = []
for field in markup.fields: for field in markup.fields:
if field.name: if field.name:
# we begin a new series of field or value definitions, we # We begin a new series of field or value definitions. We
# will record them in the 'table_fields' list before outputting # record them in the `table_fields' list before outputting
# all of them as a single table # all of them as a single table.
#
table_fields.append( field ) table_fields.append( field )
else: else:
if table_fields: if table_fields:
@ -368,7 +395,7 @@ class HtmlFormatter( Formatter ):
self.print_html_field_list( table_fields ) self.print_html_field_list( table_fields )
# #
# Formatting the index # formatting the index
# #
def index_enter( self ): def index_enter( self ):
print self.html_index_header print self.html_index_header
@ -380,7 +407,7 @@ class HtmlFormatter( Formatter ):
self.index_items[name] = url self.index_items[name] = url
def index_exit( self ): def index_exit( self ):
# block_index already contains the sorted list of index names # `block_index' already contains the sorted list of index names
count = len( self.block_index ) count = len( self.block_index )
rows = ( count + self.columns - 1 ) / self.columns rows = ( count + self.columns - 1 ) / self.columns
@ -392,7 +419,8 @@ class HtmlFormatter( Formatter ):
if i < count: if i < count:
bname = self.block_index[r + c * rows] bname = self.block_index[r + c * rows]
url = self.index_items[bname] url = self.index_items[bname]
line = line + '<td><a href="' + url + '">' + bname + '</a></td>' line = ( line + '<td><a href="' + url + '">'
+ bname + '</a></td>' )
else: else:
line = line + '<td></td>' line = line + '<td></td>'
line = line + "</tr>" line = line + "</tr>"
@ -400,9 +428,9 @@ class HtmlFormatter( Formatter ):
print "</table>" print "</table>"
print index_footer_start + \ print( index_footer_start
self.file_prefix + "toc.html" + \ + self.file_prefix + "toc.html"
index_footer_end + index_footer_end )
print self.html_footer print self.html_footer
@ -415,20 +443,20 @@ class HtmlFormatter( Formatter ):
Formatter.index_dump( self, index_filename ) Formatter.index_dump( self, index_filename )
# #
# Formatting the table of content # formatting the table of contents
# #
def toc_enter( self ): def toc_enter( self ):
print self.html_toc_header print self.html_toc_header
print "<center><h1>Table of Contents</h1></center>" print "<center><h1>Table of Contents</h1></center>"
def toc_chapter_enter( self, chapter ): def toc_chapter_enter( self, chapter ):
print chapter_header + string.join( chapter.title ) + chapter_inter print chapter_header + string.join( chapter.title ) + chapter_inter
print "<table cellpadding=5>" print "<table cellpadding=5>"
def toc_section_enter( self, section ): def toc_section_enter( self, section ):
print '<tr valign=top><td class="left">' print '<tr valign=top><td class="left">'
print '<a href="' + self.make_section_url( section ) + '">' + \ print( '<a href="' + self.make_section_url( section ) + '">'
section.title + '</a></td><td>' + section.title + '</a></td><td>' )
print self.make_html_para( section.abstract ) print self.make_html_para( section.abstract )
@ -440,14 +468,14 @@ class HtmlFormatter( Formatter ):
print chapter_footer print chapter_footer
def toc_index( self, index_filename ): def toc_index( self, index_filename ):
print chapter_header + \ print( chapter_header
'<a href="' + index_filename + '">Global Index</a>' + \ + '<a href="' + index_filename + '">Global Index</a>'
chapter_inter + chapter_footer + chapter_inter + chapter_footer )
def toc_exit( self ): def toc_exit( self ):
print toc_footer_start + \ print( toc_footer_start
self.file_prefix + "index.html" + \ + self.file_prefix + "index.html"
toc_footer_end + toc_footer_end )
print self.html_footer print self.html_footer
@ -461,7 +489,7 @@ class HtmlFormatter( Formatter ):
Formatter.toc_dump( self, toc_filename, index_filename ) Formatter.toc_dump( self, toc_filename, index_filename )
# #
# Formatting sections # formatting sections
# #
def section_enter( self, section ): def section_enter( self, section ):
print self.html_header print self.html_header
@ -495,7 +523,8 @@ class HtmlFormatter( Formatter ):
line = line + '<td></td><td>' line = line + '<td></td><td>'
if i < count: if i < count:
name = section.block_names[i] name = section.block_names[i]
line = line + '<a href="#' + name + '">' + name + '</a>' line = ( line + '<a href="#' + name + '">'
+ name + '</a>' )
line = line + '</td>' line = line + '</td>'
line = line + "</tr>" line = line + "</tr>"
@ -513,7 +542,8 @@ class HtmlFormatter( Formatter ):
# place html anchor if needed # place html anchor if needed
if block.name: if block.name:
print '<h4><a name="' + block.name + '">' + block.name + '</a></h4>' print( '<h4><a name="' + block.name + '">'
+ block.name + '</a></h4>' )
# dump the block C source lines now # dump the block C source lines now
if block.code: if block.code:
@ -524,8 +554,9 @@ class HtmlFormatter( Formatter ):
break; break;
# if not header: # if not header:
# sys.stderr.write( \ # sys.stderr.write(
# 'WARNING: No header macro for ' + block.source.filename + '.\n' ) # "WARNING: No header macro for"
# + " '" + block.source.filename + "'.\n" )
if header: if header:
print header_location_header print header_location_header
@ -552,15 +583,16 @@ class HtmlFormatter( Formatter ):
print marker_footer print marker_footer
def block_exit( self, block ): def block_exit( self, block ):
print block_footer_start + self.file_prefix + "index.html" + \ print( block_footer_start + self.file_prefix + "index.html"
block_footer_middle + self.file_prefix + "toc.html" + \ + block_footer_middle + self.file_prefix + "toc.html"
block_footer_end + block_footer_end )
def section_exit( self, section ): def section_exit( self, section ):
print html_footer print html_footer
def section_dump_all( self ): def section_dump_all( self ):
for section in self.sections: for section in self.sections:
self.section_dump( section, self.file_prefix + section.name + '.html' ) self.section_dump( section,
self.file_prefix + section.name + '.html' )
# eof # eof

View File

@ -1,15 +1,28 @@
# Utils (c) 2002, 2004, 2007, 2008 David Turner <david@freetype.org>
# #
# utils.py
#
# Auxiliary functions for the `docmaker' tool (library file).
#
# Copyright 2002, 2004, 2007, 2008, 2014 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 import string, sys, os, glob
# current output directory # current output directory
# #
output_dir = None output_dir = None
# This function is used to sort the index. It is a simple lexicographical # A function to sort the index. It is a simple lexicographical sort, except
# sort, except that it places capital letters before lowercase ones. # that it places capital letters before lowercase ones.
# #
def index_sort( s1, s2 ): def index_sort( s1, s2 ):
if not s1: if not s1:
@ -42,7 +55,7 @@ def index_sort( s1, s2 ):
return 0 return 0
# Sort input_list, placing the elements of order_list in front. # Sort `input_list', placing the elements of `order_list' in front.
# #
def sort_order_list( input_list, order_list ): def sort_order_list( input_list, order_list ):
new_list = order_list[:] new_list = order_list[:]
@ -52,9 +65,9 @@ def sort_order_list( input_list, order_list ):
return new_list return new_list
# Open the standard output to a given project documentation file. Use # Divert standard output to a given project documentation file. Use
# "output_dir" to determine the filename location if necessary and save the # `output_dir' to determine the filename location if necessary and save the
# old stdout in a tuple that is returned by this function. # old stdout handle in a tuple that is returned by this function.
# #
def open_output( filename ): def open_output( filename ):
global output_dir global output_dir
@ -69,7 +82,7 @@ def open_output( filename ):
return ( new_file, old_stdout ) return ( new_file, old_stdout )
# Close the output that was returned by "close_output". # Close the output that was returned by `open_output'.
# #
def close_output( output ): def close_output( output ):
output[0].close() output[0].close()
@ -83,15 +96,16 @@ def check_output():
if output_dir: if output_dir:
if output_dir != "": if output_dir != "":
if not os.path.isdir( output_dir ): if not os.path.isdir( output_dir ):
sys.stderr.write( "argument" + " '" + output_dir + "' " + \ sys.stderr.write( "argument"
"is not a valid directory" ) + " '" + output_dir + "' "
+ "is not a valid directory" )
sys.exit( 2 ) sys.exit( 2 )
else: else:
output_dir = None output_dir = None
def file_exists( pathname ): def file_exists( pathname ):
"""checks that a given file exists""" """Check that a given file exists."""
result = 1 result = 1
try: try:
file = open( pathname, "r" ) file = open( pathname, "r" )
@ -104,12 +118,12 @@ def file_exists( pathname ):
def make_file_list( args = None ): def make_file_list( args = None ):
"""builds a list of input files from command-line arguments""" """Build a list of input files from command-line arguments."""
file_list = [] file_list = []
# sys.stderr.write( repr( sys.argv[1 :] ) + '\n' ) # sys.stderr.write( repr( sys.argv[1 :] ) + '\n' )
if not args: if not args:
args = sys.argv[1 :] args = sys.argv[1:]
for pathname in args: for pathname in args:
if string.find( pathname, '*' ) >= 0: if string.find( pathname, '*' ) >= 0: