📄 ifile.py
字号:
# Tools for info file processing.# XXX Need to be more careful with reading ahead searching for nodes.import regexpimport string# Exported exceptions.#NoSuchFile = 'no such file'NoSuchNode = 'no such node'# The search path for info files; this is site-specific.# Directory names should end in a partname delimiter,# so they can simply be concatenated to a relative pathname.##INFOPATH = ['', ':Info.Ibrowse:', ':Info:'] # MacINFOPATH = ['', '/usr/local/emacs/info/'] # X11 on UNIX# Tunable constants.#BLOCKSIZE = 512 # Qty to align reads to, if possibleFUZZ = 2*BLOCKSIZE # Qty to back-up before searching for a nodeCHUNKSIZE = 4*BLOCKSIZE # Qty to read at once when reading lots of data# Regular expressions used.# Note that it is essential that Python leaves unrecognized backslash# escapes in a string so they can be seen by regexp.compile!#findheader = regexp.compile('\037\014?\n(.*\n)').matchfindescape = regexp.compile('\037').matchparseheader = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').matchfindfirstline = regexp.compile('^.*\n').matchfindnode = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').matchfindprev = regexp.compile('[pP]rev[ious]*:[ \t]*([^\t,\n]*)').matchfindnext = regexp.compile('[nN]ext:[ \t]*([^\t,\n]*)').matchfindup = regexp.compile('[uU]p:[ \t]*([^\t,\n]*)').matchfindmenu = regexp.compile('^\* [mM]enu:').matchfindmenuitem = regexp.compile( \ '^\* ([^:]+):[ \t]*(:|\([^\t]*\)[^\t,\n.]*|[^:(][^\t,\n.]*)').matchfindfootnote = regexp.compile( \ '\*[nN]ote ([^:]+):[ \t]*(:|[^:][^\t,\n.]*)').matchparsenoderef = regexp.compile('^\((.*)\)(.*)$').match# Get a node and all information pertaining to it.# This doesn't work if there is an indirect tag table,# and in general you are better off using icache.get_node() instead.# Functions get_whole_file() and get_file_node() provide part# functionality used by icache.# Raise NoSuchFile or NoSuchNode as appropriate.#def get_node(curfile, ref): file, node = parse_ref(curfile, ref) if node == '*': return get_whole_file(file) else: return get_file_node(file, 0, node)#def get_whole_file(file): f = try_open(file) # May raise NoSuchFile text = f.read() header, menu, footnotes = ('', '', ''), [], [] return file, '*', header, menu, footnotes, text#def get_file_node(file, offset, node): f = try_open(file) # May raise NoSuchFile text = find_node(f, offset, node) # May raise NoSuchNode node, header, menu, footnotes = analyze_node(text) return file, node, header, menu, footnotes, text# Parse a node reference into a file (possibly default) and node name.# Possible reference formats are: "NODE", "(FILE)", "(FILE)NODE".# Default file is the curfile argument; default node is Top.# A node value of '*' is a special case: the whole file should# be interpreted (by the caller!) as a single node.#def parse_ref(curfile, ref): match = parsenoderef(ref) if not match: file, node = curfile, ref else: (a, b), (a1, b1), (a2, b2) = match file, node = ref[a1:b1], ref[a2:b2] if not file: file = curfile # (Is this necessary?) if not node: node = 'Top' return file, node# Extract node name, links, menu and footnotes from the node text.#def analyze_node(text): # # Get node name and links from the header line # match = findfirstline(text) if match: (a, b) = match[0] line = text[a:b] else: line = '' node = get_it(text, findnode) prev = get_it(text, findprev) next = get_it(text, findnext) up = get_it(text, findup) # # Get the menu items, if there is a menu # menu = [] match = findmenu(text) if match: (a, b) = match[0] while 1: match = findmenuitem(text, b) if not match: break (a, b), (a1, b1), (a2, b2) = match topic, ref = text[a1:b1], text[a2:b2] if ref == ':': ref = topic menu.append((topic, ref)) # # Get the footnotes # footnotes = [] b = 0 while 1: match = findfootnote(text, b) if not match: break (a, b), (a1, b1), (a2, b2) = match topic, ref = text[a1:b1], text[a2:b2] if ref == ':': ref = topic footnotes.append((topic, ref)) # return node, (prev, next, up), menu, footnotes#def get_it(line, matcher): match = matcher(line) if not match: return '' else: (a, b), (a1, b1) = match return line[a1:b1]# Find a node in an open file.# The offset (from the tags table) is a hint about the node's position.# Pass zero if there is no tags table.# Raise NoSuchNode if the node isn't found.# NB: This seeks around in the file.#def find_node(f, offset, node): node = string.lower(node) # Just to be sure # # Position a little before the given offset, # so we may find the node even if it has moved around # in the file a little. # offset = max(0, ((offset-FUZZ) / BLOCKSIZE) * BLOCKSIZE) f.seek(offset) # # Loop, hunting for a matching node header. # while 1: buf = f.read(CHUNKSIZE) if not buf: break i = 0 while 1: match = findheader(buf, i) if match: (a,b), (a1,b1) = match start = a1 line = buf[a1:b1] i = b match = parseheader(line) if match: (a,b), (a1,b1) = match key = string.lower(line[a1:b1]) if key == node: # Got it! Now read the rest. return read_node(f, buf[start:]) elif findescape(buf, i): next = f.read(CHUNKSIZE) if not next: break buf = buf + next else: break # # If we get here, we didn't find it. Too bad. # raise NoSuchNode, node# Finish off getting a node (subroutine for find_node()).# The node begins at the start of buf and may end in buf;# if it doesn't end there, read additional data from f.#def read_node(f, buf): i = 0 match = findescape(buf, i) while not match: next = f.read(CHUNKSIZE) if not next: end = len(buf) break i = len(buf) buf = buf + next match = findescape(buf, i) else: # Got a match (a, b) = match[0] end = a # Strip trailing newlines while end > 0 and buf[end-1] == '\n': end = end-1 buf = buf[:end] return buf# Read reverse starting at offset until the beginning of a node is found.# Then return a buffer containing the beginning of the node,# with f positioned just after the buffer.# The buffer will contain at least the full header line of the node;# the caller should finish off with read_node() if it is the right node.# (It is also possible that the buffer extends beyond the node!)# Return an empty string if there is no node before the given offset.#def backup_node(f, offset): start = max(0, ((offset-CHUNKSIZE) / BLOCKSIZE) * BLOCKSIZE) end = offset while start < end: f.seek(start) buf = f.read(end-start) i = 0 hit = -1 while 1: match = findheader(buf, i) if match: (a,b), (a1,b1) = match hit = a1 i = b elif end < offset and findescape(buf, i): next = f.read(min(offset-end, BLOCKSIZE)) if not next: break buf = buf + next end = end + len(next) else: break if hit >= 0: return buf[hit:] end = start start = max(0, end - CHUNKSIZE) return ''# Make a tag table for the given file by scanning the file.# The file must be open for reading, and positioned at the beginning# (or wherever the hunt for tags must begin; it is read till the end).#def make_tags(f): tags = {} while 1: offset = f.tell() buf = f.read(CHUNKSIZE) if not buf: break i = 0 while 1: match = findheader(buf, i) if match: (a,b), (a1,b1) = match start = offset+a1 line = buf[a1:b1] i = b match = parseheader(line) if match: (a,b), (a1,b1) = match key = string.lower(line[a1:b1]) if tags.has_key(key): print 'Duplicate node:', print key tags[key] = '', start, line elif findescape(buf, i): next = f.read(CHUNKSIZE) if not next: break buf = buf + next else: break return tags# Try to open a file, return a file object if succeeds.# Raise NoSuchFile if the file can't be opened.# Should treat absolute pathnames special.#def try_open(file): for dir in INFOPATH: try: return open(dir + file, 'r') except IOError: pass raise NoSuchFile, file# A little test for the speed of make_tags().#TESTFILE = 'texinfo-1'def test_make_tags(): import time f = try_open(TESTFILE) t1 = time.millitimer() tags = make_tags(f) t2 = time.millitimer() print 'Making tag table for', `TESTFILE`, 'took', t2-t1, 'msec.'
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -