Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

158 lines
3.4 KiB

use strict;
use warnings;
use File::Basename;
main();
exit(0);
sub usage
{
$0 = basename($0);
return <<DATA;
usage: $0 [-v] prototype <exename> <funcspec>
or $0 [-v] typedef <exename> <funcspec> <typename>
-v is for verbose output
NOTE: For now, the funcspec is an undecorated name that must match exactly.
DATA
}
my $VERBOSE;
sub main
{
my $opt = shift @ARGV || die usage();
if ($opt eq '-v') {
$VERBOSE = $opt;
} else {
unshift(@ARGV, $opt);
}
my $op = shift @ARGV || die usage();
my $exe = shift @ARGV || die usage();
my $funcspec = shift @ARGV || die usage();
my $typename = shift @ARGV;
my $extra = shift @ARGV;
die usage() if $extra;
if ($op eq 'prototype') {
die usage() if $typename;
DoPrototype($exe, $funcspec);
}
elsif ($op eq 'typedef') {
die usage() if !$typename;
DoPrototype($exe, $funcspec, $typename);
}
else {
die usage();
}
}
sub FindModulePath
{
my $exe = shift || die;
my $funcspec = shift || die;
my $cmd = "mage /s $exe /l functions imports";
my @output = GetCommandOutput($cmd, $VERBOSE, 1, "mage");
map { chomp($_); } @output;
my $is_exe;
my $dll;
foreach my $line (@output) {
if ($line eq '') {
# skip
} elsif ($line =~ /^Microsoft Mage/i) {
# skip
} elsif ($line =~ /^Function: ([A-Za-z0-9_]+)$/) {
# undecorated matching
if ($1 eq $funcspec) {
$is_exe = 1;
last;
}
} elsif ($line =~ /^Import: \[([^\]]+)\] (.+)$/) {
# undecorated matching
if ($2 eq $funcspec) {
$dll = $1;
last;
}
} else {
die "Unexpected output from Mage:\n$line\n";
}
}
if (!$is_exe and !$dll) {
return undef;
}
if ($is_exe) {
return $exe;
}
my @where = GetCommandOutput("where $dll", 0, 1); # die on failure
my $path = shift @where || die "Could not locate $dll\n";
chomp($path);
return $path;
}
sub GetCommandOutput
{
my $cmd = shift || die;
my $verbose = shift;
my $die = shift;
my $pretty = shift;
print "Running: $cmd\n" if $verbose;
my @output = `$cmd`;
my $code = $? / 256;
if ($code and $die) {
if ($pretty) {
die "$pretty failed with exit code $code\n";
} else {
die "Command failed (exit code $code):\n$cmd\n";
}
}
return @output;
}
sub DoPrototype
{
my $exe = shift || die;
my $funcspec = shift || die;
my $typename = shift;
my $path = FindModulePath($exe, $funcspec);
if (!$path) {
print "No matches found\n";
exit(1);
}
my $cmd = "mage /s $path /f $funcspec";
my @output = GetCommandOutput($cmd, $VERBOSE, 1, "mage");
map { chomp($_); } @output;
foreach my $line (@output) {
if ($line =~ /^Prototype: (.+)/) {
my $proto = $1;
if (!$typename) {
print "$proto\n";
} else {
print "$proto\n" if $VERBOSE;
# grab func name, previous token is callconv, before that
# is ret type, afterwards, take parens, remove last token
# before each comma...or not...I don't recall whether
# that's required...
die if !($proto =~ /^(.*)\s+(\S+)\s+$funcspec(\(.+)$/);
my $ret = $1;
my $callconv = $2;
my $args = $3;
$args =~ s/(\s*[A-Za-z0-9_]+)\s*([,\)])/$2/g;
print "typedef $ret ($callconv *$typename)$args\n";
}
exit(0);
}
}
}