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