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