#!/usr/bin/env python
 # -*- coding: Latin-1 -*-
 """
  PySourceColor.py
 ----------------------------------------------------------------------------
  A python source to colorized html/css/xhtml converter.
  Hacked by M.E.Farmer Jr. 2004, 2005
  Python license
 ----------------------------------------------------------------------------
  - HTML markup does not create w3c valid html, but it works on every
    browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML).
  - CSS markup is w3c validated html 4.01 strict,
    but will not render correctly on all browsers.
  - XHTML markup is w3c validated xhtml 1.0 strict,
    like html 4.01, will not render correctly on all browsers.
 ----------------------------------------------------------------------------
  Features:
  -Three types of markup:
     html (default) 
     css/html 4.01 strict
     xhtml 1.0 strict
 
  -Can tokenize and colorize:
     12 types of strings
     2 comment types
     numbers
     operators
     brackets
     math operators
     class / name
     def / name
     decorator / name
     keywords
     arguments class/def/decorator
     linenumbers
     names
     text
 
  -Eight colorschemes built-in:
     null
     mono
     lite (default)
     dark 
     dark2
     idle
     viewcvs
     pythonwin
 
  -Header and footer
     set to '' for builtin header / footer.
     give path to a file containing the html
         you want added as header or footer.
 
  -Arbitrary text and html
     html markup converts all to raw (TEXT token)
     #@# for raw -> send raw text.
     #$# for span -> inline html and text.
     #%# for div -> block level html and text.
 
  -Linenumbers
     Supports all styles. New token is called LINE.
     Defaults to NAME if not defined.
 
  Style options
  -ALL markups support these text styles:
          b = bold
          i = italic
          u = underline
  -CSS and XHTML has limited support  for borders:
      HTML markup functions will ignore these.
      Optional: Border color in RGB hex
      Defaults to the text forecolor.
          #rrggbb = border color
      Border size:
          l = thick
          m = medium
          t = thin
      Border type:
          - = dashed
          . = dotted
          s = solid
          d = double
          g = groove
          r = ridge
          n = inset
          o = outset
      You can specify multiple sides,
      they will all use the same style.
      Optional: Default is full border.
          v = bottom
          < = left
          > = right
          ^ = top
      NOTE: Specify the styles you want.
            The markups will ignore unsupported styles
            Also note not all browsers can show these options
 
  -All tokens default to NAME if not defined
      so the only absolutely critical ones to define are:
      NAME, ERRORTOKEN, PAGEBACKGROUND
 
 ----------------------------------------------------------------------------
  Example usage:
 ----------------------------------------------------------------------------
  # import
  import PySourceColor as psc
  psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1)
 ----------------------------------------------------------------------------
  # from module import *
  from PySourceColor import *
  convert('c:/Python22/Lib', colors=lite, markup="css",
           header='#$#<b>This is a simpe heading</b><hr/>')
 ----------------------------------------------------------------------------
  # How to use a custom colorscheme, and most of the 'features'
  from PySourceColor import *
  new = {
    ERRORTOKEN:             ('bui','#FF8080',''),
    DECORATOR_NAME:         ('s','#AACBBC',''),
    DECORATOR:              ('n','#333333',''),
    NAME:                   ('t.<v','#1133AA','#DDFF22'),
    NUMBER:                 ('','#236676','#FF5555'),
    OPERATOR:               ('b','#454567','#BBBB11'),
    MATH_OPERATOR:          ('','#935623','#423afb'),
    BRACKETS:               ('b','#ac34bf','#6457a5'),
    COMMENT:                ('t-#0022FF','#545366','#AABBFF'),
    DOUBLECOMMENT:          ('<l#553455','#553455','#FF00FF'),
    CLASS_NAME:             ('m^v-','#000000','#FFFFFF'),
    DEF_NAME:               ('l=<v','#897845','#000022'),
    KEYWORD:                ('.b','#345345','#FFFF22'),
    SINGLEQUOTE:            ('mn','#223344','#AADDCC'),
    SINGLEQUOTE_R:          ('','#344522',''),
    SINGLEQUOTE_U:          ('','#234234',''),
    DOUBLEQUOTE:            ('m#0022FF','#334421',''),
    DOUBLEQUOTE_R:          ('','#345345',''),
    DOUBLEQUOTE_U:          ('','#678673',''),
    TRIPLESINGLEQUOTE:      ('tv','#FFFFFF','#000000'),
    TRIPLESINGLEQUOTE_R:    ('tbu','#443256','#DDFFDA'),
    TRIPLESINGLEQUOTE_U:    ('','#423454','#DDFFDA'),
    TRIPLEDOUBLEQUOTE:      ('li#236fd3b<>','#000000','#FFFFFF'),
    TRIPLEDOUBLEQUOTE_R:    ('tub','#000000','#FFFFFF'),
    TRIPLEDOUBLEQUOTE_U:    ('-', '#CCAABB','#FFFAFF'),
    LINE:                   ('ib-','#ff66aa','#7733FF'),]
    TEXT:                   ('','#546634',''), 
    PAGEBACKGROUND:         '#FFFAAA',
      }
  if __name__ == '__main__':
      import sys
      convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1,
              linenumbers=1)
      convert(sys.argv[1], './html.html', colors=new, markup='html', show=1,
              linenumbers=1)
 ----------------------------------------------------------------------------
 """

 __all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS','TOKEN_NAMES',
        'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR',
        'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS',
        'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE',
        'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT',
        'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE',
        'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND', 'LINE',
        'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle',
        'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser',
        'str2file', 'str2html', 'str2css', 'str2markup', 'path2file',
        'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage',
        'pageconvert','tagreplace']
 __title__ = 'PySourceColor'
 __version__ = "2.0"
 __date__ = '15 February 2005'
 __author__ = "M.E.Farmer Jr."
 __credits__ = '''This was originally based on a python recipe
 submitted by Jürgen Hermann to ASPN. Now based on the voices in my head.
 M.E.Farmer 2004, 2005
 Python license
 '''
 import os
 import sys
 import time
 import glob
 import getopt
 import keyword
 import token
 import tokenize
 import cStringIO
 import traceback
 import webbrowser

 # Do not edit
 NAME = token.NAME
 NUMBER = token.NUMBER
 COMMENT = tokenize.COMMENT
 OPERATOR = token.OP
 ERRORTOKEN = token.ERRORTOKEN
 ARGS = token.NT_OFFSET + 1
 DOUBLECOMMENT = token.NT_OFFSET + 2
 CLASS_NAME = token.NT_OFFSET + 3
 DEF_NAME = token.NT_OFFSET + 4
 KEYWORD = token.NT_OFFSET + 5
 SINGLEQUOTE = token.NT_OFFSET + 6
 SINGLEQUOTE_R = token.NT_OFFSET + 7
 SINGLEQUOTE_U = token.NT_OFFSET + 8
 DOUBLEQUOTE = token.NT_OFFSET + 9
 DOUBLEQUOTE_R = token.NT_OFFSET + 10
 DOUBLEQUOTE_U = token.NT_OFFSET + 11
 TRIPLESINGLEQUOTE = token.NT_OFFSET + 12
 TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13
 TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14
 TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15
 TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16
 TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17
 PAGEBACKGROUND = token.NT_OFFSET + 18
 DECORATOR = token.NT_OFFSET + 19
 DECORATOR_NAME = token.NT_OFFSET + 20
 BRACKETS = token.NT_OFFSET + 21
 MATH_OPERATOR = token.NT_OFFSET + 22
 LINE = token.NT_OFFSET + 23
 TEXT = token.NT_OFFSET + 24
 # markup classname lookup
 MARKUPDICT = {
         ERRORTOKEN:             'py_err',
         DECORATOR_NAME:         'py_decn',
         DECORATOR:              'py_dec',
         ARGS:                   'py_args',
         NAME:                   'py_name',
         NUMBER:                 'py_num',
         OPERATOR:               'py_op',
         COMMENT:                'py_com',
         DOUBLECOMMENT:          'py_dcom',
         CLASS_NAME:             'py_clsn',
         DEF_NAME:               'py_defn',
         KEYWORD:                'py_key',
         SINGLEQUOTE:            'py_sq',
         SINGLEQUOTE_R:          'py_sqr',
         SINGLEQUOTE_U:          'py_squ',
         DOUBLEQUOTE:            'py_dq',
         DOUBLEQUOTE_R:          'py_dqr',
         DOUBLEQUOTE_U:          'py_dqu',
         TRIPLESINGLEQUOTE:      'py_tsq',
         TRIPLESINGLEQUOTE_R:    'py_tsqr',
         TRIPLESINGLEQUOTE_U:    'py_tsqu',
         TRIPLEDOUBLEQUOTE:      'py_tdq',
         TRIPLEDOUBLEQUOTE_R:    'py_tdqr',
         TRIPLEDOUBLEQUOTE_U:    'py_tdqu',
         BRACKETS:               'py_bra',
         MATH_OPERATOR:          'py_mop',
         LINE:                   'py_line',
         TEXT:                   'py_text',
         }
 # might help users that want to create custom schemes
 TOKEN_NAMES= {
        'ERRORTOKEN':ERRORTOKEN,
        'DECORATOR_NAME':DECORATOR_NAME,
        'DECORATOR':DECORATOR,
        'ARGS':ARGS,
        'NAME':NAME,
        'NUMBER':NUMBER,
        'OPERATOR':OPERATOR,
        'COMMENT':COMMENT,
        'DOUBLECOMMENT':DOUBLECOMMENT,
        'CLASS_NAME':CLASS_NAME,
        'DEF_NAME':DEF_NAME,
        'KEYWORD':KEYWORD,
        'SINGLEQUOTE':SINGLEQUOTE,
        'SINGLEQUOTE_R':SINGLEQUOTE_R,
        'SINGLEQUOTE_U':SINGLEQUOTE_U,
        'DOUBLEQUOTE':DOUBLEQUOTE,
        'DOUBLEQUOTE_R':DOUBLEQUOTE_R,
        'DOUBLEQUOTE_U':DOUBLEQUOTE_U,
        'TRIPLESINGLEQUOTE':TRIPLESINGLEQUOTE,
        'TRIPLESINGLEQUOTE_R':TRIPLESINGLEQUOTE_R,
        'TRIPLESINGLEQUOTE_U':TRIPLESINGLEQUOTE_U,
        'TRIPLEDOUBLEQUOTE':TRIPLEDOUBLEQUOTE,
        'TRIPLEDOUBLEQUOTE_R':TRIPLEDOUBLEQUOTE_R,
        'TRIPLEDOUBLEQUOTE_U':TRIPLEDOUBLEQUOTE_U,
        'BRACKETS':BRACKETS,
        'MATH_OPERATOR':MATH_OPERATOR,
        'LINE':LINE,
        'TEXT':TEXT,
        'PAGEBACKGROUND':PAGEBACKGROUND,
        }

 ######################################################################
 # Edit colors and styles to taste
 # Create your own scheme, just copy one below , rename and edit.
 # Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND,
 # all missing elements will default to NAME.
 # See module docstring for details on style attributes.
 ######################################################################
 # Copy null and use it as a starter colorscheme.
 null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor')
         ERRORTOKEN:             ('','#000000',''),# Error token
         DECORATOR_NAME:         ('','#000000',''),# Decorator name
         DECORATOR:              ('','#000000',''),# @ symbol
         ARGS:                   ('','#000000',''),# class,def,deco arguments
         NAME:                   ('','#000000',''),# All other text
         NUMBER:                 ('','#000000',''),# 0->10
         OPERATOR:               ('','#000000',''),# ':','`',';',',','.','='
         MATH_OPERATOR:          ('','#000000',''),# '+','-','==','!=','*',etc
         BRACKETS:               ('','#000000',''),# '[',']','(',')','{','}'
         COMMENT:                ('','#000000',''),# Single comment
         DOUBLECOMMENT:          ('','#000000',''),## Double comment
         CLASS_NAME:             ('','#000000',''),# Class name
         DEF_NAME:               ('','#000000',''),# Def name
         KEYWORD:                ('','#000000',''),# Python keywords
         SINGLEQUOTE:            ('','#000000',''),# 'SINGLEQUOTE'
         SINGLEQUOTE_R:          ('','#000000',''),# r'SINGLEQUOTE'
         SINGLEQUOTE_U:          ('','#000000',''),# u'SINGLEQUOTE'
         DOUBLEQUOTE:            ('','#000000',''),# "DOUBLEQUOTE"
         DOUBLEQUOTE_R:          ('','#000000',''),# r"DOUBLEQUOTE"
         DOUBLEQUOTE_U:          ('','#000000',''),# u"DOUBLEQUOTE"
         TRIPLESINGLEQUOTE:      ('','#000000',''),# '''TRIPLESINGLEQUOTE'''
         TRIPLESINGLEQUOTE_R:    ('','#000000',''),# r'''TRIPLESINGLEQUOTE'''
         TRIPLESINGLEQUOTE_U:    ('','#000000',''),# u'''TRIPLESINGLEQUOTE'''
         TRIPLEDOUBLEQUOTE:      ('','#000000',''),# """TRIPLEDOUBLEQUOTE"""
         TRIPLEDOUBLEQUOTE_R:    ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE"""
         TRIPLEDOUBLEQUOTE_U:    ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE"""
         TEXT:                   ('','#000000',''),# non python text 
         LINE:                   ('>ti#555555','#000000',''),# Linenumbers
         PAGEBACKGROUND:         '#FFFFFF'# set the page background
         }

 mono = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('bu','#000000',''),
         DECORATOR:              ('b','#000000',''),
         ARGS:                   ('b','#000000',''),
         NAME:                   ('','#000000',''),
         NUMBER:                 ('b','#000000',''),
         OPERATOR:               ('b','#000000',''),
         MATH_OPERATOR:          ('b','#000000',''),
         BRACKETS:               ('b','#000000',''),
         COMMENT:                ('i','#000000',''),
         DOUBLECOMMENT:          ('b','#000000',''),
         CLASS_NAME:             ('bu','#000000',''),
         DEF_NAME:               ('b','#000000',''),
         KEYWORD:                ('b','#000000',''),
         SINGLEQUOTE:            ('','#000000',''),
         SINGLEQUOTE_R:          ('','#000000',''),
         SINGLEQUOTE_U:          ('','#000000',''),
         DOUBLEQUOTE:            ('','#000000',''),
         DOUBLEQUOTE_R:          ('','#000000',''),
         DOUBLEQUOTE_U:          ('','#000000',''),
         TRIPLESINGLEQUOTE:      ('','#000000',''),
         TRIPLESINGLEQUOTE_R:    ('','#000000',''),
         TRIPLESINGLEQUOTE_U:    ('','#000000',''),
         TRIPLEDOUBLEQUOTE:      ('i','#000000',''),
         TRIPLEDOUBLEQUOTE_R:    ('i','#000000',''),
         TRIPLEDOUBLEQUOTE_U:    ('i','#000000',''),
         TEXT:                   ('','#000000',''),
         LINE:                   ('>ti#555555','#000000',''),
         PAGEBACKGROUND:         '#FFFFFF'
         }

 dark = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('b','#FFBBAA',''),
         DECORATOR:              ('b','#CC5511',''),
         ARGS:                   ('b','#CCCCEE',''),
         NAME:                   ('','#DDDDDD',''),
         NUMBER:                 ('','#FF0000',''),
         OPERATOR:               ('b','#FAF785',''),
         MATH_OPERATOR:          ('b','#FAF785',''),
         BRACKETS:               ('b','#FAF785',''),
         COMMENT:                ('','#45FCA0',''),
         DOUBLECOMMENT:          ('i','#A7C7A9',''),
         CLASS_NAME:             ('b','#B666FD',''),
         DEF_NAME:               ('b','#EBAE5C',''),
         KEYWORD:                ('b','#8680FF',''),
         SINGLEQUOTE:            ('','#F8BAFE',''),
         SINGLEQUOTE_R:          ('','#F8BAFE',''),
         SINGLEQUOTE_U:          ('','#F8BAFE',''),
         DOUBLEQUOTE:            ('','#FF80C0',''),
         DOUBLEQUOTE_R:          ('','#FF80C0',''),
         DOUBLEQUOTE_U:          ('','#FF80C0',''),
         TRIPLESINGLEQUOTE:      ('','#FF9595',''),
         TRIPLESINGLEQUOTE_R:    ('','#FF9595',''),
         TRIPLESINGLEQUOTE_U:    ('','#FF9595',''),
         TRIPLEDOUBLEQUOTE:      ('','#B3FFFF',''),
         TRIPLEDOUBLEQUOTE_R:    ('','#B3FFFF',''),
         TRIPLEDOUBLEQUOTE_U:    ('','#B3FFFF',''),
         TEXT:                   ('','#FFFFFF',''),
         LINE:                   ('>mi#555555','#bbccbb','#333333'),
         PAGEBACKGROUND:         '#000000'
         }

 dark2 = {
         ERRORTOKEN:             ('','#FF0000',''),
         DECORATOR_NAME:         ('b','#FFBBAA',''),
         DECORATOR:              ('b','#CC5511',''),
         ARGS:                   ('b','#EEEEEE',''),
         NAME:                   ('','#C0C0C0',''),
         NUMBER:                 ('b','#00FF00',''),
         OPERATOR:               ('b','#FF090F',''),
         MATH_OPERATOR:          ('b','#F07040',''),
         BRACKETS:               ('b','#FFB90F',''),
         COMMENT:                ('i','#D0D000','#522000'),#622000
         DOUBLECOMMENT:          ('i','#D0D000','#522000'),
         CLASS_NAME:             ('b','#EE4080',''),
         DEF_NAME:               ('b','#FF8040',''),
         KEYWORD:                ('b','#4726E1',''),
         SINGLEQUOTE:            ('','#8080C0',''),
         SINGLEQUOTE_R:          ('','#8080C0',''),
         SINGLEQUOTE_U:          ('','#8080C0',''),
         DOUBLEQUOTE:            ('','#ADB9F1',''),
         DOUBLEQUOTE_R:          ('','#ADB9F1',''),
         DOUBLEQUOTE_U:          ('','#ADB9F1',''),
         TRIPLESINGLEQUOTE:      ('','#00C1C1',''),
         TRIPLESINGLEQUOTE_R:    ('','#00C1C1',''),
         TRIPLESINGLEQUOTE_U:    ('','#00C1C1',''),
         TRIPLEDOUBLEQUOTE:      ('','#33E3E3',''),
         TRIPLEDOUBLEQUOTE_R:    ('','#33E3E3',''),
         TRIPLEDOUBLEQUOTE_U:    ('','#33E3E3',''),
         TEXT:                   ('','#C0C0C0',''),
         LINE:                   ('>mi#555555','#bbccbb','#333333'),
         PAGEBACKGROUND:         '#000000'
         }

 lite = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('b','#BB4422',''),
         DECORATOR:              ('b','#3333AF',''),
         ARGS:                   ('b','#000000',''),
         NAME:                   ('','#333333',''),
         NUMBER:                 ('b','#DD2200',''),
         OPERATOR:               ('b','#000000',''),
         MATH_OPERATOR:          ('b','#000000',''),
         BRACKETS:               ('b','#000000',''),
         COMMENT:                ('','#007F00',''),
         DOUBLECOMMENT:          ('','#608060',''),
         CLASS_NAME:             ('b','#0000FF',''),
         DEF_NAME:               ('b','#9C7A00',''),#f09030
         KEYWORD:                ('b','#0000AF',''),
         SINGLEQUOTE:            ('','#600080',''),
         SINGLEQUOTE_R:          ('','#600080',''),
         SINGLEQUOTE_U:          ('','#600080',''),
         DOUBLEQUOTE:            ('','#A0008A',''),
         DOUBLEQUOTE_R:          ('','#A0008A',''),
         DOUBLEQUOTE_U:          ('','#A0008A',''),
         TRIPLESINGLEQUOTE:      ('','#337799',''),
         TRIPLESINGLEQUOTE_R:    ('','#337799',''),
         TRIPLESINGLEQUOTE_U:    ('','#337799',''),
         TRIPLEDOUBLEQUOTE:      ('','#1166AA',''),
         TRIPLEDOUBLEQUOTE_R:    ('','#1166AA',''),
         TRIPLEDOUBLEQUOTE_U:    ('','#1166AA',''),
         TEXT:                   ('','#000000',''),
         LINE:                   ('>ti#555555','#000000',''),
         PAGEBACKGROUND:         '#FFFFFF'
         }

 idle = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('','#900090',''),
         DECORATOR:              ('','#FF7700',''),
         NAME:                   ('','#000000',''),
         NUMBER:                 ('','#000000',''),
         OPERATOR:               ('','#000000',''),
         MATH_OPERATOR:          ('','#000000',''),
         BRACKETS:               ('','#000000',''),
         COMMENT:                ('','#DD0000',''),
         DOUBLECOMMENT:          ('','#DD0000',''),
         CLASS_NAME:             ('','#0000FF',''),
         DEF_NAME:               ('','#0000FF',''),
         KEYWORD:                ('','#FF7700',''),
         SINGLEQUOTE:            ('','#00AA00',''),
         SINGLEQUOTE_R:          ('','#00AA00',''),
         SINGLEQUOTE_U:          ('','#00AA00',''),
         DOUBLEQUOTE:            ('','#00AA00',''),
         DOUBLEQUOTE_R:          ('','#00AA00',''),
         DOUBLEQUOTE_U:          ('','#00AA00',''),
         TRIPLESINGLEQUOTE:      ('','#00AA00',''),
         TRIPLESINGLEQUOTE_R:    ('','#00AA00',''),
         TRIPLESINGLEQUOTE_U:    ('','#00AA00',''),
         TRIPLEDOUBLEQUOTE:      ('','#00AA00',''),
         TRIPLEDOUBLEQUOTE_R:    ('','#00AA00',''),
         TRIPLEDOUBLEQUOTE_U:    ('','#00AA00',''),
         TEXT:                   ('','#000000',''),
         LINE:                   ('>ti#555555','#000000',''),
         PAGEBACKGROUND:         '#FFFFFF'
         }

 pythonwin = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('b','#DD0080',''),
         DECORATOR:              ('b','#000080',''),
         ARGS:                   ('','#000000',''),
         NAME:                   ('','#303030',''),
         NUMBER:                 ('','#008080',''),
         OPERATOR:               ('','#000000',''),
         MATH_OPERATOR:          ('','#000000',''),
         BRACKETS:               ('','#000000',''),
         COMMENT:                ('','#007F00',''),
         DOUBLECOMMENT:          ('','#7F7F7F',''),
         CLASS_NAME:             ('b','#0000FF',''),
         DEF_NAME:               ('b','#007F7F',''),
         KEYWORD:                ('b','#000080',''),
         SINGLEQUOTE:            ('','#808000',''),
         SINGLEQUOTE_R:          ('','#808000',''),
         SINGLEQUOTE_U:          ('','#808000',''),
         DOUBLEQUOTE:            ('','#808000',''),
         DOUBLEQUOTE_R:          ('','#808000',''),
         DOUBLEQUOTE_U:          ('','#808000',''),
         TRIPLESINGLEQUOTE:      ('','#808000',''),
         TRIPLESINGLEQUOTE_R:    ('','#808000',''),
         TRIPLESINGLEQUOTE_U:    ('','#808000',''),
         TRIPLEDOUBLEQUOTE:      ('','#808000',''),
         TRIPLEDOUBLEQUOTE_R:    ('','#808000',''),
         TRIPLEDOUBLEQUOTE_U:    ('','#808000',''),
         TEXT:                   ('','#303030',''),
         LINE:                   ('>ti#555555','#000000',''),
         PAGEBACKGROUND:         '#FFFFFF'
         }

 viewcvs = {
         ERRORTOKEN:             ('s#FF0000','#FF8080',''),
         DECORATOR_NAME:         ('','#000000',''),
         DECORATOR:              ('','#000000',''),
         ARGS:                   ('','#000000',''),
         NAME:                   ('','#000000',''),
         NUMBER:                 ('','#000000',''),
         OPERATOR:               ('','#000000',''),
         MATH_OPERATOR:          ('','#000000',''),
         BRACKETS:               ('','#000000',''),
         COMMENT:                ('i','#b22222',''),
         DOUBLECOMMENT:          ('i','#b22222',''),
         CLASS_NAME:             ('','#000000',''),
         DEF_NAME:               ('b','#0000ff',''),
         KEYWORD:                ('b','#a020f0',''),
         SINGLEQUOTE:            ('b','#bc8f8f',''),
         SINGLEQUOTE_R:          ('b','#bc8f8f',''),
         SINGLEQUOTE_U:          ('b','#bc8f8f',''),
         DOUBLEQUOTE:            ('b','#bc8f8f',''),
         DOUBLEQUOTE_R:          ('b','#bc8f8f',''),
         DOUBLEQUOTE_U:          ('b','#bc8f8f',''),
         TRIPLESINGLEQUOTE:      ('b','#bc8f8f',''),
         TRIPLESINGLEQUOTE_R:    ('b','#bc8f8f',''),
         TRIPLESINGLEQUOTE_U:    ('b','#bc8f8f',''),
         TRIPLEDOUBLEQUOTE:      ('b','#bc8f8f',''),
         TRIPLEDOUBLEQUOTE_R:    ('b','#bc8f8f',''),
         TRIPLEDOUBLEQUOTE_U:    ('b','#bc8f8f',''),
         TEXT:                   ('','#000000',''),
         LINE:                   ('>ti#555555','#000000',''),
         PAGEBACKGROUND:         '#FFFFFF'
         }

 defaultColors = lite

 def Usage():
     """
  -----------------------------------------------------------------------------
   PySourceColor.py ver: %s
  -----------------------------------------------------------------------------
   Module summary:
      This module is designed to colorize python source code.
          Input--->python source
          Output-->colorized (html, html4.01/css, xhtml1.0)
      Standalone:
          This module will work from the command line with options.
          This module will work with redirected stdio.
      Imported:
          This module can be imported and used directly in your code.
  -----------------------------------------------------------------------------
   Command line options:
      -h, --help
          Optional-> Display this help message.
      -t, --test
          Optional-> Will ignore all others flags but  --profile
              test all schemes and markup combinations
      -p, --profile
          Optional-> Works only with --test or -t
              runs profile.py and makes the test work in quiet mode.
      -i, --in, --input
          Optional-> If you give input on stdin.
          Use any of these for the current dir (.,cwd)
          Input can be file or dir.
          Input from stdin use one of the following (-,stdin)
          If stdin is used as input stdout is output unless specified.
      -o, --out, --output
          Optional-> output dir for the colorized source.
              default: output dir is the input dir.
          To output html to stdout use one of the following (-,stdout)
          Stdout can be used without stdin if you give a file as input.
      -c, --color
          Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs
              default: dark 
      -s, --show
          Optional-> Show page after creation.
              default: no show
      -m, --markup
          Optional-> html, css, xhtml
              css, xhtml also support external stylesheets (-e,--external)
              default: HTML
      -e, --external
          Optional-> use with css, xhtml
              Writes an style sheet instead of embedding it in the page
              saves it as pystyle.css in the same directory.
              html markup will silently ignore this flag.
      -H, --header
          Opional-> add a page header to the top of the output
          -H
              Builtin header (name,date,hrule)
          --header
              You must specify a filename.
              The header file must be valid html
              and must handle its own font colors.
              ex. --header c:/tmp/header.txt
      -F, --footer
          Opional-> add a page footer to the bottom of the output
          -F 
              Builtin footer (hrule,name,date)
          --footer
              You must specify a filename.
              The footer file must be valid html
              and must handle its own font colors.
              ex. --footer c:/tmp/footer.txt  
      -l, --linenumbers
          Optional-> default is no linenumbers
              Adds line numbers to the start of each line in the code.
     --convertpage
          Given a webpage that has code embedded in tags it will
              convert embedded code to colorized html. 
              (see pageconvert for details)
  -----------------------------------------------------------------------------
   Option usage:
    # Test and show pages
       python PySourceColor.py -t -s
    # Test and only show profile results
       python PySourceColor.py -t -p
    # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd)
       python PySourceColor.py -i .
    # Using long options w/ =
       python PySourceColor.py --in=c:/myDir/my.py --color=lite --show
    # Using short options w/out =
       python PySourceColor.py -i c:/myDir/  -c idle -m css -e
    # Using any mix
       python PySourceColor.py --in . -o=c:/myDir --show
    # Place a custom header on your files
       python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt
  -----------------------------------------------------------------------------
   Stdio usage:
    # Stdio using no options
       python PySourceColor.py < c:/MyFile.py >> c:/tmp/MyFile.html
    # Using stdin alone automatically uses stdout for output: (stdin,-)
       python PySourceColor.py -i- < c:/MyFile.py >> c:/tmp/myfile.html
    # Stdout can also be written to directly from a file instead of stdin
       python PySourceColor.py -i c:/MyFile.py -m css -o- >> c:/tmp/myfile.html
    # Stdin can be used as input , but output can still be specified
       python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py
  _____________________________________________________________________________
  """
     print Usage.__doc__% (__version__)
     sys.exit(1)

 ###################################################### Command line interface

 def cli():
     """Handle command line args and redirections"""
     try:
         # try to get command line args
         opts, args = getopt.getopt(sys.argv[1:],
               "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet",
               "test", "external", "linenumbers", "convertpage", "profile",
               "input=", "output=", "color=", "markup=","header=", "footer="])
     except getopt.GetoptError:
         # on error print help information and exit:
         Usage()
     # init some names
     input = None
     output = None
     colorscheme = None
     markup = 'html'
     header = None
     footer = None
     linenumbers = 0
     show = 0
     quiet = 0
     test = 0
     profile = 0
     convertpage = 0
     form = None
     # if we have args then process them
     for o, a in opts:
         if o in ["-h", "--help"]:
             Usage()
             sys.exit()
         if o in ["-o", "--output", "--out"]:
             output = a
         if o in ["-i", "--input", "--in"]:
             input = a
             if input in [".", "cwd"]:
                 input = os.getcwd()
         if o in ["-s", "--show"]:
             show = 1
         if o in ["-q", "--quiet"]:
             quiet = 1
         if o in ["-t", "--test"]:
             test = 1
         if o in ["--convertpage"]:
             convertpage = 1
         if o in ["-p", "--profile"]:
             profile = 1
         if o in ["-e", "--external"]:
             form = 'external'
         if o in ["-m", "--markup"]:
             markup = str(a)
         if o in ["-l", "--linenumbers"]:
             linenumbers = 1
         if o in ["--header"]:
             header = str(a)
         elif o == "-H":
             header = ''
         if o in ["--footer"]:
             footer = str(a)
         elif o == "-F":
             footer = ''
         if o in ["-c", "--color"]:
             try:
                 colorscheme = globals().get(a.lower())
             except:
                 traceback.print_exc()
                 Usage()
     if test:
         if profile:
             import profile
             profile.run('_test(show=%s, quiet=%s)'%(show,quiet))
         else:
             # Parse this script in every possible colorscheme and markup
             _test(show,quiet)
     elif input in [None, "-", "stdin"] or output in ["-", "stdout"]:
         # determine if we are going to use stdio
         if input not in [None, "-", "stdin"]:
             if os.path.isfile(input) :
                 path2stdout(input, colors=colorscheme, markup=markup,
                             linenumbers=linenumbers, header=header,
                             footer=footer, form=form)
             else:
                 raise PathError, 'File does not exists!'
         else:
             try:
                 if sys.stdin.isatty():
                     raise InputError, 'Please check input!'
                 else:
                     if output in [None,"-","stdout"]:
                         str2stdout(sys.stdin.read(), colors=colorscheme,
                                    markup=markup, header=header,
                                    footer=footer, linenumbers=linenumbers,
                                    form=form)
                     else:
                         str2file(sys.stdin.read(), outfile=output, show=show,
                                 markup=markup, header=header, footer=footer,
                                 linenumbers=linenumbers, form=form)
             except:
                 traceback.print_exc()
                 Usage()
     else:
         if os.path.exists(input):
             if convertpage:
                 # if there was at least an input given we can proceed
                 pageconvert(input, out=output, colors=colorscheme,
                             show=show, markup=markup,linenumbers=linenumbers)
             else:
                 # if there was at least an input given we can proceed
                 convert(source=input, outdir=output, colors=colorscheme,
                         show=show, markup=markup, quiet=quiet, header=header,
                         footer=footer, linenumbers=linenumbers, form=form)
         else:
             raise PathError, 'File does not exists!'
             Usage()

 ######################################################### Simple markup tests

 def _test(show=0, quiet=0):
     """Test the parser and most of the functions.
 
        There are 19 test total(eight colorschemes in three diffrent markups,
        and a str2file test. Most functions are tested by this.
     """
     fi = sys.argv[0]
     if not fi.endswith('.exe'):# Do not test if frozen as an archive
         # this is a collection of test, most things are covered.
         path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet)
         path2file(fi, '/tmp/null_css.html', null, show=show,
                   markup='css', quiet=quiet)
         path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet)
         path2file(fi, '/tmp/mono_css.html', mono, show=show,
                   markup='css', quiet=quiet)
         path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet)
         path2file(fi, '/tmp/lite_css.html', lite, show=show,
                   markup='css', quiet=quiet, header='', footer='',
                   linenumbers=1)
         path2file(fi, '/tmp/lite_xhtml.html', lite, show=show,
                   markup='xhtml', quiet=quiet)
         path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet)
         path2file(fi, '/tmp/dark_css.html', dark, show=show,
                   markup='css', quiet=quiet, linenumbers=1)
         path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet)
         path2file(fi, '/tmp/dark2_css.html', dark2, show=show,
                   markup='css', quiet=quiet)
         path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show,
                   markup='xhtml', quiet=quiet, header='', footer='',
                   linenumbers=1, form='external')
         path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet)
         path2file(fi, '/tmp/idle_css.html', idle, show=show,
                   markup='css', quiet=quiet)
         path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show,
                   quiet=quiet, linenumbers=1)
         path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show,
                   markup='css', linenumbers=1, quiet=quiet)
         path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show,
                   quiet=quiet)
         path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show,
                   markup='css', quiet=quiet)
         teststr=r'''"""This is a test of decorators and other things"""
 # This should be line 421...
 @whatever(arg,arg2)
 @A @B(arghh) @C
 def LlamaSaysNi(arg='Ni!',arg2="RALPH"):
    """This docstring is deeply disturbed by all the llama references"""
    print '%s The Wonder Llama says %s'% (arg2,arg)
 # So I was like duh!, and he was like ya know?!,
 # and so we were both like huh...wtf!? RTFM!! LOL!!;)
 @staticmethod## Double comments are KewL.
 def LlamasRLumpy():
    """This docstring is too sexy to be here.
    """
    u"""
 =============================
 A Møøse once bit my sister...
 =============================
    """
    ## Relax, this won't hurt a bit, just a simple, painless procedure,
    ## hold still while I get the anesthetizing hammer.
    m = {'three':'1','won':'2','too':'3'}
    o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..'
    python = uR""" 
  No realli! She was Karving her initials øn the møøse with the sharpened end  
  of an interspace tøøthbrush given her by Svenge - her brother-in-law -an Oslo
  dentist and star of many Norwegian møvies: "The Høt Hands of an Oslo         
  Dentist", "Fillings of Passion", "The Huge Mølars of Horst Nordfink"..."""
    RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box?
    n = u' HERMSGERVØRDENBRØTBØRDA ' + """ YUTTE """
    t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04"
    ## We apologise for the fault in the
    ## comments. Those responsible have been
    ## sacked.
    y = '14 NORTH CHILEAN GUANACOS \
 (CLOSELY RELATED TO THE LLAMA)'
    rules = [0,1,2,3,4,5]
    print y'''
         htmlPath = os.path.abspath('/tmp/strtest.html')
         str2file(teststr, htmlPath, colors=dark, markup='xhtml',
                  linenumbers=420, show=show)
         _printinfo("  wrote %s" % htmlPath, quiet)
     else:
         Usage()
     return
 ####################################################### User funtctions

 def str2stdout(sourcestring, colors=None, title='', markup='html',
                  header=None, footer=None,
                  linenumbers=0, form=None):
     """Converts a code(string) to colorized HTML. Writes to stdout.
 
        form='code',or'snip' (for "<pre>yourcode</pre>" only)
        colors=null,mono,lite,dark,dark2,idle,or pythonwin
     """
     Parser(sourcestring, colors=colors, title=title, markup=markup,
            header=header, footer=footer,
            linenumbers=linenumbers).format(form)

 def path2stdout(sourcepath, title='', colors=None, markup='html',
                    header=None, footer=None,
                    linenumbers=0, form=None):
     """Converts code(file) to colorized HTML. Writes to stdout.
 
        form='code',or'snip' (for "<pre>yourcode</pre>" only)
        colors=null,mono,lite,dark,dark2,idle,or pythonwin
     """
     sourcestring = open(sourcepath).read()
     Parser(sourcestring, colors=colors, title=sourcepath,
            markup=markup, header=header, footer=footer,
            linenumbers=linenumbers).format(form)

 def str2html(sourcestring, colors=None, title='',
                markup='html', header=None, footer=None,
                linenumbers=0, form=None):
     """Converts a code(string) to colorized HTML. Returns an HTML string.
 
        form='code',or'snip' (for "<pre>yourcode</pre>" only)
        colors=null,mono,lite,dark,dark2,idle,or pythonwin
     """
     stringIO = cStringIO.StringIO()
     Parser(sourcestring, colors=colors, title=title, out=stringIO,
            markup=markup, header=header, footer=footer,
            linenumbers=linenumbers).format(form)
     stringIO.seek(0)
     return stringIO.read()

 def str2css(sourcestring, colors=None, title='',
               markup='css', header=None, footer=None,
               linenumbers=0, form=None):
     """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string
        
        If form != None then this will return (stylesheet_str, code_str)
        colors=null,mono,lite,dark,dark2,idle,or pythonwin
     """
     if markup.lower() not in ['css' ,'xhtml']:
         markup = 'css'
     stringIO = cStringIO.StringIO()
     parse = Parser(sourcestring, colors=colors, title=title,
                    out=stringIO, markup=markup,
                    header=header, footer=footer,
                    linenumbers=linenumbers)
     parse.format(form)
     stringIO.seek(0)
     if form != None:
         return parse._sendCSSStyle(external=1), stringIO.read()
     else:
         return None, stringIO.read()

 def str2markup(sourcestring, colors=None, title = '',
                markup='xhtml', header=None, footer=None,
               linenumbers=0, form=None):
     """ Convert code strings into ([stylesheet or None], colorized string) """
     if markup.lower() == 'html':
         return None, str2html(sourcestring, colors=colors, title=title,
                    header=header, footer=footer, markup=markup,
                    linenumbers=linenumbers, form=form)
     else:
         return str2css(sourcestring, colors=colors, title=title,
                    header=header, footer=footer, markup=markup,
                    linenumbers=linenumbers, form=form)

 def str2file(sourcestring, outfile, colors=None, title='',
                markup='html', header=None, footer=None,
                linenumbers=0, show=0, dosheet=1, form=None):
     """Converts a code string to a file.
 
        makes no attempt at correcting bad pathnames
     """
     css , html = str2markup(sourcestring, colors=colors, title='',
                     markup=markup, header=header, footer=footer,
                     linenumbers=linenumbers, form=form)
     # write html
     f = open(outfile,'wt')
     f.writelines(html)
     f.close()
     #write css
     if css != None and dosheet:
         dir = os.path.dirname(outfile)
         outfile = os.path.join(dir,'pystyle.css')
         f = open(outfile,'wt')
         f.writelines(css)
         f.close()
     if show:
         showpage(outfile)

 def path2html(sourcepath, colors=None, markup='html',
                 header=None, footer=None,
                 linenumbers=0, form=None):
     """Converts code(file) to colorized HTML. Returns an HTML string.
 
        form='code',or'snip' (for "<pre>yourcode</pre>" only)
        colors=null,mono,lite,dark,dark2,idle,or pythonwin
     """
     stringIO = cStringIO.StringIO()
     sourcestring = open(sourcepath).read()
     Parser(sourcestring, colors, title=sourcepath, out=stringIO,
            markup=markup, header=header, footer=footer,
            linenumbers=linenumbers).format(form)
     stringIO.seek(0)
     return stringIO.read()

 def convert(source, outdir=None, colors=None,
               show=0, markup='html', quiet=0,
               header=None, footer=None, linenumbers=0, form=None):
     """Takes a file or dir as input and places the html in the outdir.
 
        If outdir is none it defaults to the input dir
     """
     count=0
     # If it is a filename then path2file
     if not os.path.isdir(source):
         if os.path.isfile(source):
             count+=1
             path2file(source, outdir, colors, show, markup,
                      quiet, form, header, footer, linenumbers, count)
         else:
             raise PathError, 'File does not exist!'
     # If we pass in a dir we need to walkdir for files.
     # Then we need to colorize them with path2file
     else:
         fileList = walkdir(source)
         if fileList != None:
             # make sure outdir is a dir
             if outdir != None:
                 if os.path.splitext(outdir)[1] != '':
                     outdir = os.path.split(outdir)[0]
             for item in fileList:
                 count+=1
                 path2file(item, outdir, colors, show, markup,
                           quiet, form, header, footer, linenumbers, count)
             _printinfo('Completed colorizing %s files.'%str(count), quiet)
         else:
             _printinfo("No files to convert in dir.", quiet)

 def path2file(sourcePath, out=None, colors=None, show=0,
                 markup='html', quiet=0, form=None,
                 header=None, footer=None, linenumbers=0, count=1):
     """ Converts python source to html file"""
     # If no outdir is given we use the sourcePath
     if out == None:#this is a guess
         htmlPath = sourcePath + '.html'
     else:
         # If we do give an out_dir, and it does
         # not exist , it will be created.
         if os.path.splitext(out)[1] == '':
             if not os.path.isdir(out):
                 os.makedirs(out)
             sourceName = os.path.basename(sourcePath)
             htmlPath = os.path.join(out,sourceName)+'.html'
         # If we do give an out_name, and its dir does
         # not exist , it will be created.
         else:
             outdir = os.path.split(out)[0]
             if not os.path.isdir(outdir):
                 os.makedirs(outdir)
             htmlPath = out
     htmlPath = os.path.abspath(htmlPath)
     # Open the text and do the parsing.
     source = open(sourcePath).read()
     parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'),
                    markup, header, footer, linenumbers)
     parse.format(form)
     _printinfo("  wrote %s" % htmlPath, quiet)
     # html markup will ignore the external flag, but
     # we need to stop the blank file from being written.
     if form == 'external' and count == 1 and markup != 'html':
         cssSheet = parse._sendCSSStyle(external=1)
         cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css')
         css = open(cssPath, 'wt')
         css.write(cssSheet)
         css.close()
         _printinfo("    wrote %s" % cssPath, quiet)
     if show:
         # load HTML page into the default web browser.
         showpage(htmlPath)
     return htmlPath

 def tagreplace(sourcestr, colors=lite, markup='xhtml',
                linenumbers=0, dosheet=1, tagstart='<PY>'.lower(),
                tagend='</PY>'.lower(), stylesheet='pystyle'):
     """This is a helper function for pageconvert. Returns css, page.
     """
     if markup.lower() != 'html':
         link  = '<LINK REL="STYLESHEET" HREF="%s.CSS" TYPE="TEXT/CSS"/></HEAD>'
         css = link.lower()%stylesheet
         if css.find(sourcestr) == -1:
             sourcestr  = sourcestr.replace('</HEAD>'.lower(), css)
     starttags = sourcestr.count(tagstart)
     endtags = sourcestr.count(tagend)
     if starttags:
         if starttags == endtags:
             for _ in range(starttags):
                datastart = sourcestr.find(tagstart)
                dataend = sourcestr.find(tagend)
                data = sourcestr[datastart+len(tagstart):dataend]
                data = unescape(data)
                css , data = str2markup(data, colors=colors,
                          linenumbers=linenumbers, markup=markup, form='embed')
                start = sourcestr[:datastart]
                end = sourcestr[dataend+len(tagend):]
                sourcestr =  ''.join([start,data,end])
         else:
             raise InputError,'Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend
     if not dosheet:
         css = None
     return css, sourcestr

 def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0,
                   dosheet=1, tagstart='<PY>'.lower(), tagend='</PY>'.lower(),
                   stylesheet='pystyle', show=1, returnstr=0):
     """This function can colorize Python source
 
        that is written in a webpage enclosed in tags.
     """
     if out == None:
         out = os.path.dirname(path)
     infile = open(path, 'r').read()
     css,page  = tagreplace(sourcestr=infile,colors=colors,
                    markup=markup, linenumbers=linenumbers, dosheet=dosheet,
                    tagstart=tagstart, tagend=tagend, stylesheet=stylesheet)
     if not returnstr:
         newpath = os.path.abspath(os.path.join(
                   out,'tmp', os.path.basename(path)))
         if not os.path.exists(newpath):
             try:
                 os.makedirs(os.path.dirname(newpath))
             except:
                 pass#traceback.print_exc()
                 #Usage()
         y = open(newpath, 'w')
         y.write(page)
         y.close()
         if css:
             csspath = os.path.abspath(os.path.join(
                       out,'tmp','%s.css'%stylesheet))
             x = open(csspath,'w')
             x.write(css)
             x.close()
         if show:
             try:
                 os.startfile(newpath)
             except:
                 traceback.print_exc()
         return newpath
     else:
         return css, page

 ##################################################################### helpers

 def walkdir(dir):
     """Return a list of .py and .pyw files from a given directory.
 
        This function can be written as a generator Python 2.3, or a genexp
        in Python 2.4. But 2.2 and 2.1 would be left out....
     """
     # Get a list of files that match *.py*
     GLOB_PATTERN = os.path.join(dir, "*.[p][y]*")
     pathlist = glob.glob(GLOB_PATTERN)
     # Now filter out all but py and pyw
     filterlist = [x for x in pathlist
                         if x.endswith('.py')
                         or x.endswith('.pyw')]
     if filterlist != []:
         # if we have a list send it
         return filterlist
     else:
         return None

 def showpage(path):
     """Helper function to open webpages"""
     try:
         webbrowser.open_new(os.path.abspath(path))
     except:
         traceback.print_exc()

 def _printinfo(message, quiet):
     """Helper to print messages"""
     if not quiet:
         print message

 def escape(text):
      """escape text for html. similar to cgi.escape"""
      text = text.replace("&", "&amp;")
      text = text.replace("<", "&lt;")
      text = text.replace(">", "&gt;")
      return text

 def unescape(text):
      """unsecape escaped text"""
      text = text.replace("&quot;", '"')
      text = text.replace("&gt;", ">")
      text = text.replace("&lt;", "<")
      text = text.replace("&amp;", "&")
      return text

 ########################################################### Custom Exceptions

 class PySourceColorError(Exception):
     # Base for custom errors
     def __init__(self, msg=''):
         self._msg = msg
         Exception.__init__(self, msg)
     def __repr__(self):
         return self._msg
     __str__ = __repr__

 class PathError(PySourceColorError):
     def __init__(self, msg):
        PySourceColorError.__init__(self,
          'Path error! : %s'% msg)

 class InputError(PySourceColorError):
    def __init__(self, msg):
        PySourceColorError.__init__(self,
          'Input error! : %s'% msg)

 ########################################################## Python code parser

 class Parser(object):

     """MoinMoin python parser heavily chopped :)"""

     def __init__(self, raw, colors=None, title='', out=sys.stdout,
                    markup='html', header=None, footer=None, linenumbers=0,
                    numberlinks=0):
         """Store the source text & set some flags"""
         if colors == None:
             colors = defaultColors
         self.raw = raw.expandtabs().rstrip()
         self.title = os.path.basename(title)
         self.out = out
         self.lasttext = ''
         self.argFlag = 0
         self.classFlag = 0
         self.defFlag = 0
         self.decoratorFlag = 0
         self.external = 0
         self.numberlinks = numberlinks# experimental
         self.markup = markup.upper()
         self.colors = colors
         self.header = header
         self.footer = footer
         self.doArgs = 1 #  overrides the new tokens
         self.doNames = 1 #  overrides the new tokens
         self.doMathOps = 1 #  overrides the new tokens
         self.doBrackets = 1 #  overrides the new tokens
         self.doURL = 1 # override url conversion
         # this is the linenumber placeholder.
         # had to make it lowercase to pass self tests ;)
         self.LINENUMHOLDER = "___line___".upper()
         self.dolinenums = self.linenum = linenumbers

     def format(self, form=None):
         """Parse and send the colorized source"""
         if form in ('snip','code'):
             self.addEnds = 0
         elif form == 'embed':
             self.addEnds = 0
             self.external = 1
         else:
             if form == 'external':
                 self.external = 1
             self.addEnds = 1

         # Store line offsets in self.lines
         self.lines = [0, 0]
         pos = 0

         # Add linenumbers
         if self.dolinenums:
             start=self.LINENUMHOLDER
         else:
             start=''
         newlines = []
         lines = self.raw.splitlines(1)#keepends
         for l in lines:
              # span and div escape for customizing and embedding urls
              if (l.startswith('#$#')
                   or l.startswith('#%#')
                   or l.startswith('#@#')):
                 newlines.append(l)
              else:
                 newlines.append(start+' '+l)#space is needed
         self.raw = "".join(newlines)

         # Gather lines
         while 1:
             pos = self.raw.find('\n', pos) + 1
             if not pos: break
             self.lines.append(pos)
         self.lines.append(len(self.raw))

         # Wrap text in a filelike object
         self.pos = 0
         text = cStringIO.StringIO(self.raw)

         # Markup start
         if self.addEnds:
             self._doPageStart()
         else:
             self._doSnippetStart()

         ## Tokenize calls the __call__
         ## function for each token till done.
         # Parse the source and write out the results.
         try:
             tokenize.tokenize(text.readline, self)
         except tokenize.TokenError, ex:
             msg = ex[0]
             line = ex[1][0]
             self.out.write("<h3>ERROR: %s</h3>%s\n"%
                             (msg, self.raw[self.lines[line]:]))
             traceback.print_exc()

         # Markup end
         if self.addEnds:
             self._doPageEnd()
         else:
             self._doSnippetEnd()

     def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
         """Token handler. Order is important do not rearrange."""
         # Calculate new positions
         oldpos = self.pos
         newpos = self.lines[srow] + scol
         self.pos = newpos + len(toktext)
         # Handle newlines
         if toktype in (token.NEWLINE, tokenize.NL):
             self.out.write('\n')
             return

         # Send the original whitespace, and tokenize backslashes if present.
         # Tokenizer.py just sends continued line backslashes with whitespace.
         # This is a hack to tokenize continued line slashes as operators.
         # Should continued line backslashes be treated as operators
         # or some other token?
         if newpos > oldpos:
             if self.raw[oldpos:newpos].isspace():
                 self.out.write(self.raw[oldpos:newpos])
             else:
                 slash = self.raw[oldpos:newpos].find('\\')+oldpos
                 self.out.write(self.raw[oldpos:slash])
                 getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\')
                 self.linenum+=1
                 self.out.write(self.raw[slash+1:newpos])

         # Skip indenting tokens
         if toktype in (token.INDENT, token.DEDENT):
             self.pos = newpos
             return

         # Look for operators
         if token.LPAR <= toktype and toktype <= token.OP:
             # Trap decorators py2.4 >
             if toktext == '@':
                 toktype = DECORATOR
                 # Set a flag if this was the decorator start so
                 # the decorator name and arguments can be identified
                 self.decoratorFlag = self.argFlag = 1
             else:
                 if self.doArgs:
                     # Find the start for arguments
                     if toktext == '(' and self.argFlag:
                         self.argFlag = 2
                     # Find the end for arguments
                     elif toktext == ':':
                         self.argFlag = 0
                 ## Seperate the diffrent operator types
                 # Brackets
                 if self.doBrackets and toktext in ['[',']','(',')','{','}']:
                     toktype = BRACKETS
                 # Math operators !=, ==, +, -, **, \, <=, >= etc.
                 elif self.doMathOps and toktext not in [':','.','=',';',',',
                                                          '*=','**=','-=','+=',
                                                          '|=','^=','/=','%=',
                                                          '>>=','<<=','`']:
                     toktype = MATH_OPERATOR
                 # Operator and in-place assignment
                 else:
                     toktype = OPERATOR
                     # example how flags should work.
                     # def fun(arg=argvalue,arg2=argvalue2):
                     # 0   1  2 A 1   N   2  A  1    N     0
                     if toktext == "=" and self.argFlag == 2:
                          self.argFlag = 1
                     elif toktext == "," and self.argFlag == 1:
                         self.argFlag = 2
         # Look for keywords
         elif toktype == NAME and keyword.iskeyword(toktext):
             toktype = KEYWORD
             # Set a flag if this was the class / def start so
             # the class / def name and arguments can be identified
             if toktext in ['class', 'def']:
                 if toktext =='class' and \
                          not line[:line.find('class')].endswith('.'):
                     self.classFlag = self.argFlag = 1
                 elif toktext == 'def' and \
                          not line[:line.find('def')].endswith('.'):
                     self.defFlag = self.argFlag = 1
                 else:
                     # must have used a keyword as a name i.e. self.class
                     toktype = ERRORTOKEN

         # Look for class, def, decorator name
         elif (self.classFlag or self.defFlag or self.decoratorFlag) \
                and self.doNames:
             if self.classFlag:
                 self.classFlag = 0
                 toktype = CLASS_NAME
             elif self.defFlag:
                 self.defFlag = 0
                 toktype = DEF_NAME
             elif self.decoratorFlag:
                 self.decoratorFlag = 0
                 toktype = DECORATOR_NAME

         # Look for strings
         # Order of evaluation is important do not change.
         elif toktype == token.STRING:
             text = toktext.lower()
             # TRIPLE DOUBLE QUOTE's
             if (text[:3] == '"""'):
                 toktype = TRIPLEDOUBLEQUOTE
             elif (text[:4] == 'r"""'):
                 toktype = TRIPLEDOUBLEQUOTE_R
             elif (text[:4] == 'u"""' or
                    text[:5] == 'ur"""'):
                 toktype = TRIPLEDOUBLEQUOTE_U
             # DOUBLE QUOTE's
             elif (text[:1] == '"'):
                 toktype = DOUBLEQUOTE
             elif (text[:2] == 'r"'):
                 toktype = DOUBLEQUOTE_R
             elif (text[:2] == 'u"' or
                    text[:3] == 'ur"'):
                 toktype = DOUBLEQUOTE_U
             # TRIPLE SINGLE QUOTE's
             elif (text[:3] == "'''"):
                  toktype = TRIPLESINGLEQUOTE
             elif (text[:4] == "r'''"):
                 toktype = TRIPLESINGLEQUOTE_R
             elif (text[:4] == "u'''" or
                    text[:5] == "ur'''"):
                 toktype = TRIPLESINGLEQUOTE_U
             # SINGLE QUOTE's
             elif (text[:1] == "'"):
                 toktype = SINGLEQUOTE
             elif (text[:2] == "r'"):
                 toktype = SINGLEQUOTE_R
             elif (text[:2] == "u'" or
                    text[:3] == "ur'"):
                 toktype = SINGLEQUOTE_U

             # test for invalid string declaration
             if self.lasttext.lower() == 'ru':
                 toktype = ERRORTOKEN

         # Look for comments
         elif toktype == COMMENT:
             if toktext[:2] == "##":
                 toktype = DOUBLECOMMENT
             elif toktext[:3] == '#$#':
                 toktype = TEXT
                 self.textFlag = 'SPAN'
                 toktext = toktext[3:]
             elif toktext[:3] == '#%#':
                 toktype = TEXT
                 self.textFlag = 'DIV'
                 toktext = toktext[3:]
             elif toktext[:3] == '#@#':
                 toktype = TEXT
                 self.textFlag = 'RAW'
                 toktext = toktext[3:]
             if self.doURL:
                 # this is a 'fake helper function'
                 # Alias_name or url(URI)
                 url_pos = toktext.find('url(')
                 if url_pos != -1:
                     before = toktext[:url_pos]
                     url = toktext[url_pos+4:]
                     splitpoint = url.find(',')
                     endpoint = url.find(')')
                     after = url[endpoint+1:]
                     url = url[:endpoint]
                     if splitpoint != -1:
                         urlparts = url.split(',',1)
                         toktext = '%s<a href="%s">%s</a>%s'%(
                                    before,urlparts[0],urlparts[1].lstrip(),after)
                     else:
                         toktext = '%s<a href="%s">%s</a>%s'%(before,url,url,after)

         # Seperate errors from decorators
         elif toktype == ERRORTOKEN:
             # Bug fix for < py2.4
             # space between decorators
             if self.argFlag and toktext.isspace():
                 toktype = NAME
             # trap decorators < py2.4
             if toktext == '@':
                 toktype = DECORATOR
                 # Set a flag if this was the decorator start so
                 # the decorator name and arguments can be identified
                 self.decoratorFlag = self.argFlag = 1
             # Bug fix for py2.2 linenumbers with decorators
             if toktext.isspace():
                toktype = NAME
         # Seperate args from names
         elif (self.argFlag == 2 and
               toktype == NAME and
               toktext != 'None' and
               self.doArgs):
             toktype = ARGS

         # Look for line numbers
         # The conversion code for them is in the send_text functions.
         if toktext == self.LINENUMHOLDER:
             toktype = LINE

         # Skip blank token that made it thru
         ## bugfix for the last empty tags. 
         if toktext == '':
             return

         # Last token text history
         self.lasttext = toktext

         # escape all but the urls in the comments
         if toktype in (DOUBLECOMMENT, COMMENT):
             if toktext.find('<a href=') == -1:
                 toktext = escape(toktext)
             else:
                 pass
         elif toktype == TEXT:
             pass
         else:
             toktext = escape(toktext)

         # Send text for any markup
         getattr(self, '_send%sText'%(self.markup))(toktype, toktext)
         return

     ################################################################# Helpers

     def _doSnippetStart(self):
         if self.markup == 'HTML':
             # Start of html snippet
             self.out.write('<pre>\n')
         else:
             # Start of css/xhtml snippet
             self.out.write('<pre class="py">\n')

     def _doSnippetEnd(self):
         # End of html snippet
         self.out.write('</pre>\n')

     ######################################################## markup selectors

     def _getFile(self, filepath):
         try:
             _file = open(filepath,'r')
             content = _file.read()
             _file.close()
         except:
             traceback.print_exc()
             content = ''
         return content

     def _doPageStart(self):
         getattr(self, '_do%sStart'%(self.markup))()

     def _doPageHeader(self):
         if self.header != None:
             if self.header.find('#$#') != -1 or \
                 self.header.find('#%#') != -1:
                 self.out.write(self.header[3:])
             else:
                 if self.header != '':
                     self.header = self._getFile(self.header)
                 getattr(self, '_do%sHeader'%(self.markup))()

     def _doPageFooter(self):
         if self.footer != None:
             if self.footer.find('#$#') != -1 or \
                 self.header.find('#%#') != -1:
                 self.out.write(self.footer[3:])
             else:
                 if self.header != '':
                     self.footer = self._getFile(self.footer)
                 getattr(self, '_do%sFooter'%(self.markup))()

     def _doPageEnd(self):
         getattr(self, '_do%sEnd'%(self.markup))()

     ################################################### color/style retrieval
     ## Some of these are not used anymore but are kept for documentation

     def _getLineNumber(self):
         num = self.linenum
         self.linenum+=1
         return  str(num).rjust(5)+" "

     def _getTags(self, key):
         # style tags
         return self.colors.get(key, self.colors[NAME])[0]

     def _getForeColor(self, key):
         # get text foreground color, if not set to black
         color = self.colors.get(key, self.colors[NAME])[1]
         if color[:1] != '#':
             color = '#000000'
         return color

     def _getBackColor(self, key):
         # get text background color
         return self.colors.get(key, self.colors[NAME])[2]

     def _getPageColor(self):
         # get page background color
         return self.colors.get(PAGEBACKGROUND, '#FFFFFF')

     def _getStyle(self, key):
         # get the token style from the color dictionary
         return self.colors.get(key, self.colors[NAME])

     def _getMarkupClass(self, key):
         # get the markup class name from the markup dictionary
         return MARKUPDICT.get(key, MARKUPDICT[NAME])

     def _getDocumentCreatedBy(self):
         return '<!--This document created by %s ver.%s on: %s-->\n'%(
                   __title__,__version__,time.ctime())

     ################################################### HTML markup functions

     def _doHTMLStart(self):
         # Start of html page
         self.out.write('<!DOCTYPE html PUBLIC \
 "-//W3C//DTD HTML 4.01//EN">\n')
         self.out.write('<html><head><title>%s</title>\n'%(self.title))
         self.out.write(self._getDocumentCreatedBy())
         self.out.write('<meta http-equiv="Content-Type" \
 content="text/html;charset=iso-8859-1">\n')
         # Get background
         self.out.write('</head><body bgcolor="%s">\n'%self._getPageColor())
         self._doPageHeader()
         self.out.write('<pre>')

     def _getHTMLStyles(self, toktype, toktext):
         # Get styles
         tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
         tagstart=[]
         tagend=[]
         # check for styles and set them if needed.
         if 'b' in tags:#Bold
             tagstart.append('<b>')
             tagend.append('</b>')
         if 'i' in tags:#Italics
             tagstart.append('<i>')
             tagend.append('</i>')
         if 'u' in tags:#Underline
             tagstart.append('<u>')
             tagend.append('</u>')
         # HTML tags should be paired like so : <b><i><u>Doh!</u></i></b>
         tagend.reverse()
         starttags="".join(tagstart)
         endtags="".join(tagend)
         return starttags,endtags,color

     def _sendHTMLText(self, toktype, toktext):
         numberlinks = self.numberlinks
         # If it is an error, set a red box around the bad tokens
         # older browsers should ignore it
         if toktype == ERRORTOKEN:
             style = ' style="border: solid 1.5pt #FF0000;"'
         else:
             style = ''
         # Get styles
         starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
         # This is a hack to 'fix' multi-line  strings.
         # Multi-line strings are treated as only one token 
         # even though they can be several physical lines.
         # That makes it hard to spot the start of a line,
         # because at this level all we know about are tokens.
         if toktext.count(self.LINENUMHOLDER):
             # rip apart the string and separate it by line.
             # count lines and change all linenum token to line numbers.
             # embedded all the new font tags inside the current one.
             # Do this by ending the tag first then writing our new tags,
             # then starting another font tag exactly like the first one.
             splittext = toktext.split(self.LINENUMHOLDER)
             store = []
             store.append(splittext.pop(0))
             lstarttag, lendtag, lcolor = self._getHTMLStyles(LINE, toktext)
             count = len(splittext)
             for item in splittext:
                 num =  self._getLineNumber()
                 if numberlinks:
                     numstrip = num.strip()
                     content= '<a name="%s" href="?%s#%s">%s</a>' \
                               %(numstrip,numstrip,numstrip,num)
                 else:
                     content = num
                 if count <= 1:
                     endtag,starttag = '',''
                 linenumber= ''.join([endtag,'<font color=', lcolor, '>',
                             lstarttag, content, lendtag, '</font>' ,starttag])
                 store.append(linenumber+item)
             toktext = ''.join(store)
         # send text
         ## Output optimization
         # skip font tag if black text, but styles will still be sent. (b,u,i)
         if color !='#000000':
             startfont = '<font color="%s"%s>'%(color, style)
             endfont = '</font>'
         else:
             startfont, endfont = ('','')
         if toktype != LINE:
             self.out.write(''.join([startfont,starttag,
                                      toktext,endtag,endfont]))
         else:
             self.out.write(toktext)
         return

     def _doHTMLHeader(self):
         # Optional
         if self.header != '':
             self.out.write('%s\n'%self.header)
         else:
             color = self._getForeColor(token.NAME)
             self.out.write('<b><font color="%s"># %s \
                             <br># %s</font></b><hr>\n'%
                            (color, self.title, time.ctime()))

     def _doHTMLFooter(self):
         # Optional
         if self.footer != '':
             self.out.write('%s\n'%self.footer)
         else:
             color = self._getForeColor(token.NAME)
             self.out.write('<b><font color="%s"> \
                             <hr># %s<br># %s</font></b>\n'%
                            (color, self.title, time.ctime()))

     def _doHTMLEnd(self):
         # End of html page
         self.out.write('</pre>\n')
         # Write a little info at the bottom
         self._doPageFooter()
         self.out.write('</body></html>\n')

     #################################################### CSS markup functions

     def _getCSSStyle(self, key):
         # Get the tags and colors from the dictionary
         tags, forecolor, backcolor = self._getStyle(key)
         style=[]
         border = None
         bordercolor = None
         tags = tags.lower()
         if tags:
             # get the border color if specified
             # the border color will be appended to
             # the list after we define a border
             if '#' in tags:# border color 
                 start = tags.find('#')
                 end = start + 7
                 bordercolor = tags[start:end]
                 tags.replace(bordercolor,'',1)
             # text styles
             if 'b' in tags:# Bold
                 style.append('font-weight:bold;')
             else:
                 style.append('font-weight:normal;')
             if 'i' in tags:# Italic
                 style.append('font-style:italic;')
             if 'u' in tags:# Underline
                 style.append('text-decoration:underline;')
             # border size
             if 'l' in tags:# thick border
                 size='thick'
             elif 'm' in tags:# medium border
                 size='medium'
             elif 't' in tags:# thin border
                 size='thin'
             else:# default
                 size='medium'
             # border styles
             if 'n' in tags:# inset border
                 border='inset'
             elif 'o' in tags:# outset border
                 border='outset'
             elif 'r' in tags:# ridge border
                 border='ridge'
             elif 'g' in tags:# groove border
                 border='groove'
             elif '=' in tags:# double border
                 border='double'
             elif '.' in tags:# dotted border
                 border='dotted'
             elif '-' in tags:# dashed border
                 border='dashed'
             elif 's' in tags:# solid border 
                 border='solid'
             # border type check
             seperate_sides=0
             for side in ['<','>','^','v']:
                 if side in tags:
                     seperate_sides+=1
             # border box or seperate sides
             if seperate_sides==0 and border:
                     style.append('border: %s %s;'%(border,size))
             else:
                 if border == None:
                    border = 'solid'
                 if 'v' in tags:# bottom border
                     style.append('border-bottom:%s %s;'%(border,size))
                 if '<' in tags:# left border
                     style.append('border-left:%s %s;'%(border,size))
                 if '>' in tags:# right border
                     style.append('border-right:%s %s;'%(border,size))
                 if '^' in tags:# top border
                     style.append('border-top:%s %s;'%(border,size))
         else:
             style.append('font-weight:normal;')# inherited style fix    
         # we have to define our borders before we set colors
         if bordercolor:
             style.append('border-color:%s;'%bordercolor)
         # text forecolor  
         style.append('color:%s;'% forecolor)
         # text backcolor
         if backcolor:
             style.append('background-color:%s;'%backcolor)
         return (self._getMarkupClass(key),' '.join(style))

     def _sendCSSStyle(self, external=0):
         """ create external and internal style sheets"""
         styles = []
         external += self.external
         if not external:
             styles.append('<style type="text/css">\n<!--\n')
         # Get page background color and write styles ignore any we don't know
         styles.append('body { background:%s; }\n'%self._getPageColor())
         styles.append('.py {}\n')# this is for the styling the pre tag.
         # write out the various css styles
         for key in MARKUPDICT:
             styles.append('.%s { %s }\n'%self._getCSSStyle(key))
         if not self.external:
              styles.append('--></style>\n')
         return ''.join(styles)

     def _doCSSStart(self):
         # Start of css/html 4.01 page
         self.out.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">\n')
         self.out.write('<html><head><title>%s</title>\n'%(self.title))
         self.out.write(self._getDocumentCreatedBy())
         self.out.write('<meta http-equiv="Content-Type" \
 content="text/html;charset=iso-8859-1">\n')
         self._doCSSStyleSheet()
         self.out.write('</head>\n<body>\n')
         # Write a little info at the top.
         self._doPageHeader()
         self.out.write('<pre>')
         return

     def _doCSSStyleSheet(self):
         if not self.external:
             # write an embedded style sheet
             self.out.write(self._sendCSSStyle())
         else:
             # write a link to an external style sheet
             self.out.write('<link rel="stylesheet" \
 href="pystyle.css" type="text/css">')
         return

     def _sendCSSText(self, toktype, toktext):
         numberlinks = self.numberlinks
         # This is a hack to 'fix' multi-line strings.
         # Multi-line strings are treated as only one token 
         # even though they can be several physical lines.
         # That makes it hard to spot the start of a line,
         # because at this level all we know about are tokens.
         if toktext.count(self.LINENUMHOLDER):
             # rip apart the string and separate it by line
             # count lines and change all linenum token to line numbers
             # embed linenum span inside current span class
             newmarkup = MARKUPDICT.get(LINE, MARKUPDICT[NAME])
             lstartspan = '<span class="%s">'%(newmarkup)
             splittext = toktext.split(self.LINENUMHOLDER)
             store = []
             store.append(splittext.pop(0))
             for item in splittext:
                 num = self._getLineNumber()
                 if numberlinks:
                     numstrip = num.strip()
                     content= '<a name="%s" href="?%s#%s">%s</a>' \
                               %(numstrip,numstrip,numstrip,num)
                 else:
                     content = num
                 linenumber= ''.join([lstartspan,content,'</span>'])
                 store.append(linenumber+item)
             toktext = ''.join(store)
         # Send text
         if toktype != LINE:
             markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
             if toktype == TEXT and self.textFlag == 'DIV':
                 startspan = '<div class="%s">'%(markupclass)
                 endspan = '</div>'
             elif toktype == TEXT and self.textFlag == 'RAW':
                 startspan,endspan = ('','')
             else:
                 startspan = '<span class="%s">'%(markupclass)
                 endspan = '</span>'
             self.out.write(''.join([startspan, toktext, endspan]))
         else:
             self.out.write(toktext)
         return

     def _doCSSHeader(self):
         if self.header != '':
             self.out.write('%s\n'%self.header)
         else:
             name = MARKUPDICT.get(NAME)
             self.out.write('<div class="%s"># %s <br> \
 # %s</div><hr>\n'%(name, self.title, time.ctime()))

     def _doCSSFooter(self):
         # Optional
         if self.footer != '':
             self.out.write('%s\n'%self.footer)
         else:
             self.out.write('<hr><div class="%s"># %s <br> \
 # %s</div>\n'%(MARKUPDICT.get(NAME),self.title, time.ctime()))

     def _doCSSEnd(self):
         # End of css/html page
         self.out.write('</pre>\n')
         # Write a little info at the bottom
         self._doPageFooter()
         self.out.write('</body></html>\n')
         return

     ################################################## XHTML markup functions

     def _doXHTMLStart(self):
         # XHTML is really just XML + HTML 4.01.
         # We only need to change the page headers, 
         # and a few tags to get valid XHTML.
         # Start of xhtml page
         self.out.write('<?xml version="1.0"?>\n \
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n \
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n \
 <html xmlns="http://www.w3.org/1999/xhtml">\n')
         self.out.write('<head><title>%s</title>\n'%(self.title))
         self.out.write(self._getDocumentCreatedBy())
         self.out.write('<meta http-equiv="Content-Type" \
 content="text/html;charset=iso-8859-1"/>\n')
         self._doXHTMLStyleSheet()
         self.out.write('</head>\n<body>\n')
         # Write a little info at the top.
         self._doPageHeader()
         self.out.write('<pre>')
         return

     def _doXHTMLStyleSheet(self):
         if not self.external:
             # write an embedded style sheet
             self.out.write(self._sendCSSStyle())
         else:
             # write a link to an external style sheet
             self.out.write('<link rel="stylesheet" \
 href="pystyle.css" type="text/css"/>\n')
         return

     def _sendXHTMLText(self, toktype, toktext):
         self._sendCSSText(toktype, toktext)

     def _doXHTMLHeader(self):
         # Optional
         if self.header:
             self.out.write('%s\n'%self.header)
         else:
             name = MARKUPDICT.get(NAME)
             self.out.write('<div class="%s"># %s <br/> \
 # %s</div><hr/>\n '%(
             name, self.title, time.ctime()))

     def _doXHTMLFooter(self):
         # Optional
         if self.footer:
             self.out.write('%s\n'%self.footer)
         else:
             self.out.write('<hr/><div class="%s"># %s <br/> \
 # %s</div>\n'%(MARKUPDICT.get(NAME), self.title, time.ctime()))

     def _doXHTMLEnd(self):
         self._doCSSEnd()

 #############################################################################

 if __name__ == '__main__':
     cli()

 #############################################################################
 # PySourceColor.py
 # 2004, 2005 M.E.Farmer Jr.
 # Python license