#! /usr/bin/env python __version__ = '$Revision: 36356 $' import os.pathimport reimport stringimport sys from xml.sax.saxutils import quoteattr bang_join = "!".joinnull_join = "".join REPLACEMENTS = [ # Hackish way to deal with macros replaced with simple text (re.compile(r"\\ABC\b"), "ABC"), (re.compile(r"\\ASCII\b"), "ASCII"), (re.compile(r"\\Cpp\b"), "C++"), (re.compile(r"\\EOF\b"), "EOF"), (re.compile(r"\\NULL\b"), "NULL"), (re.compile(r"\\POSIX\b"), "POSIX"), (re.compile(r"\\UNIX\b"), "Unix"), # deal with turds left over from LaTeX2HTML (re.compile(r"<#\d+#>"), ""), ] class Node: continuation = 0 def __init__(self, link, str, seqno): self.links = [link] self.seqno = seqno for pattern, replacement in REPLACEMENTS: str = pattern.sub(replacement, str) # build up the text self.text = split_entry_text(str) self.key = split_entry_key(str) def __cmp__(self, other): """Comparison operator includes sequence number, for use with list.sort().""" return self.cmp_entry(other) or cmp(self.seqno, other.seqno) def cmp_entry(self, other): """Comparison 'operator' that ignores sequence number.""" c = 0 for i in range(min(len(self.key), len(other.key))): c = (cmp_part(self.key[i], other.key[i]) or cmp_part(self.text[i], other.text[i])) if c: break return c or cmp(self.key, other.key) or cmp(self.text, other.text) def __repr__(self): return "<Node for %s (%s)>" % (bang_join(self.text), self.seqno) def __str__(self): return bang_join(self.key) def dump(self): return "%s\1%s###%s\n" \ % ("\1".join(self.links), bang_join(self.text), self.seqno) def cmp_part(s1, s2): result = cmp(s1, s2) if result == 0: return 0 l1 = s1.lower() l2 = s2.lower() minlen = min(len(s1), len(s2)) if len(s1) < len(s2) and l1 == l2[:len(s1)]: result = -1 elif len(s2) < len(s1) and l2 == l1[:len(s2)]: result = 1 else: result = cmp(l1, l2) or cmp(s1, s2) return result def split_entry(str, which): stuff = [] parts = str.split('!') parts = [part.split('@') for part in parts] for entry in parts: if len(entry) != 1: key = entry[which] else: key = entry[0] stuff.append(key) return stuff _rmtt = re.compile(r"""(.*)<tt(?: class=['"][a-z0-9]+["'])?>(.*)</tt>(.*)$""", re.IGNORECASE)_rmparens = re.compile(r"\(\)") def split_entry_key(str): parts = split_entry(str, 1) for i in range(len(parts)): m = _rmtt.match(parts[i]) if m: parts[i] = null_join(m.group(1, 2, 3)) else: parts[i] = parts[i].lower() # remove '()' from the key: parts[i] = _rmparens.sub('', parts[i]) return map(trim_ignored_letters, parts) def split_entry_text(str): if '<' in str: m = _rmtt.match(str) if m: str = null_join(m.group(1, 2, 3)) return split_entry(str, 1) def load(fp): nodes = [] rx = re.compile("(.*)\1(.*)###(.*)$") while 1: line = fp.readline() if not line: break m = rx.match(line) if m: link, str, seqno = m.group(1, 2, 3) nodes.append(Node(link, str, seqno)) return nodes def trim_ignored_letters(s): # ignore $ to keep environment variables with the # leading letter from the name if s.startswith("$"): return s[1:].lower() else: return s.lower() def get_first_letter(s): if s.startswith("<tex2html_percent_mark>"): return "%" else: return trim_ignored_letters(s)[0] def split_letters(nodes): letter_groups = [] if nodes: group = [] append = group.append letter = get_first_letter(nodes[0].text[0]) letter_groups.append((letter, group)) for node in nodes: nletter = get_first_letter(node.text[0]) if letter != nletter: letter = nletter group = [] letter_groups.append((letter, group)) append = group.append append(node) return letter_groups def group_symbols(groups): entries = [] ident_letters = string.ascii_letters + "_" while groups[0][0] not in ident_letters: entries += groups[0][1] del groups[0] if entries: groups.insert(0, ("Symbols", entries)) # need a function to separate the nodes into columns...def split_columns(nodes, columns=1): if columns <= 1: return [nodes] # This is a rough height; we may have to increase to avoid breaks before # a subitem. colheight = int(len(nodes) / columns) numlong = int(len(nodes) % columns) if numlong: colheight = colheight + 1 else: numlong = columns cols = [] for i in range(numlong): start = i * colheight end = start + colheight cols.append(nodes[start:end]) del nodes[:end] colheight = colheight - 1 try: numshort = int(len(nodes) / colheight) except ZeroDivisionError: cols = cols + (columns - len(cols)) * [[]] else: for i in range(numshort): start = i * colheight end = start + colheight cols.append(nodes[start:end]) # # If items continue across columns, make sure they are marked # as continuations so the user knows to look at the previous column. # for i in range(len(cols) - 1): try: prev = cols[i][-1] next = cols[i + 1][0] except IndexError: return cols else: n = min(len(prev.key), len(next.key)) for j in range(n): if prev.key[j] != next.key[j]: break next.continuation = j + 1 return cols DL_LEVEL_INDENT = " " def format_column(nodes): strings = ["<dl compact='compact'>"] append = strings.append level = 0 previous = [] for node in nodes: current = node.text count = 0 for i in range(min(len(current), len(previous))): if previous[i] != current[i]: break count = i + 1 if count > level: append("<dl compact='compact'>" * (count - level) + "\n") level = count elif level > count: append("\n") append(level * DL_LEVEL_INDENT) append("</dl>" * (level - count)) level = count # else: level == count for i in range(count, len(current) - 1): term = node.text[i] level = level + 1 if node.continuation > i: extra = " (continued)" else: extra = "" append("\n<dt>%s%s\n<dd>\n%s<dl compact='compact'>" % (term, extra, level * DL_LEVEL_INDENT)) append("\n%s<dt>%s%s</a>" % (level * DL_LEVEL_INDENT, node.links[0], node.text[-1])) for link in node.links[1:]: append(",\n%s %s[Link]</a>" % (level * DL_LEVEL_INDENT, link)) previous = current append("\n") append("</dl>" * (level + 1)) return null_join(strings) def format_nodes(nodes, columns=1): strings = [] append = strings.append if columns > 1: colnos = range(columns) colheight = int(len(nodes) / columns) if len(nodes) % columns: colheight = colheight + 1 colwidth = int(100 / columns) append('<table width="100%"><tr valign="top">') for col in split_columns(nodes, columns): append('<td width="%d%%">\n' % colwidth) append(format_column(col)) append("\n</td>") append("\n</tr></table>") else: append(format_column(nodes)) return null_join(strings) def format_letter(letter): if letter == '.': lettername = ". (dot)" elif letter == '_': lettername = "_ (underscore)" else: lettername = letter.capitalize() return "\n<hr />\n<h2 id=%s>%s</h2>\n\n" \ % (quoteattr("letter-" + letter), lettername) def format_html_letters(nodes, columns, group_symbol_nodes): letter_groups = split_letters(nodes) if group_symbol_nodes: group_symbols(letter_groups) items = [] for letter, nodes in letter_groups: s = "<b><a href=\"#letter-%s\">%s</a></b>" % (letter, letter) items.append(s) s = ["<hr /><center>\n%s</center>\n" % " |\n".join(items)] for letter, nodes in letter_groups: s.append(format_letter(letter)) s.append(format_nodes(nodes, columns)) return null_join(s) def format_html(nodes, columns): return format_nodes(nodes, columns) def collapse(nodes): """Collapse sequences of nodes with matching keys into a single node. Destructive.""" if len(nodes) < 2: return prev = nodes[0] i = 1 while i < len(nodes): node = nodes[i] if not node.cmp_entry(prev): prev.links.append(node.links[0]) del nodes[i] else: i = i + 1 prev = node def dump(nodes, fp): for node in nodes: fp.write(node.dump()) def process_nodes(nodes, columns, letters=0, group_symbol_nodes=0): nodes.sort() collapse(nodes) if letters: return format_html_letters(nodes, columns, group_symbol_nodes) else: return format_html(nodes, columns) def main(): import getopt ifn = "-" ofn = "-" columns = 1 letters = 0 group_symbol_nodes = 1 opts, args = getopt.getopt(sys.argv[1:], "c:lo:", ["columns=", "dont-group-symbols", "group-symbols", "letters", "output="]) for opt, val in opts: if opt in ("-o", "--output"): ofn = val elif opt in ("-c", "--columns"): columns = int(val, 10) elif opt in ("-l", "--letters"): letters = 1 elif opt == "--group-symbols": group_symbol_nodes = 1 elif opt == "--dont-group-symbols": group_symbol_nodes = 0 if not args: args = [ifn] nodes = [] for fn in args: nodes = nodes + load(open(fn)) num_nodes = len(nodes) html = process_nodes(nodes, columns, letters, group_symbol_nodes) program = os.path.basename(sys.argv[0]) if ofn == "-": sys.stdout.write(html) sys.stderr.write("\n%s: %d index nodes" % (program, num_nodes)) else: open(ofn, "w").write(html) print print "%s: %d index nodes" % (program, num_nodes) if __name__ == "__main__": main()
#! /usr/bin/env python
__version__ = '$Revision: 36356 $'
import os.path
import re
import string
import sys
from xml.sax.saxutils import quoteattr
bang_join = "!".join
null_join = "".join
REPLACEMENTS = [
# Hackish way to deal with macros replaced with simple text
(re.compile(r"\\ABC\b"), "ABC"),
(re.compile(r"\\ASCII\b"), "ASCII"),
(re.compile(r"\\Cpp\b"), "C++"),
(re.compile(r"\\EOF\b"), "EOF"),
(re.compile(r"\\NULL\b"), "NULL"),
(re.compile(r"\\POSIX\b"), "POSIX"),
(re.compile(r"\\UNIX\b"), "Unix"),
# deal with turds left over from LaTeX2HTML
(re.compile(r"<#\d+#>"), ""),
]
class Node:
continuation = 0
def __init__(self, link, str, seqno):
self.links = [link]
self.seqno = seqno
for pattern, replacement in REPLACEMENTS:
str = pattern.sub(replacement, str)
# build up the text
self.text = split_entry_text(str)
self.key = split_entry_key(str)
def __cmp__(self, other):
"""Comparison operator includes sequence number, for use with
list.sort()."""
return self.cmp_entry(other) or cmp(self.seqno, other.seqno)
def cmp_entry(self, other):
"""Comparison 'operator' that ignores sequence number."""
c = 0
for i in range(min(len(self.key), len(other.key))):
c = (cmp_part(self.key[i], other.key[i])
or cmp_part(self.text[i], other.text[i]))
if c:
break
return c or cmp(self.key, other.key) or cmp(self.text, other.text)
def __repr__(self):
return "<Node for %s (%s)>" % (bang_join(self.text), self.seqno)
def __str__(self):
return bang_join(self.key)
def dump(self):
return "%s\1%s###%s\n" \
% ("\1".join(self.links),
bang_join(self.text),
self.seqno)
def cmp_part(s1, s2):
result = cmp(s1, s2)
if result == 0:
return 0
l1 = s1.lower()
l2 = s2.lower()
minlen = min(len(s1), len(s2))
if len(s1) < len(s2) and l1 == l2[:len(s1)]:
result = -1
elif len(s2) < len(s1) and l2 == l1[:len(s2)]:
result = 1
else:
result = cmp(l1, l2) or cmp(s1, s2)
return result
def split_entry(str, which):
stuff = []
parts = str.split('!')
parts = [part.split('@') for part in parts]
for entry in parts:
if len(entry) != 1:
key = entry[which]
else:
key = entry[0]
stuff.append(key)
return stuff
_rmtt = re.compile(r"""(.*)<tt(?: class=['"][a-z0-9]+["'])?>(.*)</tt>(.*)$""",
re.IGNORECASE)
_rmparens = re.compile(r"\(\)")
def split_entry_key(str):
parts = split_entry(str, 1)
for i in range(len(parts)):
m = _rmtt.match(parts[i])
if m:
parts[i] = null_join(m.group(1, 2, 3))
else:
parts[i] = parts[i].lower()
# remove '()' from the key:
parts[i] = _rmparens.sub('', parts[i])
return map(trim_ignored_letters, parts)
def split_entry_text(str):
if '<' in str:
m = _rmtt.match(str)
if m:
str = null_join(m.group(1, 2, 3))
return split_entry(str, 1)
def load(fp):
nodes = []
rx = re.compile("(.*)\1(.*)###(.*)$")
while 1:
line = fp.readline()
if not line:
break
m = rx.match(line)
if m:
link, str, seqno = m.group(1, 2, 3)
nodes.append(Node(link, str, seqno))
return nodes
def trim_ignored_letters(s):
# ignore $ to keep environment variables with the
# leading letter from the name
if s.startswith("$"):
return s[1:].lower()
else:
return s.lower()
def get_first_letter(s):
if s.startswith("<tex2html_percent_mark>"):
return "%"
else:
return trim_ignored_letters(s)[0]
def split_letters(nodes):
letter_groups = []
if nodes:
group = []
append = group.append
letter = get_first_letter(nodes[0].text[0])
letter_groups.append((letter, group))
for node in nodes:
nletter = get_first_letter(node.text[0])
if letter != nletter:
letter = nletter
group = []
letter_groups.append((letter, group))
append = group.append
append(node)
return letter_groups
def group_symbols(groups):
entries = []
ident_letters = string.ascii_letters + "_"
while groups[0][0] not in ident_letters:
entries += groups[0][1]
del groups[0]
if entries:
groups.insert(0, ("Symbols", entries))
# need a function to separate the nodes into columns...
def split_columns(nodes, columns=1):
if columns <= 1:
return [nodes]
# This is a rough height; we may have to increase to avoid breaks before
# a subitem.
colheight = int(len(nodes) / columns)
numlong = int(len(nodes) % columns)
if numlong:
colheight = colheight + 1
else:
numlong = columns
cols = []
for i in range(numlong):
start = i * colheight
end = start + colheight
cols.append(nodes[start:end])
del nodes[:end]
colheight = colheight - 1
try:
numshort = int(len(nodes) / colheight)
except ZeroDivisionError:
cols = cols + (columns - len(cols)) * [[]]
else:
for i in range(numshort):
start = i * colheight
end = start + colheight
cols.append(nodes[start:end])
#
# If items continue across columns, make sure they are marked
# as continuations so the user knows to look at the previous column.
#
for i in range(len(cols) - 1):
try:
prev = cols[i][-1]
next = cols[i + 1][0]
except IndexError:
return cols
else:
n = min(len(prev.key), len(next.key))
for j in range(n):
if prev.key[j] != next.key[j]:
break
next.continuation = j + 1
return cols
DL_LEVEL_INDENT = " "
def format_column(nodes):
strings = ["<dl compact='compact'>"]
append = strings.append
level = 0
previous = []
for node in nodes:
current = node.text
count = 0
for i in range(min(len(current), len(previous))):
if previous[i] != current[i]:
break
count = i + 1
if count > level:
append("<dl compact='compact'>" * (count - level) + "\n")
level = count
elif level > count:
append("\n")
append(level * DL_LEVEL_INDENT)
append("</dl>" * (level - count))
level = count
# else: level == count
for i in range(count, len(current) - 1):
term = node.text[i]
level = level + 1
if node.continuation > i:
extra = " (continued)"
else:
extra = ""
append("\n<dt>%s%s\n<dd>\n%s<dl compact='compact'>"
% (term, extra, level * DL_LEVEL_INDENT))
append("\n%s<dt>%s%s</a>"
% (level * DL_LEVEL_INDENT, node.links[0], node.text[-1]))
for link in node.links[1:]:
append(",\n%s %s[Link]</a>" % (level * DL_LEVEL_INDENT, link))
previous = current
append("\n")
append("</dl>" * (level + 1))
return null_join(strings)
def format_nodes(nodes, columns=1):
strings = []
append = strings.append
if columns > 1:
colnos = range(columns)
colheight = int(len(nodes) / columns)
if len(nodes) % columns:
colheight = colheight + 1
colwidth = int(100 / columns)
append('<table width="100%"><tr valign="top">')
for col in split_columns(nodes, columns):
append('<td width="%d%%">\n' % colwidth)
append(format_column(col))
append("\n</td>")
append("\n</tr></table>")
else:
append(format_column(nodes))
return null_join(strings)
def format_letter(letter):
if letter == '.':
lettername = ". (dot)"
elif letter == '_':
lettername = "_ (underscore)"
else:
lettername = letter.capitalize()
return "\n<hr />\n<h2 id=%s>%s</h2>\n\n" \
% (quoteattr("letter-" + letter), lettername)
def format_html_letters(nodes, columns, group_symbol_nodes):
letter_groups = split_letters(nodes)
if group_symbol_nodes:
group_symbols(letter_groups)
items = []
for letter, nodes in letter_groups:
s = "<b><a href=\"#letter-%s\">%s</a></b>" % (letter, letter)
items.append(s)
s = ["<hr /><center>\n%s</center>\n" % " |\n".join(items)]
for letter, nodes in letter_groups:
s.append(format_letter(letter))
s.append(format_nodes(nodes, columns))
return null_join(s)
def format_html(nodes, columns):
return format_nodes(nodes, columns)
def collapse(nodes):
"""Collapse sequences of nodes with matching keys into a single node.
Destructive."""
if len(nodes) < 2:
return
prev = nodes[0]
i = 1
while i < len(nodes):
node = nodes[i]
if not node.cmp_entry(prev):
prev.links.append(node.links[0])
del nodes[i]
else:
i = i + 1
prev = node
def dump(nodes, fp):
for node in nodes:
fp.write(node.dump())
def process_nodes(nodes, columns, letters=0, group_symbol_nodes=0):
nodes.sort()
collapse(nodes)
if letters:
return format_html_letters(nodes, columns, group_symbol_nodes)
else:
return format_html(nodes, columns)
def main():
import getopt
ifn = "-"
ofn = "-"
columns = 1
letters = 0
group_symbol_nodes = 1
opts, args = getopt.getopt(sys.argv[1:], "c:lo:",
["columns=", "dont-group-symbols",
"group-symbols", "letters", "output="])
for opt, val in opts:
if opt in ("-o", "--output"):
ofn = val
elif opt in ("-c", "--columns"):
columns = int(val, 10)
elif opt in ("-l", "--letters"):
letters = 1
elif opt == "--group-symbols":
group_symbol_nodes = 1
elif opt == "--dont-group-symbols":
group_symbol_nodes = 0
if not args:
args = [ifn]
nodes = []
for fn in args:
nodes = nodes + load(open(fn))
num_nodes = len(nodes)
html = process_nodes(nodes, columns, letters, group_symbol_nodes)
program = os.path.basename(sys.argv[0])
if ofn == "-":
sys.stdout.write(html)
sys.stderr.write("\n%s: %d index nodes" % (program, num_nodes))
else:
open(ofn, "w").write(html)
print
print "%s: %d index nodes" % (program, num_nodes)
if __name__ == "__main__":
main()