Next in thread →
Next in month →
Re: [docbook] DB4 to DB5 conversion stylesheet
#!/usr/bin/python3
import sys
import re
import argparse
def do_cloak(ns, old, force):
def pi(s):
return '<?' + ns.pi_wrap + ' ' + s + '?>'
def xml_escape(s):
escapes = { '<' : 'LT', '>' : 'GT', '&' : 'AMP' }
return re.sub(r'[<>&]', lambda x: pi('char ' + escapes[x.group()]), s)
def make_escapes(dtd, s):
if dtd != '':
s = s.replace(dtd, xml_escape(dtd))
s = re.sub(r'\<\?xml(.*?)\?\>', lambda m: pi('xml-pi !' + m.group(1)), s, flags=re.DOTALL)
s = re.sub(r'\<\!\[CDATA\[(.*?)\]\]\>', lambda m: pi('cdata-start') + xml_escape(m.group(1)) + pi('cdata-end'), s, flags=re.DOTALL)
# See NameStartChar/NameChar in XML spec; sorry for non-English entity names - add them if you use them.
s = re.sub(r'\&([:_A-Za-z][-.:_A-Za-z0-9]*);', lambda m: pi('entity ' + m.group(1)), s)
return s
def find_dtd(s):
if ns.no_dtd:
return ""
if "<!DOCTYPE" not in s:
return ""
# TBD: will fail if internal subset contains closing bracket followed by closing angle bracket (r'\]\s*\>'), e.g. inside an entity
m = re.search(r'\<\!DOCTYPE\s+\S+\s+(SYSTEM\s+([\'"]).*?\2|PUBLIC\s+([\'"]).*?\3\s+([\'"]).*?\4)?\s*(\[.*?\])?\s*\>', s, flags=re.DOTALL)
if m:
return m.group()
sys.stderr.write("DOCTYPE seen but not understood\n")
def find_xmldecl(s):
if ns.no_xmldecl:
return ""
m = re.search(r'\<\?xml\s+.*?\?\>', s)
if m is None:
return ""
return m.group(0)
def tmp_wrap(s):
return ("<%s:wrapper xmlns:%s='%s'>" % (ns.wrap_pfx, ns.wrap_pfx, ns.wrap_uri)) + s + ("</%s:wrapper>" % ns.wrap_pfx)
if not(force) and pi('cloak-marker') in old:
return None
xmldecl = find_xmldecl(old)
dtd = find_dtd(old)
if dtd is None:
return None
return xmldecl + dtd + tmp_wrap(make_escapes(dtd, old) + pi('cloak-marker'))
def do_uncloak(ns, old):
escapes = { 'LT' : '<', 'GT' : '>', 'AMP' : '&' }
pis = {
'xml-pi' : lambda s: "<?xml%s?>" % s[1:],
'char': lambda s: escapes[s],
'entity': lambda s: "&%s;" % s,
'cdata-start': lambda s: '<![CDATA[',
'cdata-end': lambda s: ']]>',
'cloak-marker': lambda s: ''
}
# Find the temporary wrapper
start = old.find("<%s:wrapper" % ns.wrap_pfx)
if start == -1:
return None
start = old.find(">", start)
if start == -1:
return None
start += 1
end = old.rfind("</%s:wrapper>" % ns.wrap_pfx)
if end == -1 or end <= start:
return None
new = re.sub(r'\<\?' + ns.pi_wrap + r'\s+(?P<pi>\S+?)(\s+(?P<arg>.*?))?\?\>', lambda m: pis[m.group('pi')](m.group('arg')), old[start:end])
return new
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Cloak/uncloak entities and other elements before XSLT processing')
parser.add_argument('--cloak', action='store_true', help='Cloak document (default: determine automatically)')
parser.add_argument('--uncloak', action='store_true', help='Uncloak document (default: determine automatically)')
parser.add_argument('--pi-wrap', metavar='PI', default='xxx-escape', help='Name of PI used to escape elements')
parser.add_argument('--wrap-pfx', metavar='PFX', default='tmp', help='Namespace prefix for top-level wrapper element')
parser.add_argument('--wrap-uri', metavar='URI', default='http://localhost/temporary-wrapper-namespace', help='Namespace URI for top-level wrapper element')
parser.add_argument('--no-dtd', action='store_true', help='Do not create a copy of DTD declaration')
parser.add_argument('--no-xmldecl', action='store_true', help='Do not create a copy of XML declaration')
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help='Input (stdin if omitted)')
parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout, help='Output (stdout if omitted)')
ns = parser.parse_args()
old = ns.infile.read()
if ns.uncloak:
new = do_uncloak(ns, old)
else:
new = do_cloak(ns, old, ns.cloak)
if new is None:
new = do_uncloak(ns, old)
if new is None:
sys.stderr.write("script cannot handle input\n")
sys.exit(1)
ns.outfile.write(new)
Next in thread →
Next in month →