chromium/third_party/liblouis/liblouis_list_tables.py

#!/usr/bin/env python
# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import re
import sys

import json
import optparse

# Matches the include statement in the braille table files.
INCLUDE_RE = re.compile(r"^\s*include\s+([^#\s]+)")

# Exclude these files from table validation.
IGNORE_TABLES = set()
IGNORE_TABLES.add('IPA.utb')
IGNORE_TABLES.add('bh.ctb')
IGNORE_TABLES.add('bo.ctb')
IGNORE_TABLES.add('boxes.ctb')
IGNORE_TABLES.add('de-chess.ctb')
IGNORE_TABLES.add('dra.ctb')
IGNORE_TABLES.add('en-chess.ctb')
IGNORE_TABLES.add('ethio-g1.ctb')
IGNORE_TABLES.add('gon.ctb')
IGNORE_TABLES.add('kok.ctb')
IGNORE_TABLES.add('kru.ctb')
IGNORE_TABLES.add('mun.ctb')
IGNORE_TABLES.add('mwr.ctb')
IGNORE_TABLES.add('or-in-g1.utb')
IGNORE_TABLES.add('pi.ctb')
IGNORE_TABLES.add('spaces.ctb')
IGNORE_TABLES.add('unicode-braille.utb')

def Error(msg):
  sys.stderr.write('liblouis_list_tables: %s' % msg)
  sys.exit(1)


def ToNativePath(pathname):
  return os.path.sep.join(pathname.split('/'))


def LoadTablesFile(filename):
  with open(ToNativePath(filename), 'r') as fh:
    try:
      return json.load(fh)
    except ValueError as e:
      raise ValueError('Error parsing braille table file %s: %s' %
                       (filename, e.message))


def FindFile(filename, directories):
  for d in directories:
    fullname = '/'.join([d, filename])
    if os.path.isfile(ToNativePath(fullname)):
      return fullname
  Error('File not found: %s' % filename)


def FindAllTableFiles(directory):
  ret = set()
  for filename in os.listdir(directory):
    if filename.endswith(".ctb") or filename.endswith(".utb"):
      ret.add(filename)
  return ret


def GetIncludeFiles(filename):
  result = []
  with open(ToNativePath(filename), 'r') as fh:
    for line in fh.readlines():
      match = INCLUDE_RE.match(line)
      if match:
        result.append(match.group(1))
  return result


def GetAdditionalFileTableData(filename):
  result = {}
  INCLUDE_RE = re.compile(r"^#[\+-](\S+):\s*(.+)")
  with open(ToNativePath(filename), 'r') as fh:
    for line in fh.readlines():
      match = INCLUDE_RE.match(line)
      if match:
        result[match.groups(1)[0]] = match.groups(1)[1]
  return result


def ProcessFile(output_set, filename, directories, indent=0):
  fullname = FindFile(filename, directories)
  if fullname in output_set:
    return
  output_set.add(indent*" " + fullname)
  for include_file in GetIncludeFiles(fullname):
    ProcessFile(output_set, include_file, directories, indent=2)


def GetTableFiles(tables_file, directories, extra_files):
  tables = LoadTablesFile(tables_file)
  output_set = set()
  for table in tables:
    for name in table['fileNames'].split(','):
      ProcessFile(output_set, name, directories)
  for name in extra_files:
    ProcessFile(output_set, name, directories)
  return output_set


def CheckTables(tables_file):
  tables = LoadTablesFile(tables_file)
  actual_set = set()
  for table in tables:
    for name in table['fileNames'].split(','):
      actual_set.add(name)
  expected_set = FindAllTableFiles("src/tables")
  errors = []
  for table in actual_set:
    if table not in expected_set and table not in IGNORE_TABLES:
      errors.append("Error: obsolete table not in liblouis " + table)

  new_tables = []
  for table in expected_set:
    if table not in actual_set and table not in IGNORE_TABLES:
      new_tables.append(table)
  return (errors, new_tables)

def DoMain(argv):
  "Entry point for gyp's pymod_do_main command."
  parser = optparse.OptionParser()
  # Give a clearer error message when this is used as a module.
  parser.prog = 'liblouis_list_tables'
  parser.set_usage('usage: %prog [options] listfile')
  parser.add_option('-D', '--directory', dest='directories',
                     action='append', help='Where to search for table files')
  parser.add_option('-e', '--extra_file', dest='extra_files', action='append',
                    default=[], help='Extra liblouis table file to process')
  (options, args) = parser.parse_args(argv)

  if len(args) != 1:
    parser.error('Expecting exactly one argument')
  if not options.directories:
    parser.error('At least one --directory option must be specified')
  files = GetTableFiles(args[0], options.directories, options.extra_files)
  return '\n'.join(files)


def main(argv):
  print(DoMain(argv[1:]))


if __name__ == '__main__':
  try:
    sys.exit(main(sys.argv))
  except KeyboardInterrupt:
    Error('interrupted')