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