Merge remote-tracking branch 'gvdb/master'
[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     if (m@^\s*typedef\s+enum\s*
294            ({)?\s*
295            (?:/\*<
296              (([^*]|\*(?!/))*)
297             >\s*\*/)?
298            \s*({)?
299          @x) {
300         if (defined $2) {
301             my %options = parse_trigraph ($2);
302             next if defined $options{skip};
303             $enum_prefix = $options{prefix};
304             $flags = $options{flags};
305             $option_lowercase_name = $options{lowercase_name};
306             $option_underscore_name = $options{underscore_name};
307         } else {
308             $enum_prefix = undef;
309             $flags = undef;
310             $option_lowercase_name = undef;
311             $option_underscore_name = undef;
312         }
313         if (defined $option_lowercase_name) {
314             if (defined $option_underscore_name) {
315                 print STDERR "$0: $ARGV:$.: lowercase_name overriden with underscore_name\n";
316                 $option_lowercase_name = undef;
317             } else {
318                 print STDERR "$0: $ARGV:$.: lowercase_name is deprecated, use underscore_name\n";
319             }
320         }
321         # Didn't have trailing '{' look on next lines
322         if (!defined $1 && !defined $4) {
323             while (<>) {
324                 if (s/^\s*\{//) {
325                     last;
326                 }
327             }
328         }
329
330         $seenbitshift = 0;
331         @entries = ();
332
333         # Now parse the entries
334         parse_entries (\*ARGV, $ARGV);
335
336         # figure out if this was a flags or enums enumeration
337         if (!defined $flags) {
338             $flags = $seenbitshift;
339         }
340
341         # Autogenerate a prefix
342         if (!defined $enum_prefix) {
343             for (@entries) {
344                 my $nick = $_->[2];
345                 if (!defined $nick) {
346                     my $name = $_->[0];
347                     if (defined $enum_prefix) {
348                         my $tmp = ~ ($name ^ $enum_prefix);
349                         ($tmp) = $tmp =~ /(^\xff*)/;
350                         $enum_prefix = $enum_prefix & $tmp;
351                     } else {
352                         $enum_prefix = $name;
353                     }
354                 }
355             }
356             if (!defined $enum_prefix) {
357                 $enum_prefix = "";
358             } else {
359                 # Trim so that it ends in an underscore
360                 $enum_prefix =~ s/_[^_]*$/_/;
361             }
362         } else {
363             # canonicalize user defined prefixes
364             $enum_prefix = uc($enum_prefix);
365             $enum_prefix =~ s/-/_/g;
366             $enum_prefix =~ s/(.*)([^_])$/$1$2_/;
367         }
368         
369         for $entry (@entries) {
370             my ($name,$num,$nick) = @{$entry};
371             if (!defined $nick) {
372                 ($nick = $name) =~ s/^$enum_prefix//;
373                 $nick =~ tr/_/-/;
374                 $nick = lc($nick);
375                 @{$entry} = ($name, $num, $nick);
376             }
377         }
378         
379
380         # Spit out the output
381         if (defined $option_underscore_name) {
382             $enumlong = uc $option_underscore_name;
383             $enumsym = lc $option_underscore_name;
384             $enumshort = $enumlong;
385             $enumshort =~ s/^[A-Z][A-Z0-9]*_//;
386
387             $enumname_prefix = $enumlong;
388             $enumname_prefix =~ s/_$enumshort$//;
389         } elsif (!$symprefix && !$idprefix) {
390             # enumname is e.g. GMatchType
391             $enspace = $enumname;
392             $enspace =~ s/^([A-Z][a-z]*).*$/$1/;
393
394             $enumshort = $enumname;
395             $enumshort =~ s/^[A-Z][a-z]*//;
396             $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
397             $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
398             $enumshort = uc($enumshort);
399
400             $enumname_prefix = $enumname;
401             $enumname_prefix =~ s/^([A-Z][a-z]*).*$/$1/;
402             $enumname_prefix = uc($enumname_prefix);
403
404             $enumlong = uc($enspace) . "_" . $enumshort;
405             $enumsym = lc($enspace) . "_" . lc($enumshort);
406
407             if (defined($option_lowercase_name)) {
408                 $enumsym = $option_lowercase_name;
409             }
410         } else {
411             $enumshort = $enumname;
412             if ($idprefix) {
413                 $enumshort =~ s/^${idprefix}//;
414             } else {
415                 $enumshort =~ s/^[A-Z][a-z]*//;
416             }
417             $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
418             $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
419             $enumshort = uc($enumshort);
420
421             $enumname_prefix = $symprefix && uc($symprefix) || uc($idprefix);
422
423             $enumlong = $enumname_prefix . "_" . $enumshort;
424             $enumsym = lc($enumlong);
425         }
426
427         if ($firstenum) {
428             $firstenum = 0;
429             
430             if (length($fprod)) {
431                 my $prod = $fprod;
432                 my $base = basename ($ARGV);
433
434                 $prod =~ s/\@filename\@/$ARGV/g;
435                 $prod =~ s/\@basename\@/$base/g;
436                 $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
437                 $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
438                 chomp ($prod);
439                 
440                 print "$prod\n";
441             }
442         }
443         
444         if (length($eprod)) {
445             my $prod = $eprod;
446
447             $prod =~ s/\@enum_name\@/$enumsym/g;
448             $prod =~ s/\@EnumName\@/$enumname/g;
449             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
450             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
451             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
452             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
453             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
454             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
455             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
456             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
457             chomp ($prod);
458
459             print "$prod\n";
460         }
461
462         if (length($vhead)) {
463             my $prod = $vhead;
464
465             $prod =~ s/\@enum_name\@/$enumsym/g;
466             $prod =~ s/\@EnumName\@/$enumname/g;
467             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
468             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
469             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
470             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
471             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
472             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
473             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
474             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
475             chomp ($prod);
476             
477             print "$prod\n";
478         }
479
480         if (length($vprod)) {
481             my $prod = $vprod;
482             my $next_num = 0;
483             
484             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
485             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
486             for (@entries) {
487                 my ($name,$num,$nick) = @{$_};
488                 my $tmp_prod = $prod;
489
490                 if ($prod =~ /\@valuenum\@/) {
491                     # only attempt to eval the value if it is requested
492                     # this prevents us from throwing errors otherwise
493                     if (defined $num) {
494                         # use sandboxed perl evaluation as a reasonable
495                         # approximation to C constant folding
496                         $num = $sandbox->reval ($num);
497
498                         # make sure it parsed to an integer
499                         if (!defined $num or $num !~ /^-?\d+$/) {
500                             die "Unable to parse enum value '$num'";
501                         }
502                     } else {
503                         $num = $next_num;
504                     }
505
506                     $tmp_prod =~ s/\@valuenum\@/$num/g;
507                     $next_num = $num + 1;
508                 }
509
510                 $tmp_prod =~ s/\@VALUENAME\@/$name/g;
511                 $tmp_prod =~ s/\@valuenick\@/$nick/g;
512                 if ($flags) { $tmp_prod =~ s/\@type\@/flags/g; } else { $tmp_prod =~ s/\@type\@/enum/g; }
513                 if ($flags) { $tmp_prod =~ s/\@Type\@/Flags/g; } else { $tmp_prod =~ s/\@Type\@/Enum/g; }
514                 if ($flags) { $tmp_prod =~ s/\@TYPE\@/FLAGS/g; } else { $tmp_prod =~ s/\@TYPE\@/ENUM/g; }
515                 chomp ($tmp_prod);
516
517                 print "$tmp_prod\n";
518             }
519         }
520
521         if (length($vtail)) {
522             my $prod = $vtail;
523
524             $prod =~ s/\@enum_name\@/$enumsym/g;
525             $prod =~ s/\@EnumName\@/$enumname/g;
526             $prod =~ s/\@ENUMSHORT\@/$enumshort/g;
527             $prod =~ s/\@ENUMNAME\@/$enumlong/g;
528             $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
529             if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
530             if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
531             if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
532             $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
533             $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
534             chomp ($prod);
535             
536             print "$prod\n";
537         }
538     }
539 }
540
541 if (length($ftail)) {
542     my $prod = $ftail;
543     my $base = basename ($ARGV);
544
545     $prod =~ s/\@filename\@/$ARGV/g;
546     $prod =~ s/\@basename\@/$base/g;
547     $prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
548     $prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
549     chomp ($prod);
550                 
551     print "$prod\n";
552 }
553
554 # put auto-generation comment
555 {
556     my $comment = $comment_tmpl;
557     $comment =~ s/\@comment\@/Generated data ends here/;
558     print "\n" . $comment . "\n\n";
559 }