-#!@PERL_PATH@ -w
+#! @PERL_PATH@
+
+use warnings;
+use File::Basename;
+use Safe;
# glib-mkenums.pl
# Information about the current enumeration
my $flags; # Is enumeration a bitmask?
-my $option_lowercase_name; # A lower case name to use as part of the *_get_type() function, instead of the one that we guess.
- # For instance, when an enum uses abnormal capitalization and we can not guess where to put the underscores.
+my $option_underscore_name; # Overriden underscore variant of the enum name
+ # for example to fix the cases we don't get the
+ # mixed-case -> underscorized transform right.
+my $option_lowercase_name; # DEPRECATED. A lower case name to use as part
+ # of the *_get_type() function, instead of the
+ # one that we guess. For instance, when an enum
+ # uses abnormal capitalization and we can not
+ # guess where to put the underscores.
my $seenbitshift; # Have we seen bitshift operators?
my $enum_prefix; # Prefix for this enumeration
my $enumname; # Name for this enumeration
my $enumshort; # $enumname without prefix
+my $enumname_prefix; # prefix of $enumname
my $enumindex = 0; # Global enum counter
my $firstenum = 1; # Is this the first enumeration per file?
my @entries; # [ $name, $val ] for each entry
+my $sandbox = Safe->new; # sandbox for safe evaluation of expressions
sub parse_trigraph {
my $opts = shift;
if (defined $options) {
my %options = parse_trigraph($options);
if (!defined $options{skip}) {
- push @entries, [ $name, $options{nick} ];
+ push @entries, [ $name, $value, $options{nick} ];
}
} else {
- push @entries, [ $name ];
+ push @entries, [ $name, $value ];
}
} elsif (m@^\s*\#@) {
# ignore preprocessor directives
}
sub version {
- print STDERR "glib-mkenums version glib-@GLIB_VERSION@\n";
- print STDERR "glib-mkenums comes with ABSOLUTELY NO WARRANTY.\n";
- print STDERR "You may redistribute copies of glib-mkenums under the terms of\n";
- print STDERR "the GNU General Public License which can be found in the\n";
- print STDERR "GLib source package. Sources, examples and contact\n";
- print STDERR "information are available at http://www.gtk.org\n";
+ print "glib-mkenums version glib-@GLIB_VERSION@\n";
+ print "glib-mkenums comes with ABSOLUTELY NO WARRANTY.\n";
+ print "You may redistribute copies of glib-mkenums under the terms of\n";
+ print "the GNU General Public License which can be found in the\n";
+ print "GLib source package. Sources, examples and contact\n";
+ print "information are available at http://www.gtk.org\n";
exit 0;
}
sub usage {
- print STDERR "Usage: glib-mkenums [options] [files...]\n";
- print STDERR " --fhead <text> output file header\n";
- print STDERR " --fprod <text> per input file production\n";
- print STDERR " --ftail <text> output file trailer\n";
- print STDERR " --eprod <text> per enum text (produced prior to value itarations)\n";
- print STDERR " --vhead <text> value header, produced before iterating over enum values\n";
- print STDERR " --vprod <text> value text, produced for each enum value\n";
- print STDERR " --vtail <text> value tail, produced after iterating over enum values\n";
- print STDERR " --comments <text> comment structure\n";
- print STDERR " --template file template file\n";
- print STDERR " -h, --help show this help message\n";
- print STDERR " -v, --version print version informations\n";
- print STDERR "Production text substitutions:\n";
- print STDERR " \@EnumName\@ PrefixTheXEnum\n";
- print STDERR " \@enum_name\@ prefix_the_xenum\n";
- print STDERR " \@ENUMNAME\@ PREFIX_THE_XENUM\n";
- print STDERR " \@ENUMSHORT\@ THE_XENUM\n";
- print STDERR " \@VALUENAME\@ PREFIX_THE_XVALUE\n";
- print STDERR " \@valuenick\@ the-xvalue\n";
- print STDERR " \@type\@ either enum or flags\n";
- print STDERR " \@Type\@ either Enum or Flags\n";
- print STDERR " \@TYPE\@ either ENUM or FLAGS\n";
- print STDERR " \@filename\@ name of current input file\n";
+ print "Usage:\n";
+ print " glib-mkenums [OPTION...] [FILES...]\n\n";
+ print "Help Options:\n";
+ print " -h, --help Show this help message\n\n";
+ print "Utility Options:\n";
+ print " --identifier-prefix <text> Identifier prefix\n";
+ print " --symbol-prefix <text> Symbol prefix\n";
+ print " --fhead <text> Output file header\n";
+ print " --fprod <text> Per input file production\n";
+ print " --ftail <text> Output file trailer\n";
+ print " --eprod <text> Per enum text (produced prior to value itarations)\n";
+ print " --vhead <text> Value header, produced before iterating over enum values\n";
+ print " --vprod <text> Value text, produced for each enum value\n";
+ print " --vtail <text> Value tail, produced after iterating over enum values\n";
+ print " --comments <text> Comment structure\n";
+ print " --template file Template file\n";
+ print " -v, --version Print version informations\n\n";
+ print "Production text substitutions:\n";
+ print " \@EnumName\@ PrefixTheXEnum\n";
+ print " \@enum_name\@ prefix_the_xenum\n";
+ print " \@ENUMNAME\@ PREFIX_THE_XENUM\n";
+ print " \@ENUMSHORT\@ THE_XENUM\n";
+ print " \@ENUMPREFIX\@ PREFIX\n";
+ print " \@VALUENAME\@ PREFIX_THE_XVALUE\n";
+ print " \@valuenick\@ the-xvalue\n";
+ print " \@valuenum\@ the integer value (limited support, Since: 2.26)\n";
+ print " \@type\@ either enum or flags\n";
+ print " \@Type\@ either Enum or Flags\n";
+ print " \@TYPE\@ either ENUM or FLAGS\n";
+ print " \@filename\@ name of current input file\n";
+ print " \@basename\@ base name of the current input file (Since: 2.22)\n";
exit 0;
}
# production variables:
+my $idprefix = ""; # "G", "Gtk", etc
+my $symprefix = ""; # "g", "gtk", etc, if not just lc($idprefix)
my $fhead = ""; # output file header
my $fprod = ""; # per input file production
my $ftail = ""; # output file trailer
my $vhead = ""; # value header, produced before iterating over enum values
my $vprod = ""; # value text, produced for each enum value
my $vtail = ""; # value tail, produced after iterating over enum values
-# other options
-my $comment_tmpl = "/* \@comment\@ */";
+my $comment_tmpl = ""; # comment template
sub read_template_file {
my ($file) = @_;
elsif (($in eq $2) && ($1 eq 'END') && (exists($tmpl{$2}))) {
$in = 'junk';
next;
- }
- else {
+ } else {
die "Malformed template file $file\n";
}
}
$vprod = $tmpl{'value-production'};
$vtail = $tmpl{'value-tail'};
$comment_tmpl = $tmpl{'comment'};
+
+ # default to C-style comments
+ $comment_tmpl = "/* \@comment\@ */" if $comment_tmpl eq "";
}
if (!defined $ARGV[0]) {
usage;
}
-while ($_ = $ARGV[0], /^-/) {
+while ($_=$ARGV[0],/^-/) {
shift;
last if /^--$/;
- if (/^--template$/) { read_template_file (shift); }
- elsif (/^--fhead$/) { $fhead = $fhead . shift }
- elsif (/^--fprod$/) { $fprod = $fprod . shift }
- elsif (/^--ftail$/) { $ftail = $ftail . shift }
- elsif (/^--eprod$/) { $eprod = $eprod . shift }
- elsif (/^--vhead$/) { $vhead = $vhead . shift }
- elsif (/^--vprod$/) { $vprod = $vprod . shift }
- elsif (/^--vtail$/) { $vtail = $vtail . shift }
- elsif (/^--comments$/) { $comment_tmpl = shift }
- elsif (/^--help$/ || /^-h$/) { usage; }
- elsif (/^--version$/ || /^-v$/) { version; }
+ if (/^--template$/) { read_template_file (shift); }
+ elsif (/^--identifier-prefix$/) { $idprefix = shift }
+ elsif (/^--symbol-prefix$/) { $symprefix = shift }
+ elsif (/^--fhead$/) { $fhead = $fhead . shift }
+ elsif (/^--fprod$/) { $fprod = $fprod . shift }
+ elsif (/^--ftail$/) { $ftail = $ftail . shift }
+ elsif (/^--eprod$/) { $eprod = $eprod . shift }
+ elsif (/^--vhead$/) { $vhead = $vhead . shift }
+ elsif (/^--vprod$/) { $vprod = $vprod . shift }
+ elsif (/^--vtail$/) { $vtail = $vtail . shift }
+ elsif (/^--comments$/) { $comment_tmpl = shift }
+ elsif (/^--help$/ || /^-h$/ || /^-\?$/) { usage; }
+ elsif (/^--version$/ || /^-v$/) { version; }
else { usage; }
+ last if not defined($ARGV[0]);
}
# put auto-generation comment
if (length($fhead)) {
my $prod = $fhead;
+ my $base = basename ($ARGV[0]);
$prod =~ s/\@filename\@/$ARGV[0]/g;
+ $prod =~ s/\@basename\@/$base/g;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}
([^*]+|\*(?!/))*
\*/@@gx;
+ # ignore forward declarations
+ next if /^\s*typedef\s+enum.*;/;
+
if (m@^\s*typedef\s+enum\s*
({)?\s*
(?:/\*<
(([^*]|\*(?!/))*)
>\s*\*/)?
+ \s*({)?
@x) {
if (defined $2) {
my %options = parse_trigraph ($2);
next if defined $options{skip};
$enum_prefix = $options{prefix};
$flags = $options{flags};
- $option_lowercase_name = $options{lowercase_name};
+ $option_lowercase_name = $options{lowercase_name};
+ $option_underscore_name = $options{underscore_name};
} else {
$enum_prefix = undef;
$flags = undef;
- $option_lowercase_name = undef;
+ $option_lowercase_name = undef;
+ $option_underscore_name = undef;
+ }
+ if (defined $option_lowercase_name) {
+ if (defined $option_underscore_name) {
+ print STDERR "$0: $ARGV:$.: lowercase_name overriden with underscore_name\n";
+ $option_lowercase_name = undef;
+ } else {
+ print STDERR "$0: $ARGV:$.: lowercase_name is deprecated, use underscore_name\n";
+ }
}
# Didn't have trailing '{' look on next lines
- if (!defined $1) {
+ if (!defined $1 && !defined $4) {
while (<>) {
+ if (eof) {
+ die "Hit end of file while parsing enum in $ARGV";
+ }
if (s/^\s*\{//) {
last;
}
# Autogenerate a prefix
if (!defined $enum_prefix) {
for (@entries) {
- my $nick = $_->[1];
+ my $nick = $_->[2];
if (!defined $nick) {
my $name = $_->[0];
if (defined $enum_prefix) {
}
for $entry (@entries) {
- my ($name,$nick) = @{$entry};
+ my ($name,$num,$nick) = @{$entry};
if (!defined $nick) {
($nick = $name) =~ s/^$enum_prefix//;
$nick =~ tr/_/-/;
$nick = lc($nick);
- @{$entry} = ($name, $nick);
+ @{$entry} = ($name, $num, $nick);
}
}
# Spit out the output
-
- # enumname is e.g. GMatchType
- $enspace = $enumname;
- $enspace =~ s/^([A-Z][a-z]*).*$/$1/;
-
- $enumshort = $enumname;
- $enumshort =~ s/^[A-Z][a-z]*//;
- $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
- $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
- $enumshort = uc($enumshort);
-
- $enumlong = uc($enspace) . "_" . $enumshort;
- $enumsym = lc($enspace) . "_" . lc($enumshort);
-
- #The options might override the lower case name if it could not be generated correctly:
- if (defined($option_lowercase_name)) {
- $enumsym = $option_lowercase_name;
- }
+ if (defined $option_underscore_name) {
+ $enumlong = uc $option_underscore_name;
+ $enumsym = lc $option_underscore_name;
+ $enumshort = $enumlong;
+ $enumshort =~ s/^[A-Z][A-Z0-9]*_//;
+
+ $enumname_prefix = $enumlong;
+ $enumname_prefix =~ s/_$enumshort$//;
+ } elsif (!$symprefix && !$idprefix) {
+ # enumname is e.g. GMatchType
+ $enspace = $enumname;
+ $enspace =~ s/^([A-Z][a-z]*).*$/$1/;
+
+ $enumshort = $enumname;
+ $enumshort =~ s/^[A-Z][a-z]*//;
+ $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
+ $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
+ $enumshort = uc($enumshort);
+
+ $enumname_prefix = $enumname;
+ $enumname_prefix =~ s/^([A-Z][a-z]*).*$/$1/;
+ $enumname_prefix = uc($enumname_prefix);
+
+ $enumlong = uc($enspace) . "_" . $enumshort;
+ $enumsym = lc($enspace) . "_" . lc($enumshort);
+
+ if (defined($option_lowercase_name)) {
+ $enumsym = $option_lowercase_name;
+ }
+ } else {
+ $enumshort = $enumname;
+ if ($idprefix) {
+ $enumshort =~ s/^${idprefix}//;
+ } else {
+ $enumshort =~ s/^[A-Z][a-z]*//;
+ }
+ $enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
+ $enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
+ $enumshort = uc($enumshort);
+
+ $enumname_prefix = $symprefix && uc($symprefix) || uc($idprefix);
+
+ $enumlong = $enumname_prefix . "_" . $enumshort;
+ $enumsym = lc($enumlong);
+ }
if ($firstenum) {
$firstenum = 0;
if (length($fprod)) {
my $prod = $fprod;
+ my $base = basename ($ARGV);
$prod =~ s/\@filename\@/$ARGV/g;
+ $prod =~ s/\@basename\@/$base/g;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
+ $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
+ $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}
if (length($vprod)) {
my $prod = $vprod;
+ my $next_num = 0;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
for (@entries) {
- my ($name,$nick) = @{$_};
+ my ($name,$num,$nick) = @{$_};
my $tmp_prod = $prod;
+ if ($prod =~ /\@valuenum\@/) {
+ # only attempt to eval the value if it is requested
+ # this prevents us from throwing errors otherwise
+ if (defined $num) {
+ # use sandboxed perl evaluation as a reasonable
+ # approximation to C constant folding
+ $num = $sandbox->reval ($num);
+
+ # make sure it parsed to an integer
+ if (!defined $num or $num !~ /^-?\d+$/) {
+ die "Unable to parse enum value '$num'";
+ }
+ } else {
+ $num = $next_num;
+ }
+
+ $tmp_prod =~ s/\@valuenum\@/$num/g;
+ $next_num = $num + 1;
+ }
+
$tmp_prod =~ s/\@VALUENAME\@/$name/g;
$tmp_prod =~ s/\@valuenick\@/$nick/g;
if ($flags) { $tmp_prod =~ s/\@type\@/flags/g; } else { $tmp_prod =~ s/\@type\@/enum/g; }
if ($flags) { $tmp_prod =~ s/\@Type\@/Flags/g; } else { $tmp_prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $tmp_prod =~ s/\@TYPE\@/FLAGS/g; } else { $tmp_prod =~ s/\@TYPE\@/ENUM/g; }
+ chomp ($tmp_prod);
print "$tmp_prod\n";
}
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
+ $prod =~ s/\@ENUMPREFIX\@/$enumname_prefix/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}
if (length($ftail)) {
my $prod = $ftail;
+ my $base = basename ($ARGV);
$prod =~ s/\@filename\@/$ARGV/g;
+ $prod =~ s/\@basename\@/$base/g;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
+ chomp ($prod);
print "$prod\n";
}