Merge branch '1465-bye-bye-g-gnuc-malloc' into 'master'
[glib.git] / gobject / glib-mkenums.in
blob985edd2f7e2ba90fc6f0eaff119e30bcc41dbddb
1 #!/usr/bin/env @PYTHON@
3 # If the code below looks horrible and unpythonic, do not panic.
5 # It is.
7 # This is a manual conversion from the original Perl script to
8 # Python. Improvements are welcome.
10 from __future__ import print_function, unicode_literals
12 import argparse
13 import os
14 import re
15 import sys
16 import tempfile
17 import io
18 import errno
19 import codecs
20 import locale
22 VERSION_STR = '''glib-mkenums version @VERSION@
23 glib-mkenums comes with ABSOLUTELY NO WARRANTY.
24 You may redistribute copies of glib-mkenums under the terms of
25 the GNU General Public License which can be found in the
26 GLib source package. Sources, examples and contact
27 information are available at http://www.gtk.org'''
29 # pylint: disable=too-few-public-methods
30 class Color:
31     '''ANSI Terminal colors'''
32     GREEN = '\033[1;32m'
33     BLUE = '\033[1;34m'
34     YELLOW = '\033[1;33m'
35     RED = '\033[1;31m'
36     END = '\033[0m'
39 def print_color(msg, color=Color.END, prefix='MESSAGE'):
40     '''Print a string with a color prefix'''
41     if os.isatty(sys.stderr.fileno()):
42         real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
43     else:
44         real_prefix = prefix
45     print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr)
48 def print_error(msg):
49     '''Print an error, and terminate'''
50     print_color(msg, color=Color.RED, prefix='ERROR')
51     sys.exit(1)
54 def print_warning(msg, fatal=False):
55     '''Print a warning, and optionally terminate'''
56     if fatal:
57         color = Color.RED
58         prefix = 'ERROR'
59     else:
60         color = Color.YELLOW
61         prefix = 'WARNING'
62     print_color(msg, color, prefix)
63     if fatal:
64         sys.exit(1)
67 def print_info(msg):
68     '''Print a message'''
69     print_color(msg, color=Color.GREEN, prefix='INFO')
72 def write_output(output):
73     global output_stream
74     print(output, file=output_stream)
77 # Python 2 defaults to ASCII in case stdout is redirected.
78 # This should make it match Python 3, which uses the locale encoding.
79 if sys.stdout.encoding is None:
80     output_stream = codecs.getwriter(
81         locale.getpreferredencoding())(sys.stdout)
82 else:
83     output_stream = sys.stdout
86 # Some source files aren't UTF-8 and the old perl version didn't care.
87 # Replace invalid data with a replacement character to keep things working.
88 # https://bugzilla.gnome.org/show_bug.cgi?id=785113#c20
89 def replace_and_warn(err):
90     # 7 characters of context either side of the offending character
91     print_warning('UnicodeWarning: {} at {} ({})'.format(
92         err.reason, err.start,
93         err.object[err.start - 7:err.end + 7]))
94     return ('?', err.end)
96 codecs.register_error('replace_and_warn', replace_and_warn)
99 # glib-mkenums.py
100 # Information about the current enumeration
101 flags = None # Is enumeration a bitmask?
102 option_underscore_name = '' # Overridden underscore variant of the enum name
103                             # for example to fix the cases we don't get the
104                             # mixed-case -> underscorized transform right.
105 option_lowercase_name = ''  # DEPRECATED.  A lower case name to use as part
106                             # of the *_get_type() function, instead of the
107                             # one that we guess. For instance, when an enum
108                             # uses abnormal capitalization and we can not
109                             # guess where to put the underscores.
110 seenbitshift = 0        # Have we seen bitshift operators?
111 enum_prefix = None        # Prefix for this enumeration
112 enumname = ''            # Name for this enumeration
113 enumshort = ''           # $enumname without prefix
114 enumname_prefix = ''       # prefix of $enumname
115 enumindex = 0        # Global enum counter
116 firstenum = 1        # Is this the first enumeration per file?
117 entries = []            # [ name, val ] for each entry
118 sandbox = None      # sandbox for safe evaluation of expressions
120 output = ''            # Filename to write result into
122 def parse_trigraph(opts):
123     result = {}
125     for opt in re.split(r'\s*,\s*', opts):
126         opt = re.sub(r'^\s*', '', opt)
127         opt = re.sub(r'\s*$', '', opt)
128         m = re.search(r'(\w+)(?:=(.+))?', opt)
129         assert m is not None
130         groups = m.groups()
131         key = groups[0]
132         if len(groups) > 1:
133             val = groups[1]
134         else:
135             val = 1
136         result[key] = val
137     return result
139 def parse_entries(file, file_name):
140     global entries, enumindex, enumname, seenbitshift, flags
141     looking_for_name = False
143     while True:
144         line = file.readline()
145         if not line:
146             break
148         line = line.strip()
150         # read lines until we have no open comments
151         while re.search(r'/\*([^*]|\*(?!/))*$', line):
152             line += file.readline()
154         # strip comments w/o options
155         line = re.sub(r'''/\*(?!<)
156             ([^*]+|\*(?!/))*
157            \*/''', '', line, flags=re.X)
159         line = line.rstrip()
161         # skip empty lines
162         if len(line.strip()) == 0:
163             continue
165         if looking_for_name:
166             m = re.match(r'\s*(\w+)', line)
167             if m:
168                 enumname = m.group(1)
169                 return True
171         # Handle include files
172         m = re.match(r'\#include\s*<([^>]*)>', line)
173         if m:
174             newfilename = os.path.join("..", m.group(1))
175             newfile = io.open(newfilename, encoding="utf-8",
176                               errors="replace_and_warn")
178             if not parse_entries(newfile, newfilename):
179                 return False
180             else:
181                 continue
183         m = re.match(r'\s*\}\s*(\w+)', line)
184         if m:
185             enumname = m.group(1)
186             enumindex += 1
187             return 1
189         m = re.match(r'\s*\}', line)
190         if m:
191             enumindex += 1
192             looking_for_name = True
193             continue
195         m = re.match(r'''\s*
196               (\w+)\s*                   # name
197               (?:=(                      # value
198                    \s*\w+\s*\(.*\)\s*       # macro with multiple args
199                    |                        # OR
200                    (?:[^,/]|/(?!\*))*       # anything but a comma or comment
201                   ))?,?\s*
202               (?:/\*<                    # options
203                 (([^*]|\*(?!/))*)
204                >\s*\*/)?,?
205               \s*$''', line, flags=re.X)
206         if m:
207             groups = m.groups()
208             name = groups[0]
209             value = None
210             options = None
211             if len(groups) > 1:
212                 value = groups[1]
213             if len(groups) > 2:
214                 options = groups[2]
215             if flags is None and value is not None and '<<' in value:
216                 seenbitshift = 1
218             if options is not None:
219                 options = parse_trigraph(options)
220                 if 'skip' not in options:
221                     entries.append((name, value, options.get('nick')))
222             else:
223                 entries.append((name, value))
224         elif re.match(r's*\#', line):
225             pass
226         else:
227             print_warning('Failed to parse "{}" in {}'.format(line, file_name))
228     return False
230 help_epilog = '''Production text substitutions:
231   \u0040EnumName\u0040            PrefixTheXEnum
232   \u0040enum_name\u0040           prefix_the_xenum
233   \u0040ENUMNAME\u0040            PREFIX_THE_XENUM
234   \u0040ENUMSHORT\u0040           THE_XENUM
235   \u0040ENUMPREFIX\u0040          PREFIX
236   \u0040VALUENAME\u0040           PREFIX_THE_XVALUE
237   \u0040valuenick\u0040           the-xvalue
238   \u0040valuenum\u0040            the integer value (limited support, Since: 2.26)
239   \u0040type\u0040                either enum or flags
240   \u0040Type\u0040                either Enum or Flags
241   \u0040TYPE\u0040                either ENUM or FLAGS
242   \u0040filename\u0040            name of current input file
243   \u0040basename\u0040            base name of the current input file (Since: 2.22)
247 # production variables:
248 idprefix = ""    # "G", "Gtk", etc
249 symprefix = ""   # "g", "gtk", etc, if not just lc($idprefix)
250 fhead = ""   # output file header
251 fprod = ""   # per input file production
252 ftail = ""   # output file trailer
253 eprod = ""   # per enum text (produced prior to value itarations)
254 vhead = ""   # value header, produced before iterating over enum values
255 vprod = ""   # value text, produced for each enum value
256 vtail = ""   # value tail, produced after iterating over enum values
257 comment_tmpl = ""   # comment template
259 def read_template_file(file):
260     global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl
261     tmpl = {'file-header': fhead,
262             'file-production': fprod,
263             'file-tail': ftail,
264             'enumeration-production': eprod,
265             'value-header': vhead,
266             'value-production': vprod,
267             'value-tail': vtail,
268             'comment': comment_tmpl,
269            }
270     in_ = 'junk'
272     ifile = io.open(file, encoding="utf-8", errors="replace_and_warn")
273     for line in ifile:
274         m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line)
275         if m:
276             if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl:
277                 in_ = m.group(2)
278                 continue
279             elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl:
280                 in_ = 'junk'
281                 continue
282             else:
283                 sys.exit("Malformed template file " + file)
285         if in_ != 'junk':
286             tmpl[in_] += line
288     if in_ != 'junk':
289         sys.exit("Malformed template file " + file)
291     fhead = tmpl['file-header']
292     fprod = tmpl['file-production']
293     ftail = tmpl['file-tail']
294     eprod = tmpl['enumeration-production']
295     vhead = tmpl['value-header']
296     vprod = tmpl['value-production']
297     vtail = tmpl['value-tail']
298     comment_tmpl = tmpl['comment']
300 parser = argparse.ArgumentParser(epilog=help_epilog,
301                                  formatter_class=argparse.RawDescriptionHelpFormatter)
303 parser.add_argument('--identifier-prefix', default='', dest='idprefix',
304                     help='Identifier prefix')
305 parser.add_argument('--symbol-prefix', default='', dest='symprefix',
306                     help='Symbol prefix')
307 parser.add_argument('--fhead', default=[], dest='fhead', action='append',
308                     help='Output file header')
309 parser.add_argument('--ftail', default=[], dest='ftail', action='append',
310                     help='Output file footer')
311 parser.add_argument('--fprod', default=[], dest='fprod', action='append',
312                     help='Put out TEXT every time a new input file is being processed.')
313 parser.add_argument('--eprod', default=[], dest='eprod', action='append',
314                     help='Per enum text, produced prior to value iterations')
315 parser.add_argument('--vhead', default=[], dest='vhead', action='append',
316                     help='Value header, produced before iterating over enum values')
317 parser.add_argument('--vprod', default=[], dest='vprod', action='append',
318                     help='Value text, produced for each enum value.')
319 parser.add_argument('--vtail', default=[], dest='vtail', action='append',
320                     help='Value tail, produced after iterating over enum values')
321 parser.add_argument('--comments', default='', dest='comment_tmpl',
322                     help='Comment structure')
323 parser.add_argument('--template', default='', dest='template',
324                     help='Template file')
325 parser.add_argument('--output', default=None, dest='output')
326 parser.add_argument('--version', '-v', default=False, action='store_true', dest='version',
327                     help='Print version information')
328 parser.add_argument('args', nargs='*',
329                     help='Input files')
331 options = parser.parse_args()
333 if options.version:
334     print(VERSION_STR)
335     sys.exit(0)
337 def unescape_cmdline_args(arg):
338     arg = arg.replace('\\n', '\n')
339     arg = arg.replace('\\r', '\r')
340     return arg.replace('\\t', '\t')
342 if options.template != '':
343     read_template_file(options.template)
345 idprefix += options.idprefix
346 symprefix += options.symprefix
348 # This is a hack to maintain some semblance of backward compatibility with
349 # the old, Perl-based glib-mkenums. The old tool had an implicit ordering
350 # on the arguments and templates; each argument was parsed in order, and
351 # all the strings appended. This allowed developers to write:
353 #   glib-mkenums \
354 #     --fhead ... \
355 #     --template a-template-file.c.in \
356 #     --ftail ...
358 # And have the fhead be prepended to the file-head stanza in the template,
359 # as well as the ftail be appended to the file-tail stanza in the template.
360 # Short of throwing away ArgumentParser and going over sys.argv[] element
361 # by element, we can simulate that behaviour by ensuring some ordering in
362 # how we build the template strings:
364 #   - the head stanzas are always prepended to the template
365 #   - the prod stanzas are always appended to the template
366 #   - the tail stanzas are always appended to the template
368 # Within each instance of the command line argument, we append each value
369 # to the array in the order in which it appears on the command line.
370 fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead
371 vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead
373 fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod])
374 eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod])
375 vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod])
377 ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail])
378 vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail])
380 if options.comment_tmpl != '':
381     comment_tmpl = unescape_cmdline_args(options.comment_tmpl)
382 elif comment_tmpl == "":
383     # default to C-style comments
384     comment_tmpl = "/* \u0040comment\u0040 */"
386 output = options.output
388 if output is not None:
389     (out_dir, out_fn) = os.path.split(options.output)
390     out_suffix = '_' + os.path.splitext(out_fn)[1]
391     if out_dir == '':
392         out_dir = '.'
393     fd, filename = tempfile.mkstemp(dir=out_dir)
394     os.close(fd)
395     tmpfile = io.open(filename, "w", encoding="utf-8")
396     output_stream = tmpfile
397 else:
398     tmpfile = None
400 # put auto-generation comment
401 comment = comment_tmpl.replace('\u0040comment\u0040',
402                                'This file is generated by glib-mkenums, do '
403                                'not modify it. This code is licensed under '
404                                'the same license as the containing project. '
405                                'Note that it links to GLib, so must comply '
406                                'with the LGPL linking clauses.')
407 write_output("\n" + comment + '\n')
409 def replace_specials(prod):
410     prod = prod.replace(r'\\a', r'\a')
411     prod = prod.replace(r'\\b', r'\b')
412     prod = prod.replace(r'\\t', r'\t')
413     prod = prod.replace(r'\\n', r'\n')
414     prod = prod.replace(r'\\f', r'\f')
415     prod = prod.replace(r'\\r', r'\r')
416     prod = prod.rstrip()
417     return prod
420 def warn_if_filename_basename_used(section, prod):
421     for substitution in ('\u0040filename\u0040',
422                          '\u0040basename\u0040'):
423         if substitution in prod:
424             print_warning('{} used in {} section.'.format(substitution,
425                                                           section))
427 if len(fhead) > 0:
428     prod = fhead
429     warn_if_filename_basename_used('file-header', prod)
430     prod = replace_specials(prod)
431     write_output(prod)
433 def process_file(curfilename):
434     global entries, flags, seenbitshift, enum_prefix
435     firstenum = True
437     try:
438         curfile = io.open(curfilename, encoding="utf-8",
439                           errors="replace_and_warn")
440     except IOError as e:
441         if e.errno == errno.ENOENT:
442             print_warning('No file "{}" found.'.format(curfilename))
443             return
444         raise
446     while True:
447         line = curfile.readline()
448         if not line:
449             break
451         line = line.strip()
453         # read lines until we have no open comments
454         while re.search(r'/\*([^*]|\*(?!/))*$', line):
455             line += curfile.readline()
457         # strip comments w/o options
458         line = re.sub(r'''/\*(?!<)
459            ([^*]+|\*(?!/))*
460            \*/''', '', line)
462         # ignore forward declarations
463         if re.match(r'\s*typedef\s+enum.*;', line):
464             continue
466         m = re.match(r'''\s*typedef\s+enum\s*[_A-Za-z]*[_A-Za-z0-9]*\s*
467                ({)?\s*
468                (?:/\*<
469                  (([^*]|\*(?!/))*)
470                 >\s*\*/)?
471                \s*({)?''', line, flags=re.X)
472         if m:
473             groups = m.groups()
474             if len(groups) >= 2 and groups[1] is not None:
475                 options = parse_trigraph(groups[1])
476                 if 'skip' in options:
477                     continue
478                 enum_prefix = options.get('prefix', None)
479                 flags = options.get('flags', None)
480                 if 'flags' in options:
481                     if flags is None:
482                         flags = 1
483                     else:
484                         flags = int(flags)
485                 option_lowercase_name = options.get('lowercase_name', None)
486                 option_underscore_name = options.get('underscore_name', None)
487             else:
488                 enum_prefix = None
489                 flags = None
490                 option_lowercase_name = None
491                 option_underscore_name = None
493             if option_lowercase_name is not None:
494                 if option_underscore_name is not None:
495                     print_warning("lowercase_name overridden with underscore_name")
496                     option_lowercase_name = None
497                 else:
498                     print_warning("lowercase_name is deprecated, use underscore_name")
500             # Didn't have trailing '{' look on next lines
501             if groups[0] is None and (len(groups) < 4 or groups[3] is None):
502                 while True:
503                     line = curfile.readline()
504                     if not line:
505                         print_error("Syntax error when looking for opening { in enum")
506                     if re.match(r'\s*\{', line):
507                         break
509             seenbitshift = 0
510             entries = []
512             # Now parse the entries
513             parse_entries(curfile, curfilename)
515             # figure out if this was a flags or enums enumeration
516             if flags is None:
517                 flags = seenbitshift
519             # Autogenerate a prefix
520             if enum_prefix is None:
521                 for entry in entries:
522                     if len(entry) < 3 or entry[2] is None:
523                         name = entry[0]
524                         if enum_prefix is not None:
525                             enum_prefix = os.path.commonprefix([name, enum_prefix])
526                         else:
527                             enum_prefix = name
528                 if enum_prefix is None:
529                     enum_prefix = ""
530                 else:
531                     # Trim so that it ends in an underscore
532                     enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix)
533             else:
534                 # canonicalize user defined prefixes
535                 enum_prefix = enum_prefix.upper()
536                 enum_prefix = enum_prefix.replace('-', '_')
537                 enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix)
539             fixed_entries = []
540             for e in entries:
541                 name = e[0]
542                 num = e[1]
543                 if len(e) < 3 or e[2] is None:
544                     nick = re.sub(r'^' + enum_prefix, '', name)
545                     nick = nick.replace('_', '-').lower()
546                     e = (name, num, nick)
547                 fixed_entries.append(e)
548             entries = fixed_entries
550             # Spit out the output
551             if option_underscore_name is not None:
552                 enumlong = option_underscore_name.upper()
553                 enumsym = option_underscore_name.lower()
554                 enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong)
556                 enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong)
557             elif symprefix == '' and idprefix == '':
558                 # enumname is e.g. GMatchType
559                 enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname)
561                 enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname)
562                 enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
563                 enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
564                 enumshort = enumshort.upper()
566                 enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper()
568                 enumlong = enspace.upper() + "_" + enumshort
569                 enumsym = enspace.lower() + "_" + enumshort.lower()
571                 if option_lowercase_name is not None:
572                     enumsym = option_lowercase_name
573             else:
574                 enumshort = enumname
575                 if idprefix:
576                     enumshort = re.sub(r'^' + idprefix, '', enumshort)
577                 else:
578                     enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort)
580                 enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
581                 enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
582                 enumshort = enumshort.upper()
584                 if symprefix:
585                     enumname_prefix = symprefix.upper()
586                 else:
587                     enumname_prefix = idprefix.upper()
589                 enumlong = enumname_prefix + "_" + enumshort
590                 enumsym = enumlong.lower()
592             if firstenum:
593                 firstenum = False
595                 if len(fprod) > 0:
596                     prod = fprod
597                     base = os.path.basename(curfilename)
599                     prod = prod.replace('\u0040filename\u0040', curfilename)
600                     prod = prod.replace('\u0040basename\u0040', base)
601                     prod = replace_specials(prod)
603                     write_output(prod)
605             if len(eprod) > 0:
606                 prod = eprod
608                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
609                 prod = prod.replace('\u0040EnumName\u0040', enumname)
610                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
611                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
612                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
613                 if flags:
614                     prod = prod.replace('\u0040type\u0040', 'flags')
615                 else:
616                     prod = prod.replace('\u0040type\u0040', 'enum')
617                 if flags:
618                     prod = prod.replace('\u0040Type\u0040', 'Flags')
619                 else:
620                     prod = prod.replace('\u0040Type\u0040', 'Enum')
621                 if flags:
622                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
623                 else:
624                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
625                 prod = replace_specials(prod)
626                 write_output(prod)
628             if len(vhead) > 0:
629                 prod = vhead
630                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
631                 prod = prod.replace('\u0040EnumName\u0040', enumname)
632                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
633                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
634                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
635                 if flags:
636                     prod = prod.replace('\u0040type\u0040', 'flags')
637                 else:
638                     prod = prod.replace('\u0040type\u0040', 'enum')
639                 if flags:
640                     prod = prod.replace('\u0040Type\u0040', 'Flags')
641                 else:
642                     prod = prod.replace('\u0040Type\u0040', 'Enum')
643                 if flags:
644                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
645                 else:
646                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
647                 prod = replace_specials(prod)
648                 write_output(prod)
650             if len(vprod) > 0:
651                 prod = vprod
652                 next_num = 0
654                 prod = replace_specials(prod)
655                 for name, num, nick in entries:
656                     tmp_prod = prod
658                     if '\u0040valuenum\u0040' in prod:
659                         # only attempt to eval the value if it is requested
660                         # this prevents us from throwing errors otherwise
661                         if num is not None:
662                             # use sandboxed evaluation as a reasonable
663                             # approximation to C constant folding
664                             inum = eval(num, {}, {})
666                             # make sure it parsed to an integer
667                             if not isinstance(inum, int):
668                                 sys.exit("Unable to parse enum value '%s'" % num)
669                             num = inum
670                         else:
671                             num = next_num
673                         tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num))
674                         next_num = int(num) + 1
676                     tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name)
677                     tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick)
678                     if flags:
679                         tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags')
680                     else:
681                         tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum')
682                     if flags:
683                         tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags')
684                     else:
685                         tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum')
686                     if flags:
687                         tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS')
688                     else:
689                         tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM')
690                     tmp_prod = tmp_prod.rstrip()
692                     write_output(tmp_prod)
694             if len(vtail) > 0:
695                 prod = vtail
696                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
697                 prod = prod.replace('\u0040EnumName\u0040', enumname)
698                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
699                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
700                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
701                 if flags:
702                     prod = prod.replace('\u0040type\u0040', 'flags')
703                 else:
704                     prod = prod.replace('\u0040type\u0040', 'enum')
705                 if flags:
706                     prod = prod.replace('\u0040Type\u0040', 'Flags')
707                 else:
708                     prod = prod.replace('\u0040Type\u0040', 'Enum')
709                 if flags:
710                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
711                 else:
712                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
713                 prod = replace_specials(prod)
714                 write_output(prod)
716 for fname in sorted(options.args):
717     process_file(fname)
719 if len(ftail) > 0:
720     prod = ftail
721     warn_if_filename_basename_used('file-tail', prod)
722     prod = replace_specials(prod)
723     write_output(prod)
725 # put auto-generation comment
726 comment = comment_tmpl
727 comment = comment.replace('\u0040comment\u0040', 'Generated data ends here')
728 write_output("\n" + comment + "\n")
730 if tmpfile is not None:
731     tmpfilename = tmpfile.name
732     tmpfile.close()
734     try:
735         os.unlink(options.output)
736     except OSError as error:
737         if error.errno != errno.ENOENT:
738             raise error
740     os.rename(tmpfilename, options.output)