glib-mkenums: fix parsing of flags annotation
[glib.git] / gobject / glib-mkenums.in
blobd4bfd11c3ccdd9d913c5b7a4a012774a49b1d50d
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-genmarshal comes with ABSOLUTELY NO WARRANTY.
24 You may redistribute copies of glib-genmarshal 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 # Python 2 defaults to ASCII in case stdout is redirected.
30 # This should make it match Python 3, which uses the locale encoding.
31 if sys.stdout.encoding is None:
32     output_stream = codecs.getwriter(
33         locale.getpreferredencoding())(sys.stdout)
34 else:
35     output_stream = sys.stdout
37 # pylint: disable=too-few-public-methods
38 class Color:
39     '''ANSI Terminal colors'''
40     GREEN = '\033[1;32m'
41     BLUE = '\033[1;34m'
42     YELLOW = '\033[1;33m'
43     RED = '\033[1;31m'
44     END = '\033[0m'
47 def print_color(msg, color=Color.END, prefix='MESSAGE'):
48     '''Print a string with a color prefix'''
49     if os.isatty(sys.stderr.fileno()):
50         real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
51     else:
52         real_prefix = prefix
53     print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr)
56 def print_error(msg):
57     '''Print an error, and terminate'''
58     print_color(msg, color=Color.RED, prefix='ERROR')
59     sys.exit(1)
62 def print_warning(msg, fatal=False):
63     '''Print a warning, and optionally terminate'''
64     if fatal:
65         color = Color.RED
66         prefix = 'ERROR'
67     else:
68         color = Color.YELLOW
69         prefix = 'WARNING'
70     print_color(msg, color, prefix)
71     if fatal:
72         sys.exit(1)
75 def print_info(msg):
76     '''Print a message'''
77     print_color(msg, color=Color.GREEN, prefix='INFO')
80 def write_output(output):
81     global output_stream
82     print(output, file=output_stream)
84 # glib-mkenums.py
85 # Information about the current enumeration
86 flags = None # Is enumeration a bitmask?
87 option_underscore_name = '' # Overriden underscore variant of the enum name
88                             # for example to fix the cases we don't get the
89                             # mixed-case -> underscorized transform right.
90 option_lowercase_name = ''  # DEPRECATED.  A lower case name to use as part
91                             # of the *_get_type() function, instead of the
92                             # one that we guess. For instance, when an enum
93                             # uses abnormal capitalization and we can not
94                             # guess where to put the underscores.
95 seenbitshift = 0        # Have we seen bitshift operators?
96 enum_prefix = None        # Prefix for this enumeration
97 enumname = ''            # Name for this enumeration
98 enumshort = ''           # $enumname without prefix
99 enumname_prefix = ''       # prefix of $enumname
100 enumindex = 0        # Global enum counter
101 firstenum = 1        # Is this the first enumeration per file?
102 entries = []            # [ name, val ] for each entry
103 sandbox = None      # sandbox for safe evaluation of expressions
105 output = ''            # Filename to write result into
107 def parse_trigraph(opts):
108     result = {}
110     for opt in re.split(r'\s*,\s*', opts):
111         opt = re.sub(r'^\s*', '', opt)
112         opt = re.sub(r'\s*$', '', opt)
113         m = re.search(r'(\w+)(?:=(.+))?', opt)
114         assert m is not None
115         groups = m.groups()
116         key = groups[0]
117         if len(groups) > 1:
118             val = groups[1]
119         else:
120             val = 1
121         result[key] = val
122     return result
124 def parse_entries(file, file_name):
125     global entries, enumindex, enumname, seenbitshift, flags
126     looking_for_name = False
128     while True:
129         line = file.readline()
130         if not line:
131             break
133         line = line.strip()
135         # read lines until we have no open comments
136         while re.search(r'/\*([^*]|\*(?!/))*$', line):
137             line += file.readline()
139         # strip comments w/o options
140         line = re.sub(r'''/\*(?!<)
141             ([^*]+|\*(?!/))*
142            \*/''', '', line, flags=re.X)
144         line = line.rstrip()
146         # skip empty lines
147         if len(line.strip()) == 0:
148             continue
150         if looking_for_name:
151             m = re.match(r'\s*(\w+)', line)
152             if m:
153                 enumname = m.group(1)
154                 return True
156         # Handle include files
157         m = re.match(r'\#include\s*<([^>]*)>', line)
158         if m:
159             newfilename = os.path.join("..", m.group(1))
160             newfile = io.open(newfilename, encoding="utf-8")
162             if not parse_entries(newfile, newfilename):
163                 return False
164             else:
165                 continue
167         m = re.match(r'\s*\}\s*(\w+)', line)
168         if m:
169             enumname = m.group(1)
170             enumindex += 1
171             return 1
173         m = re.match(r'\s*\}', line)
174         if m:
175             enumindex += 1
176             looking_for_name = True
177             continue
179         m = re.match(r'''\s*
180               (\w+)\s*                   # name
181               (?:=(                      # value
182                    \s*\w+\s*\(.*\)\s*       # macro with multiple args
183                    |                        # OR
184                    (?:[^,/]|/(?!\*))*       # anything but a comma or comment
185                   ))?,?\s*
186               (?:/\*<                    # options
187                 (([^*]|\*(?!/))*)
188                >\s*\*/)?,?
189               \s*$''', line, flags=re.X)
190         if m:
191             groups = m.groups()
192             name = groups[0]
193             value = None
194             options = None
195             if len(groups) > 1:
196                 value = groups[1]
197             if len(groups) > 2:
198                 options = groups[2]
199             if flags is None and value is not None and '<<' in value:
200                 seenbitshift = 1
202             if options is not None:
203                 options = parse_trigraph(options)
204                 if 'skip' not in options:
205                     entries.append((name, value, options['nick']))
206             else:
207                 entries.append((name, value))
208         elif re.match(r's*\#', line):
209             pass
210         else:
211             print_warning('Failed to parse "{}" in {}'.format(line, file_name))
212     return False
214 help_epilog = '''Production text substitutions:
215   \u0040EnumName\u0040            PrefixTheXEnum
216   \u0040enum_name\u0040           prefix_the_xenum
217   \u0040ENUMNAME\u0040            PREFIX_THE_XENUM
218   \u0040ENUMSHORT\u0040           THE_XENUM
219   \u0040ENUMPREFIX\u0040          PREFIX
220   \u0040VALUENAME\u0040           PREFIX_THE_XVALUE
221   \u0040valuenick\u0040           the-xvalue
222   \u0040valuenum\u0040            the integer value (limited support, Since: 2.26)
223   \u0040type\u0040                either enum or flags
224   \u0040Type\u0040                either Enum or Flags
225   \u0040TYPE\u0040                either ENUM or FLAGS
226   \u0040filename\u0040            name of current input file
227   \u0040basename\u0040            base name of the current input file (Since: 2.22)
231 # production variables:
232 idprefix = ""    # "G", "Gtk", etc
233 symprefix = ""   # "g", "gtk", etc, if not just lc($idprefix)
234 fhead = ""   # output file header
235 fprod = ""   # per input file production
236 ftail = ""   # output file trailer
237 eprod = ""   # per enum text (produced prior to value itarations)
238 vhead = ""   # value header, produced before iterating over enum values
239 vprod = ""   # value text, produced for each enum value
240 vtail = ""   # value tail, produced after iterating over enum values
241 comment_tmpl = ""   # comment template
243 def read_template_file(file):
244     global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl
245     tmpl = {'file-header': fhead,
246             'file-production': fprod,
247             'file-tail': ftail,
248             'enumeration-production': eprod,
249             'value-header': vhead,
250             'value-production': vprod,
251             'value-tail': vtail,
252             'comment': comment_tmpl,
253            }
254     in_ = 'junk'
256     ifile = io.open(file, encoding="utf-8")
257     for line in ifile:
258         m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line)
259         if m:
260             if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl:
261                 in_ = m.group(2)
262                 continue
263             elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl:
264                 in_ = 'junk'
265                 continue
266             else:
267                 sys.exit("Malformed template file " + file)
269         if in_ != 'junk':
270             tmpl[in_] += line
272     if in_ != 'junk':
273         sys.exit("Malformed template file " + file)
275     fhead = tmpl['file-header']
276     fprod = tmpl['file-production']
277     ftail = tmpl['file-tail']
278     eprod = tmpl['enumeration-production']
279     vhead = tmpl['value-header']
280     vprod = tmpl['value-production']
281     vtail = tmpl['value-tail']
282     comment_tmpl = tmpl['comment']
284     # default to C-style comments
285     if comment_tmpl == "":
286         comment_tmpl = "/* \u0040comment\u0040 */"
288 parser = argparse.ArgumentParser(epilog=help_epilog,
289                                  formatter_class=argparse.RawDescriptionHelpFormatter)
291 parser.add_argument('--identifier-prefix', default='', dest='idprefix',
292                     help='Identifier prefix')
293 parser.add_argument('--symbol-prefix', default='', dest='symprefix',
294                     help='symbol-prefix')
295 parser.add_argument('--fhead', default=[], dest='fhead', action='append',
296                     help='Output file header')
297 parser.add_argument('--ftail', default=[], dest='ftail', action='append',
298                     help='Per input file production')
299 parser.add_argument('--fprod', default=[], dest='fprod', action='append',
300                     help='Put out TEXT everytime a new input file is being processed.')
301 parser.add_argument('--eprod', default=[], dest='eprod', action='append',
302                     help='Per enum text (produced prior to value iterations)')
303 parser.add_argument('--vhead', default=[], dest='vhead', action='append',
304                     help='Value header, produced before iterating over enum values')
305 parser.add_argument('--vprod', default=[], dest='vprod', action='append',
306                     help='Value text, produced for each enum value.')
307 parser.add_argument('--vtail', default=[], dest='vtail', action='append',
308                     help='Value tail, produced after iterating over enum values')
309 parser.add_argument('--comments', default='', dest='comment_tmpl',
310                     help='Comment structure')
311 parser.add_argument('--template', default='', dest='template',
312                     help='Template file')
313 parser.add_argument('--output', default=None, dest='output')
314 parser.add_argument('--version', '-v', default=False, action='store_true', dest='version',
315                     help='Print version informations')
316 parser.add_argument('args', nargs='*')
318 options = parser.parse_args()
320 if options.version:
321     print(VERSION_STR)
322     sys.exit(0)
324 def unescape_cmdline_args(arg):
325     arg = arg.replace('\\n', '\n')
326     arg = arg.replace('\\r', '\r')
327     return arg.replace('\\t', '\t')
329 if options.template != '':
330     read_template_file(options.template)
332 idprefix += options.idprefix
333 symprefix += options.symprefix
335 # This is a hack to maintain some semblance of backward compatibility with
336 # the old, Perl-based glib-mkenums. The old tool had an implicit ordering
337 # on the arguments and templates; each argument was parsed in order, and
338 # all the strings appended. This allowed developers to write:
340 #   glib-mkenums \
341 #     --fhead ... \
342 #     --template a-template-file.c.in \
343 #     --ftail ...
345 # And have the fhead be prepended to the file-head stanza in the template,
346 # as well as the ftail be appended to the file-tail stanza in the template.
347 # Short of throwing away ArgumentParser and going over sys.argv[] element
348 # by element, we can simulate that behaviour by ensuring some ordering in
349 # how we build the template strings:
351 #   - the head stanzas are always prepended to the template
352 #   - the prod stanzas are always appended to the template
353 #   - the tail stanzas are always appended to the template
355 # Within each instance of the command line argument, we append each value
356 # to the array in the order in which it appears on the command line.
357 fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead
358 vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead
360 fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod])
361 eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod])
362 vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod])
364 ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail])
365 vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail])
367 if options.comment_tmpl != '':
368     comment_tmpl = unescape_cmdline_args(options.comment_tmpl)
370 output = options.output
372 if output is not None:
373     (out_dir, out_fn) = os.path.split(options.output)
374     out_suffix = '_' + os.path.splitext(out_fn)[1]
375     if out_dir == '':
376         out_dir = '.'
377     fd, filename = tempfile.mkstemp(dir=out_dir)
378     os.close(fd)
379     tmpfile = io.open(filename, "w", encoding="utf-8")
380     output_stream = tmpfile
381 else:
382     tmpfile = None
384 # put auto-generation comment
385 comment = comment_tmpl.replace('\u0040comment\u0040', 'Generated data (by glib-mkenums)')
386 write_output("\n" + comment + '\n')
388 def replace_specials(prod):
389     prod = prod.replace(r'\\a', r'\a')
390     prod = prod.replace(r'\\b', r'\b')
391     prod = prod.replace(r'\\t', r'\t')
392     prod = prod.replace(r'\\n', r'\n')
393     prod = prod.replace(r'\\f', r'\f')
394     prod = prod.replace(r'\\r', r'\r')
395     prod = prod.rstrip()
396     return prod
398 if len(fhead) > 0:
399     prod = fhead
400     base = os.path.basename(options.args[0])
402     prod = prod.replace('\u0040filename\u0040', options.args[0])
403     prod = prod.replace('\u0040basename\u0040', base)
404     prod = replace_specials(prod)
405     write_output(prod)
407 def process_file(curfilename):
408     global entries, flags, seenbitshift, enum_prefix
409     firstenum = True
411     try:
412         curfile = io.open(curfilename, encoding="utf-8")
413     except IOError as e:
414         if e.errno == errno.ENOENT:
415             print_warning('No file "{}" found.'.format(curfilename))
416             return
417         raise
419     while True:
420         line = curfile.readline()
421         if not line:
422             break
424         line = line.strip()
426         # read lines until we have no open comments
427         while re.search(r'/\*([^*]|\*(?!/))*$', line):
428             line += curfile.readline()
430         # strip comments w/o options
431         line = re.sub(r'''/\*(?!<)
432            ([^*]+|\*(?!/))*
433            \*/''', '', line)
435         # ignore forward declarations
436         if re.match(r'\s*typedef\s+enum.*;', line):
437             continue
439         m = re.match(r'''\s*typedef\s+enum\s*
440                ({)?\s*
441                (?:/\*<
442                  (([^*]|\*(?!/))*)
443                 >\s*\*/)?
444                \s*({)?''', line, flags=re.X)
445         if m:
446             groups = m.groups()
447             if len(groups) >= 2 and groups[1] is not None:
448                 options = parse_trigraph(groups[1])
449                 if 'skip' in options:
450                     continue
451                 enum_prefix = options.get('prefix', None)
452                 flags = options.get('flags', None)
453                 if 'flags' in options:
454                     if flags is None:
455                         flags = 1
456                     else:
457                         flags = int(flags)
458                 option_lowercase_name = options.get('lowercase_name', None)
459                 option_underscore_name = options.get('underscore_name', None)
460             else:
461                 enum_prefix = None
462                 flags = None
463                 option_lowercase_name = None
464                 option_underscore_name = None
466             if option_lowercase_name is not None:
467                 if option_underscore_name is not None:
468                     print_warning("lowercase_name overriden with underscore_name")
469                     option_lowercase_name = None
470                 else:
471                     print_warning("lowercase_name is deprecated, use underscore_name")
473             # Didn't have trailing '{' look on next lines
474             if groups[0] is None and (len(groups) < 4 or groups[3] is None):
475                 while True:
476                     line = curfile.readline()
477                     if re.match(r'\s*\{', line):
478                         break
480             seenbitshift = 0
481             entries = []
483             # Now parse the entries
484             parse_entries(curfile, curfilename)
486             # figure out if this was a flags or enums enumeration
487             if flags is None:
488                 flags = seenbitshift
490             # Autogenerate a prefix
491             if enum_prefix is None:
492                 for entry in entries:
493                     if len(entry) < 3 or entry[2] is None:
494                         name = entry[0]
495                         if enum_prefix is not None:
496                             enum_prefix = os.path.commonprefix([name, enum_prefix])
497                         else:
498                             enum_prefix = name
499                 if enum_prefix is None:
500                     enum_prefix = ""
501                 else:
502                     # Trim so that it ends in an underscore
503                     enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix)
504             else:
505                 # canonicalize user defined prefixes
506                 enum_prefix = enum_prefix.upper()
507                 enum_prefix = enum_prefix.replace('-', '_')
508                 enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix)
510             fixed_entries = []
511             for e in entries:
512                 name = e[0]
513                 num = e[1]
514                 if len(e) < 3 or e[2] is None:
515                     nick = re.sub(r'^' + enum_prefix, '', name)
516                     nick = nick.replace('_', '-').lower()
517                     e = (name, num, nick)
518                 fixed_entries.append(e)
519             entries = fixed_entries
521             # Spit out the output
522             if option_underscore_name is not None:
523                 enumlong = option_underscore_name.upper()
524                 enumsym = option_underscore_name.lower()
525                 enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong)
527                 enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong)
528             elif symprefix == '' and idprefix == '':
529                 # enumname is e.g. GMatchType
530                 enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname)
532                 enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname)
533                 enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
534                 enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
535                 enumshort = enumshort.upper()
537                 enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper()
539                 enumlong = enspace.upper() + "_" + enumshort
540                 enumsym = enspace.lower() + "_" + enumshort.lower()
542                 if option_lowercase_name is not None:
543                     enumsym = option_lowercase_name
544             else:
545                 enumshort = enumname
546                 if idprefix:
547                     enumshort = re.sub(r'^' + idprefix, '', enumshort)
548                 else:
549                     enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort)
551                 enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
552                 enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
553                 enumshort = enumshort.upper()
555                 if symprefix:
556                     enumname_prefix = symprefix.upper()
557                 else:
558                     enumname_prefix = idprefix.upper()
560                 enumlong = enumname_prefix + "_" + enumshort
561                 enumsym = enumlong.lower()
563             if firstenum:
564                 firstenum = False
566                 if len(fprod) > 0:
567                     prod = fprod
568                     base = os.path.basename(curfilename)
570                     prod = prod.replace('\u0040filename\u0040', curfilename)
571                     prod = prod.replace('\u0040basename\u0040', base)
572                     prod = replace_specials(prod)
574                     write_output(prod)
576             if len(eprod) > 0:
577                 prod = eprod
579                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
580                 prod = prod.replace('\u0040EnumName\u0040', enumname)
581                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
582                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
583                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
584                 if flags:
585                     prod = prod.replace('\u0040type\u0040', 'flags')
586                 else:
587                     prod = prod.replace('\u0040type\u0040', 'enum')
588                 if flags:
589                     prod = prod.replace('\u0040Type\u0040', 'Flags')
590                 else:
591                     prod = prod.replace('\u0040Type\u0040', 'Enum')
592                 if flags:
593                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
594                 else:
595                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
596                 prod = replace_specials(prod)
597                 write_output(prod)
599             if len(vhead) > 0:
600                 prod = vhead
601                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
602                 prod = prod.replace('\u0040EnumName\u0040', enumname)
603                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
604                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
605                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
606                 if flags:
607                     prod = prod.replace('\u0040type\u0040', 'flags')
608                 else:
609                     prod = prod.replace('\u0040type\u0040', 'enum')
610                 if flags:
611                     prod = prod.replace('\u0040Type\u0040', 'Flags')
612                 else:
613                     prod = prod.replace('\u0040Type\u0040', 'Enum')
614                 if flags:
615                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
616                 else:
617                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
618                 prod = replace_specials(prod)
619                 write_output(prod)
621             if len(vprod) > 0:
622                 prod = vprod
623                 next_num = 0
625                 prod = replace_specials(prod)
626                 for name, num, nick in entries:
627                     tmp_prod = prod
629                     if '\u0040valuenum\u0040' in prod:
630                         # only attempt to eval the value if it is requested
631                         # this prevents us from throwing errors otherwise
632                         if num is not None:
633                             # use sandboxed evaluation as a reasonable
634                             # approximation to C constant folding
635                             inum = eval(num, {}, {})
637                             # make sure it parsed to an integer
638                             if not isinstance(inum, int):
639                                 sys.exit("Unable to parse enum value '%s'" % num)
640                             num = inum
641                         else:
642                             num = next_num
644                         tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num))
645                         next_num = int(num) + 1
647                     tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name)
648                     tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick)
649                     if flags:
650                         tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags')
651                     else:
652                         tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum')
653                     if flags:
654                         tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags')
655                     else:
656                         tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum')
657                     if flags:
658                         tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS')
659                     else:
660                         tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM')
661                     tmp_prod = tmp_prod.rstrip()
663                     write_output(tmp_prod)
665             if len(vtail) > 0:
666                 prod = vtail
667                 prod = prod.replace('\u0040enum_name\u0040', enumsym)
668                 prod = prod.replace('\u0040EnumName\u0040', enumname)
669                 prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
670                 prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
671                 prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
672                 if flags:
673                     prod = prod.replace('\u0040type\u0040', 'flags')
674                 else:
675                     prod = prod.replace('\u0040type\u0040', 'enum')
676                 if flags:
677                     prod = prod.replace('\u0040Type\u0040', 'Flags')
678                 else:
679                     prod = prod.replace('\u0040Type\u0040', 'Enum')
680                 if flags:
681                     prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
682                 else:
683                     prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
684                 prod = replace_specials(prod)
685                 write_output(prod)
687 for fname in options.args:
688     process_file(fname)
690 if len(ftail) > 0:
691     prod = ftail
692     base = os.path.basename(options.args[-1]) # FIXME, wrong
694     prod = prod.replace('\u0040filename\u0040', 'ARGV') # wrong too
695     prod = prod.replace('\u0040basename\u0040', base)
696     prod = replace_specials(prod)
697     write_output(prod)
699 # put auto-generation comment
700 comment = comment_tmpl
701 comment = comment.replace('\u0040comment\u0040', 'Generated data ends here')
702 write_output("\n" + comment + "\n")
704 if tmpfile is not None:
705     tmpfilename = tmpfile.name
706     tmpfile.close()
707     os.unlink(options.output)
708     os.rename(tmpfilename, options.output)