# HTML related routines.
# -*- mode: python; coding: utf-8 -*-
# Copyright © 2007 Translation Project.
# Copyright © 2000 Progiciels Bourbeau-Pinard inc.
# François Pinard , 2000.
import commands, os, re, stat, string, sys
# Generic file transformations.
def transform_generic(input, html_dir=None):
# If HTML_DIR is not given, results to standard output.
if os.path.isdir(input):
# When a directory, process the interesting files it contains.
names = os.listdir(input)
names.sort()
for name in names:
if name in ('NEWS', 'README', 'THANKS', 'TODO'):
transform_generic('%s/%s' % (input, name), html_dir)
else:
base, extension = os.path.splitext(name)
if extension in ('.all', '.html'):
transform_generic('%s/%s' % (input, name), html_dir)
return
directory, filename = os.path.split(input)
if filename == 'AUTHORS':
transform_authors(input, html_dir and '%s/authors.html' % html_dir,
'Who have signed a disclaimer')
elif filename == 'NEWS':
transform_verbatim(input, html_dir and '%s/news.html' % html_dir,
'NEWS - History of user-visible changes')
elif filename == 'README':
transform_allout(input, html_dir and '%s/readme.html' % html_dir,
'README - Introductory notes')
elif filename == 'THANKS':
transform_thanks(input, html_dir and '%s/thanks.html' % html_dir,
'THANKS - People who have contributed')
elif filename == 'TODO':
transform_verbatim(input, html_dir and '%s/todo.html' % html_dir,
'TODO - Things still to be done')
else:
base, extension = os.path.splitext(filename)
output = html_dir and '%s/%s.html' % (html_dir, base)
if extension == '.all':
transform_allout(input, output, None)
elif extension == '.html':
transform_html(input, output, None)
else:
transform_verbatim(input, output,
'The %s file' % string.upper(filename))
# Configuration and layout of generated HTML pages.
class Htmlpage:
def __init__(self, input, output):
self.init_configuration()
self.init_writer(input, output)
self.init_layout()
def init_configuration(self):
self.configuration = {
'page_title': '(Title not set)',
'time_stamp': commands.getoutput('date "+%Y-%m-%d %H:%M %z"')}
try:
import config
for key, value in config.environ.items():
self.configuration[key] = value
except ImportError:
pass
def init_writer(self, input, output):
if not output:
self.cgi = 1
self.writer = sys.stdout.write
return
if input and os.path.exists(output):
input_time = os.stat(input)[stat.ST_MTIME]
output_time = os.stat(output)[stat.ST_MTIME]
if input_time <= output_time:
layout = self.configuration.get('html_layout_file')
if layout:
layout_time = os.stat(layout)[stat.ST_MTIME]
if layout_time <= output_time:
self.writer = None
return
else:
self.writer = None
return
self.cgi = 0
self.writer = open(output, 'w').write
def init_layout(self):
layout = self.configuration.get('html_layout_file')
if layout:
layout_text = open(layout).read()
else:
layout_text = self.default_layout()
self.layout_start, self.layout_end = string.split(layout_text,
'INSERTION_POINT\n')
def default_layout(self):
return '''\
%(page_title)s
INSERTION_POINT
Last recomputed on %(time_stamp)s
'''
def prologue(self, title, charset=None):
write = self.writer
if self.cgi:
if charset:
write('Content-Type: text/html; charset=%s\n\n' % charset)
else:
write('Content-Type: text/html\n\n')
self.configuration['page_title'] = title % self.configuration
text = self.layout_start % self.configuration
if self.cgi or not charset:
write(text)
else:
position = string.find(text, '= 0
position = string.find(text, '>', position+5)
assert position >= 0
position = position + 1
write(text[:position])
write('\n '
% charset)
write(text[position:])
write(' %(page_title)s
\n' % self.configuration)
line = self.html_buttons()
if line:
write(line)
write('
\n')
def epilogue(self):
write = self.writer
line = self.html_buttons()
if line:
write('
\n')
write(line)
write(self.layout_end % self.configuration)
def writelines(self, lines):
write = self.writer
for line in lines:
if string.find(line, '%(') >= 0:
try:
write(line % self.configuration)
except KeyError:
write(line)
else:
write(line)
def html_buttons(self):
return None
# Transform a page, taken almost verbatim, into HTML.
def transform_verbatim(input, output=None, title=None):
Verbatim(input, output, title)
class Verbatim(Htmlpage):
def __init__(self, input, output, title):
Htmlpage.__init__(self, input, output)
write = self.writer
if not write:
return
lines = open(input).readlines()
self.prologue(title or self.find_title(lines), charset="utf-8")
write('\n')
for line in lines:
write(enhance(line, 1))
write('\n')
self.epilogue()
def find_title(self, lines):
title = Allout.find_title(self, lines)
if not title and lines and lines[0]:
title = string.strip(lines[0])
return title
# Transform the AUTHORS file into a simple HTML table.
def transform_authors(input, output=None, title=None):
Authors(input, output, title)
class Authors(Htmlpage):
def __init__(self, input, output, title):
Htmlpage.__init__(self, input, output)
write = self.writer
if not write:
return
lines = open(input).readlines()
self.prologue(title, charset="utf-8")
write('\n')
write('| Author | Signed | Language\n')
tack = False
for line in lines:
if line[:12] == "TRANSLATIONS":
name = " ".join(line.split()[1:-1])
date = line.split()[-1]
write(' | | %s | %s' % (name, date))
tack = True
elif tack:
tack = False
if name == "Ingenieurburo":
write(' | |
\n')
continue
team = line.split("[")[1].split("]")[0]
write('%s\n' % team)
write(' |
\n')
self.epilogue()
# Transform an `allout' outline file into HTML.
def transform_allout(input, output=None, title=None):
Allout(input, output, title)
class Allout(Htmlpage):
def __init__(self, input, output, title):
Htmlpage.__init__(self, input, output)
write = self.writer
if not write:
return
lines = open(input).readlines()
if lines and lines[0] and lines[0][0] != '*':
abort('* line expected')
self.prologue(title or self.find_title(lines), charset="utf-8")
self.element = None
self.level = -1
self.margin = ' '
counter = 1
while counter < len(lines):
line = lines[counter]
#warning('%d<%s>%s' % (level, margin, line))
if line[0] == '*':
abort('Title unexpected')
match = re.match(r'\.( *)[-*+:.,@] (.+)', line)
if match:
self.maybe_stop('p')
self.maybe_stop('pre')
self.header(len(match.group(1)), match.group(2))
counter = counter + 1
continue
if line == '\n':
self.maybe_stop('p')
write(line)
counter = counter + 1
continue
if string.find(line, '\t') >= 0:
self.maybe_start('pre')
if line[:1] == '\t':
line = line[1:]
write(enhance(line))
counter = counter + 1
continue
if line[:len(self.margin)] == self.margin:
#warning('<%s>%d:%s' % (margin, match.end(), line))
line = line[len(self.margin):]
if line[-5:] == '----\n':
counter = counter + 1
continue
if string.find(line, ' ') >= 0:
self.maybe_start('pre')
else:
self.maybe_start('p')
write(enhance(line))
counter = counter + 1
self.maybe_stop('p')
self.maybe_stop('pre')
while self.level >= 0:
self.level = self.level - 1
self.margin = ' ' * (self.level+3)
write('%s\n' % self.margin)
self.epilogue()
def find_title(self, lines):
if lines and lines[0]:
match = re.match(r'\* (.*)', lines[0])
if match:
title = match.group(1)
title = re.sub(r'-\*- .* -\*-', '', title, 1)
title = re.sub(r'[ \t]allout', '', title, 1)
return string.strip(title)
return None
def header(self, goal, title):
write = self.writer
while self.level < goal:
write('%s\n' % self.margin)
self.level = self.level + 1
self.margin = ' ' * (self.level+3)
while self.level > goal:
self.level = self.level - 1
self.margin = ' ' * (self.level+3)
write('%s
' % self.margin)
write('%s%s'
% (self.margin, self.level+2, enhance(title), self.level+2))
def maybe_start(self, tag):
write = self.writer
if tag != self.element:
if self.element:
write('%s>\n' % self.element)
write('%s<%s>\n' % (self.margin, tag))
self.element = tag
def maybe_stop(self, tag):
write = self.writer
if tag == self.element:
write('%s>\n' % self.element)
self.element = None
# Transform an HTML file into another.
def transform_html(input, output=None, title=None):
Html(input, output, title)
class Html(Htmlpage):
def __init__(self, input, output, title):
Htmlpage.__init__(self, input, output)
write = self.writer
if not write:
return
lines = open(input).readlines()
self.prologue(title or self.find_title(lines), charset="utf-8")
start = 0
while start < len(lines):
line = lines[start]
start = start + 1
if string.find(line, '') >= 0:
break
if string.find(line, '') >= 0:
break
end = len(lines)
while end > start:
end = end - 1
line = lines[end]
if string.find(line, '') >= 0:
break
if string.find(line, '') >= 0:
break
self.writelines(lines[start:end])
self.epilogue()
def find_title(self, lines):
title = None
for line in lines:
if string.find(line, '') >= 0:
break
if string.find(line, '') >= 0:
break
position = string.find(line, '')
if position >= 0:
title = line[position+7:]
line = ''
position = string.find(line, '')
if position >= 0:
title = line[position+7:]
line = ''
if title:
title = title + line
position = string.find(title, '')
if position >= 0:
title = title[:position]
break
position = string.find(title, '')
if position >= 0:
title = title[:position]
break
if title:
return string.strip(title)
return None
# Transform a `THANKS' file into HTML.
def transform_thanks(input, output=None, title=None):
Thanks(input, output, title)
class Thanks(Htmlpage):
def __init__(self, input, output, title):
Htmlpage.__init__(self, input, output)
write = self.writer
if not write:
return
lines = open(input).readlines()
self.prologue(title, charset="utf-8")
# Transform the introductory paragraphs.
within_p = 0
counter = 0
while counter < len(lines):
line = lines[counter]
if line == '\n':
if within_p:
write('
\n')
within_p = 0
counter = counter + 1
continue
if re.search('\t| ', line):
if within_p:
write(' \n')
break
if not within_p:
write(' \n')
within_p = 1
write(' %s' % enhance(line))
counter = counter + 1
# Make a table with the remainder of the file.
if counter < len(lines):
write('
\n'
' \n'
' \n'
' | Contributor | \n'
' Email address | \n'
'
\n')
while counter < len(lines):
line = lines[counter]
match = re.match('([^ \t\n][^\t\n]*)$', line)
if match:
name, mailto = match.group(1), None
else:
match = re.match('([^ \t\n][^\t\n]*)\t+([^ \t\n]+)$', line)
if match:
name, mailto = match.group(1, 2)
else:
warning(line)
counter = counter + 1
continue
counter = counter + 1
if counter == len(lines):
self.produce_row(name, mailto, None)
break
line = lines[counter]
match = line and re.match('[ \t]+(http:\/\/.*[^ \t\n])$', line)
if match:
self.produce_row(name, mailto, match.group(1))
counter = counter + 1
continue
self.produce_row(name, mailto, None)
write('
\n')
self.epilogue()
def produce_row(self, name, mailto, url):
write = self.writer
write(' \n')
if url:
write(' | %s | \n'
' %s | \n' % (url, name, mailto or ''))
else:
write(' %s | \n' % name)
if mailto:
write(' %s | \n'
% (mailto, mailto))
else:
write(' | \n')
write('
\n')
# Services.
def enhance(text, verbatim=0):
text = string.replace(text, '&', '&')
text = string.replace(text, '<', '<')
text = string.replace(text, '>', '>')
text = string.replace(text, '\f', '')
if verbatim:
text = re.sub(
r'((mailto:|http://|ftp://)[-_.@~/a-zA-Z0-9]*[~/a-zA-Z0-9])',
r'\1', text)
else:
text = re.sub(
r'((mailto:|http://|ftp://)([-_.@~/a-zA-Z0-9]*[~/a-zA-Z0-9]))',
r'\3', text)
text = re.sub(
(r'(^|[^-_%+./a-zA-Z0-9:])'
r'([-_%+./a-zA-Z0-9]+@[-a-zA-Z0-9]+\.[-.a-zA-Z0-9]*[a-zA-Z0-9])'),
r'\1\2', text)
if verbatim:
text = re.sub(
r'_([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])_', r'_\1_', text)
text = re.sub(
r'\*([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])\*', r'*\1*', text)
text = re.sub(
r"`([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])'", r"`\1'",
text)
else:
text = re.sub(
r'_([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])_', r'\1', text)
text = re.sub(
r'\*([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])\*', r'\1', text)
text = re.sub(
r"`([-_@./a-zA-Z0-9]*[/a-zA-Z0-9])'", r'\1', text)
return text
def abort(message):
warning(message)
sys.exit(1)
def warning(message):
sys.stderr.write('** %s' % message)
if message[-1] != '\n':
sys.stderr.write('\n')