mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			377 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #! /usr/bin/env python
 | |
| 
 | |
| __version__ = '$Revision$'
 | |
| 
 | |
| import os.path
 | |
| import re
 | |
| import string
 | |
| import sys
 | |
| 
 | |
| 
 | |
| bang_join = "!".join
 | |
| null_join = "".join
 | |
| 
 | |
| 
 | |
| class Node:
 | |
|     __rmjunk = re.compile("<#\d+#>")
 | |
| 
 | |
|     continuation = 0
 | |
| 
 | |
|     def __init__(self, link, str, seqno):
 | |
|         self.links = [link]
 | |
|         self.seqno = seqno
 | |
|         # remove <#\d+#> left in by moving the data out of LaTeX2HTML
 | |
|         str = self.__rmjunk.sub('', 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>"]
 | |
|     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>" * (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>"
 | |
|                    % (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))
 | |
|     append("\n<p>\n")
 | |
|     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><a name=\"letter-%s\">%s</a></h2>\n\n" \
 | |
|            % (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()
 | 
