Added spec generation tool specmaker.
This commit is contained in:
parent
8d91b501dd
commit
c7a3fec5be
|
@ -6966,6 +6966,7 @@ scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
tools/cvdump/Makefile
|
tools/cvdump/Makefile
|
||||||
|
tools/specmaker/Makefile
|
||||||
tools/winebuild/Makefile
|
tools/winebuild/Makefile
|
||||||
tools/winelauncher
|
tools/winelauncher
|
||||||
tools/wmc/Makefile
|
tools/wmc/Makefile
|
||||||
|
@ -7209,6 +7210,7 @@ scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
tools/cvdump/Makefile
|
tools/cvdump/Makefile
|
||||||
|
tools/specmaker/Makefile
|
||||||
tools/winebuild/Makefile
|
tools/winebuild/Makefile
|
||||||
tools/winelauncher
|
tools/winelauncher
|
||||||
tools/wmc/Makefile
|
tools/wmc/Makefile
|
||||||
|
|
|
@ -1284,6 +1284,7 @@ scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
tools/cvdump/Makefile
|
tools/cvdump/Makefile
|
||||||
|
tools/specmaker/Makefile
|
||||||
tools/winebuild/Makefile
|
tools/winebuild/Makefile
|
||||||
tools/winelauncher
|
tools/winelauncher
|
||||||
tools/wmc/Makefile
|
tools/wmc/Makefile
|
||||||
|
|
|
@ -11,11 +11,13 @@ C_SRCS = makedep.c fnt2bdf.c bin2res.c
|
||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
cvdump \
|
cvdump \
|
||||||
|
specmaker \
|
||||||
winebuild \
|
winebuild \
|
||||||
wmc \
|
wmc \
|
||||||
wrc
|
wrc
|
||||||
|
|
||||||
INSTALLSUBDIRS = \
|
INSTALLSUBDIRS = \
|
||||||
|
specmaker \
|
||||||
winebuild \
|
winebuild \
|
||||||
wmc \
|
wmc \
|
||||||
wrc
|
wrc
|
||||||
|
@ -26,7 +28,7 @@ EXTRASUBDIRS = \
|
||||||
winapi_check/win32 \
|
winapi_check/win32 \
|
||||||
wineconf.libs
|
wineconf.libs
|
||||||
|
|
||||||
all: $(PROGRAMS) winebuild wmc wrc
|
all: $(PROGRAMS) specmaker winebuild wmc wrc
|
||||||
|
|
||||||
@MAKE_RULES@
|
@MAKE_RULES@
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Makefile
|
||||||
|
specmaker
|
|
@ -0,0 +1,35 @@
|
||||||
|
DEFS = -D__WINE__
|
||||||
|
TOPSRCDIR = @top_srcdir@
|
||||||
|
TOPOBJDIR = ../..
|
||||||
|
SRCDIR = @srcdir@
|
||||||
|
VPATH = @srcdir@
|
||||||
|
|
||||||
|
PROGRAMS = specmaker
|
||||||
|
MODULE = none
|
||||||
|
|
||||||
|
C_SRCS = \
|
||||||
|
dll.c \
|
||||||
|
main.c \
|
||||||
|
misc.c \
|
||||||
|
msmangle.c \
|
||||||
|
output.c \
|
||||||
|
search.c \
|
||||||
|
symbol.c
|
||||||
|
|
||||||
|
all: $(PROGRAMS)
|
||||||
|
|
||||||
|
@MAKE_RULES@
|
||||||
|
|
||||||
|
specmaker: $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) -o specmaker $(OBJS) $(LDFLAGS)
|
||||||
|
|
||||||
|
install:: $(PROGRAMS)
|
||||||
|
[ -d $(bindir) ] || $(MKDIR) $(bindir)
|
||||||
|
$(INSTALL_PROGRAM) specmaker $(bindir)/specmaker
|
||||||
|
$(INSTALL_PROGRAM) function_grep.pl $(bindir)/function_grep.pl
|
||||||
|
|
||||||
|
uninstall::
|
||||||
|
$(RM) $(bindir)/specmaker
|
||||||
|
$(RM) $(bindir)/function_grep.pl
|
||||||
|
|
||||||
|
### Dependencies:
|
|
@ -0,0 +1,560 @@
|
||||||
|
Specmaker - A Wine DLL tool
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
|
||||||
|
Most of the functions available in Windows, and in Windows applications, are
|
||||||
|
made available to applications from DLL's. Wine implements the Win32 API by
|
||||||
|
providing replacement's for the essential Windows DLLs in the form of Unix
|
||||||
|
shared library (.so) files, and provides a tool, winebuild, to allow Winelib
|
||||||
|
applications to link to functions exported from shared libraries/DLLs.
|
||||||
|
|
||||||
|
The first thing to note is that there are many DLLs that aren't yet
|
||||||
|
implemented in Wine. Mostly this doesn't present a problem because the native
|
||||||
|
Win32 versions of lots of DLLs can be used without problems, at least on
|
||||||
|
x86 platforms. However, one of Wine's goals is the eventual replacement of
|
||||||
|
every essential O/S DLL so that the whole API is implemented. This not only
|
||||||
|
means that a copy of the real O/S is not needed, but also that non-x86
|
||||||
|
platforms can run most Win32 programs after recompiling.
|
||||||
|
|
||||||
|
The second thing to note is that applications commonly use their own or 3rd
|
||||||
|
party DLLs to provide functionality. In order to call these functions with
|
||||||
|
a Winelib program, some 'glue' is needed. This 'glue' comes in the form of
|
||||||
|
a .spec file. The .spec file, along with some dummy code, is used to create
|
||||||
|
a Wine .so corresponding to the Windows DLL. The winebuild program can then
|
||||||
|
resolve calls made to DLL functions to call your dummy DLL. You then tell
|
||||||
|
Wine to only use the native Win32 version of the DLL, and at runtime your
|
||||||
|
calls will be made to the Win32 DLL. If you want to reimplement the dll,
|
||||||
|
you simply add the code for the DLL calls to your stub .so, and then tell
|
||||||
|
Wine to use the .so version instead [1].
|
||||||
|
|
||||||
|
These two factors mean that if you are:
|
||||||
|
|
||||||
|
A: Reimplementing a Win32 DLL for use within Wine, or
|
||||||
|
B: Compiling a Win32 application with Winelib that uses x86 DLLs
|
||||||
|
|
||||||
|
Then you will need to create a .spec file (amongst other things). If you
|
||||||
|
won't be doing either of the above, then you won't need specmaker.
|
||||||
|
|
||||||
|
Creating a .spec file is a labour intensive task during which it is easy
|
||||||
|
to make a mistake. The idea of specmaker is to automate this task and create
|
||||||
|
the majority of the support code needed for your DLL. In addition you can
|
||||||
|
have specmaker create code to help you reimplement a DLL, by providing
|
||||||
|
tracing of calls to the DLL, and (in some cases) automatically determining
|
||||||
|
the parameters, calling conventions, and return values of the DLLs functions.
|
||||||
|
|
||||||
|
You can think of specmaker as somewhat similar to the IMPLIB tool when
|
||||||
|
only its basic functionality is used.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Specmaker is a command line tool. Running it with no arguments or passing
|
||||||
|
it '-h' on the command line lists the available options:
|
||||||
|
|
||||||
|
Usage: specmaker [options] -d dll
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-d dll Use dll for input file (mandatory)
|
||||||
|
-h Display this help message
|
||||||
|
-I dir Look for prototypes in 'dir' (implies -c)
|
||||||
|
-o name Set the output dll name (default: dll)
|
||||||
|
-c Generate skeleton code (requires -I)
|
||||||
|
-t TRACE arguments (implies -c)
|
||||||
|
-f dll Forward calls to 'dll' (implies -t)
|
||||||
|
-D Generate documentation
|
||||||
|
-C Assume __cdecl calls (default: __stdcall)
|
||||||
|
-s num Start prototype search after symbol 'num'
|
||||||
|
-e num End prototype search after symbol 'num'
|
||||||
|
-q Don't show progress (quiet).
|
||||||
|
-v Show lots of detail while working (verbose).
|
||||||
|
|
||||||
|
|
||||||
|
Basic options
|
||||||
|
-------------
|
||||||
|
|
||||||
|
OPTION: -d dll Use dll for input file (mandatory)
|
||||||
|
|
||||||
|
The -d option tells specmaker which DLL you want to create a .spec file
|
||||||
|
for. You *must* give this option.
|
||||||
|
|
||||||
|
16 bit DLL's are not currently supported (Note that Winelib is intended
|
||||||
|
only for Win32 programs).
|
||||||
|
|
||||||
|
OPTION: -o name Set the output dll name (default: dll)
|
||||||
|
|
||||||
|
By default, if specmaker is run on DLL 'foo', it creates files called
|
||||||
|
'foo.spec', 'foo_main.c' etc, and prefixes any functions generated
|
||||||
|
with 'FOO_'. If '-o bar' is given, these will become 'bar.spec',
|
||||||
|
'bar_main.c' and 'BAR_' respectively.
|
||||||
|
|
||||||
|
This option is mostly useful when generating a forwarding DLL. See below
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
OPTION: -q Don't show progress (quiet).
|
||||||
|
-v Show lots of detail while working (verbose).
|
||||||
|
|
||||||
|
There are 3 levels of output while specmaker is running. The default level,
|
||||||
|
when neither -q or -v are given, prints the number of exported functions
|
||||||
|
found in the dll, followed by the name of each function as it is processed,
|
||||||
|
and a status indication of whether it was processed OK. With -v given, a
|
||||||
|
lot of information is dumped while specmaker works: this is intended to help
|
||||||
|
debug any problems. Giving -q means nothing will be printed unless a fatal
|
||||||
|
error occurs, and could be used when calling specmaker from a script.
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -C Assume __cdecl calls (default: __stdcall)
|
||||||
|
|
||||||
|
This option determines the default calling convention used by the functions
|
||||||
|
in the DLL. If specbuild cannot determine the convention, __stdcall is
|
||||||
|
used by default, unless this option has been given.
|
||||||
|
|
||||||
|
Unless -q is given, a warning will be printed for every function that
|
||||||
|
specmaker determines the calling convention for and which does not match
|
||||||
|
the assumed calling convention.
|
||||||
|
|
||||||
|
|
||||||
|
Generating stub DLLS
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If all you want to do is generate a stub DLL to allow you to link your
|
||||||
|
Winelib application to an x86 DLL, the above options are all you need.
|
||||||
|
|
||||||
|
As an example, lets assume the application you are porting uses functions
|
||||||
|
from a 3rd party dll called 'zipextra.dll', and the functions in the DLL
|
||||||
|
use the __stdcall calling convention. Copy zipextra.dll to an empty directory,
|
||||||
|
change to it, and run specmaker as follows:
|
||||||
|
|
||||||
|
specmaker -d zipextra (Note: this assumes specmaker is in your path)
|
||||||
|
|
||||||
|
The output will look something like the following:
|
||||||
|
|
||||||
|
22 exported symbols in DLL ...
|
||||||
|
Export 1 - '_OpenZipFile' ... [Ignoring]
|
||||||
|
Export 2 - '_UnZipFile' ... [Ignoring]
|
||||||
|
...
|
||||||
|
|
||||||
|
"[Ignoring]" Just tells you that specmaker isn't trying to determine the
|
||||||
|
parameters or return types of the functions, its just creating stubs.
|
||||||
|
|
||||||
|
The following files are created:
|
||||||
|
|
||||||
|
zipextra.spec
|
||||||
|
This is the .spec file. Each exported function is listed as a stub:
|
||||||
|
|
||||||
|
@ stub _OpenZipFile
|
||||||
|
@ stub _UnZipFile
|
||||||
|
...
|
||||||
|
|
||||||
|
This means that winebuild will generate dummy code for this function. That
|
||||||
|
doesn't concern us, because all we want is for winebuild to allow the
|
||||||
|
symbols to be resolved. At run-time, the functions in the native DLL will
|
||||||
|
be called; this just allows us to link.
|
||||||
|
|
||||||
|
zipextra_dll.h zipextra_main.c
|
||||||
|
These are source code files containing the minimum set of code to build
|
||||||
|
a stub DLL. The C file contains one function, ZIPEXTRA_Init, which does
|
||||||
|
nothing.
|
||||||
|
|
||||||
|
Makefile.in
|
||||||
|
This is a template for 'configure' to produce a makefile. It is designed
|
||||||
|
for a DLL that will be inserted into the Wine source tree. If your DLL
|
||||||
|
will not be part of Wine, or you don't wish to build it this way,
|
||||||
|
you should look at the Wine tool 'winemaker' to generate a DLL project.
|
||||||
|
|
||||||
|
FIXME: winemaker could run this tool automatically when generating projects
|
||||||
|
that use extra DLL's (*.lib in the "ADD LINK32" line in .dsp) ....
|
||||||
|
|
||||||
|
zipextra_install
|
||||||
|
A shell script for adding zipextra to the Wine source tree (see below).
|
||||||
|
|
||||||
|
|
||||||
|
Inserting a stub DLL into the Wine tree
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
To build your stub DLL as part of Wine, do the following:
|
||||||
|
|
||||||
|
chmod a+x ./zipextra_install
|
||||||
|
./zipextra_install <wine-path>
|
||||||
|
cd <wine-path>
|
||||||
|
autoconf
|
||||||
|
./configure
|
||||||
|
make depend && make
|
||||||
|
make install
|
||||||
|
|
||||||
|
Your application can now link with the DLL.
|
||||||
|
|
||||||
|
NOTE: **DO NOT** submit patches to Wine for 3rd party DLLs! Building DLLs
|
||||||
|
into your copy of the tree is just a simple way for you to link. When
|
||||||
|
you release your application you won't be distributing the Unix .so
|
||||||
|
anyway, just the Win32 DLL. As you update your version of Wine
|
||||||
|
you can simply re-run the procedure above (Since no patches are
|
||||||
|
involved, it should be pretty resiliant to changes).
|
||||||
|
|
||||||
|
|
||||||
|
Advanced Options
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This section discusses features of specmaker that are useful to Wine Hackers
|
||||||
|
or developers looking to reimplement a Win32 DLL for Unix. Using these
|
||||||
|
features means you will need to be able to resolve compilation problems and
|
||||||
|
have a general understanding of Wine programming.
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -I dir Look for prototypes in 'dir' (implies -c)
|
||||||
|
|
||||||
|
For all advanced functionality, you must give specmaker a directoryor file that
|
||||||
|
contains prototypes for the DLL. In the case of Windows DLLs, this could be
|
||||||
|
either the standard include directory from your compiler, or an SDK include
|
||||||
|
directory. If you have a text document with prototypes (such as documentation)
|
||||||
|
that can be used also, however you may need to delete some non-code lines to
|
||||||
|
ensure that prototypes are parsed correctly.
|
||||||
|
|
||||||
|
The 'dir' argument can also be a file specification (e.g. "include/*"). If
|
||||||
|
it contains wildcards you must quote it to prevent the shell from expanding it.
|
||||||
|
|
||||||
|
If you have no prototypes, specify /dev/null for 'dir'. Specmaker may still
|
||||||
|
be able to generate some working stub code for you.
|
||||||
|
|
||||||
|
Once you have created your DLL, if you generated code (see below), you can
|
||||||
|
backup the DLL header file created and use it for rebuilding the DLL (you
|
||||||
|
should remove the DLLNAME_ prefix from the prototypes to make this work). This
|
||||||
|
allows you to add names to the function arguments, for example, so that the
|
||||||
|
comments and prototype in the regenerated DLL will be clearer.
|
||||||
|
|
||||||
|
Specmaker searches for prototypes using 'grep', and then retrieves each
|
||||||
|
prototype by calling 'function_grep.pl', a Perl script. When you pass the -v
|
||||||
|
option on the command line, the calls to both of these programs are logged.
|
||||||
|
This allows you to see where each function definition has come from. Should
|
||||||
|
specmaker take an excessively long time to locate a prototype, you can check
|
||||||
|
that it is searching the right files; you may want to limit the number of files
|
||||||
|
searched if locating the prototype takes too long.
|
||||||
|
|
||||||
|
You can compile function_grep.pl for a slight increase in performance; see
|
||||||
|
'man perlcc' for details.
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -s num Start prototype search after symbol 'num'
|
||||||
|
-e num End prototype search after symbol 'num'
|
||||||
|
|
||||||
|
By passing the -s or -e options you can have specmaker try to generate code
|
||||||
|
for only some functions in your DLL. This may be used to generate a single
|
||||||
|
function, for example, if you wanted to add functionality to an existing DLL.
|
||||||
|
|
||||||
|
They is also useful for debugging problems, in conjunction with -v.
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -D Generate documentation
|
||||||
|
|
||||||
|
By default, specmaker generates a standard comment at the header of each
|
||||||
|
function it generates. Passing this option makes specmaker output a full
|
||||||
|
header template for standard Wine documentation, listing the parameters
|
||||||
|
and return value of the function.
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -c Generate skeleton code (requires -I)
|
||||||
|
|
||||||
|
This option tells specmaker that you want to create function stubs for
|
||||||
|
each function in the DLL. This is the most basic level of code generation.
|
||||||
|
As specmaker reads each exported symbol from the source DLL, it first tries
|
||||||
|
to demangle the name. If the name is a C++ symbol, the arguments, class and
|
||||||
|
return value are all encoded into the symbol name. Specmaker converts this
|
||||||
|
information into a C function prototype. If this fails, the file(s) specified
|
||||||
|
in the -I argument are scanned for a function prototype. If one is found it
|
||||||
|
is used for the next step of the process, code generation.
|
||||||
|
|
||||||
|
Note: C++ name demangling is currently under development. Since the algorithm
|
||||||
|
used is not documented, it must be decoded. Many simple prototypes are already
|
||||||
|
working however.
|
||||||
|
|
||||||
|
If specmaker does not find a prototype, it emits code like the following:
|
||||||
|
|
||||||
|
In the .spec file:
|
||||||
|
|
||||||
|
@stub _OpenZipFile
|
||||||
|
|
||||||
|
in the header file:
|
||||||
|
|
||||||
|
/* __cdecl ZIPEXTRA__OpenZipFile() */
|
||||||
|
|
||||||
|
in the C source file:
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* _OpenZipFile (ZIPEXTRA.@)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
__stdcall ZIPEXTRA__OpenZipFile()
|
||||||
|
{
|
||||||
|
/* '@Stubbed'ed in .spec */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
If a prototype is found, or correctly demangled, the following is emitted:
|
||||||
|
|
||||||
|
.spec:
|
||||||
|
@ stdcall _OpenZipFile ZIPEXTRA__OpenZipFile
|
||||||
|
|
||||||
|
.h:
|
||||||
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName);
|
||||||
|
|
||||||
|
.c:
|
||||||
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName)
|
||||||
|
{
|
||||||
|
TRACE("stub");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that if the prototype does not contain argument names, specmaker will
|
||||||
|
add them following the convention arg0, arg1 ... argN. If the function is
|
||||||
|
demangled C++, the first argument will be called '_this' if an implicit this
|
||||||
|
pointer is passed (i.e. the function is a non-static class member function).
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -t TRACE arguments (implies -c)
|
||||||
|
|
||||||
|
This option produces the same code as -c, except that arguments are printed
|
||||||
|
out when the function is called, so the FIXME in the above example becomes:
|
||||||
|
|
||||||
|
FIXME("(%s) stub", pszFileName);
|
||||||
|
|
||||||
|
Structs that are passed by value are printed as "struct", and functions
|
||||||
|
that take variable argument lists print "...".
|
||||||
|
|
||||||
|
|
||||||
|
OPTION: -f dll Forward calls to 'dll' (implies -t)
|
||||||
|
|
||||||
|
This is the most complicated level of code generation. The same code is
|
||||||
|
generated as -t, however support is added for forwarding calls to another
|
||||||
|
DLL. The DLL to forward to is given as 'dll'. Lets suppose we built the
|
||||||
|
examples above using "-f real_zipextra". The code generated will look like
|
||||||
|
the following:
|
||||||
|
|
||||||
|
.spec
|
||||||
|
As for -c, except if a function prototype was not found:
|
||||||
|
|
||||||
|
@ forward _OpenZipFile real_zipextra._OpenZipFile
|
||||||
|
|
||||||
|
In this case the function is forwarded to the destination DLL rather
|
||||||
|
than stubbed.
|
||||||
|
|
||||||
|
.h
|
||||||
|
As for -c.
|
||||||
|
|
||||||
|
.c
|
||||||
|
|
||||||
|
A variable "hDLL" is added to hold a pointer to the DLL to forward to, and
|
||||||
|
the initialisation code in ZIPEXTRA_Init is changed to load and free the
|
||||||
|
forward DLL automatically:
|
||||||
|
|
||||||
|
HMODULE hDLL = 0; /* DLL to call through to */
|
||||||
|
|
||||||
|
BOOL WINAPI ZIPEXTRA_Init(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||||
|
{
|
||||||
|
TRACE("(0x%08x, %ld, %p)\n", hinstDLL, fdwReason, lpvReserved);
|
||||||
|
|
||||||
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
hDLL = LoadLibraryA( "real_zipextra" );
|
||||||
|
TRACE ("Forwarding DLL (real_zipextra) loaded\n" );
|
||||||
|
}
|
||||||
|
else if (fdwReason == DLL_PROCESS_DETACH)
|
||||||
|
{
|
||||||
|
FreeLibrary( hDLL );
|
||||||
|
TRACE ("Forwarding DLL (real_zipextra) freed\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
The stub function is changed to call the forwarding DLL and return that value.
|
||||||
|
|
||||||
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName)
|
||||||
|
{
|
||||||
|
BOOL (__stdcall *pFunc)(LPCSTR) = (void*)GetProcAddress(hDLL,"_OpenZipFile");
|
||||||
|
BOOL retVal;
|
||||||
|
TRACE("((LPCSTR)%s) stub", pszFileName);
|
||||||
|
retVal = pFunc(pszFileName);
|
||||||
|
TRACE("returned (%ld)\n",(LONG)retVal));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
This allows you to investigate the workings of a DLL without interfering in
|
||||||
|
its operation in any way (unless you want to).
|
||||||
|
|
||||||
|
In the example I have been using, we probably should have used the -o option
|
||||||
|
to change the ouput name of our DLL to something else, and used the -f
|
||||||
|
option to forward to the real zipextra DLL:
|
||||||
|
|
||||||
|
specmaker -d zipextra -f zipextra -o myzipextra -I "~/zipextra/include/*h"
|
||||||
|
|
||||||
|
Then in the .spec file for our Winelib application, we add the line:
|
||||||
|
|
||||||
|
import myzipextra
|
||||||
|
|
||||||
|
When we build our application, winebuild resolves the calls to our Unix .so.
|
||||||
|
As our application runs we can see the values of all parameters passed to
|
||||||
|
the DLL, and any values returned, without having to write code to dump
|
||||||
|
them ourselves (see below for a better way to wrap a DLL for forwarding).
|
||||||
|
|
||||||
|
This isn't a very realistic example of the usefulness of this feature,
|
||||||
|
however, since we could print out the results anyway, because it is our
|
||||||
|
application making the calls to the DLL. Where DLL forwarding is most useful
|
||||||
|
is where an application or DLL we didn't write calls functions in the DLL.
|
||||||
|
In this case we can capture the sequence of calls made, and the values passed
|
||||||
|
around. This is an aid in reimplementing the DLL, since we can add code for a
|
||||||
|
function, print the results, and then call the real DLL and compare. Only
|
||||||
|
when our code is the same do we need to remove the function pointer and the
|
||||||
|
call to the real DLL. A similar feature in wine is +relay debugging. Using a
|
||||||
|
fowarding DLL allows more granular reporting of arguments, because you can
|
||||||
|
write code to dump out the contents of types/structures rather than just
|
||||||
|
their address in memory. A future version of specmaker may generate this
|
||||||
|
code automatically for common Win32 types.
|
||||||
|
|
||||||
|
See below for more information on setting up a forwarding DLL.
|
||||||
|
|
||||||
|
|
||||||
|
Problems compiling a DLL containing generated code
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
Unless you are very lucky, you will need to do a small amount of work to
|
||||||
|
get a DLL generated with -c, -t or -f to compile. The reason for this is
|
||||||
|
that most DLLs will use custom types such as structs whose definition
|
||||||
|
is not known to the code in the DLL.
|
||||||
|
|
||||||
|
Heres an example prototype from crtdll:
|
||||||
|
|
||||||
|
double __cdecl _cabs(struct _complex arg0)
|
||||||
|
|
||||||
|
The definition for the _complex struct needs to be given. Since it is passed
|
||||||
|
by value, its size also needs to be correct in order to forward the call
|
||||||
|
correctly to a native DLL. In this case the structure is 8 bytes in size, which
|
||||||
|
means that the gcc compile flag -freg-struct-return must be given when
|
||||||
|
compiling the function in order to be compatable with the native DLL. (In
|
||||||
|
general this is not an issue, but you need to be aware of such issues if you
|
||||||
|
encounter problems with your forwarding DLL).
|
||||||
|
|
||||||
|
For third party (non C++) DLL's, the header(s) supplied with the DLL can
|
||||||
|
normally be added as an include to the generated DLL header. For other DLLs
|
||||||
|
I suggest creating a seperate header in the DLL directory and adding any
|
||||||
|
needed types to that. This allows you to rebuild the DLL at whim, for example
|
||||||
|
if a new version of specmaker brings increased functionality, then you
|
||||||
|
only have to overwrite the generated files and re-include the header to take
|
||||||
|
advantage of it.
|
||||||
|
|
||||||
|
Usually there isn't much work to do to get the DLL to compile if you have
|
||||||
|
headers. As an example, building a forwarded crtdll, which contains 520
|
||||||
|
functions, required 20 types to be defined before it compiled. Of these,
|
||||||
|
about half were structures, so about 35 lines of code were needed. The only
|
||||||
|
change to the generated code was one line in the header to include the type
|
||||||
|
definitions.
|
||||||
|
|
||||||
|
To save some typing in case you don't have headers for your DLL type, specmaker
|
||||||
|
will dump dummy declarations for unknown classes and types it encounters,
|
||||||
|
if you use the -v option. These can be piped directly into a fix-up header
|
||||||
|
file for use in compiling your DLL. For example, if specmaker encounters the
|
||||||
|
(C++ ) symbol:
|
||||||
|
|
||||||
|
??0foobar@@QAE@ABV0@@Z (Which is a constructor for a foobar object)
|
||||||
|
|
||||||
|
It will emit the following with -v set:
|
||||||
|
|
||||||
|
struct foobar { int _FIXME; };
|
||||||
|
|
||||||
|
(Classes are mapped to C structs when generating code).
|
||||||
|
|
||||||
|
The output should be piped through 'sort' and 'uniq' to remove multiple
|
||||||
|
declarations, e.g:
|
||||||
|
|
||||||
|
specmaker -d foo -c -I "inc/*.h" -v | grep FIXME | sort | uniq > fixup.h
|
||||||
|
|
||||||
|
By adding '#include "fixup.h"' to foobar_dll.h your compile errors will be
|
||||||
|
greatly reduced.
|
||||||
|
|
||||||
|
If specmaker encounters a type it doesnt know that is passed by value (as in
|
||||||
|
the _cabs example above), it also prints a FIXME message like:
|
||||||
|
|
||||||
|
/* FIXME: By value type: Assumed 'int' */ typedef int ldiv_t;
|
||||||
|
|
||||||
|
If the type is not an int, you will need to change the code and possibly
|
||||||
|
the .spec entry in order to forward correctly. Otherwise, include the typedef
|
||||||
|
in your fixup header to avoid compile errors.
|
||||||
|
|
||||||
|
|
||||||
|
Using a forwarding DLL
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
To create and use a forwarding DLL to trace DLL calls, you need to first
|
||||||
|
create a DLL using the -f option as outlined above, and get it to compile.
|
||||||
|
In order to forward calls the following procedure can be used (for this
|
||||||
|
example we are going to build a forwarding msvcrt.dll for the purpose
|
||||||
|
of reimplementing it).
|
||||||
|
|
||||||
|
First we create the forwarding DLL. We will rename the real msvcrt.dll on our
|
||||||
|
system to ms_msvcrt.dll, and our msvcrt implementation will call it:
|
||||||
|
|
||||||
|
specmaker -d msvcrt -C -f ms_msvcrt -I "inc/*.h"
|
||||||
|
|
||||||
|
We then install this DLL into the Wine tree and add the types we need to
|
||||||
|
make it compile. Once the DLL compiles, we create a dummy ms_msvcrt DLL so
|
||||||
|
winebuild will resolve our forward calls to it (for the cases where specmaker
|
||||||
|
couldn't generate code and has placed an '@forward' line in the .spec file):
|
||||||
|
|
||||||
|
specmaker -d msvcrt -C -o ms_msvcrt
|
||||||
|
|
||||||
|
Install this DLL into the wine tree (since its a stub DLL, no changes are
|
||||||
|
needed to the code).
|
||||||
|
|
||||||
|
Now uncomment the line that specmaker inserted into msvcrt.spec:
|
||||||
|
|
||||||
|
#inport ms_msvcrt.dll
|
||||||
|
|
||||||
|
And recompile Wine.
|
||||||
|
|
||||||
|
Finally, we must tell Wine to only use the builtin msvcrt.dll and to only use
|
||||||
|
the native (Win32) ms_msvcrt.dll. Add the following two lines to ~/.wine/config
|
||||||
|
under the [DllOverrides] section:
|
||||||
|
|
||||||
|
;Use our implmentation of msvcrt
|
||||||
|
"msvcrt" = "builtin, so"
|
||||||
|
;Use only the Win32 ms_msvcrt
|
||||||
|
"ms_msvcrt" = "native"
|
||||||
|
|
||||||
|
At this point, when any call is made to msvcrt.dll, Our libmsvcrt.so recieves
|
||||||
|
the call. It then forwards or calls ms_msvcrt.dll, which is the native dll. We
|
||||||
|
recieve a return value and pass it back to our caller, having TRACEd the
|
||||||
|
arguments on the way.
|
||||||
|
|
||||||
|
At this point you are ready to start reimplementing the calls.
|
||||||
|
|
||||||
|
|
||||||
|
Final comments
|
||||||
|
--------------
|
||||||
|
|
||||||
|
If you have any suggestions for improving this tool, please let me know.
|
||||||
|
If anyone can help answer the FIXME questions in msmangle.c or can fill me in
|
||||||
|
on any aspect of the C++ mangling scheme, I would appreciate it. In particular
|
||||||
|
I want to know what _E and _G represent.
|
||||||
|
|
||||||
|
If you encounter a C++ symbol that doesn't demangle **AND** you have the
|
||||||
|
prototype for it, please send me the symbol as reported by specmaker and the
|
||||||
|
prototype. The more examples I have the easier it is to decypher the scheme,
|
||||||
|
and generating them myself is very slow.
|
||||||
|
|
||||||
|
Finally, although it is easy to generate a DLL, I _very strongly_ suggest that
|
||||||
|
you dont submit a generated DLL for inclusion into Wine unless you have
|
||||||
|
actually implemented a fairly reasonable portion of it. Even then, you should
|
||||||
|
only send the portions of the DLL you have implemented. Thousands of lines of
|
||||||
|
stub code don't help the project at all.
|
||||||
|
|
||||||
|
Please send questions and bug reports to jon_p_griffiths@yahoo.com.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
|
||||||
|
[1] See the Wine and Wine.conf man pages for details on how to tell Wine
|
||||||
|
whether to use native (Win32) or internal DLLs.
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* DLL symbol extraction
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
/* DOS/PE Header details */
|
||||||
|
#define DOS_HEADER_LEN 64
|
||||||
|
#define DOS_MAGIC 0x5a4d
|
||||||
|
#define DOS_PE_OFFSET 60
|
||||||
|
#define PE_HEADER_LEN 248
|
||||||
|
#define PE_MAGIC 0x4550
|
||||||
|
#define PE_COUNT_OFFSET 6
|
||||||
|
#define PE_EXPORTS_OFFSET 120
|
||||||
|
#define PE_EXPORTS_SIZE PE_EXPORTS_OFFSET + 4
|
||||||
|
#define SECTION_HEADER_LEN 40
|
||||||
|
#define SECTION_ADDR_OFFSET 12
|
||||||
|
#define SECTION_ADDR_SIZE SECTION_ADDR_OFFSET + 4
|
||||||
|
#define SECTION_POS_OFFSET SECTION_ADDR_SIZE + 4
|
||||||
|
#define EXPORT_COUNT_OFFSET 24
|
||||||
|
#define EXPORT_NAME_OFFSET EXPORT_COUNT_OFFSET + 8
|
||||||
|
|
||||||
|
/* Minimum memory needed to read both headers into a buffer */
|
||||||
|
#define MIN_HEADER_LEN (PE_HEADER_LEN * sizeof (unsigned char))
|
||||||
|
|
||||||
|
/* Normalise a pointer in the exports section */
|
||||||
|
#define REBASE(x) ((x) - exports)
|
||||||
|
|
||||||
|
/* Module globals */
|
||||||
|
static FILE *dll_file = NULL;
|
||||||
|
static char **dll_symbols = NULL;
|
||||||
|
static size_t dll_num_exports = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* Get a short from a memory block */
|
||||||
|
static inline size_t get_short (const char *mem)
|
||||||
|
{
|
||||||
|
return *((const unsigned char *)mem) +
|
||||||
|
(*((const unsigned char *)mem + 1) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get an integer from a memory block */
|
||||||
|
static inline size_t get_int (const char *mem)
|
||||||
|
{
|
||||||
|
assert (sizeof (char) == (size_t)1);
|
||||||
|
return get_short (mem) + (get_short (mem + 2) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dll_close (void);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* dll_open
|
||||||
|
*
|
||||||
|
* Open a DLL and read in exported symbols
|
||||||
|
*/
|
||||||
|
void dll_open (const char *dll_name)
|
||||||
|
{
|
||||||
|
size_t code = 0, code_len = 0, exports, exports_len, count, symbol_data;
|
||||||
|
char *buff = NULL;
|
||||||
|
dll_file = open_file (dll_name, ".dll", "r");
|
||||||
|
|
||||||
|
atexit (dll_close);
|
||||||
|
|
||||||
|
/* Read in the required DOS and PE Headers */
|
||||||
|
if (!(buff = (char *) malloc (MIN_HEADER_LEN)))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
if (fread (buff, DOS_HEADER_LEN, 1, dll_file) != 1 ||
|
||||||
|
get_short (buff) != DOS_MAGIC)
|
||||||
|
fatal ("Error reading DOS header");
|
||||||
|
|
||||||
|
if (fseek (dll_file, get_int (buff + DOS_PE_OFFSET), SEEK_SET) == -1)
|
||||||
|
fatal ("Error seeking PE header");
|
||||||
|
|
||||||
|
if (fread (buff, PE_HEADER_LEN, 1, dll_file) != 1 ||
|
||||||
|
get_int (buff) != PE_MAGIC)
|
||||||
|
fatal ("Error reading PE header");
|
||||||
|
|
||||||
|
exports = get_int (buff + PE_EXPORTS_OFFSET);
|
||||||
|
exports_len = get_int (buff + PE_EXPORTS_SIZE);
|
||||||
|
|
||||||
|
if (!exports || !exports_len)
|
||||||
|
fatal ("No exports in DLL");
|
||||||
|
|
||||||
|
if (!(count = get_short (buff + PE_COUNT_OFFSET)))
|
||||||
|
fatal ("No sections in DLL");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
printf ("DLL has %d sections\n", count);
|
||||||
|
|
||||||
|
/* Iterate through sections until we find exports */
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
if (fread (buff, SECTION_HEADER_LEN, 1, dll_file) != 1)
|
||||||
|
fatal ("Section read error");
|
||||||
|
|
||||||
|
code = get_int (buff + SECTION_ADDR_OFFSET);
|
||||||
|
code_len = get_int (buff + SECTION_ADDR_SIZE);
|
||||||
|
|
||||||
|
if (code <= exports && code + code_len > exports)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
fatal ("No export section");
|
||||||
|
|
||||||
|
code_len -= (exports - code);
|
||||||
|
|
||||||
|
if (code_len < exports_len)
|
||||||
|
fatal ("Corrupt exports");
|
||||||
|
|
||||||
|
/* Load exports section */
|
||||||
|
if (fseek (dll_file, get_int (buff + SECTION_POS_OFFSET)
|
||||||
|
+ exports - code, SEEK_SET) == -1)
|
||||||
|
fatal ("Export section seek error");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
printf ("Export data size = %d bytes\n", code_len);
|
||||||
|
|
||||||
|
if (!(buff = (char *) realloc (buff, code_len)))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
if (fread (buff, code_len, 1, dll_file) != 1)
|
||||||
|
fatal ("Read error");
|
||||||
|
|
||||||
|
dll_close();
|
||||||
|
|
||||||
|
/* Locate symbol names */
|
||||||
|
symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
|
||||||
|
|
||||||
|
if (symbol_data > code_len)
|
||||||
|
fatal ("Corrupt exports section");
|
||||||
|
|
||||||
|
if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
|
||||||
|
fatal ("No export count");
|
||||||
|
|
||||||
|
if (!(dll_symbols = (char **) malloc (dll_num_exports * sizeof (char *))))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
/* Read symbol names into 'dll_symbols' */
|
||||||
|
count = 0;
|
||||||
|
while (count < dll_num_exports)
|
||||||
|
{
|
||||||
|
const int symbol_offset = get_int (buff + symbol_data + count * 4);
|
||||||
|
const char *symbol_name_ptr = REBASE (buff + symbol_offset);
|
||||||
|
|
||||||
|
assert(symbol_name_ptr);
|
||||||
|
dll_symbols[count] = strdup (symbol_name_ptr);
|
||||||
|
assert(dll_symbols[count]);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NORMAL)
|
||||||
|
printf ("%d exported symbols in DLL\n", dll_num_exports);
|
||||||
|
|
||||||
|
free (buff);
|
||||||
|
|
||||||
|
/* Set DLL output names */
|
||||||
|
if ((buff = strrchr (globals.input_name, '/')))
|
||||||
|
globals.input_name = buff + 1; /* Strip path */
|
||||||
|
|
||||||
|
OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* dll_next_symbol
|
||||||
|
*
|
||||||
|
* Get next exported symbol from dll
|
||||||
|
*/
|
||||||
|
char* dll_next_symbol ()
|
||||||
|
{
|
||||||
|
static unsigned int current_export = 0;
|
||||||
|
|
||||||
|
assert (current_export <= dll_num_exports);
|
||||||
|
|
||||||
|
if (current_export == dll_num_exports)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert (dll_symbols);
|
||||||
|
assert (dll_symbols [current_export]);
|
||||||
|
|
||||||
|
return strdup (dll_symbols [current_export++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* dll_close
|
||||||
|
*
|
||||||
|
* Free resources used by DLL
|
||||||
|
*/
|
||||||
|
static void dll_close (void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (dll_file)
|
||||||
|
{
|
||||||
|
fclose (dll_file);
|
||||||
|
dll_file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dll_symbols)
|
||||||
|
{
|
||||||
|
for (i = 0; i < dll_num_exports; i++)
|
||||||
|
if (dll_symbols [i])
|
||||||
|
free (dll_symbols [i]);
|
||||||
|
free (dll_symbols);
|
||||||
|
dll_symbols = NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
#! /usr/bin/perl
|
||||||
|
|
||||||
|
# Copyright 2000 Patrik Stridvall
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
my $invert = 0;
|
||||||
|
my $pattern;
|
||||||
|
my @files = ();
|
||||||
|
|
||||||
|
while(defined($_ = shift)) {
|
||||||
|
if(/^-/) {
|
||||||
|
if(/^-v$/) {
|
||||||
|
$invert = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!defined($pattern)) {
|
||||||
|
$pattern = $_;
|
||||||
|
} else {
|
||||||
|
push @files, $_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $file (@files) {
|
||||||
|
open(IN, "< $file");
|
||||||
|
|
||||||
|
my $level = 0;
|
||||||
|
my $extern_c = 0;
|
||||||
|
|
||||||
|
my $again = 0;
|
||||||
|
my $lookahead = 0;
|
||||||
|
while($again || defined(my $line = <IN>)) {
|
||||||
|
if(!$again) {
|
||||||
|
chomp $line;
|
||||||
|
if($lookahead) {
|
||||||
|
$lookahead = 0;
|
||||||
|
$_ .= "\n" . $line;
|
||||||
|
} else {
|
||||||
|
$_ = $line;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$again = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove C comments
|
||||||
|
if(s/^(.*?)(\/\*.*?\*\/)(.*)$/$1 $3/s) {
|
||||||
|
$again = 1;
|
||||||
|
next;
|
||||||
|
} elsif(/^(.*?)\/\*/s) {
|
||||||
|
$lookahead = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove C++ comments
|
||||||
|
while(s/^(.*?)\/\/.*?$/$1\n/s) { $again = 1; }
|
||||||
|
if($again) { next; }
|
||||||
|
|
||||||
|
# remove empty rows
|
||||||
|
if(/^\s*$/) { next; }
|
||||||
|
|
||||||
|
# remove preprocessor directives
|
||||||
|
if(s/^\s*\#/\#/m) {
|
||||||
|
if(/^\#.*?\\$/m) {
|
||||||
|
$lookahead = 1;
|
||||||
|
next;
|
||||||
|
} elsif(s/^\#\s*(.*?)(\s+(.*?))?\s*$//m) {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove extern "C"
|
||||||
|
if(s/^\s*extern\s+"C"\s+\{//m) {
|
||||||
|
$extern_c = 1;
|
||||||
|
$again = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($level > 0)
|
||||||
|
{
|
||||||
|
my $line = "";
|
||||||
|
while(/^[^\{\}]/) {
|
||||||
|
s/^([^\{\}\'\"]*)//s;
|
||||||
|
$line .= $1;
|
||||||
|
if(s/^\'//) {
|
||||||
|
$line .= "\'";
|
||||||
|
while(/^./ && !s/^\'//) {
|
||||||
|
s/^([^\'\\]*)//s;
|
||||||
|
$line .= $1;
|
||||||
|
if(s/^\\//) {
|
||||||
|
$line .= "\\";
|
||||||
|
if(s/^(.)//s) {
|
||||||
|
$line .= $1;
|
||||||
|
if($1 eq "0") {
|
||||||
|
s/^(\d{0,3})//s;
|
||||||
|
$line .= $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$line .= "\'";
|
||||||
|
} elsif(s/^\"//) {
|
||||||
|
$line .= "\"";
|
||||||
|
while(/^./ && !s/^\"//) {
|
||||||
|
s/^([^\"\\]*)//s;
|
||||||
|
$line .= $1;
|
||||||
|
if(s/^\\//) {
|
||||||
|
$line .= "\\";
|
||||||
|
if(s/^(.)//s) {
|
||||||
|
$line .= $1;
|
||||||
|
if($1 eq "0") {
|
||||||
|
s/^(\d{0,3})//s;
|
||||||
|
$line .= $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$line .= "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s/^\{//) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
$line .= "{";
|
||||||
|
$level++;
|
||||||
|
} elsif(s/^\}//) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
$line .= "}" if $level > 1;
|
||||||
|
$level--;
|
||||||
|
if($level == -1 && $extern_c) {
|
||||||
|
$extern_c = 0;
|
||||||
|
$level = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next;
|
||||||
|
} elsif(/^class[^\}]*{/) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
$level++;
|
||||||
|
next;
|
||||||
|
} elsif(/^class[^\}]*$/) {
|
||||||
|
$lookahead = 1;
|
||||||
|
next;
|
||||||
|
} elsif(/^typedef[^\}]*;/) {
|
||||||
|
next;
|
||||||
|
} elsif(/(extern\s+|static\s+)?
|
||||||
|
(?:__inline__\s+|__inline\s+|inline\s+)?
|
||||||
|
((struct\s+|union\s+|enum\s+)?(?:\w+(?:\:\:(?:\s*operator\s*[^\)\s]+)?)?)+((\s*(?:\*|\&))+\s*|\s+))
|
||||||
|
((__cdecl|__stdcall|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK)\s+)?
|
||||||
|
((?:\w+(?:\:\:)?)+(\(\w+\))?)\s*\(([^\)]*)\)\s*
|
||||||
|
(?:\w+(?:\s*\([^\)]*\))?\s*)*\s*
|
||||||
|
(\{|\;)/sx)
|
||||||
|
{
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
if($11 eq "{") {
|
||||||
|
$level++;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $linkage = $1;
|
||||||
|
my $return_type = $2;
|
||||||
|
my $calling_convention = $7;
|
||||||
|
my $name = $8;
|
||||||
|
my $arguments = $10;
|
||||||
|
|
||||||
|
if(!defined($linkage)) {
|
||||||
|
$linkage = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!defined($calling_convention)) {
|
||||||
|
$calling_convention = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
$linkage =~ s/\s*$//;
|
||||||
|
|
||||||
|
$return_type =~ s/\s*$//;
|
||||||
|
$return_type =~ s/\s*\*\s*/*/g;
|
||||||
|
$return_type =~ s/(\*+)/ $1/g;
|
||||||
|
|
||||||
|
$arguments =~ y/\t\n/ /;
|
||||||
|
$arguments =~ s/^\s*(.*?)\s*$/$1/;
|
||||||
|
if($arguments eq "") { $arguments = "void" }
|
||||||
|
|
||||||
|
my @argument_types;
|
||||||
|
my @argument_names;
|
||||||
|
my @arguments = split(/,/, $arguments);
|
||||||
|
foreach my $n (0..$#arguments) {
|
||||||
|
my $argument_type = "";
|
||||||
|
my $argument_name = "";
|
||||||
|
my $argument = $arguments[$n];
|
||||||
|
$argument =~ s/^\s*(.*?)\s*$/$1/;
|
||||||
|
# print " " . ($n + 1) . ": '$argument'\n";
|
||||||
|
$argument =~ s/^(IN OUT(?=\s)|IN(?=\s)|OUT(?=\s)|\s*)\s*//;
|
||||||
|
$argument =~ s/^(const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s)|\s*)\s*//;
|
||||||
|
if($argument =~ /^\.\.\.$/) {
|
||||||
|
$argument_type = "...";
|
||||||
|
$argument_name = "...";
|
||||||
|
} elsif($argument =~ /^
|
||||||
|
((?:struct\s+|union\s+|enum\s+|(?:signed\s+|unsigned\s+)
|
||||||
|
(?:short\s+(?=int)|long\s+(?=int))?)?(?:\w+(?:\:\:)?)+)\s*
|
||||||
|
((?:const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s))?\s*(?:\*\s*?)*)\s*
|
||||||
|
(?:const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s))?\s*
|
||||||
|
(\w*)\s*
|
||||||
|
(?:\[\]|\s+OPTIONAL)?/x)
|
||||||
|
{
|
||||||
|
$argument_type = "$1";
|
||||||
|
if($2 ne "") {
|
||||||
|
$argument_type .= " $2";
|
||||||
|
}
|
||||||
|
$argument_name = $3;
|
||||||
|
|
||||||
|
$argument_type =~ s/\s*const\s*/ /;
|
||||||
|
$argument_type =~ s/^\s*(.*?)\s*$/$1/;
|
||||||
|
|
||||||
|
$argument_name =~ s/^\s*(.*?)\s*$/$1/;
|
||||||
|
} else {
|
||||||
|
die "$file: $.: syntax error: '$argument'\n";
|
||||||
|
}
|
||||||
|
$argument_types[$n] = $argument_type;
|
||||||
|
$argument_names[$n] = $argument_name;
|
||||||
|
# print " " . ($n + 1) . ": '$argument_type': '$argument_name'\n";
|
||||||
|
}
|
||||||
|
if($#argument_types == 0 && $argument_types[0] =~ /^void$/i) {
|
||||||
|
$#argument_types = -1;
|
||||||
|
$#argument_names = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@arguments = ();
|
||||||
|
foreach my $n (0..$#argument_types) {
|
||||||
|
if($argument_names[$n] && $argument_names[$n] ne "...") {
|
||||||
|
if($argument_types[$n] !~ /\*$/) {
|
||||||
|
$arguments[$n] = $argument_types[$n] . " " . $argument_names[$n];
|
||||||
|
} else {
|
||||||
|
$arguments[$n] = $argument_types[$n] . $argument_names[$n];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$arguments[$n] = $argument_types[$n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments = join(", ", @arguments);
|
||||||
|
if(!$arguments) { $arguments = "void"; }
|
||||||
|
|
||||||
|
if((!$invert && $name =~ /$pattern/) || ($invert && $name !~ /$pattern/)) {
|
||||||
|
if($calling_convention) {
|
||||||
|
print "$return_type $calling_convention $name($arguments)\n";
|
||||||
|
} else {
|
||||||
|
if($return_type =~ /\*$/) {
|
||||||
|
print "$return_type$name($arguments)\n";
|
||||||
|
} else {
|
||||||
|
print "$return_type $name($arguments)\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elsif(/\'[^\']*\'/s) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
} elsif(/\"[^\"]*\"/s) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
} elsif(/;/s) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
} elsif(/extern\s+"C"\s+{/s) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
} elsif(/\{/s) {
|
||||||
|
$_ = $'; $again = 1;
|
||||||
|
$level++;
|
||||||
|
} else {
|
||||||
|
$lookahead = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(IN);
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
* Option processing and main()
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
|
||||||
|
_globals globals; /* All global variables */
|
||||||
|
|
||||||
|
|
||||||
|
static void do_include (const char *arg)
|
||||||
|
{
|
||||||
|
globals.directory = arg;
|
||||||
|
globals.do_code = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline const char* strip_ext (const char *str)
|
||||||
|
{
|
||||||
|
char *ext = strstr(str, ".dll");
|
||||||
|
if (ext)
|
||||||
|
return str_substring (str, ext);
|
||||||
|
else
|
||||||
|
return strdup (str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_name (const char *arg)
|
||||||
|
{
|
||||||
|
globals.dll_name = strip_ext (arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_input (const char *arg)
|
||||||
|
{
|
||||||
|
globals.input_name = strip_ext (arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_code (void)
|
||||||
|
{
|
||||||
|
globals.do_code = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_trace (void)
|
||||||
|
{
|
||||||
|
globals.do_trace = 1;
|
||||||
|
globals.do_code = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_forward (const char *arg)
|
||||||
|
{
|
||||||
|
globals.forward_dll = arg;
|
||||||
|
globals.do_trace = 1;
|
||||||
|
globals.do_code = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_document (void)
|
||||||
|
{
|
||||||
|
globals.do_documentation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_cdecl (void)
|
||||||
|
{
|
||||||
|
globals.do_cdecl = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_quiet (void)
|
||||||
|
{
|
||||||
|
globals.do_quiet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_start (const char *arg)
|
||||||
|
{
|
||||||
|
globals.start_ordinal = atoi (arg);
|
||||||
|
if (!globals.start_ordinal)
|
||||||
|
fatal ("Invalid -s option (must be numeric)");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_end (const char *arg)
|
||||||
|
{
|
||||||
|
globals.end_ordinal = atoi (arg);
|
||||||
|
if (!globals.end_ordinal)
|
||||||
|
fatal ("Invalid -e option (must be numeric)");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void do_verbose (void)
|
||||||
|
{
|
||||||
|
globals.do_verbose = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct option
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
int has_arg;
|
||||||
|
void (*func) ();
|
||||||
|
const char *usage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct option option_table[] = {
|
||||||
|
{"-d", 1, do_input, "-d dll Use dll for input file (mandatory)"},
|
||||||
|
{"-h", 0, do_usage, "-h Display this help message"},
|
||||||
|
{"-I", 1, do_include, "-I dir Look for prototypes in 'dir' (implies -c)"},
|
||||||
|
{"-o", 1, do_name, "-o name Set the output dll name (default: dll)"},
|
||||||
|
{"-c", 0, do_code, "-c Generate skeleton code (requires -I)"},
|
||||||
|
{"-t", 0, do_trace, "-t TRACE arguments (implies -c)"},
|
||||||
|
{"-f", 1, do_forward, "-f dll Forward calls to 'dll' (implies -t)"},
|
||||||
|
{"-D", 0, do_document, "-D Generate documentation"},
|
||||||
|
{"-C", 0, do_cdecl, "-C Assume __cdecl calls (default: __stdcall)"},
|
||||||
|
{"-s", 1, do_start, "-s num Start prototype search after symbol 'num'"},
|
||||||
|
{"-e", 1, do_end, "-e num End prototype search after symbol 'num'"},
|
||||||
|
{"-q", 0, do_quiet, "-q Don't show progress (quiet)."},
|
||||||
|
{"-v", 0, do_verbose, "-v Show lots of detail while working (verbose)."},
|
||||||
|
{NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void do_usage (void)
|
||||||
|
{
|
||||||
|
const struct option *opt;
|
||||||
|
printf ("Usage: specmaker [options] -d dll\n\nOptions:\n");
|
||||||
|
for (opt = option_table; opt->name; opt++)
|
||||||
|
printf (" %s\n", opt->usage);
|
||||||
|
puts ("\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* parse_options
|
||||||
|
*
|
||||||
|
* Parse options from the argv array
|
||||||
|
*/
|
||||||
|
static void parse_options (char *argv[])
|
||||||
|
{
|
||||||
|
const struct option *opt;
|
||||||
|
char *const *ptr;
|
||||||
|
const char *arg = NULL;
|
||||||
|
|
||||||
|
ptr = argv + 1;
|
||||||
|
|
||||||
|
while (*ptr != NULL)
|
||||||
|
{
|
||||||
|
for (opt = option_table; opt->name; opt++)
|
||||||
|
{
|
||||||
|
if (opt->has_arg && !strncmp (*ptr, opt->name, strlen (opt->name)))
|
||||||
|
{
|
||||||
|
arg = *ptr + strlen (opt->name);
|
||||||
|
if (*arg == '\0')
|
||||||
|
{
|
||||||
|
ptr++;
|
||||||
|
arg = *ptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!strcmp (*ptr, opt->name))
|
||||||
|
{
|
||||||
|
arg = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opt->name)
|
||||||
|
fatal ("Unrecognized option");
|
||||||
|
|
||||||
|
if (opt->has_arg && arg != NULL)
|
||||||
|
opt->func (arg);
|
||||||
|
else
|
||||||
|
opt->func ("");
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globals.do_code && !globals.directory)
|
||||||
|
fatal ("-I must be used if generating code");
|
||||||
|
|
||||||
|
if (!globals.input_name)
|
||||||
|
fatal ("Option -d is mandatory");
|
||||||
|
|
||||||
|
if (VERBOSE && QUIET)
|
||||||
|
fatal ("Options -v and -q are mutually exclusive");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* main
|
||||||
|
*/
|
||||||
|
#ifdef __GNUC__
|
||||||
|
int main (int argc __attribute__((unused)), char *argv[])
|
||||||
|
#else
|
||||||
|
int main (int argc, char *argv[])
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
parsed_symbol symbol;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
parse_options (argv);
|
||||||
|
|
||||||
|
dll_open (globals.input_name);
|
||||||
|
|
||||||
|
output_spec_preamble ();
|
||||||
|
output_header_preamble ();
|
||||||
|
output_c_preamble ();
|
||||||
|
|
||||||
|
memset (&symbol, 0, sizeof (parsed_symbol));
|
||||||
|
|
||||||
|
while ((symbol.symbol = dll_next_symbol ()))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (NORMAL)
|
||||||
|
printf ("Export %3d - '%s' ...%c", count, symbol.symbol,
|
||||||
|
VERBOSE ? '\n' : ' ');
|
||||||
|
|
||||||
|
if (globals.do_code && count >= globals.start_ordinal
|
||||||
|
&& (!globals.end_ordinal || count <= globals.end_ordinal))
|
||||||
|
{
|
||||||
|
/* Attempt to get information about the symbol */
|
||||||
|
int result = symbol_demangle (&symbol);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
result = symbol_search (&symbol);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
/* Clean up the prototype */
|
||||||
|
symbol_clean_string (symbol.function_name);
|
||||||
|
|
||||||
|
if (NORMAL)
|
||||||
|
puts (result ? "[Not Found]" : "[OK]");
|
||||||
|
}
|
||||||
|
else if (NORMAL)
|
||||||
|
puts ("[Ignoring]");
|
||||||
|
|
||||||
|
output_spec_symbol (&symbol);
|
||||||
|
output_header_symbol (&symbol);
|
||||||
|
output_c_symbol (&symbol);
|
||||||
|
|
||||||
|
symbol_clear (&symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_makefile ();
|
||||||
|
output_install_script ();
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Finished, Cleaning up...");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Misc functions
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_create
|
||||||
|
*
|
||||||
|
* Create a single string from many substrings
|
||||||
|
*/
|
||||||
|
char *str_create(size_t num_str, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
size_t len = 1, i = 0;
|
||||||
|
char *tmp, *t;
|
||||||
|
|
||||||
|
va_start (args, num_str);
|
||||||
|
for (i = 0; i < num_str; i++)
|
||||||
|
if ((t = va_arg(args, char *)))
|
||||||
|
len += strlen (t);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
if (!(tmp = (char *) malloc (len)))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
tmp[0] = '\0';
|
||||||
|
|
||||||
|
va_start (args, num_str);
|
||||||
|
for (i = 0; i < num_str; i++)
|
||||||
|
if ((t = va_arg(args, char *)))
|
||||||
|
strcat (tmp, t);
|
||||||
|
va_end (args);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_create_num
|
||||||
|
*
|
||||||
|
* Create a single string from many substrings, terminating in a number
|
||||||
|
*/
|
||||||
|
char *str_create_num(size_t num_str, int num, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
size_t len = 8, i = 0;
|
||||||
|
char *tmp, *t;
|
||||||
|
|
||||||
|
va_start (args, num);
|
||||||
|
for (i = 0; i < num_str; i++)
|
||||||
|
if ((t = va_arg(args, char *)))
|
||||||
|
len += strlen (t);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
if (!(tmp = (char *) malloc (len)))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
tmp[0] = '\0';
|
||||||
|
|
||||||
|
va_start (args, num);
|
||||||
|
for (i = 0; i < num_str; i++)
|
||||||
|
if ((t = va_arg(args, char *)))
|
||||||
|
strcat (tmp, t);
|
||||||
|
va_end (args);
|
||||||
|
sprintf (tmp + len - 8, "%d", num);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_substring
|
||||||
|
*
|
||||||
|
* Create a new substring from a string
|
||||||
|
*/
|
||||||
|
char *str_substring(const char *start, const char *end)
|
||||||
|
{
|
||||||
|
char *newstr;
|
||||||
|
|
||||||
|
assert (start && end && end > start);
|
||||||
|
|
||||||
|
if (!(newstr = (char *) malloc (end - start + 1)))
|
||||||
|
fatal ("Out of memory");
|
||||||
|
|
||||||
|
memcpy (newstr, start, end - start);
|
||||||
|
newstr [end - start] = '\0';
|
||||||
|
|
||||||
|
return newstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_replace
|
||||||
|
*
|
||||||
|
* Swap two strings in another string, in place
|
||||||
|
* Modified PD code from 'snippets'
|
||||||
|
*/
|
||||||
|
char *str_replace (char *str, const char *oldstr, const char *newstr)
|
||||||
|
{
|
||||||
|
int oldlen, newlen;
|
||||||
|
char *p, *q;
|
||||||
|
|
||||||
|
if (!(p = strstr(str, oldstr)))
|
||||||
|
return p;
|
||||||
|
oldlen = strlen (oldstr);
|
||||||
|
newlen = strlen (newstr);
|
||||||
|
memmove (q = p + newlen, p + oldlen, strlen (p + oldlen) + 1);
|
||||||
|
memcpy (p, newstr, newlen);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_match
|
||||||
|
*
|
||||||
|
* Locate one string in another, ignoring spaces
|
||||||
|
*/
|
||||||
|
const char *str_match (const char *str, const char *match, int *found)
|
||||||
|
{
|
||||||
|
assert(str && match && found);
|
||||||
|
|
||||||
|
for (; *str == ' '; str++);
|
||||||
|
if (!strncmp (str, match, strlen (match)))
|
||||||
|
{
|
||||||
|
*found = 1;
|
||||||
|
str += strlen (match);
|
||||||
|
for (; *str == ' '; str++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*found = 0;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_find_set
|
||||||
|
*
|
||||||
|
* Locate the first occurence of a set of characters in a string
|
||||||
|
*/
|
||||||
|
const char *str_find_set (const char *str, const char *findset)
|
||||||
|
{
|
||||||
|
assert(str && findset);
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
const char *p = findset;
|
||||||
|
while (*p)
|
||||||
|
if (*p++ == *str)
|
||||||
|
return str;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* str_toupper
|
||||||
|
*
|
||||||
|
* Uppercase a string
|
||||||
|
*/
|
||||||
|
char *str_toupper (char *str)
|
||||||
|
{
|
||||||
|
char *save = str;
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
*str = toupper (*str);
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* open_file
|
||||||
|
*
|
||||||
|
* Open a file returning only on success
|
||||||
|
*/
|
||||||
|
FILE *open_file (const char *name, const char *ext, const char *mode)
|
||||||
|
{
|
||||||
|
char fname[128];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (((unsigned)snprintf (fname, sizeof (fname), "%s%s%s",
|
||||||
|
*mode == 'w' ? "./" : "", name, ext) > sizeof (fname)))
|
||||||
|
fatal ("File name too long");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
printf ("Open file %s\n", fname);
|
||||||
|
|
||||||
|
fp = fopen (fname, mode);
|
||||||
|
if (!fp)
|
||||||
|
fatal ("Cant open file");
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* fatal
|
||||||
|
*
|
||||||
|
* Fatal error handling
|
||||||
|
*/
|
||||||
|
void fatal (const char *message)
|
||||||
|
{
|
||||||
|
if (errno)
|
||||||
|
perror (message);
|
||||||
|
else
|
||||||
|
puts (message);
|
||||||
|
do_usage ();
|
||||||
|
}
|
|
@ -0,0 +1,560 @@
|
||||||
|
/*
|
||||||
|
* Demangle VC++ symbols into C function prototypes
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
/* Type for parsing mangled types */
|
||||||
|
typedef struct _compound_type
|
||||||
|
{
|
||||||
|
char dest_type;
|
||||||
|
int flags;
|
||||||
|
int have_qualifiers;
|
||||||
|
char *expression;
|
||||||
|
} compound_type;
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialise a compound type structure */
|
||||||
|
#define INIT_CT(ct) do { memset (&ct, 0, sizeof (ct)); } while (0)
|
||||||
|
|
||||||
|
/* free the memory used by a compound structure */
|
||||||
|
#define FREE_CT(ct) do { if (ct.expression) free (ct.expression); } while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Internal functions */
|
||||||
|
static char *demangle_datatype (char **str, compound_type *ct,
|
||||||
|
parsed_symbol* sym);
|
||||||
|
|
||||||
|
static char *get_constraints_convention_1 (char **str, compound_type *ct);
|
||||||
|
|
||||||
|
static char *get_constraints_convention_2 (char **str, compound_type *ct);
|
||||||
|
|
||||||
|
static char *get_type_string (const char c, const int constraints);
|
||||||
|
|
||||||
|
static int get_type_constant (const char c, const int constraints);
|
||||||
|
|
||||||
|
static char *get_pointer_type_string (compound_type *ct,
|
||||||
|
const char *expression);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* demangle_symbol
|
||||||
|
*
|
||||||
|
* Demangle a C++ linker symbol into a C prototype
|
||||||
|
*/
|
||||||
|
int symbol_demangle (parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
compound_type ct;
|
||||||
|
int is_static = 0, is_const = 0;
|
||||||
|
char *function_name = NULL;
|
||||||
|
char *class_name = NULL;
|
||||||
|
char *name;
|
||||||
|
static unsigned int hash = 0; /* In case of overloaded functions */
|
||||||
|
|
||||||
|
assert (globals.do_code);
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
|
||||||
|
hash++;
|
||||||
|
|
||||||
|
/* MS mangled names always begin with '?' */
|
||||||
|
name = sym->symbol;
|
||||||
|
if (*name++ != '?')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Attempting to demangle symbol");
|
||||||
|
|
||||||
|
/* Then function name or operator code */
|
||||||
|
if (*name == '?')
|
||||||
|
{
|
||||||
|
/* C++ operator code (one character, or two if the first is '_') */
|
||||||
|
switch (*++name)
|
||||||
|
{
|
||||||
|
case '0': function_name = strdup ("ctor"); break;
|
||||||
|
case '1': function_name = strdup ("dtor"); break;
|
||||||
|
case '2': function_name = strdup ("operator_new"); break;
|
||||||
|
case '3': function_name = strdup ("operator_delete"); break;
|
||||||
|
case '4': function_name = strdup ("operator_equals"); break;
|
||||||
|
case '5': function_name = strdup ("operator_5"); break;
|
||||||
|
case '6': function_name = strdup ("operator_6"); break;
|
||||||
|
case '7': function_name = strdup ("operator_7"); break;
|
||||||
|
case '8': function_name = strdup ("operator_equals_equals"); break;
|
||||||
|
case '9': function_name = strdup ("operator_not_equals"); break;
|
||||||
|
case 'E': function_name = strdup ("operator_plus_plus"); break;
|
||||||
|
case 'H': function_name = strdup ("operator_plus"); break;
|
||||||
|
case '_':
|
||||||
|
/* FIXME: Seems to be some kind of escape character - overloads? */
|
||||||
|
switch (*++name)
|
||||||
|
{
|
||||||
|
case '7': /* FIXME: Compiler generated default copy/assignment ctor? */
|
||||||
|
return -1;
|
||||||
|
case 'E': function_name = strdup ("_unknown_E"); break;
|
||||||
|
case 'G': function_name = strdup ("_unknown_G"); break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* FIXME: Other operators */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Type or function name terminated by '@' */
|
||||||
|
function_name = name;
|
||||||
|
while (*name && *name++ != '@') ;
|
||||||
|
if (!*name)
|
||||||
|
return -1;
|
||||||
|
function_name = str_substring (function_name, name - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either a class name, or '@' if the symbol is not a class member */
|
||||||
|
if (*name == '@')
|
||||||
|
{
|
||||||
|
class_name = strdup ("global"); /* Non member function (or a datatype) */
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Class the function is associated with, terminated by '@@' */
|
||||||
|
class_name = name;
|
||||||
|
while (*name && *name++ != '@') ;
|
||||||
|
if (*name++ != '@')
|
||||||
|
return -1;
|
||||||
|
class_name = str_substring (class_name, name - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: This is guesswork on my part, but it seems to work:
|
||||||
|
* 'Q' Means the function is passed an implicit 'this' pointer.
|
||||||
|
* 'S' Means static member function, i.e. no implicit 'this' pointer.
|
||||||
|
* 'Y' Is used for datatypes and functions, so there is no 'this' pointer.
|
||||||
|
* This character also implies some other things:
|
||||||
|
* 'Y','S' = The character after the calling convention is always the
|
||||||
|
* start of the return type code.
|
||||||
|
* 'Q' Character after the calling convention is 'const'ness code
|
||||||
|
* (only non static member functions can be const).
|
||||||
|
* 'U' also occurs, it seems to behave like Q, but probably implies
|
||||||
|
* something else.
|
||||||
|
*/
|
||||||
|
switch(*name++)
|
||||||
|
{
|
||||||
|
case 'U' :
|
||||||
|
case 'Q' :
|
||||||
|
/* Implicit 'this' pointer */
|
||||||
|
sym->arg_text [sym->argc] = str_create (3, "struct ", class_name, " *");
|
||||||
|
sym->arg_type [sym->argc] = ARG_POINTER;
|
||||||
|
sym->arg_flag [sym->argc] = 0;
|
||||||
|
sym->arg_name [sym->argc++] = strdup ("_this");
|
||||||
|
/* New struct definitions can be 'grep'ed out for making a fixup header */
|
||||||
|
if (VERBOSE)
|
||||||
|
printf ("struct %s { int _FIXME; };\n", class_name);
|
||||||
|
break;
|
||||||
|
case 'S' :
|
||||||
|
is_static = 1;
|
||||||
|
break;
|
||||||
|
case 'Y' :
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next is the calling convention */
|
||||||
|
switch (*name++)
|
||||||
|
{
|
||||||
|
case 'A':
|
||||||
|
sym->calling_convention = strdup ("__cdecl");
|
||||||
|
break;
|
||||||
|
case 'B': /* FIXME: Something to do with __declspec(dllexport)? */
|
||||||
|
case 'I': /* __fastcall */
|
||||||
|
case 'G':
|
||||||
|
sym->calling_convention = strdup ("__stdcall");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the symbol is associated with a class, its 'const' status follows */
|
||||||
|
if (sym->argc)
|
||||||
|
{
|
||||||
|
if (*name == 'B')
|
||||||
|
is_const = 1;
|
||||||
|
else if (*name != 'E')
|
||||||
|
return -1;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return type, or @ if 'void' */
|
||||||
|
if (*name == '@')
|
||||||
|
{
|
||||||
|
sym->return_text = strdup ("void");
|
||||||
|
sym->return_type = ARG_VOID;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INIT_CT (ct);
|
||||||
|
if (!demangle_datatype (&name, &ct, sym))
|
||||||
|
return -1;
|
||||||
|
sym->return_text = ct.expression;
|
||||||
|
sym->return_type = get_type_constant(ct.dest_type, ct.flags);
|
||||||
|
ct.expression = NULL;
|
||||||
|
FREE_CT (ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now come the function arguments */
|
||||||
|
while (*name && *name != 'Z')
|
||||||
|
{
|
||||||
|
/* Decode each data type and append it to the argument list */
|
||||||
|
if (*name != '@')
|
||||||
|
{
|
||||||
|
INIT_CT (ct);
|
||||||
|
if (!demangle_datatype(&name, &ct, sym))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (strcmp (ct.expression, "void"))
|
||||||
|
{
|
||||||
|
sym->arg_text [sym->argc] = ct.expression;
|
||||||
|
ct.expression = NULL;
|
||||||
|
sym->arg_type [sym->argc] = get_type_constant (ct.dest_type, ct.flags);
|
||||||
|
sym->arg_flag [sym->argc] = ct.flags;
|
||||||
|
sym->arg_name[sym->argc] = str_create_num (1, sym->argc, "arg");
|
||||||
|
sym->argc++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break; /* 'void' terminates an argument list */
|
||||||
|
FREE_CT (ct);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*name == '@')
|
||||||
|
name++;
|
||||||
|
|
||||||
|
/* Functions are always terminated by 'Z'. If we made it this far and
|
||||||
|
* Don't find it, we have incorrectly identified a data type.
|
||||||
|
*/
|
||||||
|
if (*name != 'Z')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Note: '()' after 'Z' means 'throws', but we don't care here */
|
||||||
|
|
||||||
|
/* Create the function name. Include a unique number because otherwise
|
||||||
|
* overloaded functions could have the same c signature.
|
||||||
|
*/
|
||||||
|
sym->function_name = str_create_num (4, hash, class_name, "_",
|
||||||
|
function_name, is_static ? "_static" : is_const ? "_const" : "_");
|
||||||
|
|
||||||
|
assert (sym->return_text);
|
||||||
|
assert (sym->calling_convention);
|
||||||
|
assert (sym->function_name);
|
||||||
|
|
||||||
|
free (class_name);
|
||||||
|
free (function_name);
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Demangled symbol OK");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* demangle_datatype
|
||||||
|
*
|
||||||
|
* Attempt to demangle a C++ data type, which may be compound.
|
||||||
|
* a compound type is made up of a number of simple types. e.g:
|
||||||
|
* char** = (pointer to (pointer to (char)))
|
||||||
|
*
|
||||||
|
* Uses a simple recursive descent algorithm that is broken
|
||||||
|
* and/or incomplete, without a doubt ;-)
|
||||||
|
*/
|
||||||
|
static char *demangle_datatype (char **str, compound_type *ct,
|
||||||
|
parsed_symbol* sym)
|
||||||
|
{
|
||||||
|
char *iter;
|
||||||
|
|
||||||
|
assert (str && *str);
|
||||||
|
assert (ct);
|
||||||
|
|
||||||
|
iter = *str;
|
||||||
|
|
||||||
|
if (!get_constraints_convention_1 (&iter, ct))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
switch (*iter)
|
||||||
|
{
|
||||||
|
case 'C': case 'D': case 'E': case 'F': case 'G':
|
||||||
|
case 'H': case 'I': case 'J': case 'K': case 'M':
|
||||||
|
case 'N': case 'O': case 'X': case 'Z':
|
||||||
|
/* Simple data types */
|
||||||
|
ct->dest_type = *iter++;
|
||||||
|
if (!get_constraints_convention_2 (&iter, ct))
|
||||||
|
return NULL;
|
||||||
|
ct->expression = get_type_string (ct->dest_type, ct->flags);
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
case 'V':
|
||||||
|
/* Class/struct/union */
|
||||||
|
ct->dest_type = *iter++;
|
||||||
|
if (*iter == '0' || *iter == '1')
|
||||||
|
{
|
||||||
|
/* Referring to class type (implicit 'this') */
|
||||||
|
char *stripped;
|
||||||
|
if (!sym->argc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
/* Apply our constraints to the base type (struct xxx *) */
|
||||||
|
stripped = strdup (sym->arg_text [0]);
|
||||||
|
if (!stripped)
|
||||||
|
fatal ("Out of Memory");
|
||||||
|
|
||||||
|
/* If we're a reference, re-use the pointer already in the type */
|
||||||
|
if (!ct->flags & CT_BY_REFERENCE)
|
||||||
|
stripped[ strlen (stripped) - 2] = '\0'; /* otherwise, strip it */
|
||||||
|
|
||||||
|
ct->expression = str_create (2, ct->flags & CT_CONST ? "const " :
|
||||||
|
ct->flags & CT_VOLATILE ? "volatile " : "", stripped);
|
||||||
|
free (stripped);
|
||||||
|
}
|
||||||
|
else if (*iter == '_')
|
||||||
|
{
|
||||||
|
/* The name of the class/struct, followed by '@@' */
|
||||||
|
char *struct_name = ++iter;
|
||||||
|
while (*iter && *iter++ != '@') ;
|
||||||
|
if (*iter++ != '@')
|
||||||
|
return NULL;
|
||||||
|
struct_name = str_substring (struct_name, iter - 2);
|
||||||
|
ct->expression = str_create (4, ct->flags & CT_CONST ? "const " :
|
||||||
|
ct->flags & CT_VOLATILE ? "volatile " : "", "struct ",
|
||||||
|
struct_name, ct->flags & CT_BY_REFERENCE ? " *" : "");
|
||||||
|
free (struct_name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Q': /* FIXME: Array Just treated as pointer currently */
|
||||||
|
case 'P': /* Pointer */
|
||||||
|
{
|
||||||
|
compound_type sub_ct;
|
||||||
|
INIT_CT (sub_ct);
|
||||||
|
|
||||||
|
ct->dest_type = *iter++;
|
||||||
|
if (!get_constraints_convention_2 (&iter, ct))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* FIXME: P6 = Function pointer, others who knows.. */
|
||||||
|
if (isdigit (*iter))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Recurse to get the pointed-to type */
|
||||||
|
if (!demangle_datatype (&iter, &sub_ct, sym))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ct->expression = get_pointer_type_string (ct, sub_ct.expression);
|
||||||
|
|
||||||
|
FREE_CT (sub_ct);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
/* Referring back to previously parsed type */
|
||||||
|
if (sym->argc >= (size_t)('0' - *iter))
|
||||||
|
return NULL;
|
||||||
|
ct->dest_type = sym->arg_type ['0' - *iter];
|
||||||
|
ct->expression = strdup (sym->arg_text ['0' - *iter]);
|
||||||
|
iter++;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!ct->expression)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return (char *)(*str = iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Constraints:
|
||||||
|
* There are two conventions for specifying data type constaints. I
|
||||||
|
* don't know how the compiler chooses between them, but I suspect it
|
||||||
|
* is based on ensuring that linker names are unique.
|
||||||
|
* Convention 1. The data type modifier is given first, followed
|
||||||
|
* by the data type it operates on. '?' means passed by value,
|
||||||
|
* 'A' means passed by reference. Note neither of these characters
|
||||||
|
* is a valid base data type. This is then followed by a character
|
||||||
|
* specifying constness or volatilty.
|
||||||
|
* Convention 2. The base data type (which is never '?' or 'A') is
|
||||||
|
* given first. The character modifier is optionally given after
|
||||||
|
* the base type character. If a valid character mofifier is present,
|
||||||
|
* then it only applies to the current data type if the character
|
||||||
|
* after that is not 'A' 'B' or 'C' (Because this makes a convention 1
|
||||||
|
* constraint for the next data type).
|
||||||
|
*
|
||||||
|
* The conventions are usually mixed within the same symbol.
|
||||||
|
* Since 'C' is both a qualifier and a data type, I suspect that
|
||||||
|
* convention 1 allows specifying e.g. 'volatile signed char*'. In
|
||||||
|
* convention 2 this would be 'CC' which is ambigious (i.e. Is it two
|
||||||
|
* pointers, or a single pointer + modifier?). In convention 1 it
|
||||||
|
* is encoded as '?CC' which is not ambigious. This probably
|
||||||
|
* holds true for some other types as well.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_constraints_convention_1
|
||||||
|
*
|
||||||
|
* Get type constraint information for a data type
|
||||||
|
*/
|
||||||
|
static char *get_constraints_convention_1 (char **str, compound_type *ct)
|
||||||
|
{
|
||||||
|
char *iter = *str, **retval = str;
|
||||||
|
|
||||||
|
if (ct->have_qualifiers)
|
||||||
|
return (char *)*str; /* Previously got constraints for this type */
|
||||||
|
|
||||||
|
if (*iter == '?' || *iter == 'A')
|
||||||
|
{
|
||||||
|
ct->have_qualifiers = 1;
|
||||||
|
ct->flags |= (*iter++ == '?' ? 0 : CT_BY_REFERENCE);
|
||||||
|
|
||||||
|
switch (*iter++)
|
||||||
|
{
|
||||||
|
case 'A' :
|
||||||
|
break; /* non-const, non-volatile */
|
||||||
|
case 'B' :
|
||||||
|
ct->flags |= CT_CONST;
|
||||||
|
break;
|
||||||
|
case 'C' :
|
||||||
|
ct->flags |= CT_VOLATILE;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char *)(*retval = iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_constraints_convention_2
|
||||||
|
*
|
||||||
|
* Get type constraint information for a data type
|
||||||
|
*/
|
||||||
|
static char *get_constraints_convention_2 (char **str, compound_type *ct)
|
||||||
|
{
|
||||||
|
char *iter = *str, **retval = str;
|
||||||
|
|
||||||
|
/* FIXME: Why do arrays have both convention 1 & 2 constraints? */
|
||||||
|
if (ct->have_qualifiers && ct->dest_type != 'Q')
|
||||||
|
return (char *)*str; /* Previously got constraints for this type */
|
||||||
|
|
||||||
|
ct->have_qualifiers = 1; /* Even if none, we've got all we're getting */
|
||||||
|
|
||||||
|
switch (*iter)
|
||||||
|
{
|
||||||
|
case 'A' :
|
||||||
|
if (iter[1] != 'A' && iter[1] != 'B' && iter[1] != 'C')
|
||||||
|
iter++;
|
||||||
|
break;
|
||||||
|
case 'B' :
|
||||||
|
ct->flags |= CT_CONST;
|
||||||
|
iter++;
|
||||||
|
break;
|
||||||
|
case 'C' :
|
||||||
|
/* See note above, if we find 'C' it is _not_ a signed char */
|
||||||
|
ct->flags |= CT_VOLATILE;
|
||||||
|
iter++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char *)(*retval = iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_type_string
|
||||||
|
*
|
||||||
|
* Return a string containing the name of a data type
|
||||||
|
*/
|
||||||
|
static char *get_type_string (const char c, const int constraints)
|
||||||
|
{
|
||||||
|
char *type_string;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'C': /* Signed char, fall through */
|
||||||
|
case 'D': type_string = "char"; break;
|
||||||
|
case 'E': type_string = "unsigned char"; break;
|
||||||
|
case 'F': type_string = "short int"; break;
|
||||||
|
case 'G': type_string = "unsigned short int"; break;
|
||||||
|
case 'H': type_string = "int"; break;
|
||||||
|
case 'I': type_string = "unsigned int"; break;
|
||||||
|
case 'J': type_string = "long"; break;
|
||||||
|
case 'K': type_string = "unsigned long"; break;
|
||||||
|
case 'M': type_string = "float"; break;
|
||||||
|
case 'N': type_string = "double"; break;
|
||||||
|
case 'O': type_string = "long double"; break;
|
||||||
|
case 'U':
|
||||||
|
case 'V': type_string = "struct"; break;
|
||||||
|
case 'X': return strdup ("void");
|
||||||
|
case 'Z': return strdup ("...");
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_create (3, constraints & CT_CONST ? "const " :
|
||||||
|
constraints & CT_VOLATILE ? "volatile " : "", type_string,
|
||||||
|
constraints & CT_BY_REFERENCE ? " *" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_type_constant
|
||||||
|
*
|
||||||
|
* Get the ARG_* constant for this data type
|
||||||
|
*/
|
||||||
|
static int get_type_constant (const char c, const int constraints)
|
||||||
|
{
|
||||||
|
/* Any reference type is really a pointer */
|
||||||
|
if (constraints & CT_BY_REFERENCE)
|
||||||
|
return ARG_POINTER;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
|
||||||
|
case 'J': case 'K':
|
||||||
|
return ARG_LONG;
|
||||||
|
case 'M':
|
||||||
|
return -1; /* FIXME */
|
||||||
|
case 'N': case 'O':
|
||||||
|
return ARG_DOUBLE;
|
||||||
|
case 'P': case 'Q':
|
||||||
|
return ARG_POINTER;
|
||||||
|
case 'U': case 'V':
|
||||||
|
return ARG_STRUCT;
|
||||||
|
case 'X':
|
||||||
|
return ARG_VOID;
|
||||||
|
case 'Z':
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_pointer_type_string
|
||||||
|
*
|
||||||
|
* Return a string containing 'pointer to expression'
|
||||||
|
*/
|
||||||
|
static char *get_pointer_type_string (compound_type *ct,
|
||||||
|
const char *expression)
|
||||||
|
{
|
||||||
|
/* FIXME: set a compound flag for bracketing expression if needed */
|
||||||
|
return str_create (3, ct->flags & CT_CONST ? "const " :
|
||||||
|
ct->flags & CT_VOLATILE ? "volatile " : "", expression,
|
||||||
|
ct->flags & CT_BY_REFERENCE ? " **" : " *");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,548 @@
|
||||||
|
/*
|
||||||
|
* Code generation functions
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
/* Output files */
|
||||||
|
static FILE *specfile = NULL;
|
||||||
|
static FILE *hfile = NULL;
|
||||||
|
static FILE *cfile = NULL;
|
||||||
|
|
||||||
|
static void output_spec_postamble (void);
|
||||||
|
static void output_header_postamble (void);
|
||||||
|
static void output_c_postamble (void);
|
||||||
|
static void output_prototype (FILE *file, const parsed_symbol *sym);
|
||||||
|
static void output_c_banner (const parsed_symbol *sym);
|
||||||
|
static const char *get_format_str (int type);
|
||||||
|
static const char *get_in_or_out (const parsed_symbol *sym, size_t arg);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_spec_preamble
|
||||||
|
*
|
||||||
|
* Write the first part of the .spec file
|
||||||
|
*/
|
||||||
|
void output_spec_preamble (void)
|
||||||
|
{
|
||||||
|
specfile = open_file (OUTPUT_DLL_NAME, ".spec", "w");
|
||||||
|
|
||||||
|
atexit (output_spec_postamble);
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Creating .spec preamble");
|
||||||
|
|
||||||
|
fprintf (specfile,
|
||||||
|
"# Generated from %s.dll by specmaker\nname %s\n"
|
||||||
|
"type win32\ninit %s_Init\n\nimport kernel32.dll\n"
|
||||||
|
"import ntdll.dll\n", globals.input_name, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_UC_DLL_NAME);
|
||||||
|
|
||||||
|
if (globals.forward_dll)
|
||||||
|
fprintf (specfile,"#import %s.dll\n", globals.forward_dll);
|
||||||
|
|
||||||
|
fprintf (specfile, "\n\ndebug_channels (%s)\n\n", OUTPUT_DLL_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_spec_symbol
|
||||||
|
*
|
||||||
|
* Write a symbol to the .spec file
|
||||||
|
*/
|
||||||
|
void output_spec_symbol (const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
assert (specfile);
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
|
||||||
|
if (!globals.do_code || !sym->function_name)
|
||||||
|
{
|
||||||
|
if (globals.forward_dll)
|
||||||
|
fprintf (specfile, "@ forward %s %s.%s\n", sym->symbol,
|
||||||
|
globals.forward_dll, sym->symbol);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!symbol_is_valid_c (sym))
|
||||||
|
fputc ('#', specfile);
|
||||||
|
fprintf (specfile, "@ stub %s\n", sym->symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
fprintf (specfile, "@ %s %s(", sym->varargs ? "varargs" :
|
||||||
|
symbol_is_cdecl (sym) ? "cdecl" : "stdcall", sym->symbol);
|
||||||
|
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (specfile, " %s", symbol_get_spec_type(sym, i));
|
||||||
|
|
||||||
|
if (sym->argc)
|
||||||
|
fputc (' ', specfile);
|
||||||
|
fprintf (specfile, ") %s_%s\n", OUTPUT_UC_DLL_NAME, sym->function_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_spec_postamble
|
||||||
|
*
|
||||||
|
* Write the last part of the .spec file
|
||||||
|
*/
|
||||||
|
static void output_spec_postamble (void)
|
||||||
|
{
|
||||||
|
if (specfile)
|
||||||
|
fclose (specfile);
|
||||||
|
specfile = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_header_preamble
|
||||||
|
*
|
||||||
|
* Write the first part of the .h file
|
||||||
|
*/
|
||||||
|
void output_header_preamble (void)
|
||||||
|
{
|
||||||
|
hfile = open_file (OUTPUT_DLL_NAME, "_dll.h", "w");
|
||||||
|
|
||||||
|
atexit (output_header_postamble);
|
||||||
|
|
||||||
|
fprintf (hfile,
|
||||||
|
"/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
|
||||||
|
" * DO NOT SEND GENERATED DLLS FOR INCLUSION INTO WINE !\n * \n */"
|
||||||
|
"\n#ifndef __WINE_%s_DLL_H\n#define __WINE_%s_DLL_H\n\n#include "
|
||||||
|
"\"config.h\"\n#include \"windef.h\"\n#include \"debugtools.h\"\n"
|
||||||
|
"#include \"winbase.h\"\n#include \"winnt.h\"\n\n\n",
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_UC_DLL_NAME,
|
||||||
|
OUTPUT_UC_DLL_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_header_symbol
|
||||||
|
*
|
||||||
|
* Write a symbol to the .h file
|
||||||
|
*/
|
||||||
|
void output_header_symbol (const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
assert (hfile);
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
|
||||||
|
if (!globals.do_code)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sym->function_name)
|
||||||
|
fprintf (hfile, "/* %s %s_%s(); */\n", CALLING_CONVENTION,
|
||||||
|
OUTPUT_UC_DLL_NAME, sym->symbol);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output_prototype (hfile, sym);
|
||||||
|
fputs (";\n", hfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_header_postamble
|
||||||
|
*
|
||||||
|
* Write the last part of the .h file
|
||||||
|
*/
|
||||||
|
static void output_header_postamble (void)
|
||||||
|
{
|
||||||
|
if (hfile)
|
||||||
|
{
|
||||||
|
fprintf (hfile, "\n\n\n#endif\t/* __WINE_%s_DLL_H */\n",
|
||||||
|
OUTPUT_UC_DLL_NAME);
|
||||||
|
fclose (hfile);
|
||||||
|
hfile = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_c_preamble
|
||||||
|
*
|
||||||
|
* Write the first part of the .c file
|
||||||
|
*/
|
||||||
|
void output_c_preamble (void)
|
||||||
|
{
|
||||||
|
cfile = open_file (OUTPUT_DLL_NAME, "_main.c", "w");
|
||||||
|
|
||||||
|
atexit (output_c_postamble);
|
||||||
|
|
||||||
|
fprintf (cfile,
|
||||||
|
"/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
|
||||||
|
" * DO NOT SUBMIT GENERATED DLLS FOR INCLUSION INTO WINE!\n * \n */"
|
||||||
|
"\n\n#include \"%s_dll.h\"\n\nDEFAULT_DEBUG_CHANNEL(%s);\n\n",
|
||||||
|
OUTPUT_DLL_NAME, globals.input_name, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME);
|
||||||
|
|
||||||
|
if (globals.forward_dll)
|
||||||
|
{
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Creating a forwarding DLL");
|
||||||
|
|
||||||
|
fputs ("\nHMODULE hDLL=0;\t/* DLL to call */\n\n\n", cfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (cfile,
|
||||||
|
"BOOL WINAPI %s_Init(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID "
|
||||||
|
"lpvReserved)\n{\n\tTRACE(\"(0x%%08x, %%ld, %%p)\\n\",hinstDLL,"
|
||||||
|
"fdwReason,lpvReserved);\n\n\t"
|
||||||
|
"if (fdwReason == DLL_PROCESS_ATTACH)\n\t{\n\t\t",
|
||||||
|
OUTPUT_UC_DLL_NAME);
|
||||||
|
|
||||||
|
if (globals.forward_dll)
|
||||||
|
{
|
||||||
|
fprintf (cfile,
|
||||||
|
"hDLL = LoadLibraryA( \"%s\" );\n\t\t"
|
||||||
|
"TRACE(\":Forwarding DLL (%s) loaded (%%ld)\\n\",(LONG)hDLL);",
|
||||||
|
globals.forward_dll, globals.forward_dll);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs ("/* FIXME: Initialisation */", cfile);
|
||||||
|
|
||||||
|
fputs ("\n\t}\n\telse if (fdwReason == DLL_PROCESS_DETACH)\n\t{\n\t\t",
|
||||||
|
cfile);
|
||||||
|
|
||||||
|
if (globals.forward_dll)
|
||||||
|
{
|
||||||
|
fprintf (cfile,
|
||||||
|
"FreeLibrary( hDLL );\n\t\tTRACE(\":Forwarding DLL (%s)"
|
||||||
|
" freed\\n\");", globals.forward_dll);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs ("/* FIXME: Cleanup */", cfile);
|
||||||
|
|
||||||
|
fputs ("\n\t}\n\n\treturn TRUE;\n}\n\n\n", cfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_c_symbol
|
||||||
|
*
|
||||||
|
* Write a symbol to the .c file
|
||||||
|
*/
|
||||||
|
void output_c_symbol (const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int is_void;
|
||||||
|
|
||||||
|
assert (cfile);
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
|
||||||
|
if (!globals.do_code)
|
||||||
|
return;
|
||||||
|
|
||||||
|
output_c_banner(sym);
|
||||||
|
|
||||||
|
if (!sym->function_name)
|
||||||
|
{
|
||||||
|
/* #ifdef'd dummy */
|
||||||
|
fprintf (cfile, "#if 0\n%s %s_%s()\n{\n\t%s in .spec */\n}\n#endif\n\n\n",
|
||||||
|
CALLING_CONVENTION, OUTPUT_UC_DLL_NAME, sym->symbol,
|
||||||
|
globals.forward_dll ? "/* @forward" : "/* @stub");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_void = !strcmp (sym->return_text, "void");
|
||||||
|
|
||||||
|
output_prototype (cfile, sym);
|
||||||
|
fputs ("\n{\n", cfile);
|
||||||
|
|
||||||
|
if (!globals.do_trace)
|
||||||
|
{
|
||||||
|
fputs ("\tFIXME(\":stub\\n\");\n", cfile);
|
||||||
|
if (!is_void)
|
||||||
|
fprintf (cfile, "\treturn (%s) 0;\n", sym->return_text);
|
||||||
|
fputs ("}\n\n\n", cfile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tracing, maybe forwarding as well */
|
||||||
|
if (globals.forward_dll)
|
||||||
|
{
|
||||||
|
/* Write variables for calling */
|
||||||
|
fprintf (cfile, "\t%s (%s *pFunc)(", sym->return_text,
|
||||||
|
sym->calling_convention);
|
||||||
|
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (cfile, "%s%s", i ? ", " : "", sym->arg_text [i]);
|
||||||
|
|
||||||
|
fprintf (cfile, "%s)=(void*)GetProcAddress(hDLL,\"%s\");\n%s",
|
||||||
|
sym->varargs ? ",..." : sym->argc ? "" : "void", sym->symbol,
|
||||||
|
sym->varargs ? "\tva_list valist;\n" : "");
|
||||||
|
|
||||||
|
if (!is_void)
|
||||||
|
fprintf (cfile, "\t%s retVal;\n", sym->return_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TRACE input arguments */
|
||||||
|
fprintf (cfile, "\tTRACE(\"(%s", !sym->argc ? "void" : "");
|
||||||
|
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (cfile, "%s(%s)%s", i ? "," : "", sym->arg_text [i],
|
||||||
|
get_format_str (sym->arg_type [i]));
|
||||||
|
|
||||||
|
fprintf (cfile, "%s): %s\\n\"", sym->varargs ? ",..." : "",
|
||||||
|
globals.forward_dll ? "forward" : "stub");
|
||||||
|
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
if (sym->arg_type[i] != ARG_STRUCT)
|
||||||
|
fprintf(cfile, ",%s%s%s%s", sym->arg_type[i] == ARG_LONG ? "(LONG)" : "",
|
||||||
|
sym->arg_type[i] == ARG_WIDE_STRING ? "debugstr_w(" : "",
|
||||||
|
sym->arg_name[i],
|
||||||
|
sym->arg_type[i] == ARG_WIDE_STRING ? ")" : "");
|
||||||
|
|
||||||
|
fputs (");\n", cfile);
|
||||||
|
|
||||||
|
if (!globals.forward_dll)
|
||||||
|
{
|
||||||
|
if (!is_void)
|
||||||
|
fprintf (cfile, "\treturn (%s) 0;\n", sym->return_text);
|
||||||
|
fputs ("}\n\n\n", cfile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call the DLL */
|
||||||
|
if (sym->varargs)
|
||||||
|
fprintf (cfile, "\tva_start(valist,%s);\n", sym->arg_name[sym->argc-1]);
|
||||||
|
|
||||||
|
fprintf (cfile, "\t%spFunc(", !is_void ? "retVal = " : "");
|
||||||
|
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (cfile, "%s%s", i ? "," : "", sym->arg_name [i]);
|
||||||
|
|
||||||
|
fputs (sym->varargs ? ",valist);\n\tva_end(valist);" : ");", cfile);
|
||||||
|
|
||||||
|
/* TRACE return value */
|
||||||
|
fprintf (cfile, "\n\tTRACE(\"Returned (%s)\\n\"",
|
||||||
|
get_format_str (sym->return_type));
|
||||||
|
|
||||||
|
if (!is_void)
|
||||||
|
{
|
||||||
|
if (sym->return_type == ARG_WIDE_STRING)
|
||||||
|
fputs (",debugstr_w(retVal)", cfile);
|
||||||
|
else
|
||||||
|
fprintf (cfile, ",%s%s", sym->return_type == ARG_LONG ? "(LONG)" : "",
|
||||||
|
sym->return_type == ARG_STRUCT ? "" : "retVal");
|
||||||
|
fputs (");\n\treturn retVal;\n", cfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs (");\n", cfile);
|
||||||
|
|
||||||
|
fputs ("}\n\n\n", cfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_c_postamble
|
||||||
|
*
|
||||||
|
* Write the last part of the .c file
|
||||||
|
*/
|
||||||
|
static void output_c_postamble (void)
|
||||||
|
{
|
||||||
|
if (cfile)
|
||||||
|
fclose (cfile);
|
||||||
|
cfile = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_makefile
|
||||||
|
*
|
||||||
|
* Write a Wine compatable makefile.in
|
||||||
|
*/
|
||||||
|
void output_makefile (void)
|
||||||
|
{
|
||||||
|
FILE *makefile = open_file ("Makefile", ".in", "w");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Creating makefile");
|
||||||
|
|
||||||
|
fprintf (makefile,
|
||||||
|
"# Generated from %s.dll by specmaker.\nTOPSRCDIR = @top_srcdir@\n"
|
||||||
|
"TOPOBJDIR = ../..\nSRCDIR = @srcdir@\nVPATH = @srcdir@\n"
|
||||||
|
"MODULE = %s\nEXTRALIBS = $(LIBUNICODE)\n\n"
|
||||||
|
"LDDLLFLAGS = @LDDLLFLAGS@\nSYMBOLFILE = $(MODULE).tmp.o\n\n"
|
||||||
|
"C_SRCS = \\\n\t%s_main.c\n\n@MAKE_DLL_RULES@\n\n### Dependencies:",
|
||||||
|
globals.input_name, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME);
|
||||||
|
|
||||||
|
fclose (makefile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_install_script
|
||||||
|
*
|
||||||
|
* Write a script to insert the DLL into Wine
|
||||||
|
*
|
||||||
|
* Rather than using diff/patch, several sed calls are generated
|
||||||
|
* so the script can be re-run at any time without breaking.
|
||||||
|
*/
|
||||||
|
void output_install_script (void)
|
||||||
|
{
|
||||||
|
char cmd[128];
|
||||||
|
FILE *install_file = open_file (OUTPUT_DLL_NAME, "_install", "w");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Creating install script");
|
||||||
|
|
||||||
|
fprintf (install_file,
|
||||||
|
"#!/bin/bash\n# Generated from %s.dll by specmaker.\n\n"
|
||||||
|
"if [ $# -ne 1 ] || [ ! -d $1 ] || [ ! -f"
|
||||||
|
" $1/AUTHORS ]; then\n\t[ $# -eq 1 ] && echo \"Invalid path\"\n"
|
||||||
|
"\techo \"Usage: $0 wine-base-dir\"\n\texit 1\nfi\n\n"
|
||||||
|
"if [ -d $1/dlls/%s ]; then\n\techo \"DLL is already present\"\n"
|
||||||
|
"\texit 1\nfi\n\necho Adding DLL %s to Wine build tree...\n"
|
||||||
|
"echo\n\nmkdir $1/dlls/%s\ncp %s.spec $1/dlls/%s\n"
|
||||||
|
"cp %s_main.c $1/dlls/%s\ncp %s_dll.h $1/dlls/%s\n"
|
||||||
|
"cp Makefile.in $1/dlls/%s\necho Copied DLL files\n\n"
|
||||||
|
"cd $1\n\nsed '/dlls\\/"
|
||||||
|
"x11drv\\/Makefile/{G;s/$/dlls\\/%s\\/Makefile/;}' configure.in"
|
||||||
|
" >t.tmp\nmv -f t.tmp configure.in\necho Patched configure.in\n\n"
|
||||||
|
"sed '/ws2_32/{G;s/$/\\^%s \\\\/;}' Make.rules.in | tr ^ \\\\t"
|
||||||
|
" >t.tmp\nmv -f t.tmp Make.rules.in\necho Patched Make.rules.in"
|
||||||
|
"\n\nsed '/DLLFILES =/{G;s/$/\\^%s\\/lib%s.so \\\\/;}'"
|
||||||
|
" dlls/Makefile.in| tr ^ \\\\t >t.tmp\n"
|
||||||
|
"sed '/SUBDIRS =/{G;s/$/\\^%s \\\\/;}' t.tmp | tr ^ \\\\t >t.tmp2"
|
||||||
|
"\nsed '/Map library name /{G;s/$/^\\$(RM) \\$\\@ \\&\\& \\$\\"
|
||||||
|
"(LN_S\\) %s\\/lib%s.\\@LIBEXT\\@ \\$\\@/;}' t.tmp2 | tr ^ \\\\t"
|
||||||
|
" > t.tmp\nsed '/Map library name /{G;s/$/lib%s.\\@LIBEXT\\@: "
|
||||||
|
"%s\\/lib%s.\\@LIBEXT\\@/;}' t.tmp > t.tmp2\nsed '/dll "
|
||||||
|
"dependencies /{G;s/$/%s\\/lib%s.\\@LIBEXT\\@\\: libkernel32."
|
||||||
|
"\\@LIBEXT\\@ libntdll.\\@LIBEXT\\@/;}' t.tmp2 > t.tmp\n"
|
||||||
|
"mv -f t.tmp dlls/Makefile.in\nrm -f t.tmp2\necho Patched dlls/"
|
||||||
|
"Makefile.in\n\necho\necho ...done.\necho Run \\'autoconf\\', "
|
||||||
|
"\\'./configure\\' then \\'make\\' to rebuild Wine\n\n",
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
|
||||||
|
OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME);
|
||||||
|
|
||||||
|
fclose (install_file);
|
||||||
|
snprintf (cmd, sizeof (cmd), "chmod a+x %s_install", OUTPUT_DLL_NAME);
|
||||||
|
system (cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_prototype
|
||||||
|
*
|
||||||
|
* Write a C prototype for a parsed symbol
|
||||||
|
*/
|
||||||
|
static void output_prototype (FILE *file, const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
fprintf (file, "%s %s %s_%s(", sym->return_text, sym->calling_convention,
|
||||||
|
OUTPUT_UC_DLL_NAME, sym->function_name);
|
||||||
|
|
||||||
|
if (!sym->argc)
|
||||||
|
fputs ("void", file);
|
||||||
|
else
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (file, "%s%s %s", i ? ", " : "", sym->arg_text [i],
|
||||||
|
sym->arg_name [i]);
|
||||||
|
if (sym->varargs)
|
||||||
|
fputs (", ...", file);
|
||||||
|
fputc (')', file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* output_c_banner
|
||||||
|
*
|
||||||
|
* Write a function banner to the .c file
|
||||||
|
*/
|
||||||
|
void output_c_banner (const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
fprintf (cfile, "/*********************************************************"
|
||||||
|
"*********\n *\t\t%s (%s.@)\n *\n", sym->symbol,
|
||||||
|
OUTPUT_UC_DLL_NAME);
|
||||||
|
|
||||||
|
if (globals.do_documentation && sym->function_name)
|
||||||
|
{
|
||||||
|
fputs (" *\n * PARAMS\n *\n", cfile);
|
||||||
|
|
||||||
|
if (!sym->argc)
|
||||||
|
fputs (" * None.\n *\n", cfile);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < sym->argc; i++)
|
||||||
|
fprintf (cfile, " * %s [%s]%s\n", sym->arg_name [i],
|
||||||
|
get_in_or_out(sym, i),
|
||||||
|
strcmp (sym->arg_name [i], "_this") ? "" :
|
||||||
|
" Pointer to the class object");
|
||||||
|
|
||||||
|
if (sym->varargs)
|
||||||
|
fputs (" * ...[I]\n", cfile);
|
||||||
|
fputs (" *\n", cfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs (" * RETURNS\n *\n", cfile);
|
||||||
|
|
||||||
|
if (sym->return_text && !strcmp (sym->return_text, "void"))
|
||||||
|
fputs (" * Nothing.\n", cfile);
|
||||||
|
else
|
||||||
|
fprintf (cfile, " * %s\n", sym->return_text);
|
||||||
|
}
|
||||||
|
fputs (" *\n */\n", cfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_format_str
|
||||||
|
*
|
||||||
|
* Get a string containing the correct format string for a type
|
||||||
|
*/
|
||||||
|
static const char *get_format_str (int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ARG_VOID: return "void";
|
||||||
|
case ARG_FLOAT: return "%f";
|
||||||
|
case ARG_DOUBLE: return "%g";
|
||||||
|
case ARG_POINTER: return "%p";
|
||||||
|
case ARG_WIDE_STRING:
|
||||||
|
case ARG_STRING: return "%s";
|
||||||
|
case ARG_LONG: return "%ld";
|
||||||
|
case ARG_STRUCT: return "struct";
|
||||||
|
}
|
||||||
|
assert (0);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_in_or_out
|
||||||
|
*
|
||||||
|
* Determin if a parameter is In or In/Out
|
||||||
|
*/
|
||||||
|
static const char *get_in_or_out (const parsed_symbol *sym, size_t arg)
|
||||||
|
{
|
||||||
|
assert (sym && arg < sym->argc);
|
||||||
|
assert (globals.do_documentation);
|
||||||
|
|
||||||
|
if (sym->arg_flag [arg] & CT_CONST)
|
||||||
|
return "In";
|
||||||
|
|
||||||
|
switch (sym->arg_type [arg])
|
||||||
|
{
|
||||||
|
case ARG_FLOAT:
|
||||||
|
case ARG_DOUBLE:
|
||||||
|
case ARG_LONG:
|
||||||
|
case ARG_STRUCT: return "In";
|
||||||
|
case ARG_POINTER:
|
||||||
|
case ARG_WIDE_STRING:
|
||||||
|
case ARG_STRING: return "In/Out";
|
||||||
|
}
|
||||||
|
assert (0);
|
||||||
|
return "";
|
||||||
|
}
|
|
@ -0,0 +1,318 @@
|
||||||
|
/*
|
||||||
|
* Prototype search and parsing functions
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
static char *grep_buff = NULL;
|
||||||
|
static char *fgrep_buff = NULL;
|
||||||
|
|
||||||
|
static int symbol_from_prototype (parsed_symbol *sym, const char *prototype);
|
||||||
|
static const char *get_type (parsed_symbol *sym, const char *proto, int arg);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_search
|
||||||
|
*
|
||||||
|
* Call Patrik Stridvall's 'function_grep.pl' script to retrieve a
|
||||||
|
* function prototype from include file(s)
|
||||||
|
*/
|
||||||
|
int symbol_search (parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
static const size_t MAX_RESULT_LEN = 1024;
|
||||||
|
FILE *grep;
|
||||||
|
int attempt = 0;
|
||||||
|
|
||||||
|
assert (globals.do_code);
|
||||||
|
assert (globals.directory);
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
|
||||||
|
if (!symbol_is_valid_c (sym))
|
||||||
|
return - 1;
|
||||||
|
|
||||||
|
if (!grep_buff)
|
||||||
|
grep_buff = (char *) malloc (MAX_RESULT_LEN);
|
||||||
|
|
||||||
|
if (!fgrep_buff)
|
||||||
|
fgrep_buff = (char *) malloc (MAX_RESULT_LEN);
|
||||||
|
|
||||||
|
if (!grep_buff || !fgrep_buff)
|
||||||
|
fatal ("Out of Memory");
|
||||||
|
|
||||||
|
/* Use 'grep' to tell us which possible files the function is in,
|
||||||
|
* then use 'function_grep.pl' to get the prototype. If this fails the
|
||||||
|
* first time then give grep a more general query (that doesn't
|
||||||
|
* require an opening argument brace on the line with the function name).
|
||||||
|
*/
|
||||||
|
while (attempt < 2)
|
||||||
|
{
|
||||||
|
FILE *f_grep;
|
||||||
|
char *cmd = str_create (4, "grep -d recurse -l \"", sym->symbol,
|
||||||
|
!attempt ? "[:blank:]*(\" " : "\" ", globals.directory);
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts (cmd);
|
||||||
|
|
||||||
|
fflush (NULL); /* See 'man popen' */
|
||||||
|
|
||||||
|
if (!(grep = popen (cmd, "r")))
|
||||||
|
fatal ("Cannot execute grep -l");
|
||||||
|
free (cmd);
|
||||||
|
|
||||||
|
while (fgets (grep_buff, MAX_RESULT_LEN, grep))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
|
||||||
|
;
|
||||||
|
grep_buff[i] = '\0';
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts (grep_buff);
|
||||||
|
|
||||||
|
cmd = str_create (5, "function_grep.pl ", sym->symbol,
|
||||||
|
" \"", grep_buff, "\"");
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts (cmd);
|
||||||
|
|
||||||
|
fflush (NULL); /* See 'man popen' */
|
||||||
|
|
||||||
|
if (!(f_grep = popen (cmd, "r")))
|
||||||
|
fatal ("Cannot execute function_grep.pl");
|
||||||
|
free (cmd);
|
||||||
|
|
||||||
|
while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
|
||||||
|
{
|
||||||
|
char *iter = grep_buff;
|
||||||
|
|
||||||
|
/* Keep only the first line */
|
||||||
|
symbol_clean_string(grep_buff);
|
||||||
|
|
||||||
|
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
|
||||||
|
;
|
||||||
|
grep_buff[i] = '\0';
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
puts (grep_buff);
|
||||||
|
|
||||||
|
while ((iter = strstr (iter, sym->symbol)))
|
||||||
|
{
|
||||||
|
if (iter > grep_buff && iter[-1] == ' ' &&
|
||||||
|
(iter[strlen (sym->symbol)] == ' ' ||
|
||||||
|
iter[strlen (sym->symbol)] == '('))
|
||||||
|
{
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Prototype looks OK, processing");
|
||||||
|
|
||||||
|
if (!symbol_from_prototype (sym, grep_buff))
|
||||||
|
{
|
||||||
|
pclose (f_grep);
|
||||||
|
pclose (grep);
|
||||||
|
return 0; /* OK */
|
||||||
|
}
|
||||||
|
if (VERBOSE)
|
||||||
|
puts ("Failed, trying next");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
iter += strlen (sym->symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pclose (f_grep);
|
||||||
|
}
|
||||||
|
pclose (grep);
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1; /* Not found */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_from_prototype
|
||||||
|
*
|
||||||
|
* Convert a C prototype into a symbol
|
||||||
|
*/
|
||||||
|
static int symbol_from_prototype (parsed_symbol *sym, const char *proto)
|
||||||
|
{
|
||||||
|
char *iter;
|
||||||
|
int found;
|
||||||
|
|
||||||
|
proto = get_type (sym, proto, -1); /* Get return type */
|
||||||
|
if (!proto)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
iter = (char *)str_match (proto, sym->symbol, &found);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
/* Calling Convention */
|
||||||
|
iter = strchr (iter, ' ');
|
||||||
|
if (!iter)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sym->calling_convention = str_substring (proto, iter);
|
||||||
|
|
||||||
|
iter = (char *)str_match (iter, sym->symbol, &found);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sym->calling_convention = strdup (CALLING_CONVENTION);
|
||||||
|
|
||||||
|
sym->function_name = strdup (sym->symbol);
|
||||||
|
proto = iter;
|
||||||
|
|
||||||
|
/* Now should be the arguments */
|
||||||
|
if (*proto++ != '(')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (; *proto == ' '; proto++);
|
||||||
|
|
||||||
|
if (!strncmp (proto, "void", 4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Process next argument */
|
||||||
|
str_match (proto, "...", &sym->varargs);
|
||||||
|
if (sym->varargs)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!(proto = get_type (sym, proto, sym->argc)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sym->argc++;
|
||||||
|
|
||||||
|
if (*proto == ',')
|
||||||
|
proto++;
|
||||||
|
else if (*proto != ')')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
} while (*proto != ')');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* get_type
|
||||||
|
*
|
||||||
|
* Read a type from a prototype
|
||||||
|
*/
|
||||||
|
static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
|
||||||
|
{
|
||||||
|
int is_const, is_volatile, is_struct, is_signed, is_unsigned, ptrs = 0;
|
||||||
|
char *iter, *type_str, *base_type, *catch_unsigned, dest_type;
|
||||||
|
|
||||||
|
assert (sym && sym->symbol);
|
||||||
|
assert (proto && *proto);
|
||||||
|
assert (arg < 0 || (unsigned)arg == sym->argc);
|
||||||
|
|
||||||
|
type_str = (char *)proto;
|
||||||
|
|
||||||
|
proto = str_match (proto, "const", &is_const);
|
||||||
|
proto = str_match (proto, "volatile", &is_volatile);
|
||||||
|
proto = str_match (proto, "struct", &is_struct);
|
||||||
|
if (!is_struct)
|
||||||
|
proto = str_match (proto, "union", &is_struct);
|
||||||
|
|
||||||
|
catch_unsigned = (char *)proto;
|
||||||
|
|
||||||
|
proto = str_match (proto, "unsigned", &is_unsigned);
|
||||||
|
proto = str_match (proto, "signed", &is_signed);
|
||||||
|
|
||||||
|
/* Can have 'unsigned const' or 'const unsigned' etc */
|
||||||
|
if (!is_const)
|
||||||
|
proto = str_match (proto, "const", &is_const);
|
||||||
|
if (!is_volatile)
|
||||||
|
proto = str_match (proto, "volatile", &is_volatile);
|
||||||
|
|
||||||
|
base_type = (char *)proto;
|
||||||
|
iter = (char *)str_find_set (proto, " ,*)");
|
||||||
|
if (!iter)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (arg < 0 && (is_signed || is_unsigned))
|
||||||
|
{
|
||||||
|
/* Prevent calling convention from being swallowed by 'un/signed' alone */
|
||||||
|
if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
|
||||||
|
strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
|
||||||
|
{
|
||||||
|
iter = (char *)proto;
|
||||||
|
base_type = catch_unsigned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
catch_unsigned = NULL;
|
||||||
|
|
||||||
|
/* FIXME: skip const/volatile here too */
|
||||||
|
for (proto = iter; *proto; proto++)
|
||||||
|
if (*proto == '*')
|
||||||
|
ptrs++;
|
||||||
|
else if (*proto != ' ')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!*proto)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
type_str = str_substring (type_str, proto);
|
||||||
|
if (iter == base_type || catch_unsigned)
|
||||||
|
{
|
||||||
|
/* 'unsigned' with no type */
|
||||||
|
char *tmp = str_create (2, type_str, " int");
|
||||||
|
free (type_str);
|
||||||
|
type_str = tmp;
|
||||||
|
}
|
||||||
|
symbol_clean_string (type_str);
|
||||||
|
|
||||||
|
dest_type = symbol_get_type (type_str);
|
||||||
|
|
||||||
|
if (arg < 0)
|
||||||
|
{
|
||||||
|
sym->return_text = type_str;
|
||||||
|
sym->return_type = dest_type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sym->arg_type [arg] = dest_type;
|
||||||
|
sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
|
||||||
|
|
||||||
|
if (*proto == ',' || *proto == ')')
|
||||||
|
sym->arg_name [arg] = str_create_num (1, arg, "arg");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter = (char *)str_find_set (proto, " ,)");
|
||||||
|
if (!iter)
|
||||||
|
{
|
||||||
|
free (type_str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sym->arg_name [arg] = str_substring (proto, iter);
|
||||||
|
proto = iter;
|
||||||
|
}
|
||||||
|
sym->arg_text [arg] = type_str;
|
||||||
|
|
||||||
|
}
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
/*******************************************************************
|
||||||
|
* search_cleanup
|
||||||
|
*
|
||||||
|
* Free memory used while searching (a niceity)
|
||||||
|
*/
|
||||||
|
void search_cleanup (void) __attribute__ ((destructor));
|
||||||
|
void search_cleanup (void)
|
||||||
|
{
|
||||||
|
if (grep_buff)
|
||||||
|
free (grep_buff);
|
||||||
|
|
||||||
|
if (fgrep_buff)
|
||||||
|
free (fgrep_buff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* Specmaker - A Wine DLL tool
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*
|
||||||
|
* References:
|
||||||
|
* DLL symbol extraction based on file format from alib (anthonyw.cjb.net).
|
||||||
|
*
|
||||||
|
* Option processing shamelessly cadged from winebuild (www.winehq.com).
|
||||||
|
*
|
||||||
|
* All the cool functionality (prototyping, call tracing, forwarding)
|
||||||
|
* relies on Patrik Stridvall's 'function_grep.pl' script to work.
|
||||||
|
*
|
||||||
|
* http://msdn.microsoft.com/library/periodic/period96/msj/S330.htm
|
||||||
|
* This article provides both a description and freely downloadble
|
||||||
|
* implementation, in source code form, of how to extract symbols
|
||||||
|
* from Win32 PE executables/DLL's.
|
||||||
|
*
|
||||||
|
* http://www.kegel.com/mangle.html
|
||||||
|
* Gives information on the name mangling scheme used by MS compilers,
|
||||||
|
* used as the starting point for the code here. Contains a few
|
||||||
|
* mistakes and some incorrect assumptions, but the lists of types
|
||||||
|
* are pure gold.
|
||||||
|
*/
|
||||||
|
#ifndef __WINE_SPECMAKER_H
|
||||||
|
#define __WINE_SPECMAKER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* Argument type constants */
|
||||||
|
#define MAX_FUNCTION_ARGS 32
|
||||||
|
|
||||||
|
#define ARG_VOID 0x0
|
||||||
|
#define ARG_STRING 0x1
|
||||||
|
#define ARG_WIDE_STRING 0x2
|
||||||
|
#define ARG_POINTER 0x3
|
||||||
|
#define ARG_LONG 0x4
|
||||||
|
#define ARG_DOUBLE 0x5
|
||||||
|
#define ARG_STRUCT 0x6 /* By value */
|
||||||
|
#define ARG_FLOAT 0x7
|
||||||
|
#define ARG_VARARGS 0x8
|
||||||
|
|
||||||
|
/* Compound type flags */
|
||||||
|
#define CT_BY_REFERENCE 0x1
|
||||||
|
#define CT_VOLATILE 0x2
|
||||||
|
#define CT_CONST 0x4
|
||||||
|
|
||||||
|
/* Structure holding a parsed symbol */
|
||||||
|
typedef struct __parsed_symbol
|
||||||
|
{
|
||||||
|
char *symbol;
|
||||||
|
char *return_text;
|
||||||
|
char return_type;
|
||||||
|
char *calling_convention;
|
||||||
|
char *function_name;
|
||||||
|
unsigned int varargs;
|
||||||
|
unsigned int argc;
|
||||||
|
char arg_type [MAX_FUNCTION_ARGS];
|
||||||
|
char arg_flag [MAX_FUNCTION_ARGS];
|
||||||
|
char *arg_text [MAX_FUNCTION_ARGS];
|
||||||
|
char *arg_name [MAX_FUNCTION_ARGS];
|
||||||
|
} parsed_symbol;
|
||||||
|
|
||||||
|
/* All globals */
|
||||||
|
typedef struct __globals
|
||||||
|
{
|
||||||
|
/* Options */
|
||||||
|
int do_code; /* -c, -t, -f */
|
||||||
|
int do_trace; /* -t, -f */
|
||||||
|
int do_cdecl; /* -C */
|
||||||
|
int do_quiet; /* -q */
|
||||||
|
int do_verbose; /* -v */
|
||||||
|
int do_documentation; /* -D */
|
||||||
|
|
||||||
|
/* Option arguments */
|
||||||
|
int start_ordinal; /* -s */
|
||||||
|
int end_ordinal; /* -e */
|
||||||
|
const char *directory; /* -I */
|
||||||
|
const char *input_name; /* -d */
|
||||||
|
const char *forward_dll; /* -f */
|
||||||
|
const char *dll_name; /* -o */
|
||||||
|
char *uc_dll_name; /* -o */
|
||||||
|
} _globals;
|
||||||
|
|
||||||
|
extern _globals globals;
|
||||||
|
|
||||||
|
/* Names to use for output DLL */
|
||||||
|
#define OUTPUT_DLL_NAME \
|
||||||
|
(globals.dll_name ? globals.dll_name : globals.input_name)
|
||||||
|
#define OUTPUT_UC_DLL_NAME globals.uc_dll_name
|
||||||
|
|
||||||
|
/* Verbosity levels */
|
||||||
|
#define QUIET (globals.do_quiet)
|
||||||
|
#define NORMAL (!QUIET)
|
||||||
|
#define VERBOSE (globals.do_verbose)
|
||||||
|
|
||||||
|
/* Default calling convention */
|
||||||
|
#define CALLING_CONVENTION (globals.do_cdecl ? "__cdecl" : "__stdcall")
|
||||||
|
|
||||||
|
|
||||||
|
/* DLL functions */
|
||||||
|
void dll_open (const char *dll_name);
|
||||||
|
|
||||||
|
char *dll_next_symbol (void);
|
||||||
|
|
||||||
|
/* Symbol functions */
|
||||||
|
int symbol_demangle (parsed_symbol *symbol);
|
||||||
|
|
||||||
|
int symbol_search (parsed_symbol *symbol);
|
||||||
|
|
||||||
|
void symbol_clear(parsed_symbol *sym);
|
||||||
|
|
||||||
|
int symbol_is_valid_c(const parsed_symbol *sym);
|
||||||
|
|
||||||
|
int symbol_is_cdecl(const parsed_symbol *sym);
|
||||||
|
|
||||||
|
const char *symbol_get_spec_type (const parsed_symbol *sym, size_t arg);
|
||||||
|
|
||||||
|
void symbol_clean_string (const char *string);
|
||||||
|
|
||||||
|
int symbol_get_type (const char *string);
|
||||||
|
|
||||||
|
/* Output functions */
|
||||||
|
void output_spec_preamble (void);
|
||||||
|
|
||||||
|
void output_spec_symbol (const parsed_symbol *sym);
|
||||||
|
|
||||||
|
void output_header_preamble (void);
|
||||||
|
|
||||||
|
void output_header_symbol (const parsed_symbol *sym);
|
||||||
|
|
||||||
|
void output_c_preamble (void);
|
||||||
|
|
||||||
|
void output_c_symbol (const parsed_symbol *sym);
|
||||||
|
|
||||||
|
void output_makefile (void);
|
||||||
|
|
||||||
|
void output_install_script (void);
|
||||||
|
|
||||||
|
/* Misc functions */
|
||||||
|
char *str_create (size_t num_str, ...);
|
||||||
|
|
||||||
|
char *str_create_num (size_t num_str, int num, ...);
|
||||||
|
|
||||||
|
char *str_substring(const char *start, const char *end);
|
||||||
|
|
||||||
|
char *str_replace (char *str, const char *oldstr, const char *newstr);
|
||||||
|
|
||||||
|
const char *str_match (const char *str, const char *match, int *found);
|
||||||
|
|
||||||
|
const char *str_find_set (const char *str, const char *findset);
|
||||||
|
|
||||||
|
char *str_toupper (char *str);
|
||||||
|
|
||||||
|
FILE *open_file (const char *name, const char *ext, const char *mode);
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
void do_usage (void) __attribute__ ((noreturn));
|
||||||
|
#else
|
||||||
|
void do_usage (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void fatal (const char *message);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __WINE_SPECMAKER_H */
|
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* Symbol functions
|
||||||
|
*
|
||||||
|
* Copyright 2000 Jon Griffiths
|
||||||
|
*/
|
||||||
|
#include "specmaker.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Items that are swapped in arguments after the symbol structure
|
||||||
|
* has been populated
|
||||||
|
*/
|
||||||
|
static const char *swap_after[] =
|
||||||
|
{
|
||||||
|
"\r", " ", /* Remove whitespace, normalise pointers and brackets */
|
||||||
|
"\t", " ",
|
||||||
|
" ", " ",
|
||||||
|
" * ", " *",
|
||||||
|
"* *", "**",
|
||||||
|
"* ", "*",
|
||||||
|
" ,", ",",
|
||||||
|
"( ", "(",
|
||||||
|
" )", ")",
|
||||||
|
"wchar_t", "WCHAR", /* Help with Unicode compliles */
|
||||||
|
"wctype_t", "WCHAR",
|
||||||
|
"wint_t", "WCHAR",
|
||||||
|
"unsigned __int64", "__uint64", /* Wine doesn't cope with unsigned i64's */
|
||||||
|
NULL, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Items containing these substrings are assumed to be wide character
|
||||||
|
* strings, unless they contain more that one '*'. A preceeding 'LP'
|
||||||
|
* counts as a '*', so 'LPWCSTR *' is a pointer, not a string
|
||||||
|
*/
|
||||||
|
static const char *wide_strings[] =
|
||||||
|
{
|
||||||
|
"WSTR", "WCSTR", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Items containing these substrings are assumed to be wide characters,
|
||||||
|
* unless they contain one '*'. A preceeding 'LP' counts as a '*',
|
||||||
|
* so 'WCHAR *' is string, while 'LPWCHAR *' is a pointer
|
||||||
|
*/
|
||||||
|
static const char *wide_chars[] =
|
||||||
|
{
|
||||||
|
"WCHAR", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Items containing these substrings are assumed to be ASCII character
|
||||||
|
* strings, as above
|
||||||
|
*/
|
||||||
|
static const char *ascii_strings[] =
|
||||||
|
{
|
||||||
|
"STR", "CSTR", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Items containing these substrings are assumed to be ASCII characters,
|
||||||
|
* as above
|
||||||
|
*/
|
||||||
|
static const char *ascii_chars[] =
|
||||||
|
{
|
||||||
|
"CHAR", "char", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Any type other than the following will produce a FIXME warning with -v
|
||||||
|
* when mapped to a long, to allow fixups
|
||||||
|
*/
|
||||||
|
static const char *known_longs[] =
|
||||||
|
{
|
||||||
|
"char", "CHAR", "float", "int", "INT", "short", "SHORT", "long", "LONG",
|
||||||
|
"WCHAR", "BOOL", "bool", "INT16", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_clear
|
||||||
|
*
|
||||||
|
* Free the memory used by a symbol and initialise it
|
||||||
|
*/
|
||||||
|
void symbol_clear(parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert (sym);
|
||||||
|
assert (sym->symbol);
|
||||||
|
|
||||||
|
free (sym->symbol);
|
||||||
|
|
||||||
|
if (sym->return_text)
|
||||||
|
free (sym->return_text);
|
||||||
|
|
||||||
|
if (sym->calling_convention)
|
||||||
|
free (sym->calling_convention);
|
||||||
|
|
||||||
|
if (sym->function_name)
|
||||||
|
free (sym->function_name);
|
||||||
|
|
||||||
|
for (i = sym->argc - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (sym->arg_text [i])
|
||||||
|
free (sym->arg_text [i]);
|
||||||
|
if (sym->arg_name [i])
|
||||||
|
free (sym->arg_name [i]);
|
||||||
|
}
|
||||||
|
memset (sym, 0, sizeof (parsed_symbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_is_valid_c
|
||||||
|
*
|
||||||
|
* Check if a symbol is a valid C identifier
|
||||||
|
*/
|
||||||
|
int symbol_is_valid_c(const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
assert (sym);
|
||||||
|
assert (sym->symbol);
|
||||||
|
|
||||||
|
name = sym->symbol;
|
||||||
|
|
||||||
|
while (*name)
|
||||||
|
{
|
||||||
|
if (!isalnum (*name) && *name != '_')
|
||||||
|
return 0;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_is_cdecl
|
||||||
|
*
|
||||||
|
* Check if a symbol is cdecl
|
||||||
|
*/
|
||||||
|
int symbol_is_cdecl(const parsed_symbol *sym)
|
||||||
|
{
|
||||||
|
assert (sym);
|
||||||
|
assert (sym->symbol);
|
||||||
|
|
||||||
|
if (sym->calling_convention && (strstr (sym->calling_convention, "cdecl")
|
||||||
|
|| strstr (sym->calling_convention, "CDECL")))
|
||||||
|
return 1;
|
||||||
|
else if (!sym->calling_convention)
|
||||||
|
return globals.do_cdecl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_get_spec_type
|
||||||
|
*
|
||||||
|
* Get the .spec file text for a symbols argument
|
||||||
|
*/
|
||||||
|
const char *symbol_get_spec_type (const parsed_symbol *sym, size_t arg)
|
||||||
|
{
|
||||||
|
assert (arg < sym->argc);
|
||||||
|
switch (sym->arg_type [arg])
|
||||||
|
{
|
||||||
|
case ARG_STRING: return "str";
|
||||||
|
case ARG_WIDE_STRING: return "wstr";
|
||||||
|
case ARG_POINTER: return "ptr";
|
||||||
|
case ARG_DOUBLE: return "double";
|
||||||
|
case ARG_STRUCT:
|
||||||
|
case ARG_FLOAT:
|
||||||
|
case ARG_LONG: return "long";
|
||||||
|
}
|
||||||
|
assert (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_get_type
|
||||||
|
*
|
||||||
|
* Get the ARG_ constant for a type string
|
||||||
|
*/
|
||||||
|
int symbol_get_type (const char *string)
|
||||||
|
{
|
||||||
|
const char *iter = string;
|
||||||
|
const char **tab;
|
||||||
|
int ptrs = 0;
|
||||||
|
|
||||||
|
while (*iter)
|
||||||
|
{
|
||||||
|
if (*iter == '*' || (*iter == 'L' && iter[1] == 'P')
|
||||||
|
|| (*iter == '[' && iter[1] == ']'))
|
||||||
|
ptrs++;
|
||||||
|
if (ptrs > 1)
|
||||||
|
return ARG_POINTER;
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0 or 1 pointer */
|
||||||
|
tab = wide_strings;
|
||||||
|
while (*tab++)
|
||||||
|
if (strstr (string, tab[-1]))
|
||||||
|
{
|
||||||
|
if (!ptrs) return ARG_WIDE_STRING;
|
||||||
|
else return ARG_POINTER;
|
||||||
|
}
|
||||||
|
tab = wide_chars;
|
||||||
|
while (*tab++)
|
||||||
|
if (strstr (string, tab[-1]))
|
||||||
|
{
|
||||||
|
if (!ptrs) return ARG_LONG;
|
||||||
|
else return ARG_WIDE_STRING;
|
||||||
|
}
|
||||||
|
tab = ascii_strings;
|
||||||
|
while (*tab++)
|
||||||
|
if (strstr (string, tab[-1]))
|
||||||
|
{
|
||||||
|
if (!ptrs) return ARG_STRING;
|
||||||
|
else return ARG_POINTER;
|
||||||
|
}
|
||||||
|
tab = ascii_chars;
|
||||||
|
while (*tab++)
|
||||||
|
if (strstr (string, tab[-1]))
|
||||||
|
{
|
||||||
|
if (!ptrs) return ARG_LONG;
|
||||||
|
else {
|
||||||
|
if (!strstr (string, "unsigned")) /* unsigned char * => ptr */
|
||||||
|
return ARG_STRING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ptrs)
|
||||||
|
return ARG_POINTER; /* Pointer to some other type */
|
||||||
|
|
||||||
|
/* No pointers */
|
||||||
|
if (strstr (string, "double"))
|
||||||
|
return ARG_DOUBLE;
|
||||||
|
|
||||||
|
if (strstr (string, "void"))
|
||||||
|
return ARG_VOID;
|
||||||
|
|
||||||
|
if (strstr (string, "struct") || strstr (string, "union"))
|
||||||
|
return ARG_STRUCT; /* Struct by value, ugh */
|
||||||
|
|
||||||
|
if (VERBOSE)
|
||||||
|
{
|
||||||
|
int known = 0;
|
||||||
|
|
||||||
|
tab = known_longs;
|
||||||
|
while (*tab++)
|
||||||
|
if (strstr (string, tab[-1]))
|
||||||
|
{
|
||||||
|
known = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Unknown types passed by value can be 'grep'ed out for fixup later */
|
||||||
|
if (!known)
|
||||||
|
printf ("/* FIXME: By value type: Assumed 'int' */ typedef int %s;\n",
|
||||||
|
string);
|
||||||
|
}
|
||||||
|
return ARG_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* symbol_clean_string
|
||||||
|
*
|
||||||
|
* Make a type string more Wine-friendly. Logically const :-)
|
||||||
|
*/
|
||||||
|
void symbol_clean_string (const char *string)
|
||||||
|
{
|
||||||
|
const char **tab = swap_after;
|
||||||
|
char *str = (char *)string;
|
||||||
|
|
||||||
|
#define SWAP(i, p, x, y) do { i = p; while ((i = str_replace (i, x, y))); } while(0)
|
||||||
|
|
||||||
|
while (tab [0])
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
SWAP (p, str, tab [0], tab [1]);
|
||||||
|
tab += 2;
|
||||||
|
}
|
||||||
|
if (str [strlen (str) - 1] == ' ')
|
||||||
|
str [strlen (str) - 1] = '\0'; /* no trailing space */
|
||||||
|
|
||||||
|
if (*str == ' ')
|
||||||
|
memmove (str, str + 1, strlen (str)); /* No leading spaces */
|
||||||
|
}
|
Loading…
Reference in New Issue