# NODE\tFILENAME\tANCHOR
# LANG is the document language in case it's not 'en'
# Note: The filename does not have any extension appended!
-# This file can then be used by our texi2html init script to determine
+# This file should then be used by our texi2html init script to determine
# the correct file name and anchor for external refs
+# For translated documentation: cross-references to nodes that exist
+# only in documentation in English are allowed, that's why the already
+# generated map file of docs in English is loaded with
+# --master-map-file option, then the node names that are defined in
+# the map for the manual in English but not in the translated manual
+# are added to the map for the translated manual.
+
+
import sys
import re
import os
import getopt
-options_list, files = getopt.getopt (sys.argv[1:],'o:s:hI:',
- ['output=', 'split=', 'help', 'include='])
+options_list, files = getopt.getopt (sys.argv[1:],'o:s:hI:m:k:q',
+ ['output=', 'split=',
+ 'help', 'include=',
+ 'master-map-file=',
+ 'known-missing-files=',
+ 'quiet'])
help_text = r"""Usage: %(program_name)s [OPTIONS]... TEXIFILE...
Extract files names for texinfo (sub)sections from the texinfo files.
Options:
-h, --help print this help
-I, --include=DIRECTORY append DIRECTORY to include search path
+ -m, --master-map-file=FILE use FILE as master map file
-o, --output=DIRECTORY write .xref-map files to DIRECTORY
-s, --split=MODE split manual according to MODE. Possible values
are section and custom (default)
+ -k, --known-missing-files a filename which has a list of files known
+ to be missing for this make
+ -q, --quiet suppress most messages
"""
def help (text):
outdir = '.'
split = "custom"
-include_path = []
+include_path = ['.',]
+master_map_file = ''
+known_missing_files = []
+known_missing_files_file = ''
+docs_without_directories = ['changes', 'music-glossary']
+suppress_output = False
+initial_map = {}
for opt in options_list:
o = opt[0]
a = opt[1]
if o == '-I' or o == '--include':
if os.path.isdir (a):
include_path.append (a)
+ else:
+ path_list = a.split('/')
+ file_name = path_list[len(path_list)-1]
+ if not (file_name in docs_without_directories):
+ print a, 'is not a directory.'
+ print 'Please consider adding it to the list of '
+ print 'known missing files in extract_texi_filename.py.'
elif o == '-o' or o == '--output':
outdir = a
elif o == '-s' or o == '--split':
split = a
+ elif o == '-m' or o == '--master-map-file':
+ if os.path.isfile (a):
+ master_map_file = a
+ elif o == '--known-missing-files':
+ if os.path.isfile (a):
+ known_missing_files_file = a
+ else:
+ print 'Missing files list file not found: ', a
+ elif o == '-q' or o == '--quiet':
+ suppress_output = True
else:
raise Exception ('unknown option: ' + o)
+if known_missing_files_file:
+ missing_files = open (known_missing_files_file, 'r')
+ known_missing_files = missing_files.read().splitlines()
+ missing_files.close()
if not os.path.isdir (outdir):
if os.path.exists (outdir):
os.unlink (outdir)
os.makedirs (outdir)
-include_re = re.compile (r'@include ((?!../lily-).*?\.i?texi)$', re.M)
+# Only look at @include if it is not preceeded by a @c:
+include_re = re.compile (r'^(?!.*@c .*@include)@include ((?!../lily-).*?\.i?te(xi|ly))$', re.M)
whitespaces = re.compile (r'\s+')
section_translation_re = re.compile ('^@(node|(?:unnumbered|appendix)\
(?:(?:sub){0,2}sec)?|top|chapter|(?:sub){0,2}section|\
-(?:major|chap|(?:sub){0,2})heading|translationof|lydoctitle) (.*?)\\s*$', re.MULTILINE)
+(?:major|chap|(?:sub){0,2})heading|lydoctitle|translationof|nodeprefix) \
+(.+)$', re.MULTILINE)
+external_node_re = re.compile (r'\s+@c\s+external.*')
def expand_includes (m, filename):
- filepath = os.path.join (os.path.dirname (filename), m.group(1))
+ include_name = m.group (1)
+ filepath = os.path.join (os.path.dirname (filename), include_name)
if os.path.exists (filepath):
return extract_sections (filepath)[1]
else:
for directory in include_path:
- filepath = os.path.join (directory, m.group(1))
+ filepath = os.path.join (directory, include_name)
if os.path.exists (filepath):
return extract_sections (filepath)[1]
- print "Unable to locate include file " + filepath
+ if not (include_name in known_missing_files):
+ # Not found
+ print 'Warning: No such file: ' + include_name + \
+ ' (search path: ' + ':'.join (include_path)+')'
return ''
lang_re = re.compile (r'^@documentlanguage (.+)', re.M)
result += "@" + sec[0] + " " + sec[1] + "\n"
return (lang_suffix, result)
-# Convert a given node name to its proper file name (normalization as explained
-# in the texinfo manual:
+# Convert a given node name to its proper file name (normalization as
+# explained in the texinfo manual:
# http://www.gnu.org/software/texinfo/manual/texinfo/html_node/HTML-Xref-Node-Name-Expansion.html
def texinfo_file_name(title):
# exception: The top node is always mapped to index.html
result = 't_g' + result
return result
-texinfo_re = re.compile (r'@.*{(.*)}')
+texinfo_re = re.compile (r'@.*?{(.*?)}')
def remove_texinfo (title):
- return texinfo_re.sub (r'\1', title)
+ title = title.replace ('--', '-')
+ return texinfo_re.sub (r'\1', title).strip ()
def create_texinfo_anchor (title):
return texinfo_file_name (remove_texinfo (title))
sections = section_translation_re.findall (page)
basename = os.path.splitext (os.path.basename (filename))[0]
p = os.path.join (outdir, basename) + lang_suffix + '.xref-map'
+ if not suppress_output:
+ print 'writing:', p
f = open (p, 'w')
+ node_prefix_title = ''
this_title = ''
this_filename = 'index'
this_anchor = ''
had_section = False
for sec in sections:
if sec[0] == "node":
- # Write out the cached values to the file and start a new section:
+ # Write out the cached values to the file and start a new
+ # section:
if this_title and this_title != 'Top':
f.write (this_title + "\t" + this_filename + "\t" + this_anchor + "\n")
had_section = False
this_title = remove_texinfo (sec[1])
this_anchor = create_texinfo_anchor (sec[1])
+ # delete entry from master map file
+ if this_title in initial_map:
+ del initial_map[this_title]
elif sec[0] == "translationof":
+ (original_node, external_node) = external_node_re.subn ('', sec[1])
+ original_node = remove_texinfo (original_node)
+ # The following binds the translator to use the
+ # translated node name in cross-references in case
+ # it exists
+ if external_node and original_node in initial_map:
+ del initial_map[original_node]
anchor = create_texinfo_anchor (sec[1])
- # If @translationof is used, it gives the original node name, which
- # we use for the anchor and the file name (if it is a numbered node)
+ # If @translationof is used, it gives the original
+ # node name, which we use for the anchor and the file
+ # name (if it is a numbered node)
this_anchor = anchor
if not this_unnumbered:
this_filename = anchor
+ elif original_node in initial_map:
+ this_filename = initial_map[original_node][2]
+ elif sec[0] == "nodeprefix":
+ node_prefix_title = remove_texinfo (sec[1])
+ node_prefix_anchor = create_texinfo_anchor (sec[1])
else:
- # Some pages might not use a node for every section, so treat this
- # case here, too: If we already had a section and encounter another
- # one before the next @node, we write out the old one and start
- # with the new values
- if had_section and this_title:
+ # Some pages might not use a node for every section, so
+ # treat this case here, too: If we already had a section
+ # and encounter another one before the next @node, we
+ # write out the old one and start with the new values
+ if had_section and split != 'node' and this_title:
f.write (this_title + "\t" + this_filename + "\t" + this_anchor + "\n")
this_title = remove_texinfo (sec[1])
this_anchor = create_texinfo_anchor (sec[1])
had_section = True
+ if sec[0] == "lydoctitle" and node_prefix_title:
+ this_title = "%s: %s" % (node_prefix_title, this_title)
+ this_anchor = "%s-%s" % (node_prefix_anchor, this_anchor)
+
if split == 'custom':
- # unnumbered nodes use the previously used file name, only numbered
- # nodes get their own filename! However, top-level @unnumbered
- # still get their own file.
+ # unnumbered nodes use the previously used file name,
+ # only numbered nodes get their own filename! However,
+ # top-level @unnumbered still get their own file.
this_unnumbered = unnumbered_re.match (sec[0])
if not this_unnumbered:
this_filename = this_anchor
if this_title and this_title != 'Top':
f.write (this_title + "\t" + this_filename + "\t" + this_anchor + "\n")
+
+ for node in initial_map:
+ f.write ("\t".join (initial_map[node]) + "\n")
f.close ()
+xref_map_line_re = re.compile (r'(.*?)\t(.*?)\t(.*?)$')
+if master_map_file:
+ for line in open (master_map_file):
+ m = xref_map_line_re.match (line)
+ if m:
+ initial_map[m.group (1)] = (m.group (1), m.group (2), m.group (3))
for filename in files:
- print "extract_texi_filenames.py: Processing %s" % filename
+ if not suppress_output:
+ print "extract_texi_filenames.py: Processing %s" % filename
(lang_suffix, sections) = extract_sections (filename)
process_sections (filename, lang_suffix, sections)