From c7a3fec5be90414c8aa52f43cbe518c791b11c57 Mon Sep 17 00:00:00 2001
From: Jon Griffiths <jon_p_griffiths@yahoo.com>
Date: Thu, 4 Jan 2001 19:45:49 +0000
Subject: [PATCH] Added spec generation tool specmaker.

---
 configure                        |   2 +
 configure.in                     |   1 +
 tools/Makefile.in                |   4 +-
 tools/specmaker/.cvsignore       |   2 +
 tools/specmaker/Makefile.in      |  35 ++
 tools/specmaker/README           | 560 +++++++++++++++++++++++++++++++
 tools/specmaker/dll.c            | 213 ++++++++++++
 tools/specmaker/function_grep.pl | 270 +++++++++++++++
 tools/specmaker/main.c           | 257 ++++++++++++++
 tools/specmaker/misc.c           | 210 ++++++++++++
 tools/specmaker/msmangle.c       | 560 +++++++++++++++++++++++++++++++
 tools/specmaker/output.c         | 548 ++++++++++++++++++++++++++++++
 tools/specmaker/search.c         | 318 ++++++++++++++++++
 tools/specmaker/specmaker.h      | 172 ++++++++++
 tools/specmaker/symbol.c         | 286 ++++++++++++++++
 15 files changed, 3437 insertions(+), 1 deletion(-)
 create mode 100644 tools/specmaker/.cvsignore
 create mode 100644 tools/specmaker/Makefile.in
 create mode 100644 tools/specmaker/README
 create mode 100644 tools/specmaker/dll.c
 create mode 100755 tools/specmaker/function_grep.pl
 create mode 100644 tools/specmaker/main.c
 create mode 100644 tools/specmaker/misc.c
 create mode 100644 tools/specmaker/msmangle.c
 create mode 100644 tools/specmaker/output.c
 create mode 100644 tools/specmaker/search.c
 create mode 100644 tools/specmaker/specmaker.h
 create mode 100644 tools/specmaker/symbol.c

diff --git a/configure b/configure
index 650e96111ec..993c9e93b77 100755
--- a/configure
+++ b/configure
@@ -6966,6 +6966,7 @@ scheduler/Makefile
 server/Makefile
 tools/Makefile
 tools/cvdump/Makefile
+tools/specmaker/Makefile
 tools/winebuild/Makefile
 tools/winelauncher
 tools/wmc/Makefile
@@ -7209,6 +7210,7 @@ scheduler/Makefile
 server/Makefile
 tools/Makefile
 tools/cvdump/Makefile
+tools/specmaker/Makefile
 tools/winebuild/Makefile
 tools/winelauncher
 tools/wmc/Makefile
diff --git a/configure.in b/configure.in
index d7e4fe4e947..7fcd9e8cc1a 100644
--- a/configure.in
+++ b/configure.in
@@ -1284,6 +1284,7 @@ scheduler/Makefile
 server/Makefile
 tools/Makefile
 tools/cvdump/Makefile
+tools/specmaker/Makefile
 tools/winebuild/Makefile
 tools/winelauncher
 tools/wmc/Makefile
diff --git a/tools/Makefile.in b/tools/Makefile.in
index 4e5384bd46a..234fad8f4be 100644
--- a/tools/Makefile.in
+++ b/tools/Makefile.in
@@ -11,11 +11,13 @@ C_SRCS = makedep.c fnt2bdf.c bin2res.c
 
 SUBDIRS = \
 	cvdump \
+	specmaker \
 	winebuild \
 	wmc \
 	wrc
 
 INSTALLSUBDIRS = \
+	specmaker \
 	winebuild \
 	wmc \
 	wrc
@@ -26,7 +28,7 @@ EXTRASUBDIRS = \
 	winapi_check/win32 \
 	wineconf.libs
 
-all: $(PROGRAMS) winebuild wmc wrc
+all: $(PROGRAMS) specmaker winebuild wmc wrc
 
 @MAKE_RULES@
 
diff --git a/tools/specmaker/.cvsignore b/tools/specmaker/.cvsignore
new file mode 100644
index 00000000000..3f5e3b853e4
--- /dev/null
+++ b/tools/specmaker/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+specmaker
diff --git a/tools/specmaker/Makefile.in b/tools/specmaker/Makefile.in
new file mode 100644
index 00000000000..e3c7bfdf173
--- /dev/null
+++ b/tools/specmaker/Makefile.in
@@ -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:
diff --git a/tools/specmaker/README b/tools/specmaker/README
new file mode 100644
index 00000000000..e720b7922c9
--- /dev/null
+++ b/tools/specmaker/README
@@ -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.
+
diff --git a/tools/specmaker/dll.c b/tools/specmaker/dll.c
new file mode 100644
index 00000000000..52ef299c2ce
--- /dev/null
+++ b/tools/specmaker/dll.c
@@ -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;
+  }
+}
diff --git a/tools/specmaker/function_grep.pl b/tools/specmaker/function_grep.pl
new file mode 100755
index 00000000000..894c4e30972
--- /dev/null
+++ b/tools/specmaker/function_grep.pl
@@ -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);	
+}
diff --git a/tools/specmaker/main.c b/tools/specmaker/main.c
new file mode 100644
index 00000000000..2b99e5d25f5
--- /dev/null
+++ b/tools/specmaker/main.c
@@ -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;
+}
diff --git a/tools/specmaker/misc.c b/tools/specmaker/misc.c
new file mode 100644
index 00000000000..cdb3ae771f4
--- /dev/null
+++ b/tools/specmaker/misc.c
@@ -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 ();
+}
diff --git a/tools/specmaker/msmangle.c b/tools/specmaker/msmangle.c
new file mode 100644
index 00000000000..b9b68764e98
--- /dev/null
+++ b/tools/specmaker/msmangle.c
@@ -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 ? " **" : " *");
+
+}
diff --git a/tools/specmaker/output.c b/tools/specmaker/output.c
new file mode 100644
index 00000000000..b966a24dfb6
--- /dev/null
+++ b/tools/specmaker/output.c
@@ -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 "";
+}
\ No newline at end of file
diff --git a/tools/specmaker/search.c b/tools/specmaker/search.c
new file mode 100644
index 00000000000..289b8721230
--- /dev/null
+++ b/tools/specmaker/search.c
@@ -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
+
diff --git a/tools/specmaker/specmaker.h b/tools/specmaker/specmaker.h
new file mode 100644
index 00000000000..0fc636771a6
--- /dev/null
+++ b/tools/specmaker/specmaker.h
@@ -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 */
diff --git a/tools/specmaker/symbol.c b/tools/specmaker/symbol.c
new file mode 100644
index 00000000000..225312878ca
--- /dev/null
+++ b/tools/specmaker/symbol.c
@@ -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 */
+}
-- 
GitLab