Release 950522

Sun May 21 12:30:30 1995  Alexandre Julliard  (julliard@sunsite.unc.edu)

	* [debugger/hash.c] [debugger/info.c]
	Added support for symbolic segmented addresses. Add symbols for all
	built-in API entry points.

	* [if1632/relay.c] [include/dlls.h]
	Removed dll_table structure, as we now use the built-in module
	structures.

	* [if1632/relay.c] [loader/main.c]
	Removed winestat option, as it was no longer very meaningful.

	* [include/stackframe.h]
	New macro MAKE_SEGPTR that creates a segmented pointer to a local
	variable on the 32-bit stack.

	* [loader/module.c]
	Added support for multiple instances of an application.
	Implemented LoadModule() and FreeModule().

	* [loader/ne_image.c] [loader/task.c]
	Moved initialisation of built-in DLLs to InitTask().

	* [memory/global.c]
	Implemented discardable blocks.

	* [misc/file.c]
	Search path of current executable in OpenFile().
	Fixed bug with searching in Windows path.

	* [misc/lstr.c]
	Hard-coded translation tables for Ansi<->Oem.

	* [misc/user.c]
	Moved some global initializations to InitApp(), because they need
	a task context to be performed.

	* [objects/dc.c]
	Handle R2_BLACK and R2_WHITE specially so that they work correctly
	with palette displays.

	* [tools/build.c]
	Suppressed generation of the C file for DLL specs, because it's no
	longer needed. Output all the assembly code directly to stdout.
	Some changes to integrate Win32 support from Martin von	Loewis. 

	* [windows/msgbox.c]
	Moved message box code from misc/ to windows/.

Mon May 15 23:40:04 1995  Martin Ayotte (wine@trgcorp.mksinfo.qc.ca)

	* [misc/audio.c] [misc/mcicda.c] [misc/mcianim.c] [misc/midi.c]
	  [misc/mmaux.c] [misc/mmsystem.c]
	Modify code & use pointers conversion macros.
	Make cdaudio & wave devices work again (only using some applets).

	* [misc/profile.c]
	Change getc() to fgetc() where needed.

Mon May 15 22:10:56 1995  Martin von Loewis  <loewis@informatik.hu-berlin.de>

	* [if1632/Imakefile]
	added entries for the new files gdi32.spec, kernel32.spec,
	user32.spec, shell32.spec and winprocs32.spec.

	* [if1632/commdlg.spec][if1632/kernel.spec][if1632/shell.spec]
	  [if1632/storage.spec][if1632/system.spec][if1632/user.spec]
	ChooseFont, RESERVED5, InternalExtractIcon: Marked as stubs
	ExtractAssociatedIcon, DoEnvironmentSubst, DumpIcon:
		stub implementations provided 
	marked storage.dll,storege.sys functions as stubs

	* [include/pe_image.h]
	Added structures WIN32_builtin and  WIN32_function

	* [include/peexe.h]
	PE_Import_Directory: renamed reserved fields to 
		TimeDate, Forwarder, Thunk_List

	* [include/winerror.h]
	New file.

	* [loader/main.c]
	called RELAY32_Init

	* [loader/pe_image.c]
	xmmap: map BSS anonymous
	dump_imports: renamed to fixup_imports, do the fixup of imported
	              symbols
	PE_LoadImage: pass raw data size to xmmap

	* [loader/resource.c]
	DumpIcon: new function

	* [misc/kernel32.c]
	New file.

	* [misc/main.c]
	make stdout and stderr unbuffered

	* [misc/shell.c]
	DoEnvironmentSubst: new function

	* [objects/font.c]
	FONT_MatchFont: try oblique if there is no italic

	* [rc/Imakefile][rc/parser.l]
	yywrap: new function
	Don't link with libfl.a on Linux

	* [tools/build.c]
	Added keywords stdcall, subsystem, base
	GenerateForWin32: new function
	BuildSpecFiles: call GenerateForWin32 if subsystem is win32

Mon May 15 10:38:14 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
        
        * [controls/listbox.c] [controls/combo.c] [windows/defwnd.c]
	Minor fixes.
	
	* [misc/message.c] [misc/main.c] [rc/sysres*.rc] [include/texts.h]
	Rewrote message box handling.
	
	* [windows/dialog.c]
	Dialogs should be invisible until after WM_INITDIALOG is seent.
	Don't switch to invisible dialog items on a TAB keypress.
	
	* [windows/mdi.c]
	Send WM_NCPAINT message in MDIRestoreChild().
	
	* [windows/painting.c]
	Fixed typo (&& -> &).
	
	* [windows/message.c] [if1632/user.spec]
	Implemented PostAppMessage().
	
	* [windows/event.c]
	SetCapture(0) should act like ReleaseCapture().

Tue May  9 11:55:52 1995     Eddie C. Dost             (ecd@dressler.de)

	* [Imakefile]
	Changed CDEBUGFLAGS for systems running __ELF__ (temporarily)
	Added ASFLAGS to exported variables.

	* [debugger/readline/Imakefile]
	Moved defines for libreadline from DEFINES to EXTRA_DEFINES

	* [memory/local.c] [miscemu/int21.c]
	Added some more debugging outputs.

Mon May  8 00:55:27 MET DST 1995	  Dag Asheim (dash@ifi.uio.no)

	* [misc/message.c]
	Fixed a "FIXME" concerning norwegian translation.

Sun May  7 23:25:23 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
        
	* [*/*]
        Removed warnings in a couple of files and deleted some obsolete code.

        * [controls/listbox.c]
        Cleanup, speed improvements & lots of bug fixes.

        * [controls/combo.c]
	Mostly rewritten. This is still very buggy, but not quite as bad as 
	before.

        * [include/commdlg.h] [misc/commdlg.c]
        Removed the need for sysres.dll. Small bug fixes.
	
        * [objects/oembitmap.c] [include/bitmaps/<many>] [include/windows.h]
          [loader/library.c] [loader/main.c] [rc/sysres*.rc]
        Removed sysres.dll and replaced the remaining bitmaps/icons with
        XPM equivalents.

        * [misc/message.c] [windows/nonclient.c] [misc/main.c]
          [if1632/winprocs.spec]
        "About Wine..." now brings up a standard ShellAbout() window with
        the Wine icon and the list of contributors.
	
	* [misc/shell.c]
	Fixed ShellAbout()/AboutDialogProc() to show the right icon.

	* [windows/event.c]
	Small hack for non-alphanumeric keys: Dont't send the ascii value in
	the WM_KEYDOWN message, but some unused code instead. Should be done
	properly by sending different codes for each key. The edit control
	used to get a VK_DELETE message each time the user typed '.'.

	* [windows/class.c]
	Removed a check for CS_GLOBALCLASS in CLASS_FindClassByName().
	This used to be no problem, but breaks Resource Workshop in 950403.
	
	* [objects/dib.c]
	New diagnostic for a bug I've been encountering. If it shows up,
	please report it.

Sun May  7 23:11:18 EDT 1995  William Magro (wmagro@tc.cornell.edu)

	* [objects/color.c]
	Handle situation when 'dc' exists, but palette mapping
	does not.  (Fixes kidpix2 demo.)

Sun May  7 03:32:00 1995  Charles M. Hannum  (mycroft@mit.edu)

	* [loader/ldt.c]
	LDT_Print: Only show the number of entries that the kernel
	returned. Make this work for NetBSD.

Fri May  5 02:53:26 1995  Charles M. Hannum  (mycroft@mit.edu)

	* [debugger/dbg.y] [include/wine.h] [loader/signal.c]
	Modify cs and ds selector values for NetBSD-current.

	* [debugger/debug.l]
	$sp, $esp: Use RN_ESP_AT_SIGNAL rather than RN_ESP.

	* [debugger/regpos.h]
	Modify sigcontext format for NetBSD-current.
	SC_ESP: Use RN_ESP_AT_SIGNAL rather than RN_ESP.

	* [include/ldt.h]
	SELECTOR_TO_ENTRY: Explicitly clear the top half of the selector
	value, since only 16 bits of it may have been saved.

	* [misc/winsocket.c]
	Set structure packing with `#pragma pack' to accomodate
	other/older compilers.

Tue May  2 18:15:01 1995 Paal Beyer (beyer@idt.unit.no)
	
	* [misc/commdlg.c]
	Fixed path-names so when changing directory the listboxes
	changes too.
	
	* [debugger/dbg.y debugger/debug.l wine.ini]
	Added SymbolTableFile to wine.ini so symbols can be read
	without standing in the directory containing wine.sym.
	Added the possibility to specify full name of wine.sym from
	the debugger prompt.
diff --git a/loader/module.c b/loader/module.c
index 7ea2944..c4af5bf 100644
--- a/loader/module.c
+++ b/loader/module.c
@@ -12,20 +12,19 @@
 #include <unistd.h>
 #include "windows.h"
 #include "dlls.h"
+#include "dos_fs.h"
 #include "global.h"
 #include "ldt.h"
 #include "module.h"
 #include "neexe.h"
+#include "stackframe.h"
+#include "task.h"
 #include "toolhelp.h"
 #include "stddebug.h"
 /* #define DEBUG_MODULE */
 #include "debug.h"
 
 
-extern BYTE KERNEL_Module_Start[], KERNEL_Module_End[];
-
-extern struct dll_name_table_entry_s dll_builtin_table[];
-
 static HMODULE hFirstModule = 0;
 
 
@@ -36,18 +35,20 @@
  */
 BOOL MODULE_Init(void)
 {
+    extern void load_entrypoints( HMODULE );
+
     HMODULE hModule;
     NE_MODULE *pModule;
     SEGTABLEENTRY *pSegTable;
     struct dll_table_s *table;
+    char *dosmem;
     int i;
 
       /* Create the built-in modules */
 
-    for (i = 0; i < N_BUILTINS; i++)
+    for (i = 0, table = dll_builtin_table; i < N_BUILTINS; i++, table++)
     {
-        if (!dll_builtin_table[i].dll_is_used) continue;
-        table = dll_builtin_table[i].table;
+        if (!table->used) continue;
 
         hModule = GLOBAL_CreateBlock( GMEM_MOVEABLE, table->module_start,
                                       table->module_end - table->module_start,
@@ -58,7 +59,7 @@
         table->hModule = hModule;
 
         dprintf_module( stddeb, "Built-in %s: hmodule=%04x\n",
-                        dll_builtin_table[i].dll_name, hModule );
+                        table->name, hModule );
 
           /* Allocate the code segment */
 
@@ -81,7 +82,51 @@
 
         pModule->next = hFirstModule;
         hFirstModule = hModule;
+        load_entrypoints( hModule );
     }
+
+      /* Initialize some KERNEL exported values */
+
+    if (!(hModule = GetModuleHandle( "KERNEL" ))) return TRUE;
+
+      /* KERNEL.178: __WINFLAGS */
+    MODULE_SetEntryPoint( hModule, 178, GetWinFlags() );
+
+    /* Allocate 7 64k segments for 0000, A000, B000, C000, D000, E000, F000. */
+
+    dosmem = malloc( 0x70000 );
+
+    MODULE_SetEntryPoint( hModule, 183,  /* KERNEL.183: __0000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 193,  /* KERNEL.193: __0040H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x400,
+                                     0x100, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 174,  /* KERNEL.174: __A000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x10000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 181,  /* KERNEL.181: __B000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x20000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 182,  /* KERNEL.182: __B800H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x28000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 195,  /* KERNEL.195: __C000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x30000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 179,  /* KERNEL.179: __D000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x40000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 190,  /* KERNEL.190: __E000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x50000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 173,  /* KERNEL.173: __ROMBIOS */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x60000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+    MODULE_SetEntryPoint( hModule, 194,  /* KERNEL.194: __F000H */
+                          GLOBAL_CreateBlock( GMEM_FIXED, dosmem + 0x60000,
+                                     0x10000, hModule, FALSE, FALSE, FALSE ) );
+
     return TRUE;
 }
 
@@ -237,7 +282,7 @@
     close( cachedfd );
     hCachedModule = hModule;
     name = ((LOADEDFILEINFO*)((char*)pModule + pModule->fileinfo))->filename;
-    cachedfd = open( name /* DOS_GetUnixFileName( name ) */, O_RDONLY );
+    cachedfd = open( DOS_GetUnixFileName( name ), O_RDONLY );
     dprintf_module( stddeb, "MODULE_OpenFile: opened '%s' -> %d\n",
                     name, cachedfd );
     return cachedfd;
@@ -263,7 +308,9 @@
         {
             /* FIXME: this is needed because heap growing is not implemented */
             pModule->heap_size = 0x10000 - minsize;
+            /* For tasks, the DGROUP is allocated by MODULE_MakeNewInstance */
             minsize = 0x10000;
+            if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) continue;
         }
         pSegment->selector = GLOBAL_Alloc( GMEM_ZEROINIT | GMEM_FIXED,
                                       minsize, hModule,
@@ -282,7 +329,7 @@
 /***********************************************************************
  *           MODULE_LoadExeHeader
  */
-HMODULE MODULE_LoadExeHeader( int fd, char *filename )
+HMODULE MODULE_LoadExeHeader( int fd, OFSTRUCT *ofs )
 {
     struct mz_header_s mz_header;
     struct ne_header_s ne_header;
@@ -316,7 +363,7 @@
 
     size = sizeof(NE_MODULE) +
              /* loaded file info */
-           sizeof(LOADEDFILEINFO) + strlen(filename) +
+           sizeof(LOADEDFILEINFO) + strlen(ofs->szPathName) +
              /* segment table */
            ne_header.n_segment_tab * sizeof(SEGTABLEENTRY) +
              /* resource table */
@@ -335,6 +382,7 @@
     FarSetOwner( hModule, hModule );
     pModule = (NE_MODULE *)GlobalLock( hModule );
     memcpy( pModule, &ne_header, sizeof(NE_MODULE) );
+    pModule->count = 0;
     pData = (BYTE *)(pModule + 1);
 
     /* Read the fast-load area */
@@ -359,12 +407,12 @@
     /* Store the filename information */
 
     pModule->fileinfo = (int)pData - (int)pModule;
-    ((LOADEDFILEINFO*)pData)->length = sizeof(LOADEDFILEINFO)+strlen(filename);
+    ((LOADEDFILEINFO*)pData)->length = sizeof(LOADEDFILEINFO)+strlen(ofs->szPathName);
     ((LOADEDFILEINFO*)pData)->fixed_media = TRUE;
     ((LOADEDFILEINFO*)pData)->error = 0;
     ((LOADEDFILEINFO*)pData)->date = 0;
     ((LOADEDFILEINFO*)pData)->time = 0;
-    strcpy( ((LOADEDFILEINFO*)pData)->filename, filename );
+    strcpy( ((LOADEDFILEINFO*)pData)->filename, ofs->szPathName );
     pData += ((LOADEDFILEINFO*)pData)->length--;
 
     /* Get the segment table */
@@ -451,6 +499,17 @@
     }
     else pModule->nrname_handle = 0;
 
+    /* Allocate a segment for the implicitly-loaded DLLs */
+
+    if (pModule->modref_count)
+    {
+        pModule->dlls_to_init = GLOBAL_Alloc(GMEM_ZEROINIT,
+                                    (pModule->modref_count+1)*sizeof(HMODULE),
+                                    hModule, FALSE, FALSE, FALSE );
+        if (!pModule->dlls_to_init) return 11;  /* invalid exe */
+    }
+    else pModule->dlls_to_init = 0;
+
     if (debugging_module) MODULE_PrintModule( hModule );
     pModule->next = hFirstModule;
     hFirstModule = hModule;
@@ -459,6 +518,62 @@
 
 
 /***********************************************************************
+ *           MODULE_MakeNewInstance
+ *
+ * Create a new instance of the specified module.
+ */
+HINSTANCE MODULE_MakeNewInstance( HMODULE hModule, LOADPARAMS *params )
+{
+    NE_MODULE *pModule;
+    SEGTABLEENTRY *pSegment;
+    HINSTANCE hNewInstance, hPrevInstance;
+    int minsize;
+
+    if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
+    if (!pModule->dgroup) return hModule;  /* No DGROUP -> return the module */
+
+    pSegment = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
+    hPrevInstance = pSegment->selector;
+
+      /* Don't create a new instance if it's a library */
+
+    if (pModule->flags & NE_FFLAGS_LIBMODULE) return hPrevInstance;
+    if (params == (LOADPARAMS*)-1) return hPrevInstance;
+
+      /* Allocate the new data segment */
+
+    minsize = pSegment->minsize ? pSegment->minsize : 0x10000;
+    if (pModule->ss == pModule->dgroup) minsize += pModule->stack_size;
+    minsize += pModule->heap_size;
+    hNewInstance = GLOBAL_Alloc( GMEM_ZEROINIT | GMEM_FIXED,
+                                 minsize, hModule, FALSE, FALSE, FALSE );
+    if (!hNewInstance) return 0;
+    pSegment->selector = hNewInstance;
+    NE_LoadSegment( hModule, pModule->dgroup );
+
+      /* Create a new task for this instance */
+    if (!TASK_CreateTask( hModule, hNewInstance, hPrevInstance,
+                          params->hEnvironment,
+                          (LPSTR)PTR_SEG_TO_LIN( params->cmdLine ),
+                          *((WORD *)PTR_SEG_TO_LIN(params->showCmd)+1) ))
+    {
+        GlobalFree( hNewInstance );
+        return 0;
+    }
+
+      /* Initialize the local heap */
+
+    if (pModule->heap_size)
+    {
+        WORD heapstart = pSegment->minsize;
+        if (pModule->ss == pModule->dgroup) heapstart += pModule->stack_size;
+        LocalInit( hNewInstance, heapstart, heapstart + pModule->heap_size );
+    }
+    return hNewInstance;
+}
+
+
+/***********************************************************************
  *           MODULE_GetOrdinal
  *
  * Lookup the ordinal for a given name.
@@ -538,8 +653,6 @@
 
     if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
 
-    dprintf_module( stddeb, "MODULE_GetEntryPoint(%04x,%d)\n",
-                    hModule, ordinal );
     p = (BYTE *)pModule + pModule->entry_table;
     while (*p && (curOrdinal + *p <= ordinal))
     {
@@ -552,16 +665,11 @@
             default:   p += 2 + *p * 3; break;  /* fixed */
         }
     }
-    if (!*p)
-    {
-        dprintf_module( stddeb, "  Not found (last=%d)\n", curOrdinal-1 );
-        return 0;
-    }
+    if (!*p) return 0;
 
     switch(p[1])
     {
         case 0:  /* unused */
-            dprintf_module( stddeb, "  Found, but entry is unused\n" );
             return 0;
         case 0xff:  /* moveable */
             p += 2 + 6 * (ordinal - curOrdinal);
@@ -575,8 +683,6 @@
             break;
     }
 
-    dprintf_module( stddeb, "  Found, logical addr = %04x:%04x\n",
-                    sel, offset );
     if (sel == 0xfe) sel = 0xffff;  /* constant entry */
     else sel = NE_SEG_TABLE(pModule)[sel-1].selector;
     return MAKELONG( offset, sel );
@@ -584,6 +690,87 @@
 
 
 /***********************************************************************
+ *           MODULE_SetEntryPoint
+ *
+ * Change the value of an entry point. Use with caution!
+ * It can only change the offset value, not the selector.
+ */
+BOOL MODULE_SetEntryPoint( HMODULE hModule, WORD ordinal, WORD offset )
+{
+    NE_MODULE *pModule;
+    WORD curOrdinal = 1;
+    BYTE *p;
+
+    if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return FALSE;
+
+    p = (BYTE *)pModule + pModule->entry_table;
+    while (*p && (curOrdinal + *p <= ordinal))
+    {
+          /* Skipping this bundle */
+        curOrdinal += *p;
+        switch(p[1])
+        {
+            case 0:    p += 2; break;  /* unused */
+            case 0xff: p += 2 + *p * 6; break;  /* moveable */
+            default:   p += 2 + *p * 3; break;  /* fixed */
+        }
+    }
+    if (!*p) return FALSE;
+
+    switch(p[1])
+    {
+        case 0:  /* unused */
+            return FALSE;
+        case 0xff:  /* moveable */
+            p += 2 + 6 * (ordinal - curOrdinal);
+            *(WORD *)(p + 4) = offset;
+            break;
+        default:  /* fixed */
+            p += 2 + 3 * (ordinal - curOrdinal);
+            *(WORD *)(p + 1) = offset;
+            break;
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *           MODULE_GetEntryPointName
+ *
+ * Return the entry point name for a given ordinal.
+ * Used only by relay debugging.
+ * Warning: returned pointer is to a Pascal-type string.
+ */
+LPSTR MODULE_GetEntryPointName( HMODULE hModule, WORD ordinal )
+{
+    register char *cpnt;
+    NE_MODULE *pModule;
+
+    if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
+
+      /* First search the resident names */
+
+    cpnt = (char *)pModule + pModule->name_table;
+    while (*cpnt)
+    {
+        cpnt += *cpnt + 1 + sizeof(WORD);
+        if (*(WORD *)(cpnt + *cpnt + 1) == ordinal) return cpnt;
+    }
+
+      /* Now search the non-resident names table */
+
+    if (!pModule->nrname_handle) return 0;  /* No non-resident table */
+    cpnt = (char *)GlobalLock( pModule->nrname_handle );
+    while (*cpnt)
+    {
+        cpnt += *cpnt + 1 + sizeof(WORD);
+        if (*(WORD *)(cpnt + *cpnt + 1) == ordinal) return cpnt;
+    }
+    return NULL;
+}
+
+
+/***********************************************************************
  *           MODULE_GetModuleName
  */
 LPSTR MODULE_GetModuleName( HMODULE hModule )
@@ -602,10 +789,208 @@
 
 
 /**********************************************************************
+ *	    MODULE_FindModule
+ *
+ * Find a module from a path name.
+ */
+HMODULE MODULE_FindModule( LPCSTR path )
+{
+    HMODULE hModule = hFirstModule;
+    LPCSTR filename, dotptr, modulepath, modulename;
+    BYTE len, *name_table;
+
+    if (!(filename = strrchr( path, '\\' ))) filename = path;
+    if ((dotptr = strrchr( filename, '.' )) != NULL)
+        len = (BYTE)(dotptr - filename);
+    else len = strlen( filename );
+
+    while(hModule)
+    {
+        NE_MODULE *pModule = (NE_MODULE *)GlobalLock( hModule );
+        if (!pModule) break;
+        modulepath = ((LOADEDFILEINFO*)((char*)pModule + pModule->fileinfo))->filename;
+        if (!(modulename = strrchr( modulepath, '\\' )))
+            modulename = modulepath;
+        if (!strcasecmp( modulename, filename )) return hModule;
+
+        name_table = (BYTE *)pModule + pModule->name_table;
+        if ((*name_table == len) && !strncasecmp(filename, name_table+1, len))
+            return hModule;
+        hModule = pModule->next;
+    }
+    return 0;
+}
+
+
+/**********************************************************************
+ *	    MODULE_FreeModule
+ *
+ * Remove a module from memory.
+ */
+static void MODULE_FreeModule( HMODULE hModule )
+{
+    HMODULE *hPrevModule;
+    NE_MODULE *pModule;
+    SEGTABLEENTRY *pSegment;
+    WORD *pModRef;
+    int i;
+
+    if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return;
+
+    /* FIXME: should call the exit code for the library here */
+
+      /* Remove it from the linked list */
+
+    hPrevModule = &hFirstModule;
+    while (*hPrevModule && (*hPrevModule != hModule))
+    {
+        hPrevModule = &((NE_MODULE *)GlobalLock( *hPrevModule ))->next;
+    }
+    if (*hPrevModule) *hPrevModule = pModule->next;
+
+      /* Free all the segments */
+
+    pSegment = NE_SEG_TABLE( pModule );
+    for (i = 1; i <= pModule->seg_count; i++, pSegment++)
+    {
+        GlobalFree( pSegment->selector );
+    }
+
+      /* Free the referenced modules */
+
+    pModRef = NE_MODULE_TABLE( pModule );
+    for (i = 0; i < pModule->modref_count; i++, pModRef++)
+    {
+        FreeModule( *pModRef );
+    }
+
+      /* Free the module storage */
+
+    if (pModule->nrname_handle) GlobalFree( pModule->nrname_handle );
+    if (pModule->dlls_to_init) GlobalFree( pModule->dlls_to_init );
+    GlobalFree( hModule );
+}
+
+
+/**********************************************************************
  *	    LoadModule    (KERNEL.45)
  */
-HINSTANCE MODULE_LoadModule( LPCSTR name, LPVOID paramBlock )
+HINSTANCE LoadModule( LPCSTR name, LPVOID paramBlock )
 {
+    HMODULE hModule;
+    HANDLE hInstance;
+    NE_MODULE *pModule;
+    WORD *pModRef, *pDLLs;
+    int i, fd;
+
+    hModule = MODULE_FindModule( name );
+    if (!hModule)  /* We have to load the module */
+    {
+        OFSTRUCT ofs;
+        if (strchr( name, '/' )) name = DOS_GetDosFileName( name );
+        if ((fd = OpenFile( name, &ofs, OF_READ )) == -1)
+            return 2;  /* File not found */
+
+          /* Create the module structure */
+
+        if ((hModule = MODULE_LoadExeHeader( fd, &ofs )) < 32)
+        {
+            close( fd );
+            fprintf( stderr, "LoadModule: can't load '%s', error=%d\n",
+                     name, hModule );
+            return hModule;
+        }
+        pModule = (NE_MODULE *)GlobalLock( hModule );
+
+          /* Allocate the segments for this module */
+
+        MODULE_CreateSegments( hModule );
+
+          /* Load the referenced DLLs */
+
+        pModRef = (WORD *)((char *)pModule + pModule->modref_table);
+        pDLLs = (WORD *)GlobalLock( pModule->dlls_to_init );
+        for (i = 0; i < pModule->modref_count; i++, pModRef++)
+        {
+            char buffer[256];
+            BYTE *pstr = (BYTE *)pModule + pModule->import_table + *pModRef;
+            memcpy( buffer, pstr + 1, *pstr );
+            strcpy( buffer + *pstr, ".dll" );
+            dprintf_module( stddeb, "Loading '%s'\n", buffer );
+            if (!(*pModRef = MODULE_FindModule( buffer )))
+            {
+                /* If the DLL is not loaded yet, load it and store */
+                /* its handle in the list of DLLs to initialize.   */
+                HMODULE hDLL;
+
+                if ((hDLL = LoadModule( buffer, (LPVOID)-1 )) == 2)  /* file not found */
+                {
+                    char *p;
+
+                    /* Try with prepending the path of the current module */
+                    GetModuleFileName( hModule, buffer, 256 );
+                    if (!(p = strrchr( buffer, '\\' ))) p = buffer;
+                    memcpy( p + 1, pstr + 1, *pstr );
+                    strcpy( p + 1 + *pstr, ".dll" );
+                    hDLL = LoadModule( buffer, (LPVOID)-1 );
+                }
+                if (hDLL < 32)
+                {
+                    fprintf( stderr, "Could not load '%s' required by '%s', error = %d\n",
+                             buffer, name, hDLL );
+                    return 2;  /* file not found */
+                }
+                *pModRef = GetExePtr( hDLL );
+                *pDLLs++ = *pModRef;
+            }
+            else  /* Increment the reference count of the DLL */
+            {
+                NE_MODULE *pOldDLL = (NE_MODULE *)GlobalLock( *pModRef );
+                if (pOldDLL) pOldDLL->count++;
+            }
+        }
+
+          /* Load the segments (except the DGROUP) */
+
+        for (i = 1; i <= pModule->seg_count; i++)
+            if (i != pModule->dgroup) NE_LoadSegment( hModule, i );
+
+          /* Create an instance for this module */
+
+        hInstance = MODULE_MakeNewInstance( hModule, (LOADPARAMS*)paramBlock );
+
+          /* Fixup the functions prologs */
+
+        NE_FixupPrologs( hModule );
+
+          /* Make sure the usage count is 1 on the first loading of  */
+          /* the module, even if it contains circular DLL references */
+
+        pModule->count = 1;
+    }
+    else
+    {
+        pModule = (NE_MODULE *)GlobalLock( hModule );
+        hInstance = MODULE_MakeNewInstance( hModule, (LOADPARAMS*)paramBlock );
+        pModule->count++;
+    }
+
+    return hInstance;
+}
+
+
+/**********************************************************************
+ *	    FreeModule    (KERNEL.46)
+ */
+BOOL FreeModule( HANDLE hModule )
+{
+    NE_MODULE *pModule;
+
+    hModule = GetExePtr( hModule );  /* In case we were passed an hInstance */
+    if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return FALSE;
+
+    if (--pModule->count == 0) MODULE_FreeModule( hModule );
+    return TRUE;
 }
 
 
@@ -614,21 +999,15 @@
  */
 HMODULE GetModuleHandle( LPCSTR name )
 {
-    char buffer[16];
-    BYTE len;
-    HMODULE hModule;
+    BYTE len = strlen(name);
+    HMODULE hModule = hFirstModule;
 
-    strncpy( buffer, name, 15 );
-    buffer[15] = '\0';
-    len = strlen(buffer);
-    AnsiUpper( buffer );
-
-    hModule = hFirstModule;
     while( hModule )
     {
         NE_MODULE *pModule = (NE_MODULE *)GlobalLock( hModule );
         char *pname = (char *)pModule + pModule->name_table;
-        if (((BYTE)*pname == len) && !memcmp( pname+1, buffer, len )) break;
+        if (((BYTE)*pname == len) && !strncasecmp( pname+1, name, len ))
+            break;
         hModule = pModule->next;
     }
     dprintf_module( stddeb, "GetModuleHandle('%s'): returning %04x\n",
@@ -669,6 +1048,122 @@
 }
 
 
+/***********************************************************************
+ *           LoadLibrary   (KERNEL.95)
+ */
+HANDLE LoadLibrary( LPCSTR libname )
+{
+    HANDLE handle;
+
+    dprintf_module( stddeb, "LoadLibrary: (%08x) %s\n", (int)libname, libname);
+    handle = LoadModule( libname, (LPVOID)-1 );
+    if (handle == 2)  /* file not found */
+    {
+        char buffer[256];
+        strcpy( buffer, libname );
+        strcat( buffer, ".dll" );
+        handle = LoadModule( buffer, (LPVOID)-1 );
+    }
+    if (handle >= 32) NE_InitializeDLLs( handle );
+    return handle;
+}
+
+
+/***********************************************************************
+ *           FreeLibrary   (KERNEL.96)
+ */
+void FreeLibrary( HANDLE handle )
+{
+    dprintf_module( stddeb,"FreeLibrary: %04x\n", handle );
+    FreeModule( handle );
+}
+
+
+/***********************************************************************
+ *           WinExec   (KERNEL.166)
+ */
+HANDLE WinExec( LPSTR lpCmdLine, WORD nCmdShow )
+{
+    LOADPARAMS params;
+    HLOCAL cmdShowHandle, cmdLineHandle;
+    HANDLE handle;
+    WORD *cmdShowPtr;
+    char *p, *cmdline, filename[256];
+
+    if (!(cmdShowHandle = GlobalAlloc( 0, 2 * sizeof(WORD) ))) return 0;
+    if (!(cmdLineHandle = GlobalAlloc( 0, 256 ))) return 0;
+
+      /* Store nCmdShow */
+
+    cmdShowPtr = (WORD *)GlobalLock( cmdShowHandle );
+    cmdShowPtr[0] = 2;
+    cmdShowPtr[1] = nCmdShow;
+
+      /* Build the filename and command-line */
+
+    cmdline = (char *)GlobalLock( cmdLineHandle );
+    strncpy( filename, lpCmdLine, 256 );
+    filename[255] = '\0';
+    for (p = filename; *p && (*p != ' ') && (*p != '\t'); p++);
+    if (*p)
+    {
+        strncpy( cmdline, p + 1, 128 );
+        cmdline[127] = '\0';
+    }
+    else cmdline[0] = '\0';
+    *p = '\0';
+
+      /* Now load the executable file */
+
+    params.hEnvironment = SELECTOROF( GetDOSEnvironment() );
+    params.cmdLine  = WIN16_GlobalLock( cmdLineHandle );
+    params.showCmd  = WIN16_GlobalLock( cmdShowHandle );
+    params.reserved = 0;
+    handle = LoadModule( filename, &params );
+    if (handle == 2)  /* file not found */
+    {
+        strcat( filename, ".exe" );
+        handle = LoadModule( filename, &params );
+    }
+
+    GlobalFree( cmdShowHandle );
+    GlobalFree( cmdLineHandle );
+    return handle;
+}
+
+
+/***********************************************************************
+ *           GetProcAddress   (KERNEL.50)
+ */
+FARPROC GetProcAddress( HANDLE hModule, SEGPTR name )
+{
+    WORD ordinal;
+    SEGPTR ret;
+
+    if (!hModule) hModule = GetCurrentTask();
+    hModule = GetExePtr( hModule );
+
+    if (HIWORD(name) != 0)
+    {
+        ordinal = MODULE_GetOrdinal( hModule, (LPSTR)PTR_SEG_TO_LIN(name) );
+        dprintf_module( stddeb, "GetProcAddress: %04x '%s'\n",
+                        hModule, (LPSTR)PTR_SEG_TO_LIN(name) );
+    }
+    else
+    {
+        ordinal = LOWORD(name);
+        dprintf_module( stddeb, "GetProcAddress: %04x %04x\n",
+                        hModule, ordinal );
+    }
+    if (!ordinal) return (FARPROC)0;
+
+    ret = MODULE_GetEntryPoint( hModule, ordinal );
+
+    dprintf_module( stddeb, "GetProcAddress: returning %08lx\n", ret );
+    return (FARPROC)ret;
+}
+
+
 /**********************************************************************
  *	    ModuleFirst    (TOOLHELP.59)
  */