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