Commit 37fd2d1c authored by Patrik Stridvall's avatar Patrik Stridvall Committed by Alexandre Julliard

Made a new improved version of winapi-check in perl.

parent 26e16530
#!/bin/sh
# This quick and dirty script prints the names of functions that don't have a
# WINAPI in its header declaration or don't appear in any .c file at all.
# I don't think it's very intelligent to use it when having < 64 MB ;)
# FIXME: This script skips .spec function entries that occupy two or more
# .spec lines !
if [ ! -e ./ChangeLog ] ; then
echo You are not in Wine main directory !
exit
fi
echo crtdll.spec, ntdll.spec and wsock32.spec will be mentioned many times,
echo as they use original UNIX functions that don\'t exist in Wine .c code.
echo
FIND_LIST="`find . -name "*.c"`"
for i in if1632/*.spec relay32/*.spec
do
# skip wprocs.spec, as it contains many funcs that don't need a WINAPI
if [ $i = "if1632/wprocs.spec" ] ; then
continue
fi
LINE="`egrep "stdcall|cdecl|pascal|register" $i|grep -v "^#"|tr -d " "|tr "\n" " "`"
for j in $LINE
do
if [ -n "`echo "$j"|grep \)`" ] ; then
FUNC="`echo $j|cut -f2 -d\)|cut -f1 -d'#'`"
if [ -n "$FUNC" ] ; then
if [ -z "`grep -B 1 $FUNC $FIND_LIST|egrep "WINAPI|__cdecl|VFWAPI|DC_GET_VAL|DC_SET_MODE|REGS_ENTRYPOINT"`" ] ; then
case $FUNC in # "black list"
"GetBrushOrgEx16" ) ;;
"GetCurrentPositionEx16" ) ;;
"GetViewportExtEx16" ) ;;
"GetViewportOrgEx16" ) ;;
"GetWindowExtEx16" ) ;;
"GetWindowOrgEx16" ) ;;
"GetBrushOrgEx32" ) ;;
"GetCurrentPositionEx32" ) ;;
"GetViewportExtEx32" ) ;;
"GetViewportOrgEx32" ) ;;
"GetWindowExtEx32" ) ;;
"GetWindowOrgEx32" ) ;;
* ) echo "$i: $FUNC" ;;
esac
fi
fi
fi
done
done
__ctype
__divdi3
__eprintf
__fixunsdfdi
__flsbuf
__iob
__moddi3
_environ
_exit
_fxstat
_lwp_create
_lwp_exit
_lwp_makecontext
_lxstat
_sysconf
_xmknod
_xstat
abs
access
acos
asctime
asin
atan
atan2
atexit
atof
atoi
atol
bsearch
bzero
calloc
ceil
cfgetospeed
chmod
close
closedir
cos
cosh
ctime
div
dup
dup2
environ
errno
execlp
execvp
exit
exp
fabs
fclose
fcntl
fdopen
feof
fflush
ffs
fgetc
fgetpos
fgets
floor
fmod
fopen
fork
fprintf
fputc
fputs
fread
free
frexp
fseek
fsetpos
fsync
ftruncate
fwrite
getcwd
getenv
getlogin
getnetbyname
getpid
getpwuid
gettimeofday
getuid
gmtime
hypot
inet_network
ioctl
isalnum
isalpha
isatty
iscntrl
isdigit
isgraph
islower
isprint
ispunct
isspace
isupper
isxdigit
j0
j1
jn
kill
labs
ldexp
ldiv
localtime
log
log10
longjmp
lseek
malloc
mblen
memchr
memcmp
memcpy
memmove
memset
mkdir
mktime
mmap
modf
mprotect
msync
munmap
open
opendir
perror
pipe
poll
pow
printf
putchar
putenv
puts
qsort
raise
rand
read
readdir
realloc
remove
rename
rmdir
select
setbuf
setlocale
setsid
settimeofday
setvbuf
shmat
shmctl
shmdt
shmget
sigaction
sigaddset
sigaltstack
sigemptyset
signal
sigprocmask
sin
sin
sinh
sleep
snprintf
sprintf
sqrt
srand
sscanf
statfs
strcasecmp
strcat
strchr
strcmp
strcoll
strcpy
strcspn
strdup
strerror
strftime
strlen
strncasecmp
strncat
strncmp
strncpy
strpbrk
strrchr
strspn
strstr
strtod
strtok
strtol
strtoul
strxfrm
sysi86
system
tan
tanh
tcflush
tcgetattr
tcsetattr
tempnam
time
times
tmpnam
tolower
toupper
towlower
towupper
unlink
usleep
utime
vfprintf
vsprintf
wait4
write
y0
y1
yn
package nativeapi;
use strict;
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
bless ($self, $class);
my $functions = \%{$self->{FUNCTIONS}};
my $file = shift;
open(IN, "< $file");
$/ = "\n";
while(<IN>) {
s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begin and end of line
s/^(.*?)\s*#.*$/$1/; # remove comments
/^$/ && next; # skip empty lines
$$functions{$_} = 1;
}
close(IN);
return $self;
}
sub is_function {
my $self = shift;
my $functions = \%{$self->{FUNCTIONS}};
my $name = shift;
return $$functions{$name};
}
1;
package parser;
BEGIN {
use Exporter ();
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(&init
&transact &commit &rollback &token
&dump_state
&either &filter &many &many1 &separate &separate1 &sequence);
}
my @stack;
my $current;
my $next;
sub init {
@stack = ();
$current = [];
$next = shift;
@$next = grep {
$_->{type} !~ /^comment|preprocessor$/;
} @$next;
}
sub dump_state {
print "stack: [\n";
for my $tokens (@stack) {
print " [\n";
for my $token (@$tokens) {
print " " . $token->{type} . ": " . $token->{data} . "\n";
}
print " ]\n";
}
print "]\n";
print "current: [\n";
for my $token (@$current) {
print " " . $token->{type} . ": " . $token->{data} . "\n";
}
print "]\n";
print "next: [\n";
for my $token (@$next) {
print " " . $token->{type} . ": " . $token->{data} . "\n";
}
print "]\n";
}
sub token {
my $token = shift @$next;
push @$current, $token;
return $token;
};
sub transact {
push @stack, $current;
$current = [];
}
sub commit {
my $oldcurrent = $current;
$current = pop @stack;
push @$current, @$oldcurrent;
}
sub rollback {
unshift @$next, @$current;
$current = pop @stack;
}
sub filter {
my $parser = shift;
my $filter = shift;
transact;
my $r1 = &$parser;
if(defined($r1)) {
my $r2 = &$filter($r1);
if($r2) {
commit;
return $r1;
} else {
rollback;
return undef;
}
} else {
rollback;
return undef;
}
}
sub either {
for my $parser (@_) {
transact;
my $r = &$parser;
if(defined($r)) {
commit;
return $r;
} else {
rollback;
}
}
return undef;
}
sub sequence {
transact;
my $rs = [];
for my $parser (@_) {
my $r = &$parser;
if(defined($r)) {
push @$rs, $r;
} else {
rollback;
return undef;
}
}
commit;
return $rs;
}
sub separate {
my $parser = shift;
my $separator = shift;
my $rs = [];
while(1) {
my $r = &$parser;
if(defined($r)) {
push @$rs, $r;
} else {
last;
}
my $s = &$separator;
if(!defined($r)) {
last;
}
}
return $rs;
}
sub separate1 {
my $parser = shift;
my $separator = shift;
transact;
my $rs = separate($parser,$separator);
if($#$rs != -1) {
commit;
return $rs;
} else {
rollback;
return undef;
}
}
sub many {
my $parser = shift;
my $rs = [];
while(1) {
my $r = &$parser;
if(defined($r)) {
push @$rs, $r;
} else {
last;
}
}
return $rs;
}
sub many1 {
my $parser = shift;
transact;
my $rs = many($parser);
if($#$rs != -1) {
commit;
return $rs;
} else {
rollback;
return undef;
}
}
1;
%long
COLORREF
DWORD
FOURCC
HCONV
HCONVLIST
HDDEDATA
HHOOK
HKEY
HRESULT
HSZ
LCID
LCTYPE
LHCLIENTDOC
LHSERVER
LHSERVERDOC
LONG
LPARAM
LRESULT
POINT16
ULONG
%longlong
LARGE_INTEGER
%ptr
ACMDRIVERENUMCB16
ACMFILTERENUMCB16
ACMFILTERTAGENUMCB16
ACMFORMATENUMCB16
ACMFORMATTAGENUMCB16
BITMAP16 *
BITMAPINFO *
BITMAPINFOHEADER *
BOOL16 *
BSTR16 *
CLASSENTRY *
CLSID *
COLORREF *
CONTEXT86 *
CURSORICONINFO *
DEVMODEA *
DOCINFO16 *
DWORD *
FARPROC *
FARPROC16 *
FILETIME *
GLOBALENTRY *
GLOBALINFO *
GUID *
HANDLE *
HANDLETABLE16 *
HHOOK *
HICON16 *
HINSTANCE16 *
HMIDIIN16 *
HMIDIOUT16 *
HMIDISTRM16 *
HPCSTR
HPSTR
HTASK16 *
HWAVEIN16 *
HWAVEOUT16 *
IID *
IMalloc16 *
INT16 *
INTERFACEDATA *
IStorage16 *
IStorage16 **
IStream16 *
IStream16 **
ITypeInfo **
IUnknown *
LHCLIENTDOC *
LHSERVERDOC *
LOCAL32ENTRY *
LOCAL32INFO *
LOCALENTRY *
LOCALINFO *
LOGBRUSH16 *
LOGFONT16 *
LOGPALETTE *
LOGPEN16 *
LPABC16
LPACMDRIVERDETAILS16
LPACMFILTERCHOOSE16
LPACMFILTERDETAILS16
LPACMFILTERTAGDETAILS16
LPACMFORMATCHOOSE16
LPACMFORMATDETAILS16
LPACMFORMATTAGDETAILS16
LPACMSTREAMHEADER16
LPAUXCAPS16
LPBSTR16
LPBYTE
LPCATCHBUF
LPCHOOSECOLOR16
LPCHOOSEFONT16
LPCLSID
LPCOMSTAT16
LPCONVCONTEXT16
LPCONVINFO16
LPCURSORINFO
LPCVOID
LPDCB16
LPDEVMODEA
LPDRIVERINFOSTRUCT16
LPDWORD
LPENUMLOGFONT16
LPFILETIME
LPFONTINFO16
LPGLYPHMETRICS16
LPHACMDRIVER16
LPHACMDRIVERID16
LPHACMSTREAM16
LPHANDLE
LPHKEY
LPHMIXER16
LPICONINFO16
LPINT16
LPJOYCAPS16
LPJOYINFO16
LPKERNINGPAIR16
LPLOGFONT16
LPMALLOC16 *
LPMEMORY_BASIC_INFORMATION
LPMESSAGEFILTER
LPMESSAGEFILTER *
LPMIDIHDR16
LPMIDIINCAPS16
LPMIDIOUTCAPS16
LPMIXERCAPS16
LPMIXERCONTROLDETAILS16
LPMIXERLINE16
LPMIXERLINECONTROLS16
LPMMCKINFO
LPMMIOPROC16
LPMMTIME16
LPMONIKER *
LPMSG16
LPMSG16_32
LPMSGBOXPARAMS16
LPNEWTEXTMETRIC16
LPOFSTRUCT
LPOLECLIENT
LPOLEOBJECT
LPOLEOBJECT *
LPOLESERVERDOC
LPOLESTR16 *
LPDROPTARGET
LPOUTLINETEXTMETRIC16
LPPAINTSTRUCT16
LPPALETTEENTRY
LPPDEVICE
LPPOINT16
LPQUEUESTRUCT16 *
LPRASTERIZER_STATUS
LPRECT16
LPRGNDATA
LPRUNNINGOBJECTTABLE *
LPSCROLLINFO
LPSIZE16
LPSTORAGE16
LPTEXTXFORM16
LPTIMECAPS16
LPUINT16
LPUNKNOWN
LPVOID
LPVOID *
LPWAVEFILTER
LPWAVEFORMATEX
LPWAVEINCAPS16
LPWAVEOUTCAPS16
LPWIN32SINFO
LPWORD
LPWSADATA
MAT2 *
MEMMANINFO *
MENUITEMINFO16 *
METARECORD *
MIDIHDR16 *
MMCKINFO *
MMIOINFO16 *
MODULEENTRY *
MSG16 *
MSG32_16 *
OFSTRUCT *
OSVERSIONINFO16 *
PAINTSTRUCT16 *
PALETTEENTRY *
POINT16 *
RECT16 *
REFCLSID
REFGUID
REFIID
RGBQUAD *
SCROLLINFO *
SECURITY_ATTRIBUTES *
SEGINFO *
SEGPTR *
SNB16
STACKTRACEENTRY *
STATSTG16 *
SYSHEAPINFO *
SYSLEVEL *
TASKENTRY *
TEXTMETRIC16 *
THUNKLET *
TIMERINFO *
UINT16 *
ULARGE_INTEGER *
ULONG *
VOID *
WAVEHDR *
WIN32_FIND_DATAA *
WINDEBUGINFO *
WINDOWPLACEMENT16 *
WNDCLASS16 *
WNDCLASSEX16 *
WORD *
char *
struct ThunkDataCommon *
struct Win87EmInfoStruct *
struct sockaddr *
struct tagCURSORICONINFO *
struct timeval *
void *
ws_fd_set16 *
%s_word
INT16
%segptr
DLGPROC16
FARPROC16
FONTENUMPROC16
FONTENUMPROCEX16
GOBJENUMPROC16
GRAYSTRINGPROC16
HOOKPROC16
LINEDDAPROC16
LPTIMECALLBACK16
MFENUMPROC16
PROPENUMPROC16
SEGPTR
TIMERPROC16
WNDENUMPROC16
WNDPROC16
%segstr
BSTR16
%str
LPCOLESTR16
LPCSTR
LPCWSTR
LPSTR
LPOLESTR16
%unknown
struct in_addr
%void
void
VOID
%word
ATOM
BOOL16
BYTE
CHAR
HACCEL16
HACMDRIVER16
HACMDRIVERID16
HACMOBJ16
HACMSTREAM16
HANDLE16
HBITMAP16
HBRUSH16
HCURSOR16
HDC16
HDROP16
HDRVR16
HDWP16
HFILE16
HFONT16
HGDIOBJ16
HGLOBAL16
HICON16
HINSTANCE16
HLOCAL16
HMENU16
HMETAFILE16
HMIDIIN16
HMIDIOUT16
HMIDISTRM16
HMIXER16
HMIXEROBJ16
HMMIO16
HMODULE16
HPALETTE16
HPEN16
HPJOB16
HPQ16
HQUEUE16
HRGN16
HRSRC16
HTASK16
HWAVEIN16
HWAVEOUT16
HWND16
LANGID
MMRESULT16
OLESTATUS
SOCKET16
UINT16
WING_DITHER_TYPE
WORD
WPARAM16
%unknown --forbidden
BOOL
FARPROC
HANDLE
HINSTANCE
HMODULE
HWND
INT
LPOLESTR
UINT
YIELDPROC
int
long
short
u_long
u_short
unsigned
package winapi;
use strict;
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
bless ($self, $class);
my $allowed_kind = \%{$self->{ALLOWED_KIND}};
my $allowed_modules = \%{$self->{ALLOWED_MODULES}};
my $allowed_modules_limited = \%{$self->{ALLOWED_MODULES_LIMITED}};
my $translate_argument = \%{$self->{TRANSLATE_ARGUMENT}};
$self->{NAME} = shift;
my $file = shift;
my @modules;
my $kind;
my $forbidden = 0;
open(IN, "< $file") || die "$file: $!\n";
$/ = "\n";
while(<IN>) {
s/^\s*?(.*?)\s*$/$1/; # remove whitespace at begin and end of line
s/^(.*?)\s*#.*$/$1/; # remove comments
/^$/ && next; # skip empty lines
if(s/^%(\S+)\s*//) {
$kind = $1;
@modules = ();
$forbidden = 0;
$$allowed_kind{$kind} = 1;
if(/^--module=(\S*)/) {
@modules = split(/,/, $1);
} elsif(/^--forbidden/) {
$forbidden = 1;
}
} elsif(defined($kind)) {
my $type = $_;
if(!$forbidden) {
for my $module (@modules) {
$$allowed_modules_limited{$type} = 1;
$$allowed_modules{$type}{$module} = 1;
}
} else {
$$allowed_modules_limited{$type} = 1;
}
$$translate_argument{$type} = $kind;
} else {
print "$file: file must begin with %<type> statement\n";
exit 1;
}
}
close(IN);
return $self;
}
sub get_spec_file_type {
my $proto = shift;
my $class = ref($proto) || $proto;
my $file = shift;
my $type;
open(IN, "< $file") || die "$file: $!\n";
$/ = "\n";
while(<IN>) {
if(/^type\s*(\w+)/) {
$type = $1;
last;
}
}
close(IN);
return $type;
}
sub read_spec_files {
my $proto = shift;
my $class = ref($proto) || $proto;
my $win16api = shift;
my $win32api = shift;
foreach my $file (split(/\n/, `find . -name \\*.spec`)) {
my $type = 'winapi'->get_spec_file_type($file);
if($type eq "win16") {
$win16api->parse_spec_file($file);
} elsif($type eq "win32") {
$win32api->parse_spec_file($file);
}
}
}
sub parse_spec_file {
my $self = shift;
my $function_arguments = \%{$self->{FUNCTION_ARGUMENTS}};
my $function_calling_convention = \%{$self->{FUNCTION_CALLING_CONVENTION}};
my $function_stub = \%{$self->{FUNCTION_STUB}};
my $function_module = \%{$self->{FUNCTION_MODULE}};
my $file = shift;
my $type;
my $module;
open(IN, "< $file") || die "$file: $!\n";
$/ = "\n";
my $header = 1;
my $lookahead = 0;
while($lookahead || defined($_ = <IN>)) {
$lookahead = 0;
s/^\s*(.*?)\s*$/$1/;
s/^(.*?)\s*#.*$/$1/;
/^$/ && next;
if($header) {
if(/^name\s*(\S*)/) { $module = $1; }
if(/^\d+/) { $header = 0 };
next;
}
if(/^\d+\s+(pascal|pascal16|stdcall|cdecl|register|interrupt|varargs)\s+(\S+)\s*\(\s*(.*?)\s*\)\s*(\S+)$/) {
my $calling_convention = $1;
my $external_name = $2;
my $arguments = $3;
my $internal_name = $4;
# FIXME: Internal name existing more than once not handled properly
$$function_arguments{$internal_name} = $arguments;
$$function_calling_convention{$internal_name} = $calling_convention;
$$function_module{$internal_name} = $module;
} elsif(/^\d+\s+stub\s+(\S+)$/) {
my $external_name = $1;
$$function_stub{$external_name} = 1;
$$function_module{$external_name} = $module;
} elsif(/^\d+\s+(equate|long|word|extern|forward)/) {
# ignore
} else {
my $next_line = <IN>;
if($next_line =~ /^\d/) {
die "$file: $.: syntax error: '$_'\n";
} else {
$_ .= $next_line;
$lookahead = 1;
}
}
}
close(IN);
}
sub name {
my $self = shift;
return $self->{NAME};
}
sub is_allowed_kind {
my $self = shift;
my $allowed_kind = \%{$self->{ALLOWED_KIND}};
my $kind = shift;
if(defined($kind)) {
return $$allowed_kind{$kind};
} else {
return 0;
}
}
sub allowed_type_in_module {
my $self = shift;
my $allowed_modules = \%{$self->{ALLOWED_MODULES}};
my $allowed_modules_limited = \%{$self->{ALLOWED_MODULES_LIMITED}};
my $type = shift;
my $module = shift;
return !$$allowed_modules_limited{$type} || $$allowed_modules{$type}{$module};
}
sub translate_argument {
my $self = shift;
my $translate_argument = \%{$self->{TRANSLATE_ARGUMENT}};
my $argument = shift;
return $$translate_argument{$argument};
}
sub all_declared_types {
my $self = shift;
my $translate_argument = \%{$self->{TRANSLATE_ARGUMENT}};
return sort(keys(%$translate_argument));
}
sub found_type {
my $self = shift;
my $type_found = \%{$self->{TYPE_FOUND}};
my $name = shift;
$$type_found{$name}++;
}
sub type_found {
my $self = shift;
my $type_found= \%{$self->{TYPE_FOUND}};
my $name = shift;
return $$type_found{$name};
}
sub all_functions {
my $self = shift;
my $function_calling_convention = \%{$self->{FUNCTION_CALLING_CONVENTION}};
return sort(keys(%$function_calling_convention));
}
sub all_functions_found {
my $self = shift;
my $function_found = \$self->{FUNCTION_FOUND};
return sort(keys(%$function_found));
}
sub function_calling_convention {
my $self = shift;
my $function_calling_convention = \%{$self->{FUNCTION_CALLING_CONVENTION}};
my $name = shift;
return $$function_calling_convention{$name};
}
sub is_function {
my $self = shift;
my $function_calling_convention = \%{$self->{FUNCTION_CALLING_CONVENTION}};
my $name = shift;
return $$function_calling_convention{$name};
}
sub is_shared_function {
my $self = shift;
my $function_shared = \%{$self->{FUNCTION_SHARED}};
my $name = shift;
return $$function_shared{$name};
}
sub found_shared_function {
my $self = shift;
my $function_shared = \%{$self->{FUNCTION_SHARED}};
my $name = shift;
$$function_shared{$name} = 1;
}
sub function_arguments {
my $self = shift;
my $function_arguments = \%{$self->{FUNCTION_ARGUMENTS}};
my $name = shift;
return $$function_arguments{$name};
}
sub function_module {
my $self = shift;
my $function_module = \%{$self->{FUNCTION_MODULE}};
my $name = shift;
if($self->is_function($name)) {
return $$function_module{$name};
} else {
return undef;
}
}
sub function_stub {
my $self = shift;
my $function_stub = \%{$self->{FUNCTION_STUB}};
my $name = shift;
return $$function_stub{$name};
}
sub found_function {
my $self = shift;
my $function_found = \%{$self->{FUNCTION_FOUND}};
my $name = shift;
$$function_found{$name}++;
}
sub function_found {
my $self = shift;
my $function_found = \%{$self->{FUNCTION_FOUND}};
my $name = shift;
return $$function_found{$name};
}
1;
#!/usr/bin/perl -w
# Copyright 1999 Patrik Stridvall
use strict;
BEGIN {
require "tools/winapi_check/winapi.pm";
require "tools/winapi_check/nativeapi.pm";
require "tools/winapi_check/winapi_local.pm";
require "tools/winapi_check/winapi_global.pm";
require "tools/winapi_check/winapi_options.pm";
require "tools/winapi_check/winapi_parser.pm";
import winapi;
import nativeapi;
import winapi_local;
import winapi_global;
import winapi_options;
import winapi_parser;
}
my $options = winapi_options->new(\@ARGV);
if($options->help) {
$options->show_help;
exit;
}
my $win16api = 'winapi'->new("win16", "tools/winapi_check/win16api.dat");
my $win32api = 'winapi'->new("win32", "tools/winapi_check/win32api.dat");
'winapi'->read_spec_files($win16api, $win32api);
my $nativeapi = 'nativeapi'->new("tools/winapi_check/nativeapi.dat");
for my $name ($win32api->all_functions) {
my $module16 = $win16api->function_module($name);
my $module32 = $win32api->function_module($name);
if(defined($module16)) {
$win16api->found_shared_function($name);
$win32api->found_shared_function($name);
if($options->shared) {
print "*.spec: $name: is shared between $module16 (Win16) and $module32 (Win32)\n";
}
}
}
foreach my $file ($options->files) {
my $found_function = sub {
my $return_type = shift;
my $calling_convention = shift;
my $name = shift;
my $refarguments = shift;
my @arguments = @$refarguments;
if($options->global) {
$win16api->found_type($return_type) if $options->win16;
$win32api->found_type($return_type) if $options->win32;
for my $argument (@arguments) {
$win16api->found_type($argument) if $options->win16;
$win32api->found_type($argument) if $options->win32;
}
$win16api->found_function($name) if $options->win16;
$win32api->found_function($name) if $options->win32;
}
if($options->local) {
my $module16 = $win16api->function_module($name);
my $module32 = $win32api->function_module($name);
my $output = sub {
my $module = shift;
return sub {
my $msg = shift;
print "$file: $module: $return_type $calling_convention $name(" . join(",", @arguments) . "): $msg\n";
}
};
my $output16 = &$output($module16);
my $output32 = &$output($module32);
if($options->argument) {
if($options->win16 && $options->report_module($module16)) {
winapi_local::check_arguments $options, $output16,
$return_type, $calling_convention, $name, [@arguments], $win16api;
}
if($options->win32 && $options->report_module($module32)) {
winapi_local::check_arguments $options, $output32,
$return_type, $calling_convention, $name, [@arguments], $win32api;
}
}
if($options->misplaced) {
my $module;
if($file =~ m'^dlls/(.*)/') {
$module = $1;
}
if($options->win16 && $options->report_module($module16)) {
if(!defined($module) || $module ne $module16) {
&$output16("function misplaced");
}
}
if($options->win32 && $options->report_module($module32)) {
if(!defined($module) || $module ne $module32) {
&$output32("function misplaced");
}
}
}
}
};
winapi_parser::parse_c_file $options, $file, $found_function;
}
if($options->global) {
winapi_global::check $options, $win16api, $nativeapi if $options->win16;
winapi_global::check $options, $win32api, $nativeapi if $options->win32;
}
package winapi_global;
use strict;
sub check {
my $options = shift;
my $winapi = shift;
my $nativeapi = shift;
my $winver = $winapi->name;
if($options->argument) {
foreach my $type ($winapi->all_declared_types) {
if(!$winapi->type_found($type) && $type ne "CONTEXT86 *") {
print "*.c: $winver: $type: ";
print "type not used\n";
}
}
}
if($options->declared) {
foreach my $name ($winapi->all_functions) {
if(!$winapi->function_found($name) && !$nativeapi->is_function($name)) {
print "*.c: $winver: $name: ";
print "function declared but not implemented: " . $winapi->function_arguments($name) . "\n";
}
}
}
if($options->implemented) {
foreach my $name ($winapi->all_functions_found) {
if($winapi->function_stub($name)) {
print "*.c: $winver: $name: ";
print "function implemented but not declared\n";
}
}
}
}
1;
package winapi_local;
use strict;
sub check_arguments {
my $options = shift;
my $output = shift;
my $return_type = shift;
my $calling_convention = shift;
my $name = shift;
my $refargument_types = shift;
my @argument_types = @$refargument_types;
my $winapi = shift;
my $module = $winapi->function_module($name);
my $forbidden_return_type = 0;
my $implemented_return_kind;
if(!defined($implemented_return_kind = $winapi->translate_argument($return_type))) {
if($return_type ne "") {
&$output("no translation defined: " . $return_type);
}
} elsif(!$winapi->is_allowed_kind($implemented_return_kind) || !$winapi->allowed_type_in_module($return_type,$module)) {
$forbidden_return_type = 1;
if($options->report_argument_forbidden($return_type)) {
&$output("forbidden return type: $return_type ($implemented_return_kind)");
}
}
my $segmented = 0;
if($implemented_return_kind =~ /^segptr|segstr$/) {
$segmented = 1;
}
my $implemented_calling_convention;
if($winapi->name eq "win16") {
if($calling_convention =~ /^__cdecl$/) {
$implemented_calling_convention = "cdecl";
} elsif($calling_convention = ~ /^__stdcall|VFWAPI|WINAPI$/) {
if($implemented_return_kind =~ /^s_word|word|void$/) {
$implemented_calling_convention = "pascal16";
} else {
$implemented_calling_convention = "pascal";
}
}
} elsif($winapi->name eq "win32") {
if($calling_convention =~ /^__cdecl$/) {
$implemented_calling_convention = "cdecl";
} elsif($calling_convention =~ /^VFWAPIV|WINAPIV$/) {
$implemented_calling_convention = "varargs";
} elsif($calling_convention = ~ /^__stdcall|VFWAPI|WINAPI$/) {
$implemented_calling_convention = "stdcall";
}
}
my $declared_calling_convention = $winapi->function_calling_convention($name);
my @declared_argument_kinds = split(/\s+/, $winapi->function_arguments($name));
if($declared_calling_convention =~ /^register|interrupt$/) {
push @declared_argument_kinds, "ptr";
}
if($declared_calling_convention =~ /^register|interupt$/ &&
(($winapi->name eq "win32" && $implemented_calling_convention eq "stdcall") ||
(($winapi->name eq "win16" && $implemented_calling_convention =~ /^pascal/))))
{
# correct
} elsif($implemented_calling_convention ne $declared_calling_convention &&
!($declared_calling_convention =~ /^pascal/ && $forbidden_return_type))
{
if($options->calling_convention) {
&$output("calling convention mismatch: $implemented_calling_convention != $declared_calling_convention");
}
}
if($declared_calling_convention eq "varargs") {
if($#argument_types != -1 && $argument_types[$#argument_types] eq "...") {
pop @argument_types;
} else {
&$output("function not implemented as vararg");
}
} elsif($#argument_types != -1 && $argument_types[$#argument_types] eq "...") {
&$output("function not declared as vararg");
}
if($name =~ /^CRTDLL__ftol|CRTDLL__CIpow$/) {
# ignore
} elsif($#argument_types != $#declared_argument_kinds) {
if($options->argument_count) {
&$output("argument count differs: " . ($#argument_types + 1) . " != " . ($#declared_argument_kinds + 1));
}
} else {
my $n = 0;
my @argument_kinds = map {
my $type = $_;
my $kind = "unknown";
if(!defined($kind = $winapi->translate_argument($type))) {
&$output("no translation defined: " . $type);
} elsif(!$winapi->is_allowed_kind($kind) ||
!$winapi->allowed_type_in_module($type, $module)) {
if($options->report_argument_forbidden($type)) {
&$output("forbidden argument " . ($n + 1) . " type (" . $type . ")");
}
}
$n++;
$kind;
} @argument_types;
for my $n (0..$#argument_kinds) {
if(!defined($argument_kinds[$n]) || !defined($declared_argument_kinds[$n])) { next; }
if($argument_kinds[$n] =~ /^segptr|segstr$/ ||
$declared_argument_kinds[$n] =~ /^segptr|segstr$/)
{
$segmented = 1;
}
if($argument_kinds[$n] ne $declared_argument_kinds[$n]) {
if($options->report_argument_kind($argument_kinds[$n]) ||
$options->report_argument_kind($declared_argument_kinds[$n]))
{
&$output("argument " . ($n + 1) . " type mismatch: " .
$argument_types[$n] . " ($argument_kinds[$n]) != " . $declared_argument_kinds[$n]);
}
}
}
}
if($segmented && $options->shared_segmented && $winapi->is_shared_function($name)) {
&$output("function using segmented pointers shared between Win16 och Win32");
}
}
1;
package winapi_options;
use strict;
sub parser_comma_list {
my $prefix = shift;
my $value = shift;
if(defined($prefix) && $prefix eq "no") {
return { active => 0, filter => 0, hash => {} };
} elsif(defined($value)) {
my %names;
for my $name (split /,/, $value) {
$names{$name} = 1;
}
return { active => 1, filter => 1, hash => \%names };
} else {
return { active => 1, filter => 0, hash => {} };
}
}
my %options = (
"debug" => { default => 0, description => "debug mode" },
"help" => { default => 0, description => "help mode" },
"verbose" => { default => 0, description => "verbose mode" },
"win16" => { default => 1, description => "Win16 checking" },
"win32" => { default => 1, description => "Win32 checking" },
"shared" => { default => 0, description => "show shared functions between Win16 and Win32" },
"shared-segmented" => { default => 0, description => "segmented shared functions between Win16 and Win32 checking" },
"local" => { default => 1, description => "local checking" },
"module" => {
default => { active => 1, filter => 0, hash => {} },
parent => "local",
parser => \&parser_comma_list,
description => "module filter"
},
"argument" => { default => 1, parent => "local", description => "argument checking" },
"argument-count" => { default => 1, parent => "argument", description => "argument count checking" },
"argument-forbidden" => {
default => { active => 0, filter => 0, hash => {} },
parent => "argument",
parser => \&parser_comma_list,
description => "argument forbidden checking"
},
"argument-kind" => {
default => { active => 0, filter => 0, hash => {} },
parent => "argument",
parser => \&parser_comma_list,
description => "argument kind checking"
},
"calling-convention" => { default => 0, parent => "local", description => "calling convention checking" },
"misplaced" => { default => 0, parent => "local", description => "checking for misplaced functions" },
"global" => { default => 1, description => "global checking" },
"declared" => { default => 1, parent => "global", description => "declared checking" },
"implemented" => { default => 0, parent => "global", description => "implemented checking" }
);
my %short_options = (
"d" => "debug",
"?" => "help",
"v" => "verbose"
);
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
bless ($self, $class);
my $refarguments = shift;
my @ARGV = @$refarguments;
for my $name (sort(keys(%options))) {
my $option = $options{$name};
my $key = uc($name);
$key =~ tr/-/_/;
$$option{key} = $key;
my $refvalue = \${$self->{$key}};
$$refvalue = $$option{default};
}
my $files = \@{$self->{FILES}};
my $module = \${$self->{MODULE}};
my $global = \${$self->{GLOBAL}};
while(defined($_ = shift @ARGV)) {
if(/^-([^=]*)(=(.*))?$/) {
my $name;
my $value;
if(defined($2)) {
$name = $1;
$value = $3;
} else {
$name = $1;
}
if($name =~ /^([^-].*)$/) {
$name = $short_options{$1};
} else {
$name =~ s/^-(.*)$/$1/;
}
my $prefix;
if($name =~ /^no-(.*)$/) {
$name = $1;
$prefix = "no";
if(defined($value)) {
print STDERR "<internal>: options with prefix 'no' can't take parameters\n";
exit 1;
}
}
my $option = $options{$name};
if(defined($option)) {
my $key = $$option{key};
my $parser = $$option{parser};
my $refvalue = \${$self->{$key}};
if(defined($parser)) {
$$refvalue = &$parser($prefix,$value);
} else {
if(defined($value)) {
$$refvalue = $value;
} elsif(!defined($prefix)) {
$$refvalue = 1;
} else {
$$refvalue = 0;
}
}
next;
}
}
if(/^--module-dlls$/) {
my @dirs = `cd dlls && find ./ -type d ! -name CVS`;
my %names;
for my $dir (@dirs) {
chomp $dir;
$dir =~ s/^\.\/(.*)$/$1/;
next if $dir eq "";
$names{$dir} = 1;
}
$$module = { active => 1, filter => 1, hash => \%names };
}
elsif(/^-(.*)$/) {
print STDERR "<internal>: unknown option: $&\n";
print STDERR "<internal>: usage: winapi-check [--help] [<files>]\n";
exit 1;
} else {
push @$files, $_;
}
}
if($#$files == -1) {
@$files = map {
s/^.\/(.*)$/$1/;
$_;
} split(/\n/, `find . -name \\*.c`);
} else {
$$global = 0
}
return $self;
}
sub show_help {
my $self = shift;
my $maxname = 0;
for my $name (sort(keys(%options))) {
if(length($name) > $maxname) {
$maxname = length($name);
}
}
print "usage: winapi-check [--help] [<files>]\n";
print "\n";
for my $name (sort(keys(%options))) {
my $option = $options{$name};
my $description = $$option{description};
my $default = $$option{default};
my $output;
if(ref($default) ne "HASH") {
if($default) {
$output = "--no-$name";
} else {
$output = "--$name";
}
} else {
if($default->{active}) {
$output = "--[no-]$name\[=<value>]";
} else {
$output = "--$name\[=<value>]";
}
}
print "$output";
for (0..(($maxname - length($name) + 14) - (length($output) - length($name) + 1))) { print " "; }
if(ref($default) ne "HASH") {
if($default) {
print "Disable $description\n";
} else {
print "Enable $description\n";
}
} else {
if($default->{active}) {
print "(Disable) $description\n";
} else {
print "Enable $description\n";
}
}
}
}
sub AUTOLOAD {
my $self = shift;
my $name = $winapi_options::AUTOLOAD;
$name =~ s/^.*::(.[^:]*)$/\U$1/;
my $refvalue = $self->{$name};
if(!defined($refvalue)) {
die "<internal>: winapi_options.pm: member $name does not exists\n";
}
return $$refvalue;
}
sub files { my $self = shift; return @{$self->{FILES}}; }
sub report_module {
my $self = shift;
my $module = $self->module;
my $name = shift;
if(defined($name)) {
return $module->{active} && (!$module->{filter} || $module->{hash}->{$name});
} else {
return 0;
}
}
sub report_argument_forbidden {
my $self = shift;
my $argument_forbidden = $self->argument_forbidden;
my $type = shift;
return $argument_forbidden->{active} && (!$argument_forbidden->{filter} || $argument_forbidden->{hash}->{$type});
}
sub report_argument_kind {
my $self = shift;
my $argument_kind = $self->argument_kind;
my $kind = shift;
return $argument_kind->{active} && (!$argument_kind->{filter} || $argument_kind->{hash}->{$kind});
}
1;
package winapi_parser;
use strict;
sub parse_c_file {
my $options = shift;
my $file = shift;
my $function_found_callback = shift;
my $level = 0;
my $again = 0;
my $lookahead = 0;
my $lookahead_count = 0;
print STDERR "Processing file '$file' ... " if $options->verbose;
open(IN, "< $file") || die "<internal>: $file: $!\n";
$/ = "\n";
while($again || defined(my $line = <IN>)) {
if(!$again) {
chomp $line;
if($lookahead) {
$lookahead = 0;
$_ .= "\n" . $line;
} else {
$_ = $line;
$lookahead_count = 0;
}
$lookahead_count++;
print "$level: $line\n" if $options->debug >= 2;
} else {
$lookahead_count = 0;
$again = 0;
}
# remove comments
if(s/^(.*?)\/\*.*?\*\/(.*)$/$1 $2/s) { $again = 1; next };
if(/^(.*?)\/\*/s) {
$lookahead = 1;
next;
}
# remove empty rows
if(/^\s*$/) { next; }
# remove preprocessor directives
if(s/^\s*\#.*$//m) { $again = 1; next; }
if($level > 0)
{
s/^[^\{\}]*//s;
if(/^\{/) {
$_ = $'; $again = 1;
print "+1: $_\n" if $options->debug >= 2;
$level++;
} elsif(/^\}/) {
$_ = $'; $again = 1;
print "-1: $_\n" if $options->debug >= 2;
$level--;
}
next;
} elsif(/((struct\s+|union\s+|enum\s+)?\w+((\s*\*)+\s*|\s+))(__cdecl|__stdcall|VFWAPIV|VFWAPI|WINAPIV|WINAPI)\s+(\w+(\(\w+\))?)\s*\(([^\)]*)\)\s*(\{|\;)/s) {
$_ = $'; $again = 1;
if($9 eq ";") {
next;
} elsif($9 eq "{") {
$level++;
}
my $return_type = $1;
my $calling_convention = $5;
my $name = $6;
my $arguments = $8;
$return_type =~ s/\s*$//;
$return_type =~ s/\s*\*\s*/*/g;
$return_type =~ s/(\*+)/ $1/g;
$name =~ s/^REGS_FUNC\((.*?)\)/$1/;
$arguments =~ y/\t\n/ /;
$arguments =~ s/^\s*(.*?)\s*$/$1/;
if($arguments eq "") { $arguments = "void" }
my @arguments = split(/,/, $arguments);
foreach my $n (0..$#arguments) {
my $argument = $arguments[$n];
$argument =~ s/^\s*(.*?)\s*$/$1/;
#print " " . ($n + 1) . ": '$argument'\n";
$argument =~ s/^(const(?=\s)|IN(?=\s)|OUT(?=\s)|(\s*))\s*//;
if($argument =~ /^...$/) {
$argument = "...";
} elsif($argument =~ /^((struct\s+|union\s+|enum\s+)?\w+)\s*((\*\s*?)*)\s*/) {
$argument = "$1";
if($3 ne "") {
$argument .= " $3";
}
} else {
die "$file: $.: syntax error: '$argument'\n";
}
$arguments[$n] = $argument;
#print " " . ($n + 1) . ": '" . $arguments[$n] . "'\n";
}
if($#arguments == 0 && $arguments[0] =~ /^void$/i) { $#arguments = -1; }
if($options->debug) {
print "$file: $return_type $calling_convention $name(" . join(",", @arguments) . ")\n";
}
&$function_found_callback($return_type,$calling_convention,$name,\@arguments);
} elsif(/DC_(GET_X_Y|GET_VAL_16)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
$_ = $'; $again = 1;
my @arguments = ("HDC16");
&$function_found_callback($2, "WINAPI", $3, \@arguments);
} elsif(/DC_(GET_VAL_32)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,.*?\)/s) {
$_ = $'; $again = 1;
my @arguments = ("HDC");
&$function_found_callback($2, "WINAPI", $3, \@arguments);
} elsif(/DC_(GET_VAL_EX)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
$_ = $'; $again = 1;
my @arguments16 = ("HDC16", "LP" . $5 . "16");
my @arguments32 = ("HDC", "LP" . $5);
&$function_found_callback("BOOL16", "WINAPI", $2 . "16", \@arguments16);
&$function_found_callback("BOOL", "WINAPI", $2, \@arguments32);
} elsif(/DC_(SET_MODE)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
$_ = $'; $again = 1;
my @arguments16 = ("HDC16", "INT16");
my @arguments32 = ("HDC", "INT");
&$function_found_callback("INT16", "WINAPI", $2 . "16", \@arguments16);
&$function_found_callback("INT", "WINAPI", $2, \@arguments32);
} elsif(/WAVEOUT_SHORTCUT_(1|2)\s*\(\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*\)/s) {
$_ = $'; $again = 1;
print "$_";
if($1 eq "1") {
my @arguments16 = ("HWAVEOUT16", $4);
my @arguments32 = ("HWAVEOUT", $4);
&$function_found_callback("UINT16", "WINAPI", "waveOut" . $2 . "16", \@arguments16);
&$function_found_callback("UINT", "WINAPI", "waveOut" . $2, \@arguments32);
} elsif($1 eq 2) {
my @arguments16 = ("UINT16", $4);
my @arguments32 = ("UINT", $4);
&$function_found_callback("UINT16", "WINAPI", "waveOut". $2 . "16", \@arguments16);
&$function_found_callback("UINT", "WINAPI", "waveOut" . $2, \@arguments32)
}
} elsif(/;/s) {
$_ = $'; $again = 1;
} elsif(/\{/s) {
$_ = $'; $again = 1;
print "+1: $_\n" if $options->debug >= 2;
$level++;
} else {
$lookahead = 1;
}
}
close(IN);
print STDERR "done\n" if $options->verbose;
print "$file: <>: not at toplevel at end of file\n" unless $level == 0;
}
1;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment