Update gspawn-win*helper* Visual C++ projects
[platform/upstream/glib.git] / gobject / glib-mkenums.in
1 #! @PERL_PATH@
2
3 use warnings;
4 use File::Basename;
5 use Safe;
6
7 # glib-mkenums.pl 
8 # Information about the current enumeration
9 my $flags;                      # Is enumeration a bitmask?
10 my $option_underscore_name;     # Overriden underscore variant of the enum name
11                                 # for example to fix the cases we don't get the
12                                 # mixed-case -> underscorized transform right.
13 my $option_lowercase_name;      # DEPRECATED.  A lower case name to use as part
14                                 # of the *_get_type() function, instead of the
15                                 # one that we guess. For instance, when an enum
16                                 # uses abnormal capitalization and we can not
17                                 # guess where to put the underscores.
18 my $seenbitshift;               # Have we seen bitshift operators?
19 my $enum_prefix;                # Prefix for this enumeration
20 my $enumname;                   # Name for this enumeration
21 my $enumshort;                  # $enumname without prefix
22 my $enumname_prefix;            # prefix of $enumname
23 my $enumindex = 0;              # Global enum counter
24 my $firstenum = 1;              # Is this the first enumeration per file?
25 my @entries;                    # [ $name, $val ] for each entry
26 my $sandbox = Safe->new;        # sandbox for safe evaluation of expressions
27
28 sub parse_trigraph {
29     my $opts = shift;
30     my @opts;
31
32     for $opt (split /\s*,\s*/, $opts) {
33         $opt =~ s/^\s*//;
34         $opt =~ s/\s*$//;
35         my ($key,$val) = $opt =~ /(\w+)(?:=(.+))?/;
36         defined $val or $val = 1;
37         push @opts, $key, $val;
38     }
39     @opts;
40 }
41 sub parse_entries {
42     my $file = shift;
43     my $file_name = shift;
44     my $looking_for_name = 0;
45     
46     while (<$file>) {
47         # read lines until we have no open comments
48         while (m@/\*([^*]|\*(?!/))*$@) {
49             my $new;
50             defined ($new = <$file>) || die "Unmatched comment in $ARGV";
51             $_ .= $new;
52         }
53         # strip comments w/o options
54         s@/\*(?!<)
55             ([^*]+|\*(?!/))*
56            \*/@@gx;
57         
58         # strip newlines
59         s@\n@ @;
60         
61         # skip empty lines
62         next if m@^\s*$@;
63         
64         if ($looking_for_name) {
65             if (/^\s*(\w+)/) {
66                 $enumname = $1;
67                 return 1;
68             }
69         }
70         
71         # Handle include files
72         if (/^\#include\s*<([^>]*)>/ ) {
73             my $file= "../$1";
74             open NEWFILE, $file or die "Cannot open include file $file: $!\n";
75             
76             if (parse_entries (\*NEWFILE, $NEWFILE)) {
77                 return 1;
78             } else {
79                 next;
80             }
81         }
82         
83         if (/^\s*\}\s*(\w+)/) {
84             $enumname = $1;
85             $enumindex++;
86             return 1;
87         }
88         
89         if (/^\s*\}/) {
90             $enumindex++;
91             $looking_for_name = 1;
92             next;
93         }
94
95         if (m@^\s*
96               (\w+)\s*                   # name
97               (?:=(                      # value
98                    \s*\w+\s*\(.*\)\s*       # macro with multiple args
99                    |                        # OR
100                    (?:[^,/]|/(?!\*))*       # anything but a comma or comment
101                   ))?,?\s*
102               (?:/\*<                    # options
103                 (([^*]|\*(?!/))*)
104                >\s*\*/)?,?
105               \s*$
106              @x) {
107             my ($name, $value, $options) = ($1,$2,$3);
108
109             if (!defined $flags && defined $value && $value =~ /<</) {
110                 $seenbitshift = 1;
111             }
112
113             if (defined $options) {
114                 my %options = parse_trigraph($options);
115                 if (!defined $options{skip}) {
116                     push @entries, [ $name, $value, $options{nick} ];
117                 }
118             } else {
119                 push @entries, [ $name, $value ];
120             }
121         } elsif (m@^\s*\#@) {
122             # ignore preprocessor directives
123         } else {
124             print STDERR "$0: $file_name:$.: Failed to parse `$_'\n";
125         }
126     }
127
128     return 0;
129 }
130
131 sub version {
132     print "glib-mkenums version glib-@GLIB_VERSION@\n";
133     print "glib-mkenums comes with ABSOLUTELY NO WARRANTY.\n";
134     print "You may redistribute copies of glib-mkenums under the terms of\n";
135     print "the GNU General Public License which can be found in the\n";
136     print "GLib source package. Sources, examples and contact\n";
137     print "information are available at http://www.gtk.org\n";
138     exit 0;
139 }
140 sub usage {
141     print "Usage:\n";
142     print "  glib-mkenums [OPTION...] [FILES...]\n\n";
143     print "Help Options:\n";
144     print "  -h, --help            Show this help message\n\n";
145     print "Utility Options:\n";
146     print "  --identifier-prefix <text>   Identifier prefix\n";
147     print "  --symbol-prefix <text>       Symbol prefix\n";
148     print "  --fhead <text>               Output file header\n";
149     print "  --fprod <text>               Per input file production\n";
150     print "  --ftail <text>               Output file trailer\n";
151     print "  --eprod <text>               Per enum text (produced prior to value itarations)\n";
152     print "  --vhead <text>               Value header, produced before iterating over enum values\n";
153     print "  --vprod <text>               Value text, produced for each enum value\n";
154     print "  --vtail <text>               Value tail, produced after iterating over enum values\n";
155     print "  --comments <text>            Comment structure\n";
156     print "  --template file              Template file\n";
157     print "  -v, --version                Print version informations\n\n";
158     print "Production text substitutions:\n";
159     print "  \@EnumName\@            PrefixTheXEnum\n";
160     print "  \@enum_name\@           prefix_the_xenum\n";
161     print "  \@ENUMNAME\@            PREFIX_THE_XENUM\n";
162     print "  \@ENUMSHORT\@           THE_XENUM\n";
163     print "  \@ENUMPREFIX\@          PREFIX\n";
164     print "  \@VALUENAME\@           PREFIX_THE_XVALUE\n";
165     print "  \@valuenick\@           the-xvalue\n";
166     print "  \@valuenum\@            the integer value (limited support, Since: 2.26)\n";
167     print "  \@type\@                either enum or flags\n";
168     print "  \@Type\@                either Enum or Flags\n";
169     print "  \@TYPE\@                either ENUM or FLAGS\n";
170     print "  \@filename\@            name of current input file\n";
171     print "  \@basename\@            base name of the current input file (Since: 2.22)\n";
172     exit 0;
173 }
174
175 # production variables:
176 my $idprefix = "";    # "G", "Gtk", etc
177 my $symprefix = "";   # "g", "gtk", etc, if not just lc($idprefix)
178 my $fhead = "";   # output file header
179 my $fprod = "";   # per input file production
180 my $ftail = "";   # output file trailer
181 my $eprod = "";   # per enum text (produced prior to value itarations)
182 my $vhead = "";   # value header, produced before iterating over enum values
183 my $vprod = "";   # value text, produced for each enum value
184 my $vtail = "";   # value tail, produced after iterating over enum values
185 my $comment_tmpl = "";   # comment template
186
187 sub read_template_file {
188   my ($file) = @_;
189   my %tmpl = ('file-header', $fhead, 
190               'file-production', $fprod, 
191               'file-tail', $ftail, 
192               'enumeration-production', $eprod,
193               'value-header', $vhead,
194               'value-production', $vprod,
195               'value-tail', $vtail,
196               'comment', $comment_tmpl);
197   my $in = 'junk';
198   open (FILE, $file) || die "Can't open $file: $!\n";
199   while (<FILE>) {
200     if (/^\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\//) {
201       if (($in eq 'junk') && ($1 eq 'BEGIN') && (exists($tmpl{$2}))) {
202         $in = $2;
203         next;
204       }
205       elsif (($in eq $2) && ($1 eq 'END') && (exists($tmpl{$2}))) {
206         $in = 'junk';
207         next;
208       } else {
209           die "Malformed template file $file\n";
210       }
211     }
212     if (!($in eq 'junk')) {
213         $tmpl{$in} .= $_;
214     }
215   }
216   close (FILE);
217   if (!($in eq 'junk')) {
218       die "Malformed template file $file\n";
219   }
220   $fhead = $tmpl{'file-header'};
221   $fprod = $tmpl{'file-production'};
222   $ftail = $tmpl{'file-tail'};
223   $eprod = $tmpl{'enumeration-production'};
224   $vhead = $tmpl{'value-header'};
225   $vprod = $tmpl{'value-production'};
226   $vtail = $tmpl{'value-tail'};
227   $comment_tmpl = $tmpl{'comment'};
228
229   # default to C-style comments
230   $comment_tmpl = "/* \@comment\@ */" if $comment_tmpl eq "";
231 }
232
233 if (!defined $ARGV[0]) {
234     usage;
235 }
236 while ($_=$ARGV[0],/^-/) {
237     shift;
238     last if /^--$/;
239     if (/^--template$/)                      { read_template_file (shift); }
240     elsif (/^--identifier-prefix$/)          { $idprefix = shift }
241     elsif (/^--symbol-prefix$/)              { $symprefix = shift }
242     elsif (/^--fhead$/)                      { $fhead = $fhead . shift }
243     elsif (/^--fprod$/)                      { $fprod = $fprod . shift }
244     elsif (/^--ftail$/)                      { $ftail = $ftail . shift }
245     elsif (/^--eprod$/)                      { $eprod = $eprod . shift }
246     elsif (/^--vhead$/)                      { $vhead = $vhead . shift }
247     elsif (/^--vprod$/)                      { $vprod = $vprod . shift }
248     elsif (/^--vtail$/)                      { $vtail = $vtail . shift }
249     elsif (/^--comments$/)                   { $comment_tmpl = shift }
250     elsif (/^--help$/ || /^-h$/ || /^-\?$/)  { usage; }
251     elsif (/^--version$/ || /^-v$/)          { version; }
252     else { usage; }
253     last if not defined($ARGV[0]);
254 }
255
256 # put auto-generation comment
257 {
258     my $comment = $comment_tmpl;
259     $comment =~ s/\@comment\@/Generated data (by glib-mkenums)/;
260     print "\n" . $comment . "\n\n";
261 }
262
263 if (length($fhead)) {
264     my $prod = $fhead;
265     my $base = basename ($ARGV[0]);
266
267     $prod =~ s/\@filename\@/$ARGV[0]/g;
268     $prod =~ s/\@basename\@/$base/g;
269     $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
270     $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
271     chomp ($prod);
272                 
273     print "$prod\n";
274 }
275
276 while (<>) {
277     if (eof) {
278         close (ARGV);           # reset line numbering
279         $firstenum = 1;         # Flag to print filename at next enum
280     }
281
282     # read lines until we have no open comments
283     while (m@/\*([^*]|\*(?!/))*$@) {
284         my $new;
285         defined ($new = <>) || die "Unmatched comment in $ARGV";
286         $_ .= $new;
287     }
288     # strip comments w/o options
289     s@/\*(?!<)
290        ([^*]+|\*(?!/))*
291        \*/@@gx;
292         
293     # ignore forward declarations
294     next if /^\s*typedef\s+enum.*;/;
295
296     if (m@^\s*typedef\s+enum\s*
297            ({)?\s*
298            (?:/\*<
299              (([^*]|\*(?!/))*)
300             >\s*\*/)?
301            \s*({)?
302          @x) {
303         if (defined $2) {
304             my %options = parse_trigraph ($2);
305             next if defined $options{skip};
306             $enum_prefix = $options{prefix};
307             $flags = $options{flags};
308             $option_lowercase_name = $options{lowercase_name};
309             $option_underscore_name = $options{underscore_name};
310         } else {
311             $enum_prefix = undef;
312             $flags = undef;
313             $option_lowercase_name = undef;
314             $option_underscore_name = undef;
315         }
316         if (defined $option_lowercase_name) {
317             if (defined $option_underscore_name) {
318                 print STDERR "$0: $ARGV:$.: lowercase_name overriden with underscore_name\n";
319                 $option_lowercase_name = undef;
320             } else {
321                 print STDERR "$0: $ARGV:$.: lowercase_name is deprecated, use underscore_name\n";
322             }
323         }
324         # Didn't have trailing '{' look on next lines
325         if (!defined $1 && !defined $4) {
326             while (<>) {
327                 if (eof) {
328                     die "Hit end of file while parsing enum in $ARGV";
329                 }
330                 if (s/^\s*\{//) {
331                     last;
332                 }
333             }
334         }
335
336         $seenbitshift = 0;
337         @entries = ();
338
339         # Now parse the entries
340         parse_entries (\*ARGV, $ARGV);
341
342         # figure out if this was a flags or enums enumeration
343         if (!defined $flags) {
344             $flags = $seenbitshift;
345         }
346
347         # Autogenerate a prefix
348         if (!defined $enum_prefix) {
349             for (@entries) {
350                 my $nick = $_->[2];
351                 if (!defined $nick) {
352                     my $name = $_->[0];
353                     if (defined $enum_prefix) {
354                         my $tmp = ~ ($name ^ $enum_prefix);
355                         ($tmp) = $tmp =~ /(^\xff*)/;
356                         $enum_prefix = $enum_prefix & $tmp;
357                     } else {
358                         $enum_prefix = $name;
359                     }
360                 }
361             }
362             if (!defined $enum_prefix) {
363                 $enum_prefix = "";
364             } else {
365                 # Trim so that it ends in an underscore
366                 $enum_prefix =~ s/_[^_]*$/_/;
367             }
368         } else {
369             # canonicalize user defined prefixes
370             $enum_prefix = uc($enum_prefix);
371             $enum_prefix =~ s/-/_/g;
372             $enum_prefix =~ s/(.*)([^_])$/$1$2_/;
373         }
374         
375         for $entry (@entries) {
376             my ($name,$num,$nick) = @{$entry};
377             if (!defined $nick) {
378                 ($nick = $name) =~ s/^$enum_prefix//;
379                 $nick =~ tr/_/-/;
380                 $nick = lc($nick);
381                 @{$entry} = ($name, $num, $nick);
382             }
383         }
384         
385
386         # Spit out the output
387         if (defined $option_underscore_name) {
388             $enumlong = uc $option_underscore_name;
389             $enumsym = lc $option_underscore_name;
390             $enumshort = $enumlong;
391             $enumshort =~ s/^[A-Z][A-Z0-9]*_//;
392
393             $enumname_prefix = $enumlong;
394             $enumname_prefix =~ s/_$enumshort$//;
395         } elsif (!$symprefix && !$idprefix) {
396             # enumname is e.g. GMatchType
397             $enspace = $enumname;
398             $enspace =~ s/^([A-Z][a-z]*).*$/$1/;
399
400             $enumshort = $enumname;
401             $enumshort =~ s/^[A-Z][a-z]*//;
402             $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
403             $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
404             $enumshort = uc($enumshort);
405
406             $enumname_prefix = $enumname;
407             $enumname_prefix =~ s/^([A-Z][a-z]*).*$/$1/;
408             $enumname_prefix = uc($enumname_prefix);
409
410             $enumlong = uc($enspace) . "_" . $enumshort;
411             $enumsym = lc($enspace) . "_" . lc($enumshort);
412
413             if (defined($option_lowercase_name)) {
414                 $enumsym = $option_lowercase_name;
415             }
416         } else {
417             $enumshort = $enumname;
418             if ($idprefix) {
419                 $enumshort =~ s/^${idprefix}//;
420             } else {
421                 $enumshort =~ s/^[A-Z][a-z]*//;
422             }
423             $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
424             $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
425             $enumshort = uc($enumshort);
426
427             $enumname_prefix = $symprefix && uc($symprefix) || uc($idprefix);
428
429             $enumlong = $enumname_prefix . "_" . $enumshort;
430             $enumsym = lc($enumlong);
431         }
432
433         if ($firstenum) {
434             $firstenum = 0;
435             
436             if (length($fprod)) {
437                 my $prod = $fprod;
438                 my $base = basename ($ARGV);
439
440                 $prod =~ s/\@filename\@/$ARGV/g;
441                 $prod =~ s/\@basename\@/$base/g;
442                 $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
443                 $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
444                 chomp ($prod);
445                 
446                 print "$prod\n";
447             }
448         }
449         
450         if (length($eprod)) {
451             my $prod = $eprod;
452
453             $prod =~ s/\@enum_name\@/$enumsym/g;
454             $prod =~ s/\@EnumName\@/$enumname/g;
455             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
456             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
457             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
458             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
459             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
460             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
461             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
462             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
463             chomp ($prod);
464
465             print "$prod\n";
466         }
467
468         if (length($vhead)) {
469             my $prod = $vhead;
470
471             $prod =~ s/\@enum_name\@/$enumsym/g;
472             $prod =~ s/\@EnumName\@/$enumname/g;
473             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
474             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
475             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
476             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
477             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
478             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
479             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
480             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
481             chomp ($prod);
482             
483             print "$prod\n";
484         }
485
486         if (length($vprod)) {
487             my $prod = $vprod;
488             my $next_num = 0;
489             
490             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
491             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
492             for (@entries) {
493                 my ($name,$num,$nick) = @{$_};
494                 my $tmp_prod = $prod;
495
496                 if ($prod =~ /\@valuenum\@/) {
497                     # only attempt to eval the value if it is requested
498                     # this prevents us from throwing errors otherwise
499                     if (defined $num) {
500                         # use sandboxed perl evaluation as a reasonable
501                         # approximation to C constant folding
502                         $num = $sandbox->reval ($num);
503
504                         # make sure it parsed to an integer
505                         if (!defined $num or $num !~ /^-?\d+$/) {
506                             die "Unable to parse enum value '$num'";
507                         }
508                     } else {
509                         $num = $next_num;
510                     }
511
512                     $tmp_prod =~ s/\@valuenum\@/$num/g;
513                     $next_num = $num + 1;
514                 }
515
516                 $tmp_prod =~ s/\@VALUENAME\@/$name/g;
517                 $tmp_prod =~ s/\@valuenick\@/$nick/g;
518                 if ($flags) { $tmp_prod =~ s/\@type\@/flags/g; } else { $tmp_prod =~ s/\@type\@/enum/g; }
519                 if ($flags) { $tmp_prod =~ s/\@Type\@/Flags/g; } else { $tmp_prod =~ s/\@Type\@/Enum/g; }
520                 if ($flags) { $tmp_prod =~ s/\@TYPE\@/FLAGS/g; } else { $tmp_prod =~ s/\@TYPE\@/ENUM/g; }
521                 chomp ($tmp_prod);
522
523                 print "$tmp_prod\n";
524             }
525         }
526
527         if (length($vtail)) {
528             my $prod = $vtail;
529
530             $prod =~ s/\@enum_name\@/$enumsym/g;
531             $prod =~ s/\@EnumName\@/$enumname/g;
532             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
533             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
534             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
535             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
536             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
537             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
538             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
539             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
540             chomp ($prod);
541             
542             print "$prod\n";
543         }
544     }
545 }
546
547 if (length($ftail)) {
548     my $prod = $ftail;
549     my $base = basename ($ARGV);
550
551     $prod =~ s/\@filename\@/$ARGV/g;
552     $prod =~ s/\@basename\@/$base/g;
553     $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
554     $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
555     chomp ($prod);
556                 
557     print "$prod\n";
558 }
559
560 # put auto-generation comment
561 {
562     my $comment = $comment_tmpl;
563     $comment =~ s/\@comment\@/Generated data ends here/;
564     print "\n" . $comment . "\n\n";
565 }