Release 950606

Tue Jun  6 12:11:41 1995  Alexandre Julliard  (julliard@sunsite.unc.edu)

	* [controls/menu.c]
	Fixed bug with drawing multi-column menus with vertical separator.

	* [debugger/debug.l]
	Fixed NULL-pointer reference after readline().

	* [if1632/winprocs.spec] [miscemu/int21.c] [miscemu/interrupts.c]
	Added interrupt vector emulation. Allows to retrieve an interrupt
	vector and jump to it without crashing.

	* [loader/ldt.c]
	Moved ldt.c to memory directory.

	* [loader/task.c]
	Implemented LockCurrentTask() and GetInstanceData().

	* [objects/bitblt.c]
	Fixed a bug that caused StretchBlt() to use wrong colors when
	stretching a monochrome bitmap to a color display.

	* [objects/bitmap.c]
	Fixed a segmented pointer bug in CreateBitmapIndirect().

	* [tools/build.c]
	Added possibility to have arguments for register functions; used
	by interrupt vectors to remove the flags from the stack.
	Generate a new function CallTo32_LargeStack(), that allows calling
	a 32-bit function using the original 32-bit stack, for functions
	that need more that 64k of stack.

Tue May 30 10:29:56 1995  Martin von Loewis  <martin@informatik.hu-berlin.de>

	* [if1632/shell.spec] [misc/shell.c]
	DoEnvironmentSubst: fixed prototype

	* [if1632/gdi.spec] [objects/palette.c]
	SetSystemPaletteUse: new function

	* [if1632/kernel.spec] [loader/resource.c]
	DirectResAlloc: new function

	* [if1632/user.spec] [windows/keyboard.c]
	SetKeyboardState: new function

Mon May 29 12:58:28 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
        
	* [tools/build.c]
        Prevent interrupts from destroying the args for a 32 bit function
        by loading the correct value into %esp directly after %ss.

	* [loader/ne_image.c] [loader/module.c]
	The new instance must be created earlier in LoadModule(), so that
	fixups referencing it will be handled correctly.
        Initialize the local heap for a DGROUP in NE_LoadSegment().
	
	* [objects/dib.c]
	Like RLE8 bitmaps, RLE4 bitmaps don't always end with a proper code.
	This used to crash Wine. Fixed.

        * [objects/text.c]
	Fix possible null pointer dereference in debugging output.
	
	* [misc/commdlg.c]
	Handle user input in the edit control better. Some bugs fixed.
	
	* [memory/local.c]
	Started implementing moveable blocks. This is unfinished (!), but
	at least it does not seem to break things.

Wed May 24 13:26:36 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
        
	* [loader/module.c]
	LoadModule(): DLLs occasionally have a data segment, and they work
	much better if it is loaded :-)
	LoadLibrary(): pass HMODULE instead of HINSTANCE to NE_InitializeDLLs.
	FindModule(): also strip off the last backslash of the pathnames
	(Winhelp tried to load C:\WINDOWS\SYSTEM\COMMDLG.DLL).
	GetModuleHandle(): just call MODULE_FindModule, it does the same job,
	only better.
	
	* [loader/ne_image.c]
	LocalInit() the heap of a DLL in NE_InitDLL. (This is probably
	not really correct, it seems that all programs and DLLs try to do
	this themselves. But they pass weird parameters.)
	NE_InitializeDLLs should also call NE_InitDLL for the passed hModule.
	
	* [loader/task.c] [misc/user.c]
	Finish global initializations in InitTask instead of InitApp, or
	all the DLLs will be initialized in InitTask without any available
	window classes!
diff --git a/memory/Imakefile b/memory/Imakefile
index 3364fec..8dbf16f 100644
--- a/memory/Imakefile
+++ b/memory/Imakefile
@@ -5,6 +5,7 @@
 SRCS = \
 	selector.c \
 	global.c \
+	ldt.c \
 	local.c
 
 OBJS = $(SRCS:.c=.o)
diff --git a/memory/global.c b/memory/global.c
index 7a0a144..d7fd15b 100644
--- a/memory/global.c
+++ b/memory/global.c
@@ -139,7 +139,7 @@
 
       /* Fixup the size */
 
-    if (size >= GLOBAL_MAX_ALLOC_SIZE - 0x0f) return 0;
+    if (size >= GLOBAL_MAX_ALLOC_SIZE - 0x1f) return 0;
     if (size == 0) size = 0x20;
     else size = (size + 0x1f) & ~0x1f;
 
diff --git a/memory/ldt.c b/memory/ldt.c
new file mode 100644
index 0000000..73e1260
--- /dev/null
+++ b/memory/ldt.c
@@ -0,0 +1,206 @@
+/*
+ * LDT manipulation functions
+ *
+ * Copyright 1993 Robert J. Amstadt
+ * Copyright 1995 Alexandre Julliard
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include "ldt.h"
+#include "stddebug.h"
+#include "debug.h"
+
+#ifndef WINELIB
+
+#ifdef linux
+#include <linux/unistd.h>
+#include <linux/head.h>
+#include <linux/ldt.h>
+
+_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount)
+#endif  /* linux */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+#include <machine/segments.h>
+
+extern int i386_get_ldt(int, union descriptor *, int);
+extern int i386_set_ldt(int, union descriptor *, int);
+#endif  /* __NetBSD__ || __FreeBSD__ */
+
+#endif  /* ifndef WINELIB */
+
+        
+/***********************************************************************
+ *           LDT_BytesToEntry
+ *
+ * Convert the raw bytes of the descriptor to an ldt_entry structure.
+ */
+static void LDT_BytesToEntry( unsigned long *buffer, ldt_entry *content )
+{
+    content->base  = (*buffer >> 16) & 0x0000ffff;
+    content->limit = *buffer & 0x0000ffff;
+    buffer++;
+    content->base  |= (*buffer & 0xff000000) | ((*buffer << 16) & 0x00ff0000);
+    content->limit |= (*buffer & 0x000f0000);
+    content->type           = (*buffer >> 10) & 3;
+    content->seg_32bit      = (*buffer & 0x00400000) != 0;
+    content->read_only      = (*buffer & 0x00000200) == 0;
+    content->limit_in_pages = (*buffer & 0x00800000) != 0;
+}
+
+
+/***********************************************************************
+ *           LDT_GetEntry
+ *
+ * Retrieve an LDT entry.
+ */
+int LDT_GetEntry( int entry, ldt_entry *content )
+{
+    int ret = 0;
+
+#ifdef WINELIB
+    content->base           = ldt_copy[entry].base;
+    content->limit          = ldt_copy[entry].limit;
+    content->type           = SEGMENT_DATA;
+    content->seg_32bit      = 0;
+    content->read_only      = 0;
+    content->limit_in_pages = 0;
+#else  /* WINELIB */
+
+#ifdef linux
+    int size = (entry + 1) * 2 * sizeof(long);
+    long *buffer = (long *) malloc( size );
+    ret = modify_ldt( 0, buffer, size );
+    LDT_BytesToEntry( &buffer[entry*2], content );
+    free( buffer );
+#endif  /* linux */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+    long buffer[2];
+    ret = i386_get_ldt( entry, (union descriptor *)buffer, 1 );
+    LDT_BytesToEntry( buffer, content );
+#endif  /* __NetBSD__ || __FreeBSD__ */
+
+#endif  /* WINELIB */
+
+    return ret;
+}
+
+
+/***********************************************************************
+ *           LDT_SetEntry
+ *
+ * Set an LDT entry.
+ */
+int LDT_SetEntry( int entry, ldt_entry *content )
+{
+    int ret = 0;
+
+    dprintf_ldt(stddeb,
+	  "LDT_SetEntry: entry=%04x base=%08lx limit=%05lx %s %d-bit flags=%c%c%c\n",
+          entry, content->base, content->limit,
+          content->limit_in_pages ? "pages" : "bytes",
+          content->seg_32bit ? 32 : 16,
+          content->read_only && (content->type & SEGMENT_CODE) ? '-' : 'r',
+          content->read_only || (content->type & SEGMENT_CODE) ? '-' : 'w',
+          (content->type & SEGMENT_CODE) ? 'x' : '-' );
+
+#ifndef WINELIB
+#ifdef linux
+    {
+        struct modify_ldt_ldt_s ldt_info;
+
+        memset( &ldt_info, 0, sizeof(ldt_info) );
+        ldt_info.entry_number   = entry;
+        ldt_info.base_addr      = content->base;
+        ldt_info.limit          = content->limit;
+        ldt_info.seg_32bit      = content->seg_32bit != 0;
+        ldt_info.contents       = content->type;
+        ldt_info.read_exec_only = content->read_only != 0;
+        ldt_info.limit_in_pages = content->limit_in_pages != 0;
+        ret = modify_ldt(1, &ldt_info, sizeof(ldt_info));
+    }
+#endif  /* linux */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+    {
+        long d[2];
+
+        d[0] = ((content->base & 0x0000ffff) << 16) |
+                (content->limit & 0x0ffff);
+        d[1] = (content->base & 0xff000000) |
+               ((content->base & 0x00ff0000)>>16) |
+               (content->limit & 0xf0000) |
+               (content->type << 10) |
+               ((content->read_only == 0) << 9) |
+               ((content->seg_32bit != 0) << 22) |
+               ((content->limit_in_pages != 0) << 23) |
+               0xf000;
+        ret = i386_set_ldt(entry, (union descriptor *)d, 1);
+        if (ret < 0)
+        {
+            perror("i386_set_ldt");
+            fprintf(stderr,
+		"Did you reconfigure the kernel with \"options USER_LDT\"?\n");
+    	    exit(1);
+        }
+    }
+#endif  /* __NetBSD__ || __FreeBSD__ */
+#endif  /* ifndef WINELIB */
+
+    if (ret < 0) return ret;
+    ldt_copy[entry].base = content->base;
+    if (!content->limit_in_pages) ldt_copy[entry].limit = content->limit;
+    else ldt_copy[entry].limit = (content->limit << 12) | 0x0fff;
+    return ret;
+}
+
+
+/***********************************************************************
+ *           LDT_Print
+ *
+ * Print the content of the LDT on stdout.
+ */
+void LDT_Print()
+{
+    int i;
+
+#ifdef WINELIB
+    for (i = 0; i < LDT_SIZE; i++)
+    {
+        if (ldt_copy[i].base || ldt_copy[i].limit)
+        {
+            fprintf( stderr, "%04x: sel=%04x base=%08x limit=%05x\n",
+                     i, ENTRY_TO_SELECTOR(i),
+                     ldt_copy[i].base, ldt_copy[i].limit );
+        }
+    }
+#else  /* WINELIB */
+
+    long buffer[2*LDT_SIZE];
+    ldt_entry content;
+    int n;
+
+#ifdef linux
+    n = modify_ldt( 0, buffer, sizeof(buffer) ) / 8;
+#endif  /* linux */
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+    n = i386_get_ldt( 0, (union descriptor *)buffer, LDT_SIZE );
+#endif  /* __NetBSD__ || __FreeBSD__ */
+    for (i = 0; i < n; i++)
+    {
+        LDT_BytesToEntry( &buffer[2*i], &content );
+        if (content.base || content.limit)
+        {
+            fprintf( stderr, "%04x: sel=%04x base=%08lx limit=%05lx %s type=%d\n",
+                    i, ENTRY_TO_SELECTOR(i),
+                    content.base, content.limit,
+                    content.limit_in_pages ? "(pages)" : "(bytes)",
+                    content.type );
+        }
+    }
+#endif  /* WINELIB */
+}
diff --git a/memory/local.c b/memory/local.c
index 134c1bf..1cd4a58 100644
--- a/memory/local.c
+++ b/memory/local.c
@@ -17,6 +17,7 @@
 #include "ldt.h"
 #include "instance.h"
 #include "local.h"
+#include "module.h"
 #include "stackframe.h"
 #include "toolhelp.h"
 #include "stddebug.h"
@@ -39,13 +40,15 @@
 } LOCALARENA;
 
 #define ARENA_HEADER_SIZE      4
+#define ARENA_HEADER( handle) ( ((handle) & ~3) - ARENA_HEADER_SIZE)
 
   /* Arena types (stored in 'prev' field of the arena) */
 #define LOCAL_ARENA_FREE       0
 #define LOCAL_ARENA_FIXED      1
 #define LOCAL_ARENA_MOVEABLE   3
 
-
+#define LMEM_NOCOMPACT 0x0010
+#define LMEM_NODISCARD 0x0020
 
 typedef struct
 {
@@ -93,6 +96,9 @@
 #define ARENA_NEXT(ptr,arena)      (ARENA_PTR(ptr,arena)->next)
 #define ARENA_FLAGS(ptr,arena)     (ARENA_PTR(ptr,arena)->prev & 3)
 
+  /* determine whether the handle belongs to a fixed or a moveable block */
+#define HANDLE_FIXED(handle) (((handle) & 3) == 0)
+#define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2)
 
 /***********************************************************************
  *           LOCAL_GetHeap
@@ -103,6 +109,7 @@
 {
     LOCALHEAPINFO *pInfo;
     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
+    dprintf_local( stddeb, "Heap at %04x\n", ptr->heap );
     if (!ptr->heap) return 0;
     pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
     if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
@@ -138,6 +145,7 @@
         next = pNext->next;
     }
 
+    dprintf_local( stddeb, "Local_AddFreeBlock %04x, next %04x\n", block, next );
       /* Insert the free block in the free-list */
 
     pArena->free_prev = pNext->free_prev;
@@ -202,6 +210,7 @@
 
       /* Remove the block from the free-list */
 
+    dprintf_local( stddeb, "Local_RemoveBlock\n");
     pArena = ARENA_PTR( baseptr, block );
     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
         LOCAL_RemoveFreeBlock( baseptr, block );
@@ -229,6 +238,7 @@
     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
     WORD arena;
 
+    if (!debugging_local) return;
     if (!pInfo)
     {
         printf( "Local Heap corrupted!  ds=%04x\n", ds );
@@ -266,7 +276,8 @@
         }
         if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
         {
-            printf( "*** arena->next->prev != arena\n" );
+            printf( "*** arena->next->prev != arena (%04x, %04x)\n",
+		   pArena->next, ARENA_PTR(ptr,pArena->next)->prev);
             break;
         }
         arena = pArena->next;
@@ -283,7 +294,8 @@
     WORD heapInfoArena, freeArena, lastArena;
     LOCALHEAPINFO *pHeapInfo;
     LOCALARENA *pArena, *pFirstArena, *pLastArena;
-
+    NE_MODULE *pModule;
+    
       /* The initial layout of the heap is: */
       /* - first arena         (FIXED)      */
       /* - heap info structure (FIXED)      */
@@ -295,14 +307,24 @@
     ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
     pHeapInfo = LOCAL_GetHeap(selector);
       /* If there's already a local heap in this segment, */
-      /* we simply return TRUE. Helps some programs, but  */
-      /* does not seem to be 100% correct yet (there are  */
-      /* still some "heap corrupted" messages in LocalAlloc */
+      /* we simply return TRUE. This helps some programs. */
     if (pHeapInfo)  {
       dprintf_local(stddeb,"LocalInit: Heap %04x initialized twice.\n",selector);
       if (debugging_local) LOCAL_PrintHeap(selector);
       return TRUE;
     }
+
+#if 0
+      /* Check if the segment is the DGROUP of a module */
+
+    if ((pModule = (NE_MODULE *)GlobalLock( GetExePtr( selector ) )))
+    {
+        SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
+        if (pModule->dgroup && (pSeg->selector == selector))
+            start = max( start, pSeg->minsize );
+    }
+#endif
+
     start = LALIGN( max( start, sizeof(INSTANCEDATA) ) );
     heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
     freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
@@ -335,6 +357,7 @@
     pHeapInfo->items   = 4;
     pHeapInfo->first   = start;
     pHeapInfo->last    = lastArena;
+    pHeapInfo->htable  = 0;
     pHeapInfo->hdelta  = 0x20;
     pHeapInfo->extra   = 0x200;
     pHeapInfo->minsize = lastArena - freeArena;
@@ -352,7 +375,7 @@
       /* Initialise the last block */
 
     pLastArena = ARENA_PTR( ptr, lastArena );
-    pLastArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
+    pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
     pLastArena->next      = lastArena;  /* this one */
     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
     pLastArena->free_prev = freeArena;
@@ -361,49 +384,80 @@
       /* Store the local heap address in the instance data */
 
     ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
+    LOCAL_PrintHeap( selector );
     return TRUE;
 }
 
+/***********************************************************************
+ *           LOCAL_Compact
+ */
+static WORD LOCAL_Compact( WORD ds, WORD minfree, WORD flags )
+{
+    if (flags & LMEM_NOCOMPACT) return 0;
+    
+    return 0;
+}
 
 /***********************************************************************
- *           LOCAL_Alloc
- *
- * Implementation of LocalAlloc().
+ *           LOCAL_FindFreeBlock
  */
-HLOCAL LOCAL_Alloc( WORD ds, WORD flags, WORD size )
+static HLOCAL LOCAL_FindFreeBlock( WORD ds, WORD size )
 {
     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
     LOCALHEAPINFO *pInfo;
     LOCALARENA *pArena;
     WORD arena;
 
-    dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
-
-      /* Find a suitable free block */
-
     if (!(pInfo = LOCAL_GetHeap( ds ))) {
-      dprintf_local( stddeb, "LocalAlloc: Heap not found\n");
-      LOCAL_PrintHeap(ds);
-      return 0;
+	dprintf_local( stddeb, "Local_FindFreeBlock: Local heap not found\n" );
+	LOCAL_PrintHeap(ds);
+	return 0;
     }
-    size += ARENA_HEADER_SIZE;
-    size = LALIGN( max( size, sizeof(LOCALARENA) ) );
+
     arena = pInfo->first;
     pArena = ARENA_PTR( ptr, arena );
-    for (;;)
-    {
-        if (arena == pArena->free_next) {
-	  fprintf(stderr, "Local heap full\n");
-	  if (debugging_local) LOCAL_PrintHeap(ds);
-          return 0;  /* not found */
-	}
+    while (arena != pArena->free_next) {
         arena = pArena->free_next;
         pArena = ARENA_PTR( ptr, arena );
-        if (pArena->size >= size) break;
+        if (pArena->size >= size) return arena;
+    }
+    dprintf_local( stddeb, "Local_FindFreeBlock: not enough space\n" );
+    if (debugging_local) LOCAL_PrintHeap(ds);
+    return 0;
+}
+
+/***********************************************************************
+ *           LOCAL_GetBlock
+ */
+static HLOCAL LOCAL_GetBlock( WORD ds, WORD size, WORD flags )
+{
+    char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
+    LOCALHEAPINFO *pInfo;
+    LOCALARENA *pArena;
+    WORD arena;
+
+    if (!(pInfo = LOCAL_GetHeap( ds ))) {
+	dprintf_local( stddeb, "Local_GetBlock: Local heap not found\n");
+	LOCAL_PrintHeap(ds);
+	return 0;
+    }
+    
+    size += ARENA_HEADER_SIZE;
+    size = LALIGN( max( size, sizeof(LOCALARENA) ) );
+
+      /* Find a suitable free block */
+    arena = LOCAL_FindFreeBlock( ds, size );
+    if (arena == 0) {
+	LOCAL_Compact( ds, size, flags );
+	arena = LOCAL_FindFreeBlock( ds, size );
+    }
+    if (arena == 0) {
+	fprintf( stderr, "Local_GetBlock: not enough space!\n" );
     }
 
+    dprintf_local( stddeb, "LOCAL_GetBlock size = %04x\n", size );
       /* Make a block out of the free arena */
-
+    pArena = ARENA_PTR( ptr, arena );
     if (pArena->size > size + LALIGN(sizeof(LOCALARENA)))
     {
         LOCAL_AddBlock( ptr, arena, arena+size );
@@ -412,97 +466,78 @@
     }
     LOCAL_RemoveFreeBlock( ptr, arena );
 
-    dprintf_local( stddeb, "LocalAlloc: returning %04x\n",
-                   arena + ARENA_HEADER_SIZE );
+    dprintf_local( stddeb, "Local_GetBlock: arena at %04x\n", arena );
     return arena + ARENA_HEADER_SIZE;
 }
 
-
 /***********************************************************************
- *           LOCAL_ReAlloc
- *
- * Implementation of LocalReAlloc().
+ *           LOCAL_NewHTable
  */
-HLOCAL LOCAL_ReAlloc( WORD ds, HLOCAL handle, WORD size, WORD flags )
+static BOOL LOCAL_NewHTable( WORD ds )
 {
     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
     LOCALHEAPINFO *pInfo;
-    LOCALARENA *pArena, *pNext;
-    WORD arena, newhandle;
+    HLOCAL handle;
 
-    dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
-                   handle, size, flags, ds );
-    if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
-    arena = handle - ARENA_HEADER_SIZE;
-    pArena = ARENA_PTR( ptr, arena );
-    if (flags & LMEM_MODIFY) {
-      dprintf_local( stddeb, "LMEM_MODIFY set\n");
-      return handle;
-    }
-    if (!size) size = 1;
-    size = LALIGN( size );
-
-      /* Check for size reduction */
-
-    if (size < pArena->next - handle)
-    {
-        if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
-        {
-              /* It is worth making a new free block */
-            LOCAL_AddBlock( ptr, arena, handle + size );
-            LOCAL_AddFreeBlock( ptr, handle + size );
-            pInfo->items++;
-        }
-        dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
-        return handle;
+    dprintf_local( stddeb, "Local_NewHTable\n" );
+    if (!(pInfo = LOCAL_GetHeap( ds ))) {
+      dprintf_local( stddeb, "Local heap not found\n");
+      LOCAL_PrintHeap(ds);
+      return FALSE;
     }
 
-      /* Check if the next block is free */
-
-    pNext = ARENA_PTR( ptr, pArena->next );
-    if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
-        (size <= pNext->next - handle))
-    {
-        LOCAL_RemoveBlock( ptr, pArena->next );
-        if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
-        {
-              /* It is worth making a new free block */
-            LOCAL_AddBlock( ptr, arena, handle + size );
-            LOCAL_AddFreeBlock( ptr, handle + size );
-            pInfo->items++;
-        }
-        dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
-        return handle;
-    }
-
-      /* Now we have to allocate a new block */
-
-    newhandle = LOCAL_Alloc( ds, flags, size );
-    if (!newhandle) return 0;
-    memcpy( ptr + newhandle, ptr + handle, pArena->next - handle );
-    LOCAL_Free( ds, handle );
-    dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
-    return newhandle;
+    handle = LOCAL_GetBlock( ds, pInfo->hdelta*4 + 2, LMEM_FIXED );
+    if (handle == 0) return FALSE;
+    *(WORD *)(ptr + handle) = 0; /* no handles in this block yet */
+    pInfo->htable = handle;
+    return TRUE;
 }
 
+/***********************************************************************
+ *           LOCAL_GetNewHandle
+ */
+static HLOCAL LOCAL_GetNewHandle( WORD ds )
+{
+    char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
+    LOCALHEAPINFO *pInfo;
+    WORD count;
+    
+    if (!(pInfo = LOCAL_GetHeap( ds ))) {
+	dprintf_local( stddeb, "LOCAL_GetNewHandle: Local heap not found\n");
+	LOCAL_PrintHeap(ds);
+	return 0;
+    }
+    /* Check if we need a new handle table */
+    if (pInfo->htable == 0) 
+        if (!LOCAL_NewHTable( ds )) return 0;
+    if (*(WORD *)(ptr + pInfo->htable) == pInfo->hdelta) 
+        if (!LOCAL_NewHTable( ds )) return 0;
+
+    /* increase count */
+    count = (*(WORD *)(ptr + pInfo->htable))++;
+    dprintf_local( stddeb, "Local_GetNewHandle: %04x\n", pInfo->htable + 2 + 4*count );
+    return pInfo->htable + 2 + 4*count;
+}
 
 /***********************************************************************
- *           LOCAL_Free
- *
- * Implementation of LocalFree().
+ *           LOCAL_FreeArena
  */
-HLOCAL LOCAL_Free( WORD ds, HLOCAL handle )
+HLOCAL LOCAL_FreeArena( WORD ds, WORD arena )
 {
     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
     LOCALHEAPINFO *pInfo;
     LOCALARENA *pArena, *pPrev, *pNext;
-    WORD arena;
 
-    dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
-    if (!(pInfo = LOCAL_GetHeap( ds ))) return handle;
-    arena = handle - ARENA_HEADER_SIZE;
+    dprintf_local( stddeb, "LocalFreeArena: %04x ds=%04x\n", arena, ds );
+    if (!(pInfo = LOCAL_GetHeap( ds ))) return arena;
+
     pArena = ARENA_PTR( ptr, arena );
-    if ((pArena->prev & 3) == LOCAL_ARENA_FREE) return handle;
+    if ((pArena->prev & 3) == LOCAL_ARENA_FREE) {
+	/* shouldn't happen */
+	fprintf( stderr, "LocalFreeArena: Trying to free a block twice!\n" );
+	LOCAL_PrintHeap( ds );
+	return arena;
+    }
 
       /* Check if we can merge with the previous block */
 
@@ -533,6 +568,153 @@
 
 
 /***********************************************************************
+ *           LOCAL_Free
+ *
+ * Implementation of LocalFree().
+ */
+HLOCAL LOCAL_Free( WORD ds, HLOCAL handle )
+{
+    char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
+    WORD arena;
+
+    dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
+    
+    if (HANDLE_FIXED( handle )) {
+	arena = ARENA_HEADER( handle );
+    } else {
+	arena = ARENA_HEADER( *(WORD *)(ptr + handle) );
+	dprintf_local( stddeb, "LocalFree: real block at %04x\n", arena);
+    }
+    arena = LOCAL_FreeArena( ds, arena );
+    if (arena != 0) return handle; /* couldn't free it */
+    return 0;
+}
+
+
+/***********************************************************************
+ *           LOCAL_Alloc
+ *
+ * Implementation of LocalAlloc().
+ */
+HLOCAL LOCAL_Alloc( WORD ds, WORD flags, WORD size )
+{
+    char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
+    HLOCAL handle;
+    
+    dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
+
+    if (flags & LMEM_MOVEABLE) {
+	LOCALHANDLEENTRY *plhe;
+	HLOCAL hmem;
+	
+	hmem = LOCAL_GetBlock( ds, size + 2, flags );
+	if (hmem == 0) return 0;
+	handle = LOCAL_GetNewHandle( ds );
+	if (handle == 0) {
+	    fprintf( stderr, "LocalAlloc: couldn't get handle\n");
+	    LOCAL_FreeArena( ds, ARENA_HEADER(hmem) );
+	    return 0;
+	}
+	*(WORD *)(ptr + hmem) = handle;
+	plhe = (LOCALHANDLEENTRY *)(ptr + handle);
+	plhe->addr = hmem + 2;
+	plhe->lock = 0;
+    } else {
+	handle = LOCAL_GetBlock( ds, size, flags );
+    }
+    return handle;
+}
+
+
+/***********************************************************************
+ *           LOCAL_ReAlloc
+ *
+ * Implementation of LocalReAlloc().
+ */
+HLOCAL LOCAL_ReAlloc( WORD ds, HLOCAL handle, WORD size, WORD flags )
+{
+    char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
+    LOCALHEAPINFO *pInfo;
+    LOCALARENA *pArena, *pNext;
+    WORD arena, newhandle, blockhandle, nextarena;
+
+    dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
+                   handle, size, flags, ds );
+    if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
+    
+    if (HANDLE_FIXED( handle )) {
+	blockhandle = handle;
+    } else {
+	size += 2;
+	blockhandle = *(WORD *)(ptr + handle);
+	dprintf_local( stddeb, "  blockhandle %04x (%04x)\n", blockhandle,
+		      *(WORD *)(ptr + blockhandle - 2));
+    }
+    arena = ARENA_HEADER( blockhandle );
+    dprintf_local( stddeb, "LocalReAlloc: arena is %04x\n", arena );
+    pArena = ARENA_PTR( ptr, arena );
+    
+    if (flags & LMEM_MODIFY) {
+      dprintf_local( stddeb, "LMEM_MODIFY set\n");
+      return handle;
+    }
+    if (!size) size = 1;
+    size = LALIGN( size );
+    nextarena = LALIGN(blockhandle + size);
+
+      /* Check for size reduction */
+
+    if (nextarena < pArena->next)
+    {
+        if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
+        {
+	    dprintf_local( stddeb, "size reduction, making new free block\n");
+              /* It is worth making a new free block */
+            LOCAL_AddBlock( ptr, arena, nextarena );
+            LOCAL_AddFreeBlock( ptr, nextarena );
+            pInfo->items++;
+        }
+        dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
+        return handle;
+    }
+
+      /* Check if the next block is free */
+
+    pNext = ARENA_PTR( ptr, pArena->next );
+    if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
+        (nextarena <= pNext->next))
+    {
+        LOCAL_RemoveBlock( ptr, pArena->next );
+        if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
+        {
+	    dprintf_local( stddeb, "size increase, making new free block\n");
+              /* It is worth making a new free block */
+            LOCAL_AddBlock( ptr, arena, nextarena );
+            LOCAL_AddFreeBlock( ptr, nextarena );
+            pInfo->items++;
+        }
+        dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
+        return handle;
+    }
+
+      /* Now we have to allocate a new block */
+
+    newhandle = LOCAL_GetBlock( ds, size, flags );
+    if (newhandle == 0) return 0;
+    memcpy( ptr + newhandle, ptr + (arena + ARENA_HEADER_SIZE), size );
+    LOCAL_FreeArena( ds, arena );
+    if (HANDLE_MOVEABLE( handle )) {
+	newhandle += 2;
+	dprintf_local( stddeb, "LocalReAlloc: fixing handle\n");
+	*(WORD *)(ptr + handle) = newhandle;
+	newhandle = handle;
+    }
+    dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
+    return newhandle;
+}
+
+
+/***********************************************************************
  *           LOCAL_Size
  *
  * Implementation of LocalSize().
@@ -589,6 +771,11 @@
  */
 WORD LocalLock( HLOCAL handle )
 {
+    char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
+
+    if (HANDLE_MOVEABLE(handle)) {
+	handle = *(WORD *)(ptr + handle);
+    }
     return handle;
 }
 
@@ -616,7 +803,12 @@
  */
 HLOCAL LocalHandle( WORD addr )
 {
+    char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
+
     dprintf_local( stddeb, "LocalHandle: %04x\n", addr );
+    if (HANDLE_MOVEABLE( addr )) {
+	addr = *(WORD *)(ptr + addr - 2);
+    }
     return addr;
 }