|  | #!/usr/bin/perl -w | 
|  | # | 
|  | # Update spec files across dlls that share an implementation | 
|  | # | 
|  | # Copyright 2011 Alexandre Julliard | 
|  | # | 
|  | # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | # | 
|  |  | 
|  | use strict; | 
|  |  | 
|  | my %funcs; | 
|  | my $group_head; | 
|  |  | 
|  | my @dll_groups = | 
|  | ( | 
|  | [ | 
|  | "msvcrt", | 
|  | "msvcr90", | 
|  | "msvcirt", | 
|  | "msvcr100", | 
|  | "msvcr80", | 
|  | "msvcr71", | 
|  | "msvcr70", | 
|  | "msvcrt40", | 
|  | "msvcrt20", | 
|  | "msvcrtd", | 
|  | "crtdll", | 
|  | ], | 
|  | [ | 
|  | "msvcrt", | 
|  | "msvcp90", | 
|  | "msvcp100", | 
|  | "msvcp80", | 
|  | "msvcp71", | 
|  | "msvcp70", | 
|  | "msvcp60", | 
|  | ], | 
|  | [ | 
|  | "d3dx9_36", | 
|  | "d3dx9_43", | 
|  | "d3dx9_42", | 
|  | "d3dx9_41", | 
|  | "d3dx9_40", | 
|  | "d3dx9_39", | 
|  | "d3dx9_38", | 
|  | "d3dx9_37", | 
|  | "d3dx9_35", | 
|  | "d3dx9_34", | 
|  | "d3dx9_33", | 
|  | "d3dx9_32", | 
|  | "d3dx9_31", | 
|  | "d3dx9_30", | 
|  | "d3dx9_29", | 
|  | "d3dx9_28", | 
|  | "d3dx9_27", | 
|  | "d3dx9_26", | 
|  | "d3dx9_25", | 
|  | "d3dx9_24", | 
|  | ], | 
|  | [ | 
|  | "d3dx10_43", | 
|  | "d3dx10_42", | 
|  | "d3dx10_41", | 
|  | "d3dx10_40", | 
|  | "d3dx10_39", | 
|  | "d3dx10_38", | 
|  | "d3dx10_37", | 
|  | "d3dx10_36", | 
|  | "d3dx10_35", | 
|  | "d3dx10_34", | 
|  | "d3dx10_33", | 
|  | ], | 
|  | [ | 
|  | "d3dcompiler_43", | 
|  | "d3dcompiler_42", | 
|  | "d3dcompiler_41", | 
|  | "d3dcompiler_40", | 
|  | "d3dcompiler_39", | 
|  | "d3dcompiler_38", | 
|  | "d3dcompiler_37", | 
|  | "d3dcompiler_36", | 
|  | "d3dcompiler_35", | 
|  | "d3dcompiler_34", | 
|  | "d3dcompiler_33", | 
|  | ], | 
|  | [ | 
|  | "xinput1_3", | 
|  | "xinput1_2", | 
|  | "xinput1_1", | 
|  | "xinput9_1_0", | 
|  | ], | 
|  | [ | 
|  | "setupapi", | 
|  | "cfgmgr32", | 
|  | ], | 
|  | [ | 
|  | "vcomp", | 
|  | "vcomp100", | 
|  | ], | 
|  | [ | 
|  | "atl", | 
|  | "atl80", | 
|  | ], | 
|  | ); | 
|  |  | 
|  | my $update_flags = 0; | 
|  | my $show_duplicates = 0; | 
|  |  | 
|  | foreach my $arg (@ARGV) | 
|  | { | 
|  | if ($arg eq "-f") { $update_flags = 1; } | 
|  | elsif ($arg eq "-d") { $show_duplicates = 1; } | 
|  | } | 
|  |  | 
|  | sub update_file($) | 
|  | { | 
|  | my $file = shift; | 
|  | my $ret = !(-f $file) || system "cmp $file $file.new >/dev/null"; | 
|  | if (!$ret) | 
|  | { | 
|  | unlink "$file.new"; | 
|  | } | 
|  | else | 
|  | { | 
|  | #system "diff -u $file $file.new"; | 
|  | rename "$file.new", "$file"; | 
|  | print "$file updated\n"; | 
|  | } | 
|  | return $ret; | 
|  | } | 
|  |  | 
|  | # parse a spec file line | 
|  | sub parse_line($$$) | 
|  | { | 
|  | my ($name, $line, $_) = @_; | 
|  |  | 
|  | if (/^\s*(\@|\d+)\s+(stdcall|cdecl|varargs|thiscall|stub|extern)\s+((?:-\S+\s+)*)([A-Za-z0-9_\@\$?]+)(?:\s*(\([^)]*\)))?(?:\s+([A-Za-z0-9_\@\$?.]+))?(\s*\#.*)?/) | 
|  | { | 
|  | return ( "ordinal" => $1, "callconv" => $2, "flags" => $3, "name" => $4, "args" => $5 || "", | 
|  | "target" => $6 || $4, "comment" => $7, "spec" => $name ); | 
|  | } | 
|  | return () if /^\s*$/; | 
|  | return () if /^\s*\#/; | 
|  | printf STDERR "$name.spec:$line: error: Unrecognized line $_\n"; | 
|  | } | 
|  |  | 
|  | sub read_spec_file($) | 
|  | { | 
|  | my $name = shift; | 
|  | my $file = "dlls/$name/$name.spec"; | 
|  | my %stubs; | 
|  | open SPEC, "<$file" or die "cannot open $file"; | 
|  | while (<SPEC>) | 
|  | { | 
|  | chomp; | 
|  | my %descr = parse_line( $name, $., $_ ); | 
|  | next unless %descr; | 
|  |  | 
|  | my $func = $descr{name}; | 
|  | next if defined $funcs{$func}; | 
|  | $funcs{$func} = \%descr; | 
|  | } | 
|  | close SPEC; | 
|  | } | 
|  |  | 
|  | sub update_spec_file($) | 
|  | { | 
|  | my $name = shift; | 
|  | my $file = "dlls/$name/$name.spec"; | 
|  | my %stubs; | 
|  |  | 
|  | open SPEC, "<$file" or die "cannot open $file"; | 
|  | open NEW, ">$file.new" or die "cannot create $file.new"; | 
|  | while (<SPEC>) | 
|  | { | 
|  | chomp; | 
|  |  | 
|  | my $commented_out = 0; | 
|  | my %descr = parse_line( $name, $., $_ ); | 
|  | if (!%descr) | 
|  | { | 
|  | # check for commented out exports | 
|  | if (/^\s*\#\s*((?:\@|\d+)\s+)?((?:extern|stub|stdcall|cdecl|varargs|thiscall)\s+.*)/) | 
|  | { | 
|  | $commented_out = 1; | 
|  | %descr = parse_line( $name, $., ($1 || "\@ ") . $2 ); | 
|  | } | 
|  | } | 
|  | goto done unless %descr; | 
|  |  | 
|  | my $func = $descr{name}; | 
|  | if (!defined $funcs{$func}) | 
|  | { | 
|  | $funcs{$func} = \%descr unless $commented_out; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | my %parent = %{$funcs{$func}}; | 
|  | goto done if $parent{spec} eq $descr{spec};  # the definition is in this spec file | 
|  | goto done if $descr{comment} && $descr{comment} =~ /don't forward/; | 
|  | if ($descr{callconv} ne "stub" && $descr{target} !~ /\./ && !$commented_out) | 
|  | { | 
|  | printf "%s:%u: note: %s already defined in %s\n", $file, $., $func, $parent{spec} if $show_duplicates; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | my $flags = ($parent{callconv} ne "stub" || $update_flags) ? $parent{flags} : $descr{flags}; | 
|  |  | 
|  | if ($parent{callconv} ne "stub" || $parent{args}) | 
|  | { | 
|  | my $callconv = $parent{callconv} ne "stub" ? $parent{callconv} : | 
|  | $parent{spec} =~ /msvc/ ? "cdecl" : "stdcall";  # hack | 
|  | $_ = sprintf "$descr{ordinal} %s %s%s", $callconv, $flags, $func; | 
|  |  | 
|  | if ($parent{target} =~ /$group_head\./)  # use the same forward as parent if possible | 
|  | { | 
|  | $_ .= sprintf "%s %s", $parent{args}, $parent{target}; | 
|  | } | 
|  | else | 
|  | { | 
|  | $_ .= sprintf "%s %s.%s", $parent{args}, $parent{spec}, $func; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | $_ = sprintf "$descr{ordinal} stub %s%s", $flags, $func; | 
|  | } | 
|  | $_ .= $descr{comment} || ""; | 
|  |  | 
|  | done: | 
|  | print NEW "$_\n"; | 
|  | } | 
|  | close SPEC; | 
|  | close NEW; | 
|  | update_file( $file ); | 
|  | } | 
|  |  | 
|  | sub sync_spec_files(@) | 
|  | { | 
|  | %funcs = (); | 
|  | $group_head = shift; | 
|  | read_spec_file( $group_head ); | 
|  | foreach my $spec (@_) { update_spec_file($spec); } | 
|  | } | 
|  |  | 
|  | foreach my $group (@dll_groups) | 
|  | { | 
|  | sync_spec_files( @{$group} ); | 
|  | } |