diff options
Diffstat (limited to 'doc/edit-sgml.py')
-rwxr-xr-x | doc/edit-sgml.py | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/doc/edit-sgml.py b/doc/edit-sgml.py new file mode 100755 index 0000000..e83c008 --- /dev/null +++ b/doc/edit-sgml.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# +# fontconfig/doc/edit-sgml.py +# +# Copyright © 2003 Keith Packard +# Copyright © 2020 Tim-Philipp Müller +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of the author(s) not be used in +# advertising or publicity pertaining to distribution of the software without +# specific, written prior permission. The authors make no +# representations about the suitability of this software for any purpose. It +# is provided "as is" without express or implied warranty. +# +# THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +import argparse +import sys +import re + +parser = argparse.ArgumentParser() +parser.add_argument('template') +parser.add_argument('input') +parser.add_argument('output') + +args = parser.parse_known_args() + +template_fn = args[0].template +output_fn = args[0].output +input_fn = args[0].input + +# ------------- +# Read template +# ------------- + +with open(template_fn, 'r', encoding='utf8') as f: + template_text = f.read() + +template_lines = template_text.strip().split('\n') + +# ------------------------------------- +# Read replacement sets from .fncs file +# ------------------------------------- + +replacement_sets = [] + +# TODO: also allow '-' for stdin +with open(input_fn, 'r', encoding='utf8') as f: + fncs_text = f.read() + +# split into replacement sets +fncs_chunks = fncs_text.strip().split('@@') + +for chunk in fncs_chunks: + # get rid of any preamble such as license and FcFreeTypeQueryAll decl in fcfreetype.fncs + start = chunk.find('@') + if start: + chunk = chunk[start:] + + # split at '@' and remove empty lines (keep it simple instead of doing fancy + # things with regular expression matches, we control the input after all) + lines = [line for line in chunk.split('@') if line.strip()] + + replacement_set = {} + + while lines: + tag = lines.pop(0).strip() + # FIXME: this hard codes the tag used in funcs.sgml - we're lazy + if tag.startswith('PROTOTYPE'): + text = '' + else: + text = lines.pop(0).strip() + if text.endswith('%'): + text = text[:-1] + ' ' + + replacement_set[tag] = text + + if replacement_set: + replacement_sets += [replacement_set] + +# ---------------- +# Open output file +# ---------------- + +if output_fn == '-': + fout = sys.stdout +else: + fout = open(output_fn, "w", encoding='utf8') + +# ---------------- +# Process template +# ---------------- + +def do_replace(template_lines, rep, tag_suffix=''): + skip_tag = None + skip_lines = False + loop_lines = [] + loop_tag = None + + for t_line in template_lines: + # This makes processing easier and is the case for our templates + if t_line.startswith('@') and not t_line.endswith('@'): + sys.exit('Template lines starting with @ are expected to end with @, please fix me!') + + if loop_tag: + loop_lines += [t_line] + + # Check if line starts with a directive + if t_line.startswith('@?'): + tag = t_line[2:-1] + tag_suffix + if skip_tag: + sys.exit('Recursive skipping not supported, please fix me!') + skip_tag = tag + skip_lines = tag not in rep + elif t_line.startswith('@:'): + if not skip_tag: + sys.exit('Skip else but no active skip list?!') + skip_lines = skip_tag in rep + elif t_line.startswith('@;'): + if not skip_tag: + sys.exit('Skip end but no active skip list?!') + skip_tag = None + skip_lines = False + elif t_line.startswith('@{'): + if loop_tag or tag_suffix != '': + sys.exit('Recursive looping not supported, please fix me!') + loop_tag = t_line[2:-1] + elif t_line.startswith('@}'): + tag = t_line[2:-1] + tag_suffix + if not loop_tag: + sys.exit('Loop end but no active loop?!') + if loop_tag != tag: + sys.exit(f'Loop end but loop tag mismatch: {loop_tag} != {tag}!') + loop_lines.pop() # remove loop end directive + suffix = '+' + while loop_tag + suffix in rep: + do_replace(loop_lines, rep, suffix) + suffix += '+' + loop_tag = None + loop_lines = [] + else: + if not skip_lines: + # special-case inline optional substitution (hard-codes specific pattern in funcs.sgml because we're lazy) + output_line = re.sub(r'@\?(RET)@@RET@@:@(void)@;@', lambda m: rep.get(m.group(1) + tag_suffix, m.group(2)), t_line) + # replace any substitution tags with their respective substitution text + output_line = re.sub(r'@(\w+)@', lambda m: rep.get(m.group(1) + tag_suffix, ''), output_line) + print(output_line, file=fout) + +# process template for each replacement set +for rep in replacement_sets: + do_replace(template_lines, rep) |