(root)/
fontconfig-2.14.2/
doc/
edit-sgml.py
       1  #!/usr/bin/env python3
       2  #
       3  # fontconfig/doc/edit-sgml.py
       4  #
       5  # Copyright © 2003 Keith Packard
       6  # Copyright © 2020 Tim-Philipp Müller
       7  #
       8  # Permission to use, copy, modify, distribute, and sell this software and its
       9  # documentation for any purpose is hereby granted without fee, provided that
      10  # the above copyright notice appear in all copies and that both that
      11  # copyright notice and this permission notice appear in supporting
      12  # documentation, and that the name of the author(s) not be used in
      13  # advertising or publicity pertaining to distribution of the software without
      14  # specific, written prior permission.  The authors make no
      15  # representations about the suitability of this software for any purpose.  It
      16  # is provided "as is" without express or implied warranty.
      17  #
      18  # THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
      19  # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
      20  # EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
      21  # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
      22  # DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
      23  # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      24  # PERFORMANCE OF THIS SOFTWARE.
      25  
      26  import argparse
      27  import sys
      28  import re
      29  
      30  parser = argparse.ArgumentParser()
      31  parser.add_argument('template')
      32  parser.add_argument('input')
      33  parser.add_argument('output')
      34  
      35  args = parser.parse_known_args()
      36  
      37  template_fn = args[0].template
      38  output_fn = args[0].output
      39  input_fn = args[0].input
      40  
      41  # -------------
      42  # Read template
      43  # -------------
      44  
      45  with open(template_fn, 'r', encoding='utf8') as f:
      46    template_text = f.read()
      47  
      48  template_lines = template_text.strip().split('\n')
      49  
      50  # -------------------------------------
      51  # Read replacement sets from .fncs file
      52  # -------------------------------------
      53  
      54  replacement_sets = []
      55  
      56  # TODO: also allow '-' for stdin
      57  with open(input_fn, 'r', encoding='utf8') as f:
      58    fncs_text = f.read()
      59  
      60  # split into replacement sets
      61  fncs_chunks = fncs_text.strip().split('@@')
      62  
      63  for chunk in fncs_chunks:
      64    # get rid of any preamble such as license and FcFreeTypeQueryAll decl in fcfreetype.fncs
      65    start = chunk.find('@')
      66    if start:
      67      chunk = chunk[start:]
      68  
      69    # split at '@' and remove empty lines (keep it simple instead of doing fancy
      70    # things with regular expression matches, we control the input after all)
      71    lines = [line for line in chunk.split('@') if line.strip()]
      72  
      73    replacement_set = {}
      74  
      75    while lines:
      76      tag = lines.pop(0).strip()
      77      # FIXME: this hard codes the tag used in funcs.sgml - we're lazy
      78      if tag.startswith('PROTOTYPE'):
      79        text = ''
      80      else:
      81        text = lines.pop(0).strip()
      82        if text.endswith('%'):
      83          text = text[:-1] + ' '
      84  
      85      replacement_set[tag] = text
      86  
      87    if replacement_set:
      88      replacement_sets += [replacement_set]
      89  
      90  # ----------------
      91  # Open output file
      92  # ----------------
      93  
      94  if output_fn == '-':
      95    fout = sys.stdout
      96  else:
      97    fout = open(output_fn, "w", encoding='utf8')
      98  
      99  # ----------------
     100  # Process template
     101  # ----------------
     102  
     103  def do_replace(template_lines, rep, tag_suffix=''):
     104    skip_tag = None
     105    skip_lines = False
     106    loop_lines = []
     107    loop_tag = None
     108  
     109    for t_line in template_lines:
     110      # This makes processing easier and is the case for our templates
     111      if t_line.startswith('@') and not t_line.endswith('@'):
     112        sys.exit('Template lines starting with @ are expected to end with @, please fix me!')
     113  
     114      if loop_tag:
     115        loop_lines += [t_line]
     116  
     117      # Check if line starts with a directive
     118      if t_line.startswith('@?'):
     119        tag = t_line[2:-1] + tag_suffix
     120        if skip_tag:
     121          sys.exit('Recursive skipping not supported, please fix me!')
     122        skip_tag = tag
     123        skip_lines = tag not in rep
     124      elif t_line.startswith('@:'):
     125        if not skip_tag:
     126          sys.exit('Skip else but no active skip list?!')
     127        skip_lines = skip_tag in rep
     128      elif t_line.startswith('@;'):
     129        if not skip_tag:
     130          sys.exit('Skip end but no active skip list?!')
     131        skip_tag = None
     132        skip_lines = False
     133      elif t_line.startswith('@{'):
     134        if loop_tag or tag_suffix != '':
     135          sys.exit('Recursive looping not supported, please fix me!')
     136        loop_tag = t_line[2:-1]
     137      elif t_line.startswith('@}'):
     138        tag = t_line[2:-1] + tag_suffix
     139        if not loop_tag:
     140          sys.exit('Loop end but no active loop?!')
     141        if loop_tag != tag:
     142          sys.exit(f'Loop end but loop tag mismatch: {loop_tag} != {tag}!')
     143        loop_lines.pop() # remove loop end directive
     144        suffix = '+'
     145        while loop_tag + suffix in rep:
     146          do_replace(loop_lines, rep, suffix)
     147          suffix += '+'
     148        loop_tag = None
     149        loop_lines = []
     150      else:
     151        if not skip_lines:
     152          # special-case inline optional substitution (hard-codes specific pattern in funcs.sgml because we're lazy)
     153          output_line = re.sub(r'@\?(RET)@@RET@@:@(void)@;@', lambda m: rep.get(m.group(1) + tag_suffix, m.group(2)), t_line)
     154          # replace any substitution tags with their respective substitution text
     155          output_line = re.sub(r'@(\w+)@', lambda m: rep.get(m.group(1) + tag_suffix, ''), output_line)
     156          print(output_line, file=fout)
     157  
     158  # process template for each replacement set
     159  for rep in replacement_sets:
     160    do_replace(template_lines, rep)