Replaced tempnam by mkstemps.
[wine] / tools / winapi / winapi_extract
index 3680bdb..9781995 100755 (executable)
@@ -1,6 +1,21 @@
 #!/usr/bin/perl -w
 
-# Copyright 2001 Patrik Stridvall
+# Copyright 2002 Patrik Stridvall
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
 
 use strict;
 
@@ -10,89 +25,78 @@ BEGIN {
 }
 
 use config qw(
-    &file_type &file_skip &files_skip &get_spec_files
+    &file_type &files_skip &files_filter &get_spec_files
     $current_dir $wine_dir $winapi_dir $winapi_check_dir
 );
-use modules;
-use nativeapi;
-use output;
-use options;
-use winapi;
-use winapi_function;
-use winapi_parser;
-
-my $output = 'output'->new;
-
-my %options_long = (
-    "debug" => { default => 0, description => "debug mode" },
-    "help" => { default => 0, description => "help mode" },
-    "verbose" => { default => 0, description => "verbose mode" },
-
-    "progress" => { default => 1, description => "show progress" },
-
-    "win16" => { default => 1, description => "Win16 extraction" },
-    "win32" => { default => 1, description => "Win32 extraction" },
-
-    "local" =>  { default => 1, description => "local extraction" },
-    "global" => { default => 1, description => "global extraction" },
+use output qw($output);
+use winapi_extract_options qw($options);
 
-    "spec-files" => { default => 1, parent => "global", description => "spec files extraction" },
-    "stub-statistics" => { default => 0, parent => "global", description => "stub statistics" },
-);
+if($options->progress) {
+    $output->enable_progress;
+} else {
+    $output->disable_progress;
+}
 
-my %options_short = (
-    "d" => "debug",
-    "?" => "help",
-    "v" => "verbose"
-);
+use c_parser;
+use function;
+use type;
 
-my $options_usage = "usage: winapi_extract [--help] [<files>]\n";
+use winapi_c_parser;
+use winapi_function;
 
-my $options = 'options'->new(\%options_long, \%options_short, $options_usage);
+use vars qw($win16api $win32api @winapis);
+if ($options->spec_files || $options->implemented || $options->stub_statistics || $options->winetest) {
+    require winapi;
+    import winapi qw($win16api $win32api @winapis);
+}
 
+my %module2entries;
 my %module2spec_file;
-my %module2type;
-{
+if($options->spec_files || $options->winetest) {
     local $_;
 
     foreach my $spec_file (get_spec_files("winelib")) {
-       my $module;
-       my $type;
+       my $entries = [];
+
+       my $module = $spec_file;
+       $module =~ s/^.*?([^\/]*)\.spec$/$1/;
+
+       my $type = "win32";
 
        open(IN, "< $wine_dir/$spec_file");
-       while(<IN>) {
+
+       my $header = 1;
+       my $lookahead = 0;
+       while($lookahead || defined($_ = <IN>)) {
+           $lookahead = 0;
+
            s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begining and end of line
            s/^(.*?)\s*#.*$/$1/;  # remove comments
            /^$/ && next;         # skip empty lines
 
-           if(/^name\s+(.*?)$/) {
-               $module = $1;
-               $module2spec_file{$module} = $spec_file;
-           } elsif(/^type\s+(.*?)$/) {
-               $type = $1;
-               $module2type{$module} = $type;
+           if($header)  {
+               if(/^\d+|@/) {
+                   $header = 0;
+                   $lookahead = 1;
+               }
+               next;
            }
-       }
-       close(IN);
-    }
-}
 
-my $modules = 'modules'->new($options, $output, $wine_dir, $current_dir, \&file_type, "$winapi_check_dir/modules.dat");
+           if(/^(@|\d+)\s+stdcall\s+(\w+)\s*\(\s*([^\)]*)\s*\)/) {
+               my $ordinal = $1;
+               my $name = $2;
+               my @args = split(/\s+/, $3);
 
-my $win16api = 'winapi'->new($options, $output, "win16", "$winapi_check_dir/win16");
-my $win32api = 'winapi'->new($options, $output, "win32", "$winapi_check_dir/win32");
-my @winapis = ($win16api, $win32api);
+               push @$entries, [$name, "undef", \@args];
+           }
+       }
+       close(IN);
 
-if($wine_dir eq ".") {
-    'winapi'->read_all_spec_files($modules, $wine_dir, $current_dir, \&file_type, $win16api, $win32api);
-} else {
-    my @spec_files = $modules->allowed_spec_files($wine_dir, $current_dir);
-    'winapi'->read_spec_files($modules, $wine_dir, $current_dir, \@spec_files, $win16api, $win32api);
+       $module2spec_file{$module} = $spec_file;
+       $module2entries{$module} = $entries;
+    }
 }
 
-my $nativeapi = 'nativeapi'->new($options, $output, "$winapi_check_dir/nativeapi.dat", 
-                                "$wine_dir/configure.in", "$wine_dir/include/config.h.in");
-
 my %specifications;
 
 sub documentation_specifications {
@@ -104,7 +108,6 @@ sub documentation_specifications {
     my $return_type = $function->return_type;
     my $linkage = $function->linkage;
     my $internal_name = $function->internal_name;
-    my @argument_types = @{$function->argument_types};
 
     if($linkage eq "static") {
        return;
@@ -148,7 +151,7 @@ sub documentation_specifications {
                    $output->write("$external_name ($module.$ordinal) already exists\n");
                }
            }
-           
+
            if($options->debug) {
                $output->write("$external_name ($module.$ordinal)\n");
            }
@@ -156,83 +159,259 @@ sub documentation_specifications {
     }
 }
 
-my %module_pseudo_stub_count16;
-my %module_pseudo_stub_count32;
+my %module_pseudo_stub;
 
-sub statements_stub {
+sub statements_pseudo_stub {
     my $function = shift;
 
+    my $pseudo_stub = 0;
     my $statements = $function->statements;
     if(defined($statements) && $statements =~ /FIXME[^;]*stub/s) {
        if($options->win16) {
+           my $external_name16 = $function->external_name16;
            foreach my $module16 ($function->modules16) {
-               $module_pseudo_stub_count16{$module16}++;
+               $module_pseudo_stub{$module16}{$external_name16}++;
+               $pseudo_stub = 1;
            }
        }
        if($options->win32) {
+           my $external_name32 = $function->external_name32;
            foreach my $module32 ($function->modules32) {
-               $module_pseudo_stub_count32{$module32}++;
+               $module_pseudo_stub{$module32}{$external_name32}++;
+               $pseudo_stub = 1;
            }
        }
     }
+
+    return $pseudo_stub;
+}
+
+my @h_files = ();
+if($options->headers) {
+    @h_files = $options->h_files;
+    @h_files = files_skip(@h_files);
+    @h_files = files_filter("winelib", @h_files);
 }
 
-my @c_files = $options->c_files;
-@c_files = files_skip(@c_files);
-@c_files = files_filter("winelib", @c_files);
+my @c_files = ();
+if($options->spec_files || $options->pseudo_implemented || $options->pseudo_stub_statistics) {
+    @c_files = $options->c_files;
+    @c_files = files_skip(@c_files);
+    @c_files = files_filter("winelib", @c_files);
+}
 
 my $progress_output;
 my $progress_current = 0;
-my $progress_max = scalar(@c_files);
+my $progress_max = scalar(@h_files) + scalar(@c_files);
 
-foreach my $file (@c_files) {
+foreach my $file (@h_files, @c_files) {
     my %functions;
 
     $progress_current++;
-    if($options->progress) {
-       $output->progress("$file: file $progress_current of $progress_max");
+
+    {
+       open(IN, "< $file");
+       local $/ = undef;
+       $_ = <IN>;
+       close(IN);
+    }
+
+    my $max_line = 0;
+    {
+      local $_ = $_;
+      while(s/^.*?\n//) { $max_line++; }
+      if($_) { $max_line++; }
     }
 
+    my $parser;
+    if (!$options->old) {
+       $parser = new c_parser($file);
+    } else {
+       $parser = new winapi_c_parser($file);
+    }
+
+    my $function;
+    my $line;
+
+    my $update_output = sub {
+       my $progress = "";
+       my $prefix = "";
+
+       $progress .= "$file (file $progress_current of $progress_max)";
+       $prefix .= "$file:";
+
+       if(defined($function)) {
+           my $name = $function->name;
+           my $begin_line = $function->begin_line;
+           my $begin_column = $function->begin_column;
+
+           $progress .= ": function $name";
+           $prefix .= "$begin_line.$begin_column: function $name: ";
+       } else {
+           $prefix .= " "; 
+       }
+
+       if(defined($line)) {
+           $progress .= ": line $line of $max_line";
+       }
+
+       $output->progress($progress);
+       $output->prefix($prefix);
+    };
+
+    &$update_output();
+
     my $found_function = sub {
-       my $function = shift;
-
-       my $documentation_line = $function->documentation_line;
-       my $documentation = $function->documentation;
-       my $function_line = $function->function_line;
-       my $linkage = $function->linkage;
-       my $return_type = $function->return_type;
-       my $calling_convention = $function->calling_convention;
-       my $internal_name = $function->internal_name;
-       my @argument_types = @{$function->argument_types};
-       my @argument_names = @{$function->argument_names};
-       my @argument_documentations = @{$function->argument_documentations};
-       my $statements = $function->statements;
-
-       $functions{$internal_name} = $function;
-
-       $output->prefix("$file: " . $function->prefix);
-
-       if($options->spec_files) {
-           documentation_specifications($function);
+       $function = shift;
+
+       my $name = $function->name;
+       $functions{$name} = $function;
+
+       if ($function->statements) {
+           &$update_output();
+       }
+
+       my $old_function;
+       if($options->implemented || $options->stub_statistics) {
+           $old_function = 'winapi_function'->new;
+       } else {
+           $old_function = 'function'->new;
+       }
+
+       $old_function->file($function->file);
+       $old_function->debug_channels([]); # FIXME: Not complete
+
+       $old_function->documentation_line(0); # FIXME: Not complete
+       $old_function->documentation(""); # FIXME: Not complete
+
+       $old_function->function_line($function->begin_line());
+       $old_function->linkage($function->linkage);
+       $old_function->return_type($function->return_type);
+       $old_function->calling_convention($function->calling_convention);
+       $old_function->internal_name($function->name);
+       if (defined($function->argument_types)) {
+           $old_function->argument_types([@{$function->argument_types}]);
+       }
+       if (defined($function->argument_names)) {
+           $old_function->argument_names([@{$function->argument_names}]);
+       }
+       $old_function->argument_documentations([]); # FIXME: Not complete
+       $old_function->statements_line($function->statements_line);
+       $old_function->statements($function->statements);
+
+       if($options->spec_files || $options->winetest) {
+           documentation_specifications($old_function);
+       }
+
+       if ($function->statements) {
+           $function = undef;
+           &$update_output();
+       } else {
+           $function = undef;
        }
 
-       if($options->stub_statistics) {
-           statements_stub($function);
+       my $pseudo_stub = 0;
+       if ($options->pseudo_implemented || $options->pseudo_stub_statistics) {
+           $pseudo_stub = statements_pseudo_stub($old_function);
        }
 
-       $output->prefix("");
+       my $module = $old_function->module;
+       my $external_name = $old_function->external_name;
+       my $statements = $old_function->statements;
+       if ($options->pseudo_implemented && $module && $external_name && $statements) {
+           my @external_names = split(/\s*&\s*/, $external_name);
+           my @modules = split(/\s*&\s*/, $module);
+
+           my @external_names2;
+           while(defined(my $external_name = shift @external_names) &&
+                 defined(my $module = shift @modules))
+           {
+               if ($pseudo_stub) {
+                   $output->write("$module.$external_name: pseudo implemented\n");
+               } else {
+                   $output->write("$module.$external_name: implemented\n");
+               }
+           }
+       }
     };
+    $parser->set_found_function_callback($found_function);
+
+    my $found_line = sub {
+       $line = shift;
 
-    my $found_preprocessor = sub {
-       my $directive = shift;
-       my $argument = shift;
+       &$update_output;
     };
+    $parser->set_found_line_callback($found_line);
+
+    my $found_type = sub {
+       my $type = shift;
 
-    winapi_parser::parse_c_file $options, $output, $file, $found_function, $found_preprocessor;
+       &$update_output();
+       
+       my $kind = $type->kind;
+       my $_name = $type->_name;
+       my $name = $type->name;
+       
+       foreach my $field ($type->fields) {
+           my $field_type_name = $field->type_name;
+           my $field_name = $field->name;
 
-    my @internal_names = keys(%functions);
-    if($#internal_names < 0) {
-       $output->write("$file: doesn't contain any functions\n");
+           if ($options->struct && $kind =~ /^(?:struct|union)$/) {
+               if ($name) {
+                   $output->write("$name:$field_type_name:$field_name\n");
+               } else {
+                   $output->write("$kind $_name:$field_type_name:$field_name\n");
+               }
+           }
+       }
+
+       return 1;
+    };
+    $parser->set_found_type_callback($found_type);
+
+    {
+       my $line = 1;
+       my $column = 0;
+       if(!$parser->parse_c_file(\$_, \$line, \$column)) {
+           $output->write("can't parse file\n");
+       }
+    }
+
+    $output->prefix("");
+}
+
+
+if($options->implemented && !$options->pseudo_implemented) {
+    foreach my $winapi (@winapis) {
+       my $type = $winapi->name;
+
+       if($type eq "win16" && !$options->win16) { next; }
+       if($type eq "win32" && !$options->win32) { next; }
+
+       foreach my $module ($winapi->all_modules) {
+           foreach my $external_name ($winapi->all_functions_in_module($module)) {
+                my $external_calling_convention =
+                    $winapi->function_external_calling_convention_in_module($module, $external_name);
+
+                if($external_calling_convention eq "forward") {
+                    (my $forward_module, my $forward_external_name) =
+                        $winapi->function_forward_final_destination($module, $external_name);
+
+                    my $forward_external_calling_convention =
+                        $winapi->function_external_calling_convention_in_module($forward_module, $forward_external_name);
+
+                    if(!defined($forward_external_calling_convention)) {
+                        next;
+                    }
+
+                    $external_calling_convention = $forward_external_calling_convention;
+                }
+
+                if ($external_calling_convention ne "stub") {
+                    $output->write("*.spec: $module.$external_name: implemented\n");
+                }
+           }
+       }
     }
 }
 
@@ -247,156 +426,292 @@ sub output_function {
 
     my $return_kind;
     my $calling_convention;
-    my @argument_kinds;
+    my $refargument_kinds;
     if($type eq "win16") {
        $return_kind = $function->return_kind16 || "undef";
        $calling_convention = $function->calling_convention16 || "undef";
-       @argument_kinds = map { $_ || "undef"; } @{$function->argument_kinds16};
+       $refargument_kinds = $function->argument_kinds16;
     } elsif($type eq "win32") {
        $return_kind = $function->return_kind32 || "undef";
        $calling_convention = $function->calling_convention32 || "undef";
-       @argument_kinds = map { $_ || "undef"; } @{$function->argument_kinds32};
+       $refargument_kinds = $function->argument_kinds32;
     }
 
-    print OUT "$ordinal $calling_convention $external_name(@argument_kinds) $internal_name\n";
+    if(defined($refargument_kinds)) {
+       my @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
+       print OUT "$ordinal $calling_convention $external_name(@argument_kinds) $internal_name\n";
+    } else {
+       print OUT "$ordinal $calling_convention $external_name() $internal_name # FIXME: arguments undefined\n";
+    }
 }
 
 if($options->spec_files) {
-    foreach my $module (keys(%specifications)) {
-       my $spec_file = $module2spec_file{$module};
-       my $type = $module2type{$module};
-       
-       if(!defined($spec_file) || !defined($type)) {
-           $output->write("$module: doesn't exist\n");
-           next;
-       }
-       
-       $spec_file .= "2";
-       
-       $output->progress("$spec_file");
-       open(OUT, "> $wine_dir/$spec_file");
-
-       print OUT "name $module\n";
-       print OUT "type $type\n";
-       if(exists($specifications{$module}{init})) {
-           my $function = $specifications{$module}{init}{function};
-           print OUT "init " . $function->internal_name . "\n";
-       }
-       print OUT "\n";
-       
-       my %debug_channels;
-       if(exists($specifications{$module}{init})) {
-           my $function = $specifications{$module}{init}{function};
-           foreach my $debug_channel (@{$function->debug_channels}) {
-               $debug_channels{$debug_channel}++;
+    foreach my $winapi (@winapis) {
+       my $type = $winapi->name;
+
+       if($type eq "win16" && !$options->win16) { next; }
+       if($type eq "win32" && !$options->win32) { next; }
+
+       foreach my $module ($winapi->all_modules) {
+           my $spec_file = $module2spec_file{$module};
+
+           if(!defined($spec_file) || !defined($type)) {
+               $output->write("$module: doesn't exist\n");
+               next;
            }
-       }
-       foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
-           my $function = $specifications{$module}{fixed}{$ordinal}{function};
-           foreach my $debug_channel (@{$function->debug_channels}) {
-               $debug_channels{$debug_channel}++;
+
+           $spec_file .= "2";
+
+           $output->progress("$spec_file");
+           open(OUT, "> $wine_dir/$spec_file");
+
+           if(exists($specifications{$module}{init})) {
+               my $function = $specifications{$module}{init}{function};
+               print OUT "init " . $function->internal_name . "\n";
            }
-       }
-       foreach my $name (sort(keys(%{$specifications{$module}{unfixed}}))) {
-           my $function = $specifications{$module}{unfixed}{$name}{function}; 
-           foreach my $debug_channel (@{$function->debug_channels}) {
-               $debug_channels{$debug_channel}++;
+           print OUT "\n";
+
+           my %debug_channels;
+           if(exists($specifications{$module}{init})) {
+               my $function = $specifications{$module}{init}{function};
+               foreach my $debug_channel (@{$function->debug_channels}) {
+                   $debug_channels{$debug_channel}++;
+               }
            }
-       }
-       foreach my $name (sort(keys(%{$specifications{$module}{unknown}}))) {
-           my $function = $specifications{$module}{unknown}{$name}{function};
-           foreach my $debug_channel (@{$function->debug_channels}) {
-               $debug_channels{$debug_channel}++;
+           foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
+               my $function = $specifications{$module}{fixed}{$ordinal}{function};
+               foreach my $debug_channel (@{$function->debug_channels}) {
+                   $debug_channels{$debug_channel}++;
+               }
+           }
+           foreach my $name (sort(keys(%{$specifications{$module}{unfixed}}))) {
+               my $function = $specifications{$module}{unfixed}{$name}{function};
+               foreach my $debug_channel (@{$function->debug_channels}) {
+                   $debug_channels{$debug_channel}++;
+               }
+           }
+           foreach my $name (sort(keys(%{$specifications{$module}{unknown}}))) {
+               my $function = $specifications{$module}{unknown}{$name}{function};
+               foreach my $debug_channel (@{$function->debug_channels}) {
+                   $debug_channels{$debug_channel}++;
+               }
            }
-       }
 
-       my @debug_channels = sort(keys(%debug_channels));
-       if($#debug_channels >= 0) { 
-           print OUT "debug_channels (" .  join(" ", @debug_channels) . ")\n";
-           print OUT "\n";
+           my @debug_channels = sort(keys(%debug_channels));
+           if($#debug_channels >= 0) {
+               print OUT "debug_channels (" .  join(" ", @debug_channels) . ")\n";
+               print OUT "\n";
+           }
+
+           my $empty = 1;
+
+           if(!$empty) {
+               print OUT "\n";
+               $empty = 1;
+           }
+           foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
+               my $entry = $specifications{$module}{unknown}{$external_name};
+               my $ordinal = $entry->{ordinal};
+               my $function = $entry->{function};
+               print OUT "# ";
+               output_function(\*OUT, $type, $ordinal, $external_name, $function);
+               $empty = 0;
+           }
+
+           if(!$empty) {
+               print OUT "\n";
+               $empty = 1;
+           }
+           foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
+               my $entry = $specifications{$module}{fixed}{$ordinal};
+               my $external_name = $entry->{external_name};
+               my $function = $entry->{function};
+               output_function(\*OUT, $type, $ordinal, $external_name, $function);
+               $empty = 0;
+           }
+
+           if(!$empty) {
+               print OUT "\n";
+               $empty = 1;
+           }
+           foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
+               my $entry = $specifications{$module}{unfixed}{$external_name};
+               my $ordinal = $entry->{ordinal};
+               my $function = $entry->{function};
+               output_function(\*OUT, $type, $ordinal, $external_name, $function);
+               $empty = 0;
+           }
+
+           close(OUT);
        }
-       
-       my $empty = 1;
+    }
+}
 
-       if(!$empty) {
-           print OUT "\n";
-           $empty = 1;
+if($options->stub_statistics) {
+    foreach my $winapi (@winapis) {
+       my $type = $winapi->name;
+
+       if($type eq "win16" && !$options->win16) { next; }
+       if($type eq "win32" && !$options->win32) { next; }
+
+       my %module_counts;
+         foreach my $module ($winapi->all_modules) {
+             foreach my $external_name ($winapi->all_functions_in_module($module)) {
+                my $external_calling_convention =
+                    $winapi->function_external_calling_convention_in_module($module, $external_name);
+                if($external_calling_convention !~ /^forward|stub$/) {
+                    if($module_pseudo_stub{$module}{$external_name}) {
+                        $external_calling_convention = "pseudo_stub";
+                    }
+                } elsif($external_calling_convention eq "forward") {
+                    (my $forward_module, my $forward_external_name) =
+                        $winapi->function_forward_final_destination($module, $external_name);
+
+                    my $forward_external_calling_convention =
+                        $winapi->function_external_calling_convention_in_module($forward_module, $forward_external_name);
+
+                    if(!defined($forward_external_calling_convention)) {
+                        next;
+                    }
+
+                    if($forward_external_calling_convention ne "stub" &&
+                       $module_pseudo_stub{$forward_module}{$forward_external_name})
+                    {
+                        $forward_external_calling_convention = "pseudo_stub";
+                    }
+
+                    $external_calling_convention = "forward_$forward_external_calling_convention";
+                }
+
+                $module_counts{$module}{$external_calling_convention}++;
+            }
+        }
+
+       foreach my $module ($winapi->all_modules) {
+           my $pseudo_stubs = $module_counts{$module}{pseudo_stub} || 0;
+           my $real_stubs = $module_counts{$module}{stub} || 0;
+           my $forward_pseudo_stubs = $module_counts{$module}{forward_pseudo_stub} || 0;
+           my $forward_real_stubs = $module_counts{$module}{forward_stub} || 0;
+
+           my $forwards = 0;
+           my $total = 0;
+           foreach my $calling_convention (keys(%{$module_counts{$module}})) {
+               my $count = $module_counts{$module}{$calling_convention};
+               if($calling_convention =~ /^forward/) {
+                   $forwards += $count;
+               }
+               $total += $count;
+           }
+
+           if($total > 0) {
+               my $stubs = $real_stubs + $pseudo_stubs;
+
+               $output->write("*.c: $module: ");
+               $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo) " .
+                              "and $forwards are forwards\n");
+           }
+
+           if($forwards > 0) {
+               my $forward_stubs = $forward_real_stubs + $forward_pseudo_stubs;
+
+               $output->write("*.c: $module: ");
+               $output->write("$forward_stubs of $forwards forwarded functions are stubs " .
+                              "($forward_real_stubs real, $forward_pseudo_stubs pseudo)\n");
+           }
        }
+    }
+}
+
+if($options->winetest) {
+    foreach my $module ($win32api->all_modules) {
+       my $type = "win32";
+
+        my $package = $module;
+       $package =~ s/\.dll$//;
+        $package =~ s/\./_/g;
+
+       my @entries;
+
        foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
            my $entry = $specifications{$module}{unknown}{$external_name};
-           my $ordinal = $entry->{ordinal}; 
-           my $function = $entry->{function}; 
-           print OUT "# ";
-           output_function(\*OUT, $type, $ordinal, $external_name, $function);
-           $empty = 0;
+           push @entries, $entry;
        }
 
-       if(!$empty) {
-           print OUT "\n";
-           $empty = 1;
-       }
        foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
            my $entry = $specifications{$module}{fixed}{$ordinal};
-           my $external_name = $entry->{external_name}; 
-           my $function = $entry->{function}; 
-           output_function(\*OUT, $type, $ordinal, $external_name, $function);
-           $empty = 0;
+           push @entries, $entry;
        }
 
-       if(!$empty) {
-           print OUT "\n";
-           $empty = 1;
-       }
        foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
            my $entry = $specifications{$module}{unfixed}{$external_name};
-           my $ordinal = $entry->{ordinal};
-           my $function = $entry->{function};
-           output_function(\*OUT, $type, $ordinal, $external_name, $function);
-           $empty = 0;
+           push @entries, $entry;
        }
 
-       close(OUT);
-    }
-}
+       my $n = 0;
+       foreach my $entry (@entries) {
+           my $external_name = $entry->{external_name};
+           my $ordinal = $entry->{ordinal};
+           my $function = $entry->{function};
 
-if($options->stub_statistics) {
-    foreach my $winapi (@winapis) {
-       if($winapi->name eq "win16" && !$options->win16) { next; }
-       if($winapi->name eq "win32" && !$options->win32) { next; }
+           my $return_kind = $function->return_kind32 || "undef";
+           my $calling_convention = $function->calling_convention32 || "undef";
+           my $refargument_kinds = $function->argument_kinds32;
 
-       my %module_stub_count;
-       my %module_total_count;
-       
-       foreach my $internal_name ($winapi->all_internal_functions,$winapi->all_functions_stub) {
-           foreach my $module (split(/ \& /, $winapi->function_internal_module($internal_name))) {
-               if($winapi->function_stub($internal_name)) {
-                   $module_stub_count{$module}++;
-               }
-               $module_total_count{$module}++;
+           my @argument_kinds;
+           if(defined($refargument_kinds)) {
+               @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
            }
-       }
 
-       foreach my $module ($winapi->all_modules) {
-           my $pseudo_stubs;
-           if($winapi->name eq "win16") {
-               $pseudo_stubs = $module_pseudo_stub_count16{$module};
-           } elsif($winapi->name eq "win32") {
-               $pseudo_stubs = $module_pseudo_stub_count32{$module};
-           }
+           next if $calling_convention ne "stdcall";
+           next if $external_name eq "\@";
+
+           if($n == 0) {
+                open(OUT, "> $wine_dir/programs/winetest/include/${package}.pm");
+
+                print OUT "package ${package};\n";
+                print OUT "\n";
+
+                print OUT "use strict;\n";
+                print OUT "\n";
 
-           my $real_stubs = $module_stub_count{$module};
-           my $total = $module_total_count{$module};
+                print OUT "require Exporter;\n";
+                print OUT "\n";
 
-           if(!defined($real_stubs)) { $real_stubs = 0; }
-           if(!defined($pseudo_stubs)) { $pseudo_stubs = 0; }
-           if(!defined($total)) { $total = 0;}
+                print OUT "use wine;\n";
+                print OUT "use vars qw(\@ISA \@EXPORT \@EXPORT_OK);\n";
+                print OUT "\n";
 
-           my $stubs = $real_stubs + $pseudo_stubs;
-    
-           $output->write("*.c: $module: ");
-           $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo)\n");
+                print OUT "\@ISA = qw(Exporter);\n";
+                print OUT "\@EXPORT = qw();\n";
+                print OUT "\@EXPORT_OK = qw();\n";
+                print OUT "\n";
+
+                print OUT "my \$module_declarations = {\n";
+           } elsif($n > 0) {
+               print OUT ",\n";
+           }
+
+           print OUT "    \"\Q$external_name\E\" => [\"$return_kind\",  [";
+           my $m = 0;
+           foreach my $argument_kind (@argument_kinds) {
+               if($m > 0) {
+                   print OUT ", ";
+               }
+               print OUT "\"$argument_kind\"";
+               $m++;
+           }
+           print OUT "]]";
+           $n++;
        }
-    } 
-}
 
-$output->hide_progress;
+       if($n > 0) {
+           print OUT "\n";
+           print OUT "};\n";
+           print OUT "\n";
+            print OUT "&wine::declare(\"$module\",\%\$module_declarations);\n";
+            print OUT "push \@EXPORT, map { \"&\" . \$_; } sort(keys(\%\$module_declarations));\n";
+           print OUT "1;\n";
+            close(OUT);
+       }
+    }
+}