Blame gobject/glib-mkenums.in

Packit ae235b
#!/usr/bin/env @PYTHON@
Packit ae235b
Packit ae235b
# If the code below looks horrible and unpythonic, do not panic.
Packit ae235b
#
Packit ae235b
# It is.
Packit ae235b
#
Packit ae235b
# This is a manual conversion from the original Perl script to
Packit ae235b
# Python. Improvements are welcome.
Packit ae235b
#
Packit ae235b
from __future__ import print_function, unicode_literals
Packit ae235b
Packit ae235b
import argparse
Packit ae235b
import os
Packit ae235b
import re
Packit ae235b
import sys
Packit ae235b
import tempfile
Packit ae235b
import io
Packit ae235b
import errno
Packit ae235b
import codecs
Packit ae235b
import locale
Packit ae235b
Packit ae235b
VERSION_STR = '''glib-mkenums version @VERSION@
Packit ae235b
glib-mkenums comes with ABSOLUTELY NO WARRANTY.
Packit ae235b
You may redistribute copies of glib-mkenums under the terms of
Packit ae235b
the GNU General Public License which can be found in the
Packit ae235b
GLib source package. Sources, examples and contact
Packit ae235b
information are available at http://www.gtk.org'''
Packit ae235b
Packit ae235b
# pylint: disable=too-few-public-methods
Packit ae235b
class Color:
Packit ae235b
    '''ANSI Terminal colors'''
Packit ae235b
    GREEN = '\033[1;32m'
Packit ae235b
    BLUE = '\033[1;34m'
Packit ae235b
    YELLOW = '\033[1;33m'
Packit ae235b
    RED = '\033[1;31m'
Packit ae235b
    END = '\033[0m'
Packit ae235b
Packit ae235b
Packit ae235b
def print_color(msg, color=Color.END, prefix='MESSAGE'):
Packit ae235b
    '''Print a string with a color prefix'''
Packit ae235b
    if os.isatty(sys.stderr.fileno()):
Packit ae235b
        real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
Packit ae235b
    else:
Packit ae235b
        real_prefix = prefix
Packit ae235b
    print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr)
Packit ae235b
Packit ae235b
Packit ae235b
def print_error(msg):
Packit ae235b
    '''Print an error, and terminate'''
Packit ae235b
    print_color(msg, color=Color.RED, prefix='ERROR')
Packit ae235b
    sys.exit(1)
Packit ae235b
Packit ae235b
Packit ae235b
def print_warning(msg, fatal=False):
Packit ae235b
    '''Print a warning, and optionally terminate'''
Packit ae235b
    if fatal:
Packit ae235b
        color = Color.RED
Packit ae235b
        prefix = 'ERROR'
Packit ae235b
    else:
Packit ae235b
        color = Color.YELLOW
Packit ae235b
        prefix = 'WARNING'
Packit ae235b
    print_color(msg, color, prefix)
Packit ae235b
    if fatal:
Packit ae235b
        sys.exit(1)
Packit ae235b
Packit ae235b
Packit ae235b
def print_info(msg):
Packit ae235b
    '''Print a message'''
Packit ae235b
    print_color(msg, color=Color.GREEN, prefix='INFO')
Packit ae235b
Packit ae235b
Packit ae235b
def write_output(output):
Packit ae235b
    global output_stream
Packit ae235b
    print(output, file=output_stream)
Packit ae235b
Packit ae235b
Packit ae235b
# Python 2 defaults to ASCII in case stdout is redirected.
Packit ae235b
# This should make it match Python 3, which uses the locale encoding.
Packit ae235b
if sys.stdout.encoding is None:
Packit ae235b
    output_stream = codecs.getwriter(
Packit ae235b
        locale.getpreferredencoding())(sys.stdout)
Packit ae235b
else:
Packit ae235b
    output_stream = sys.stdout
Packit ae235b
Packit ae235b
Packit ae235b
# Some source files aren't UTF-8 and the old perl version didn't care.
Packit ae235b
# Replace invalid data with a replacement character to keep things working.
Packit ae235b
# https://bugzilla.gnome.org/show_bug.cgi?id=785113#c20
Packit ae235b
def replace_and_warn(err):
Packit ae235b
    # 7 characters of context either side of the offending character
Packit ae235b
    print_warning('UnicodeWarning: {} at {} ({})'.format(
Packit ae235b
        err.reason, err.start,
Packit ae235b
        err.object[err.start - 7:err.end + 7]))
Packit ae235b
    return ('?', err.end)
Packit ae235b
Packit ae235b
codecs.register_error('replace_and_warn', replace_and_warn)
Packit ae235b
Packit ae235b
Packit ae235b
# glib-mkenums.py
Packit ae235b
# Information about the current enumeration
Packit ae235b
flags = None # Is enumeration a bitmask?
Packit ae235b
option_underscore_name = '' # Overriden underscore variant of the enum name
Packit ae235b
                            # for example to fix the cases we don't get the
Packit ae235b
                            # mixed-case -> underscorized transform right.
Packit ae235b
option_lowercase_name = ''  # DEPRECATED.  A lower case name to use as part
Packit ae235b
                            # of the *_get_type() function, instead of the
Packit ae235b
                            # one that we guess. For instance, when an enum
Packit ae235b
                            # uses abnormal capitalization and we can not
Packit ae235b
                            # guess where to put the underscores.
Packit ae235b
seenbitshift = 0        # Have we seen bitshift operators?
Packit ae235b
enum_prefix = None        # Prefix for this enumeration
Packit ae235b
enumname = ''            # Name for this enumeration
Packit ae235b
enumshort = ''           # $enumname without prefix
Packit ae235b
enumname_prefix = ''       # prefix of $enumname
Packit ae235b
enumindex = 0        # Global enum counter
Packit ae235b
firstenum = 1        # Is this the first enumeration per file?
Packit ae235b
entries = []            # [ name, val ] for each entry
Packit ae235b
sandbox = None      # sandbox for safe evaluation of expressions
Packit ae235b
Packit ae235b
output = ''            # Filename to write result into
Packit ae235b
Packit ae235b
def parse_trigraph(opts):
Packit ae235b
    result = {}
Packit ae235b
Packit ae235b
    for opt in re.split(r'\s*,\s*', opts):
Packit ae235b
        opt = re.sub(r'^\s*', '', opt)
Packit ae235b
        opt = re.sub(r'\s*$', '', opt)
Packit ae235b
        m = re.search(r'(\w+)(?:=(.+))?', opt)
Packit ae235b
        assert m is not None
Packit ae235b
        groups = m.groups()
Packit ae235b
        key = groups[0]
Packit ae235b
        if len(groups) > 1:
Packit ae235b
            val = groups[1]
Packit ae235b
        else:
Packit ae235b
            val = 1
Packit ae235b
        result[key] = val
Packit ae235b
    return result
Packit ae235b
Packit ae235b
def parse_entries(file, file_name):
Packit ae235b
    global entries, enumindex, enumname, seenbitshift, flags
Packit ae235b
    looking_for_name = False
Packit ae235b
Packit ae235b
    while True:
Packit ae235b
        line = file.readline()
Packit ae235b
        if not line:
Packit ae235b
            break
Packit ae235b
Packit ae235b
        line = line.strip()
Packit ae235b
Packit ae235b
        # read lines until we have no open comments
Packit ae235b
        while re.search(r'/\*([^*]|\*(?!/))*$', line):
Packit ae235b
            line += file.readline()
Packit ae235b
Packit ae235b
        # strip comments w/o options
Packit ae235b
        line = re.sub(r'''/\*(?!<)
Packit ae235b
            ([^*]+|\*(?!/))*
Packit ae235b
           \*/''', '', line, flags=re.X)
Packit ae235b
Packit ae235b
        line = line.rstrip()
Packit ae235b
Packit ae235b
        # skip empty lines
Packit ae235b
        if len(line.strip()) == 0:
Packit ae235b
            continue
Packit ae235b
Packit ae235b
        if looking_for_name:
Packit ae235b
            m = re.match(r'\s*(\w+)', line)
Packit ae235b
            if m:
Packit ae235b
                enumname = m.group(1)
Packit ae235b
                return True
Packit ae235b
Packit ae235b
        # Handle include files
Packit ae235b
        m = re.match(r'\#include\s*<([^>]*)>', line)
Packit ae235b
        if m:
Packit ae235b
            newfilename = os.path.join("..", m.group(1))
Packit ae235b
            newfile = io.open(newfilename, encoding="utf-8",
Packit ae235b
                              errors="replace_and_warn")
Packit ae235b
Packit ae235b
            if not parse_entries(newfile, newfilename):
Packit ae235b
                return False
Packit ae235b
            else:
Packit ae235b
                continue
Packit ae235b
Packit ae235b
        m = re.match(r'\s*\}\s*(\w+)', line)
Packit ae235b
        if m:
Packit ae235b
            enumname = m.group(1)
Packit ae235b
            enumindex += 1
Packit ae235b
            return 1
Packit ae235b
Packit ae235b
        m = re.match(r'\s*\}', line)
Packit ae235b
        if m:
Packit ae235b
            enumindex += 1
Packit ae235b
            looking_for_name = True
Packit ae235b
            continue
Packit ae235b
Packit ae235b
        m = re.match(r'''\s*
Packit ae235b
              (\w+)\s*                   # name
Packit ae235b
              (?:=(                      # value
Packit ae235b
                   \s*\w+\s*\(.*\)\s*       # macro with multiple args
Packit ae235b
                   |                        # OR
Packit ae235b
                   (?:[^,/]|/(?!\*))*       # anything but a comma or comment
Packit ae235b
                  ))?,?\s*
Packit ae235b
              (?:/\*<                    # options
Packit ae235b
                (([^*]|\*(?!/))*)
Packit ae235b
               >\s*\*/)?,?
Packit ae235b
              \s*$''', line, flags=re.X)
Packit ae235b
        if m:
Packit ae235b
            groups = m.groups()
Packit ae235b
            name = groups[0]
Packit ae235b
            value = None
Packit ae235b
            options = None
Packit ae235b
            if len(groups) > 1:
Packit ae235b
                value = groups[1]
Packit ae235b
            if len(groups) > 2:
Packit ae235b
                options = groups[2]
Packit ae235b
            if flags is None and value is not None and '<<' in value:
Packit ae235b
                seenbitshift = 1
Packit ae235b
Packit ae235b
            if options is not None:
Packit ae235b
                options = parse_trigraph(options)
Packit ae235b
                if 'skip' not in options:
Packit ae235b
                    entries.append((name, value, options['nick']))
Packit ae235b
            else:
Packit ae235b
                entries.append((name, value))
Packit ae235b
        elif re.match(r's*\#', line):
Packit ae235b
            pass
Packit ae235b
        else:
Packit ae235b
            print_warning('Failed to parse "{}" in {}'.format(line, file_name))
Packit ae235b
    return False
Packit ae235b
Packit ae235b
help_epilog = '''Production text substitutions:
Packit ae235b
  \u0040EnumName\u0040            PrefixTheXEnum
Packit ae235b
  \u0040enum_name\u0040           prefix_the_xenum
Packit ae235b
  \u0040ENUMNAME\u0040            PREFIX_THE_XENUM
Packit ae235b
  \u0040ENUMSHORT\u0040           THE_XENUM
Packit ae235b
  \u0040ENUMPREFIX\u0040          PREFIX
Packit ae235b
  \u0040VALUENAME\u0040           PREFIX_THE_XVALUE
Packit ae235b
  \u0040valuenick\u0040           the-xvalue
Packit ae235b
  \u0040valuenum\u0040            the integer value (limited support, Since: 2.26)
Packit ae235b
  \u0040type\u0040                either enum or flags
Packit ae235b
  \u0040Type\u0040                either Enum or Flags
Packit ae235b
  \u0040TYPE\u0040                either ENUM or FLAGS
Packit ae235b
  \u0040filename\u0040            name of current input file
Packit ae235b
  \u0040basename\u0040            base name of the current input file (Since: 2.22)
Packit ae235b
'''
Packit ae235b
Packit ae235b
Packit ae235b
# production variables:
Packit ae235b
idprefix = ""    # "G", "Gtk", etc
Packit ae235b
symprefix = ""   # "g", "gtk", etc, if not just lc($idprefix)
Packit ae235b
fhead = ""   # output file header
Packit ae235b
fprod = ""   # per input file production
Packit ae235b
ftail = ""   # output file trailer
Packit ae235b
eprod = ""   # per enum text (produced prior to value itarations)
Packit ae235b
vhead = ""   # value header, produced before iterating over enum values
Packit ae235b
vprod = ""   # value text, produced for each enum value
Packit ae235b
vtail = ""   # value tail, produced after iterating over enum values
Packit ae235b
comment_tmpl = ""   # comment template
Packit ae235b
Packit ae235b
def read_template_file(file):
Packit ae235b
    global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl
Packit ae235b
    tmpl = {'file-header': fhead,
Packit ae235b
            'file-production': fprod,
Packit ae235b
            'file-tail': ftail,
Packit ae235b
            'enumeration-production': eprod,
Packit ae235b
            'value-header': vhead,
Packit ae235b
            'value-production': vprod,
Packit ae235b
            'value-tail': vtail,
Packit ae235b
            'comment': comment_tmpl,
Packit ae235b
           }
Packit ae235b
    in_ = 'junk'
Packit ae235b
Packit ae235b
    ifile = io.open(file, encoding="utf-8", errors="replace_and_warn")
Packit ae235b
    for line in ifile:
Packit ae235b
        m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line)
Packit ae235b
        if m:
Packit ae235b
            if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl:
Packit ae235b
                in_ = m.group(2)
Packit ae235b
                continue
Packit ae235b
            elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl:
Packit ae235b
                in_ = 'junk'
Packit ae235b
                continue
Packit ae235b
            else:
Packit ae235b
                sys.exit("Malformed template file " + file)
Packit ae235b
Packit ae235b
        if in_ != 'junk':
Packit ae235b
            tmpl[in_] += line
Packit ae235b
Packit ae235b
    if in_ != 'junk':
Packit ae235b
        sys.exit("Malformed template file " + file)
Packit ae235b
Packit ae235b
    fhead = tmpl['file-header']
Packit ae235b
    fprod = tmpl['file-production']
Packit ae235b
    ftail = tmpl['file-tail']
Packit ae235b
    eprod = tmpl['enumeration-production']
Packit ae235b
    vhead = tmpl['value-header']
Packit ae235b
    vprod = tmpl['value-production']
Packit ae235b
    vtail = tmpl['value-tail']
Packit ae235b
    comment_tmpl = tmpl['comment']
Packit ae235b
Packit ae235b
parser = argparse.ArgumentParser(epilog=help_epilog,
Packit ae235b
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
Packit ae235b
Packit ae235b
parser.add_argument('--identifier-prefix', default='', dest='idprefix',
Packit ae235b
                    help='Identifier prefix')
Packit ae235b
parser.add_argument('--symbol-prefix', default='', dest='symprefix',
Packit ae235b
                    help='symbol-prefix')
Packit ae235b
parser.add_argument('--fhead', default=[], dest='fhead', action='append',
Packit ae235b
                    help='Output file header')
Packit ae235b
parser.add_argument('--ftail', default=[], dest='ftail', action='append',
Packit ae235b
                    help='Per input file production')
Packit ae235b
parser.add_argument('--fprod', default=[], dest='fprod', action='append',
Packit ae235b
                    help='Put out TEXT everytime a new input file is being processed.')
Packit ae235b
parser.add_argument('--eprod', default=[], dest='eprod', action='append',
Packit ae235b
                    help='Per enum text (produced prior to value iterations)')
Packit ae235b
parser.add_argument('--vhead', default=[], dest='vhead', action='append',
Packit ae235b
                    help='Value header, produced before iterating over enum values')
Packit ae235b
parser.add_argument('--vprod', default=[], dest='vprod', action='append',
Packit ae235b
                    help='Value text, produced for each enum value.')
Packit ae235b
parser.add_argument('--vtail', default=[], dest='vtail', action='append',
Packit ae235b
                    help='Value tail, produced after iterating over enum values')
Packit ae235b
parser.add_argument('--comments', default='', dest='comment_tmpl',
Packit ae235b
                    help='Comment structure')
Packit ae235b
parser.add_argument('--template', default='', dest='template',
Packit ae235b
                    help='Template file')
Packit ae235b
parser.add_argument('--output', default=None, dest='output')
Packit ae235b
parser.add_argument('--version', '-v', default=False, action='store_true', dest='version',
Packit ae235b
                    help='Print version informations')
Packit ae235b
parser.add_argument('args', nargs='*')
Packit ae235b
Packit ae235b
options = parser.parse_args()
Packit ae235b
Packit ae235b
if options.version:
Packit ae235b
    print(VERSION_STR)
Packit ae235b
    sys.exit(0)
Packit ae235b
Packit ae235b
def unescape_cmdline_args(arg):
Packit ae235b
    arg = arg.replace('\\n', '\n')
Packit ae235b
    arg = arg.replace('\\r', '\r')
Packit ae235b
    return arg.replace('\\t', '\t')
Packit ae235b
Packit ae235b
if options.template != '':
Packit ae235b
    read_template_file(options.template)
Packit ae235b
Packit ae235b
idprefix += options.idprefix
Packit ae235b
symprefix += options.symprefix
Packit ae235b
Packit ae235b
# This is a hack to maintain some semblance of backward compatibility with
Packit ae235b
# the old, Perl-based glib-mkenums. The old tool had an implicit ordering
Packit ae235b
# on the arguments and templates; each argument was parsed in order, and
Packit ae235b
# all the strings appended. This allowed developers to write:
Packit ae235b
#
Packit ae235b
#   glib-mkenums \
Packit ae235b
#     --fhead ... \
Packit ae235b
#     --template a-template-file.c.in \
Packit ae235b
#     --ftail ...
Packit ae235b
#
Packit ae235b
# And have the fhead be prepended to the file-head stanza in the template,
Packit ae235b
# as well as the ftail be appended to the file-tail stanza in the template.
Packit ae235b
# Short of throwing away ArgumentParser and going over sys.argv[] element
Packit ae235b
# by element, we can simulate that behaviour by ensuring some ordering in
Packit ae235b
# how we build the template strings:
Packit ae235b
#
Packit ae235b
#   - the head stanzas are always prepended to the template
Packit ae235b
#   - the prod stanzas are always appended to the template
Packit ae235b
#   - the tail stanzas are always appended to the template
Packit ae235b
#
Packit ae235b
# Within each instance of the command line argument, we append each value
Packit ae235b
# to the array in the order in which it appears on the command line.
Packit ae235b
fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead
Packit ae235b
vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead
Packit ae235b
Packit ae235b
fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod])
Packit ae235b
eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod])
Packit ae235b
vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod])
Packit ae235b
Packit ae235b
ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail])
Packit ae235b
vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail])
Packit ae235b
Packit ae235b
if options.comment_tmpl != '':
Packit ae235b
    comment_tmpl = unescape_cmdline_args(options.comment_tmpl)
Packit ae235b
elif comment_tmpl == "":
Packit ae235b
    # default to C-style comments
Packit ae235b
    comment_tmpl = "/* \u0040comment\u0040 */"
Packit ae235b
Packit ae235b
output = options.output
Packit ae235b
Packit ae235b
if output is not None:
Packit ae235b
    (out_dir, out_fn) = os.path.split(options.output)
Packit ae235b
    out_suffix = '_' + os.path.splitext(out_fn)[1]
Packit ae235b
    if out_dir == '':
Packit ae235b
        out_dir = '.'
Packit ae235b
    fd, filename = tempfile.mkstemp(dir=out_dir)
Packit ae235b
    os.close(fd)
Packit ae235b
    tmpfile = io.open(filename, "w", encoding="utf-8")
Packit ae235b
    output_stream = tmpfile
Packit ae235b
else:
Packit ae235b
    tmpfile = None
Packit ae235b
Packit ae235b
# put auto-generation comment
Packit ae235b
comment = comment_tmpl.replace('\u0040comment\u0040',
Packit ae235b
                               'This file is generated by glib-mkenums, do '
Packit ae235b
                               'not modify it. This code is licensed under '
Packit ae235b
                               'the same license as the containing project. '
Packit ae235b
                               'Note that it links to GLib, so must comply '
Packit ae235b
                               'with the LGPL linking clauses.')
Packit ae235b
write_output("\n" + comment + '\n')
Packit ae235b
Packit ae235b
def replace_specials(prod):
Packit ae235b
    prod = prod.replace(r'\\a', r'\a')
Packit ae235b
    prod = prod.replace(r'\\b', r'\b')
Packit ae235b
    prod = prod.replace(r'\\t', r'\t')
Packit ae235b
    prod = prod.replace(r'\\n', r'\n')
Packit ae235b
    prod = prod.replace(r'\\f', r'\f')
Packit ae235b
    prod = prod.replace(r'\\r', r'\r')
Packit ae235b
    prod = prod.rstrip()
Packit ae235b
    return prod
Packit ae235b
Packit ae235b
if len(fhead) > 0:
Packit ae235b
    prod = fhead
Packit ae235b
    base = os.path.basename(options.args[0])
Packit ae235b
Packit ae235b
    prod = prod.replace('\u0040filename\u0040', options.args[0])
Packit ae235b
    prod = prod.replace('\u0040basename\u0040', base)
Packit ae235b
    prod = replace_specials(prod)
Packit ae235b
    write_output(prod)
Packit ae235b
Packit ae235b
def process_file(curfilename):
Packit ae235b
    global entries, flags, seenbitshift, enum_prefix
Packit ae235b
    firstenum = True
Packit ae235b
Packit ae235b
    try:
Packit ae235b
        curfile = io.open(curfilename, encoding="utf-8",
Packit ae235b
                          errors="replace_and_warn")
Packit ae235b
    except IOError as e:
Packit ae235b
        if e.errno == errno.ENOENT:
Packit ae235b
            print_warning('No file "{}" found.'.format(curfilename))
Packit ae235b
            return
Packit ae235b
        raise
Packit ae235b
Packit ae235b
    while True:
Packit ae235b
        line = curfile.readline()
Packit ae235b
        if not line:
Packit ae235b
            break
Packit ae235b
Packit ae235b
        line = line.strip()
Packit ae235b
Packit ae235b
        # read lines until we have no open comments
Packit ae235b
        while re.search(r'/\*([^*]|\*(?!/))*$', line):
Packit ae235b
            line += curfile.readline()
Packit ae235b
Packit ae235b
        # strip comments w/o options
Packit ae235b
        line = re.sub(r'''/\*(?!<)
Packit ae235b
           ([^*]+|\*(?!/))*
Packit ae235b
           \*/''', '', line)
Packit ae235b
Packit ae235b
        # ignore forward declarations
Packit ae235b
        if re.match(r'\s*typedef\s+enum.*;', line):
Packit ae235b
            continue
Packit ae235b
Packit ae235b
        m = re.match(r'''\s*typedef\s+enum\s*[_A-Za-z]*[_A-Za-z0-9]*\s*
Packit ae235b
               ({)?\s*
Packit ae235b
               (?:/\*<
Packit ae235b
                 (([^*]|\*(?!/))*)
Packit ae235b
                >\s*\*/)?
Packit ae235b
               \s*({)?''', line, flags=re.X)
Packit ae235b
        if m:
Packit ae235b
            groups = m.groups()
Packit ae235b
            if len(groups) >= 2 and groups[1] is not None:
Packit ae235b
                options = parse_trigraph(groups[1])
Packit ae235b
                if 'skip' in options:
Packit ae235b
                    continue
Packit ae235b
                enum_prefix = options.get('prefix', None)
Packit ae235b
                flags = options.get('flags', None)
Packit ae235b
                if 'flags' in options:
Packit ae235b
                    if flags is None:
Packit ae235b
                        flags = 1
Packit ae235b
                    else:
Packit ae235b
                        flags = int(flags)
Packit ae235b
                option_lowercase_name = options.get('lowercase_name', None)
Packit ae235b
                option_underscore_name = options.get('underscore_name', None)
Packit ae235b
            else:
Packit ae235b
                enum_prefix = None
Packit ae235b
                flags = None
Packit ae235b
                option_lowercase_name = None
Packit ae235b
                option_underscore_name = None
Packit ae235b
Packit ae235b
            if option_lowercase_name is not None:
Packit ae235b
                if option_underscore_name is not None:
Packit ae235b
                    print_warning("lowercase_name overriden with underscore_name")
Packit ae235b
                    option_lowercase_name = None
Packit ae235b
                else:
Packit ae235b
                    print_warning("lowercase_name is deprecated, use underscore_name")
Packit ae235b
Packit ae235b
            # Didn't have trailing '{' look on next lines
Packit ae235b
            if groups[0] is None and (len(groups) < 4 or groups[3] is None):
Packit ae235b
                while True:
Packit ae235b
                    line = curfile.readline()
Packit ae235b
                    if not line:
Packit ae235b
                        print_error("Syntax error when looking for opening { in enum")
Packit ae235b
                    if re.match(r'\s*\{', line):
Packit ae235b
                        break
Packit ae235b
Packit ae235b
            seenbitshift = 0
Packit ae235b
            entries = []
Packit ae235b
Packit ae235b
            # Now parse the entries
Packit ae235b
            parse_entries(curfile, curfilename)
Packit ae235b
Packit ae235b
            # figure out if this was a flags or enums enumeration
Packit ae235b
            if flags is None:
Packit ae235b
                flags = seenbitshift
Packit ae235b
Packit ae235b
            # Autogenerate a prefix
Packit ae235b
            if enum_prefix is None:
Packit ae235b
                for entry in entries:
Packit ae235b
                    if len(entry) < 3 or entry[2] is None:
Packit ae235b
                        name = entry[0]
Packit ae235b
                        if enum_prefix is not None:
Packit ae235b
                            enum_prefix = os.path.commonprefix([name, enum_prefix])
Packit ae235b
                        else:
Packit ae235b
                            enum_prefix = name
Packit ae235b
                if enum_prefix is None:
Packit ae235b
                    enum_prefix = ""
Packit ae235b
                else:
Packit ae235b
                    # Trim so that it ends in an underscore
Packit ae235b
                    enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix)
Packit ae235b
            else:
Packit ae235b
                # canonicalize user defined prefixes
Packit ae235b
                enum_prefix = enum_prefix.upper()
Packit ae235b
                enum_prefix = enum_prefix.replace('-', '_')
Packit ae235b
                enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix)
Packit ae235b
Packit ae235b
            fixed_entries = []
Packit ae235b
            for e in entries:
Packit ae235b
                name = e[0]
Packit ae235b
                num = e[1]
Packit ae235b
                if len(e) < 3 or e[2] is None:
Packit ae235b
                    nick = re.sub(r'^' + enum_prefix, '', name)
Packit ae235b
                    nick = nick.replace('_', '-').lower()
Packit ae235b
                    e = (name, num, nick)
Packit ae235b
                fixed_entries.append(e)
Packit ae235b
            entries = fixed_entries
Packit ae235b
Packit ae235b
            # Spit out the output
Packit ae235b
            if option_underscore_name is not None:
Packit ae235b
                enumlong = option_underscore_name.upper()
Packit ae235b
                enumsym = option_underscore_name.lower()
Packit ae235b
                enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong)
Packit ae235b
Packit ae235b
                enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong)
Packit ae235b
            elif symprefix == '' and idprefix == '':
Packit ae235b
                # enumname is e.g. GMatchType
Packit ae235b
                enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname)
Packit ae235b
Packit ae235b
                enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname)
Packit ae235b
                enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
Packit ae235b
                enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
Packit ae235b
                enumshort = enumshort.upper()
Packit ae235b
Packit ae235b
                enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper()
Packit ae235b
Packit ae235b
                enumlong = enspace.upper() + "_" + enumshort
Packit ae235b
                enumsym = enspace.lower() + "_" + enumshort.lower()
Packit ae235b
Packit ae235b
                if option_lowercase_name is not None:
Packit ae235b
                    enumsym = option_lowercase_name
Packit ae235b
            else:
Packit ae235b
                enumshort = enumname
Packit ae235b
                if idprefix:
Packit ae235b
                    enumshort = re.sub(r'^' + idprefix, '', enumshort)
Packit ae235b
                else:
Packit ae235b
                    enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort)
Packit ae235b
Packit ae235b
                enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
Packit ae235b
                enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
Packit ae235b
                enumshort = enumshort.upper()
Packit ae235b
Packit ae235b
                if symprefix:
Packit ae235b
                    enumname_prefix = symprefix.upper()
Packit ae235b
                else:
Packit ae235b
                    enumname_prefix = idprefix.upper()
Packit ae235b
Packit ae235b
                enumlong = enumname_prefix + "_" + enumshort
Packit ae235b
                enumsym = enumlong.lower()
Packit ae235b
Packit ae235b
            if firstenum:
Packit ae235b
                firstenum = False
Packit ae235b
Packit ae235b
                if len(fprod) > 0:
Packit ae235b
                    prod = fprod
Packit ae235b
                    base = os.path.basename(curfilename)
Packit ae235b
Packit ae235b
                    prod = prod.replace('\u0040filename\u0040', curfilename)
Packit ae235b
                    prod = prod.replace('\u0040basename\u0040', base)
Packit ae235b
                    prod = replace_specials(prod)
Packit ae235b
Packit ae235b
                    write_output(prod)
Packit ae235b
Packit ae235b
            if len(eprod) > 0:
Packit ae235b
                prod = eprod
Packit ae235b
Packit ae235b
                prod = prod.replace('\u0040enum_name\u0040', enumsym)
Packit ae235b
                prod = prod.replace('\u0040EnumName\u0040', enumname)
Packit ae235b
                prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
Packit ae235b
                prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
Packit ae235b
                prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
Packit ae235b
                prod = replace_specials(prod)
Packit ae235b
                write_output(prod)
Packit ae235b
Packit ae235b
            if len(vhead) > 0:
Packit ae235b
                prod = vhead
Packit ae235b
                prod = prod.replace('\u0040enum_name\u0040', enumsym)
Packit ae235b
                prod = prod.replace('\u0040EnumName\u0040', enumname)
Packit ae235b
                prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
Packit ae235b
                prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
Packit ae235b
                prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
Packit ae235b
                prod = replace_specials(prod)
Packit ae235b
                write_output(prod)
Packit ae235b
Packit ae235b
            if len(vprod) > 0:
Packit ae235b
                prod = vprod
Packit ae235b
                next_num = 0
Packit ae235b
Packit ae235b
                prod = replace_specials(prod)
Packit ae235b
                for name, num, nick in entries:
Packit ae235b
                    tmp_prod = prod
Packit ae235b
Packit ae235b
                    if '\u0040valuenum\u0040' in prod:
Packit ae235b
                        # only attempt to eval the value if it is requested
Packit ae235b
                        # this prevents us from throwing errors otherwise
Packit ae235b
                        if num is not None:
Packit ae235b
                            # use sandboxed evaluation as a reasonable
Packit ae235b
                            # approximation to C constant folding
Packit ae235b
                            inum = eval(num, {}, {})
Packit ae235b
Packit ae235b
                            # make sure it parsed to an integer
Packit ae235b
                            if not isinstance(inum, int):
Packit ae235b
                                sys.exit("Unable to parse enum value '%s'" % num)
Packit ae235b
                            num = inum
Packit ae235b
                        else:
Packit ae235b
                            num = next_num
Packit ae235b
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num))
Packit ae235b
                        next_num = int(num) + 1
Packit ae235b
Packit ae235b
                    tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name)
Packit ae235b
                    tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick)
Packit ae235b
                    if flags:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags')
Packit ae235b
                    else:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum')
Packit ae235b
                    if flags:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags')
Packit ae235b
                    else:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum')
Packit ae235b
                    if flags:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS')
Packit ae235b
                    else:
Packit ae235b
                        tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM')
Packit ae235b
                    tmp_prod = tmp_prod.rstrip()
Packit ae235b
Packit ae235b
                    write_output(tmp_prod)
Packit ae235b
Packit ae235b
            if len(vtail) > 0:
Packit ae235b
                prod = vtail
Packit ae235b
                prod = prod.replace('\u0040enum_name\u0040', enumsym)
Packit ae235b
                prod = prod.replace('\u0040EnumName\u0040', enumname)
Packit ae235b
                prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
Packit ae235b
                prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
Packit ae235b
                prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040type\u0040', 'enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Flags')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040Type\u0040', 'Enum')
Packit ae235b
                if flags:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
Packit ae235b
                else:
Packit ae235b
                    prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
Packit ae235b
                prod = replace_specials(prod)
Packit ae235b
                write_output(prod)
Packit ae235b
Packit ae235b
for fname in sorted(options.args):
Packit ae235b
    process_file(fname)
Packit ae235b
Packit ae235b
if len(ftail) > 0:
Packit ae235b
    prod = ftail
Packit ae235b
    base = os.path.basename(options.args[-1]) # FIXME, wrong
Packit ae235b
Packit ae235b
    prod = prod.replace('\u0040filename\u0040', 'ARGV') # wrong too
Packit ae235b
    prod = prod.replace('\u0040basename\u0040', base)
Packit ae235b
    prod = replace_specials(prod)
Packit ae235b
    write_output(prod)
Packit ae235b
Packit ae235b
# put auto-generation comment
Packit ae235b
comment = comment_tmpl
Packit ae235b
comment = comment.replace('\u0040comment\u0040', 'Generated data ends here')
Packit ae235b
write_output("\n" + comment + "\n")
Packit ae235b
Packit ae235b
if tmpfile is not None:
Packit ae235b
    tmpfilename = tmpfile.name
Packit ae235b
    tmpfile.close()
Packit ae235b
Packit ae235b
    try:
Packit ae235b
        os.unlink(options.output)
Packit ae235b
    except OSError as error:
Packit ae235b
        if error.errno != errno.ENOENT:
Packit ae235b
            raise error
Packit ae235b
Packit ae235b
    os.rename(tmpfilename, options.output)