Release 960428
[wine] / memory / local.c
1 /*
2  * Local heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 /*
8  * Note:
9  * All local heap functions need the current DS as first parameter
10  * when called from the emulation library, so they take one more
11  * parameter than usual.
12  */
13
14 #ifndef WINELIB
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include "windows.h"
19 #include "ldt.h"
20 #include "instance.h"
21 #include "local.h"
22 #include "module.h"
23 #include "stackframe.h"
24 #include "toolhelp.h"
25 #include "stddebug.h"
26 #include "debug.h"
27
28
29 #ifndef WINELIB
30 #pragma pack(1)
31 #endif
32
33 typedef struct
34 {
35 /* Arena header */
36     WORD prev;          /* Previous arena | arena type */
37     WORD next;          /* Next arena */
38 /* Start of the memory block or free-list info */
39     WORD size;          /* Size of the free block */
40     WORD free_prev;     /* Previous free block */
41     WORD free_next;     /* Next free block */
42 } LOCALARENA;
43
44 #define ARENA_HEADER_SIZE      4
45 #define ARENA_HEADER( handle) ( ((handle) & ~3) - ARENA_HEADER_SIZE)
46
47   /* Arena types (stored in 'prev' field of the arena) */
48 #define LOCAL_ARENA_FREE       0
49 #define LOCAL_ARENA_FIXED      1
50 #define LOCAL_ARENA_MOVEABLE   3
51
52 /* Layout of a handle entry table
53  *
54  * WORD                     count of entries
55  * LOCALHANDLEENTRY[count]  entries
56  * WORD                     near ptr to next table
57  */
58 typedef struct
59 {
60     WORD addr;                /* Address of the MOVEABLE block */
61     BYTE flags;               /* Flags for this block */
62     BYTE lock;                /* Lock count */
63 } LOCALHANDLEENTRY;
64
65 typedef struct
66 {
67     WORD check;                 /* 00 Heap checking flag */
68     WORD freeze;                /* 02 Heap frozen flag */
69     WORD items;                 /* 04 Count of items on the heap */
70     WORD first;                 /* 06 First item of the heap */
71     WORD pad1;                  /* 08 Always 0 */
72     WORD last;                  /* 0a Last item of the heap */
73     WORD pad2;                  /* 0c Always 0 */
74     BYTE ncompact;              /* 0e Compactions counter */
75     BYTE dislevel;              /* 0f Discard level */
76     DWORD distotal;             /* 10 Total bytes discarded */
77     WORD htable;                /* 14 Pointer to handle table */
78     WORD hfree;                 /* 16 Pointer to free handle table */
79     WORD hdelta;                /* 18 Delta to expand the handle table */
80     WORD expand;                /* 1a Pointer to expand function (unused) */
81     WORD pstat;                 /* 1c Pointer to status structure (unused) */
82     FARPROC notify WINE_PACKED; /* 1e Pointer to LocalNotify() function */
83     WORD lock;                  /* 22 Lock count for the heap */
84     WORD extra;                 /* 24 Extra bytes to allocate when expanding */
85     WORD minsize;               /* 26 Minimum size of the heap */
86     WORD magic;                 /* 28 Magic number */
87 } LOCALHEAPINFO;
88
89 #ifndef WINELIB
90 #pragma pack(4)
91 #endif
92
93 #define LOCAL_HEAP_MAGIC  0x484c  /* 'LH' */
94
95
96   /* All local heap allocations are aligned on 4-byte boundaries */
97 #define LALIGN(word)          (((word) + 3) & ~3)
98
99 #define ARENA_PTR(ptr,arena)       ((LOCALARENA *)((char*)(ptr)+(arena)))
100 #define ARENA_PREV(ptr,arena)      (ARENA_PTR(ptr,arena)->prev & ~3)
101 #define ARENA_NEXT(ptr,arena)      (ARENA_PTR(ptr,arena)->next)
102 #define ARENA_FLAGS(ptr,arena)     (ARENA_PTR(ptr,arena)->prev & 3)
103
104   /* determine whether the handle belongs to a fixed or a moveable block */
105 #define HANDLE_FIXED(handle) (((handle) & 3) == 0)
106 #define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2)
107
108 /***********************************************************************
109  *           LOCAL_GetHeap
110  *
111  * Return a pointer to the local heap, making sure it exists.
112  */
113 static LOCALHEAPINFO *LOCAL_GetHeap( WORD ds )
114 {
115     LOCALHEAPINFO *pInfo;
116     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
117     dprintf_local( stddeb, "Heap at %p, %04x\n", ptr, ptr->heap );
118     if (!ptr->heap) return NULL;
119     if (IsBadReadPtr((SEGPTR)MAKELONG( ptr->heap, ds ), sizeof(LOCALHEAPINFO)))
120         return NULL;
121     pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
122     if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
123     return pInfo;
124 }
125
126
127 /***********************************************************************
128  *           LOCAL_MakeBlockFree
129  *
130  * Make a block free, inserting it in the free-list.
131  * 'block' is the handle of the block arena; 'baseptr' points to
132  * the beginning of the data segment containing the heap.
133  */
134 static void LOCAL_MakeBlockFree( char *baseptr, WORD block )
135 {
136     LOCALARENA *pArena, *pNext;
137     WORD next;
138
139       /* Mark the block as free */
140
141     pArena = ARENA_PTR( baseptr, block );
142     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
143     pArena->size = pArena->next - block;
144     
145       /* Find the next free block (last block is always free) */
146
147     next = pArena->next;
148     for (;;)
149     {
150         pNext = ARENA_PTR( baseptr, next );
151         if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
152         next = pNext->next;
153     }
154
155     dprintf_local( stddeb, "Local_AddFreeBlock %04x, next %04x\n", block, next );
156       /* Insert the free block in the free-list */
157
158     pArena->free_prev = pNext->free_prev;
159     pArena->free_next = next;
160     ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
161     pNext->free_prev  = block;
162 }
163
164
165 /***********************************************************************
166  *           LOCAL_RemoveFreeBlock
167  *
168  * Remove a block from the free-list.
169  * 'block' is the handle of the block arena; 'baseptr' points to
170  * the beginning of the data segment containing the heap.
171  */
172 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
173 {
174       /* Mark the block as fixed */
175
176     LOCALARENA *pArena = ARENA_PTR( baseptr, block );
177     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
178
179       /* Remove it from the list */
180
181     ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
182     ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
183 }
184
185
186 /***********************************************************************
187  *           LOCAL_AddBlock
188  *
189  * Insert a new block in the heap.
190  * 'new' is the handle of the new block arena; 'baseptr' points to
191  * the beginning of the data segment containing the heap; 'prev' is
192  * the block before the new one.
193  */
194 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
195 {
196     LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
197     LOCALARENA *pNew  = ARENA_PTR( baseptr, new );
198
199     pNew->prev = prev | LOCAL_ARENA_FIXED;
200     pNew->next = pPrev->next;
201     ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
202     ARENA_PTR(baseptr,pPrev->next)->prev |= new;
203     pPrev->next = new;
204 }
205
206
207 /***********************************************************************
208  *           LOCAL_RemoveBlock
209  *
210  * Remove a block from the heap.
211  * 'block' is the handle of the block arena; 'baseptr' points to
212  * the beginning of the data segment containing the heap.
213  */
214 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
215 {
216     LOCALARENA *pArena, *pTmp;
217
218       /* Remove the block from the free-list */
219
220     dprintf_local( stddeb, "Local_RemoveBlock\n");
221     pArena = ARENA_PTR( baseptr, block );
222     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
223         LOCAL_RemoveFreeBlock( baseptr, block );
224
225       /* If the previous block is free, expand its size */
226
227     pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
228     if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
229         pTmp->size += pArena->next - block;
230
231       /* Remove the block from the linked list */
232
233     pTmp->next = pArena->next;
234     pTmp = ARENA_PTR( baseptr, pArena->next );
235     pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
236 }
237
238
239 /***********************************************************************
240  *           LOCAL_PrintHeap
241  */
242 static void LOCAL_PrintHeap( WORD ds )
243 {
244     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
245     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
246     WORD arena;
247
248     if (!debugging_local) return;
249     if (!pInfo)
250     {
251         printf( "Local Heap corrupted!  ds=%04x\n", ds );
252         return;
253     }
254     printf( "Local Heap  ds=%04x first=%04x last=%04x items=%d\n",
255             ds, pInfo->first, pInfo->last, pInfo->items );
256
257     arena = pInfo->first;
258     for (;;)
259     {
260         LOCALARENA *pArena = ARENA_PTR(ptr,arena);
261         printf( "  %04x: prev=%04x next=%04x type=%d\n", arena,
262                 pArena->prev & ~3, pArena->next, pArena->prev & 3 );
263         if (arena == pInfo->first)
264         {
265             printf( "        size=%d free_prev=%04x free_next=%04x\n",
266                     pArena->size, pArena->free_prev, pArena->free_next );
267         }
268         if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
269         {
270             printf( "        size=%d free_prev=%04x free_next=%04x\n",
271                     pArena->size, pArena->free_prev, pArena->free_next );
272             if (pArena->next == arena) break;  /* last one */
273             if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
274             {
275                 printf( "*** arena->free_next->free_prev != arena\n" );
276                 break;
277             }
278         }
279         if (pArena->next == arena)
280         {
281             printf( "*** last block is not marked free\n" );
282             break;
283         }
284         if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
285         {
286             printf( "*** arena->next->prev != arena (%04x, %04x)\n",
287                    pArena->next, ARENA_PTR(ptr,pArena->next)->prev);
288             break;
289         }
290         arena = pArena->next;
291     }
292 }
293
294
295 /***********************************************************************
296  *           LocalInit   (KERNEL.4)
297  */
298 BOOL LocalInit( HANDLE selector, WORD start, WORD end )
299 {
300     char *ptr;
301     WORD heapInfoArena, freeArena, lastArena;
302     LOCALHEAPINFO *pHeapInfo;
303     LOCALARENA *pArena, *pFirstArena, *pLastArena;
304     NE_MODULE *pModule;
305     
306       /* The initial layout of the heap is: */
307       /* - first arena         (FIXED)      */
308       /* - heap info structure (FIXED)      */
309       /* - large free block    (FREE)       */
310       /* - last arena          (FREE)       */
311
312     dprintf_local(stddeb, "LocalInit: %04x %04x-%04x\n", selector, start, end);
313     if (!selector) selector = CURRENT_DS;
314     pHeapInfo = LOCAL_GetHeap(selector);
315
316     if (pHeapInfo)  {
317       fprintf( stderr, "LocalInit: Heap %04x initialized twice.\n", selector);
318       if (debugging_local) LOCAL_PrintHeap(selector);
319     }
320
321     if (start == 0) {
322       /* Check if the segment is the DGROUP of a module */
323
324         if ((pModule = MODULE_GetPtr( GetExePtr( selector ) )))
325         {
326             SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
327             if (pModule->dgroup && (pSeg->selector == selector)) {
328                 /* We can't just use the simple method of using the value
329                  * of minsize + stacksize, since there are programs that
330                  * resize the data segment before calling InitTask(). So,
331                  * we must put it at the end of the segment */
332                 start = GlobalSize( GlobalHandle( selector ) );
333                 start -= end;
334                 end += start;
335                 dprintf_local( stddeb," new start %04x, minstart: %04x\n", start, pSeg->minsize + pModule->stack_size);
336             }
337         }
338     }
339     ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
340
341     start = LALIGN( MAX( start, sizeof(INSTANCEDATA) ) );
342     heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
343     freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
344                         + sizeof(LOCALHEAPINFO) );
345     lastArena = (end - sizeof(LOCALARENA)) & ~3;
346
347       /* Make sure there's enough space.       */
348
349     if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE;
350
351       /* Initialise the first arena */
352
353     pFirstArena = ARENA_PTR( ptr, start );
354     pFirstArena->prev      = start | LOCAL_ARENA_FIXED;
355     pFirstArena->next      = heapInfoArena;
356     pFirstArena->size      = LALIGN(sizeof(LOCALARENA));
357     pFirstArena->free_prev = start;  /* this one */
358     pFirstArena->free_next = freeArena;
359
360       /* Initialise the arena of the heap info structure */
361
362     pArena = ARENA_PTR( ptr, heapInfoArena );
363     pArena->prev = start | LOCAL_ARENA_FIXED;
364     pArena->next = freeArena;
365
366       /* Initialise the heap info structure */
367
368     pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
369     memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
370     pHeapInfo->items   = 4;
371     pHeapInfo->first   = start;
372     pHeapInfo->last    = lastArena;
373     pHeapInfo->htable  = 0;
374     pHeapInfo->hdelta  = 0x20;
375     pHeapInfo->extra   = 0x200;
376     pHeapInfo->minsize = lastArena - freeArena;
377     pHeapInfo->magic   = LOCAL_HEAP_MAGIC;
378
379       /* Initialise the large free block */
380
381     pArena = ARENA_PTR( ptr, freeArena );
382     pArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
383     pArena->next      = lastArena;
384     pArena->size      = lastArena - freeArena;
385     pArena->free_prev = start;
386     pArena->free_next = lastArena;
387
388       /* Initialise the last block */
389
390     pLastArena = ARENA_PTR( ptr, lastArena );
391     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
392     pLastArena->next      = lastArena;  /* this one */
393     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
394     pLastArena->free_prev = freeArena;
395     pLastArena->free_next = lastArena;  /* this one */
396
397       /* Store the local heap address in the instance data */
398
399     ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
400     LOCAL_PrintHeap( selector );
401     return TRUE;
402 }
403
404 /***********************************************************************
405  *           LOCAL_GrowHeap
406  */
407 static void LOCAL_GrowHeap( WORD ds )
408 {
409     HANDLE hseg = GlobalHandle( ds );
410     LONG oldsize = GlobalSize( hseg );
411     LONG end;
412     LOCALHEAPINFO *pHeapInfo;
413     WORD freeArena, lastArena;
414     LOCALARENA *pArena, *pLastArena;
415     char *ptr;
416     
417     /* if nothing can be gained, return */
418     if (oldsize > 0xfff0) return;
419     hseg = GlobalReAlloc( hseg, 0x10000, GMEM_FIXED );
420     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
421     pHeapInfo = LOCAL_GetHeap( ds );
422     if (pHeapInfo == NULL) {
423         fprintf( stderr, "Local_GrowHeap: heap not found\n" );
424         return;
425     }
426     end = GlobalSize( hseg );
427     lastArena = (end - sizeof(LOCALARENA)) & ~3;
428
429       /* Update the HeapInfo */
430     pHeapInfo->items++;
431     freeArena = pHeapInfo->last;
432     pHeapInfo->last = lastArena;
433     pHeapInfo->minsize += end - oldsize;
434     
435       /* grow the old last block */
436       /* FIXME: merge two adjacent free blocks */
437     pArena = ARENA_PTR( ptr, freeArena );
438     pArena->size      = lastArena - freeArena;
439     pArena->next      = lastArena;
440     pArena->free_next = lastArena;
441     
442       /* Initialise the new last block */
443
444     pLastArena = ARENA_PTR( ptr, lastArena );
445     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
446     pLastArena->next      = lastArena;  /* this one */
447     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
448     pLastArena->free_prev = freeArena;
449     pLastArena->free_next = lastArena;  /* this one */
450     
451     dprintf_local( stddeb, "Heap expanded\n" );
452     LOCAL_PrintHeap( ds );
453 }
454
455 /***********************************************************************
456  *           LOCAL_Compact
457  */
458 static WORD LOCAL_Compact( WORD ds, WORD minfree, WORD flags )
459 {
460     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
461     LOCALHEAPINFO *pInfo;
462     LOCALARENA *pArena;
463     WORD arena;
464     WORD freespace = 0;
465     
466     if (!(pInfo = LOCAL_GetHeap( ds )))
467     {
468         fprintf( stderr, "Local_FindFreeBlock: Local heap not found\n" );
469         LOCAL_PrintHeap(ds);
470         return 0;
471     }
472
473     arena = pInfo->first;
474     pArena = ARENA_PTR( ptr, arena );
475     while (arena != pArena->free_next) {
476         arena = pArena->free_next;
477         pArena = ARENA_PTR( ptr, arena );
478         if (pArena->size >= freespace) freespace = pArena->size;
479     }
480
481     if(freespace < ARENA_HEADER_SIZE)
482         freespace = 0;
483     else
484         freespace -= ARENA_HEADER_SIZE;
485     
486     if (flags & LMEM_NOCOMPACT) return freespace;
487     
488     if (flags & LMEM_NODISCARD) return freespace;
489     return freespace;
490 }
491
492 /***********************************************************************
493  *           LOCAL_FindFreeBlock
494  */
495 static HLOCAL LOCAL_FindFreeBlock( WORD ds, WORD size )
496 {
497     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
498     LOCALHEAPINFO *pInfo;
499     LOCALARENA *pArena;
500     WORD arena;
501
502     if (!(pInfo = LOCAL_GetHeap( ds )))
503     {
504         fprintf( stderr, "Local_FindFreeBlock: Local heap not found\n" );
505         LOCAL_PrintHeap(ds);
506         return 0;
507     }
508
509     arena = pInfo->first;
510     pArena = ARENA_PTR( ptr, arena );
511     for (;;) {
512         arena = pArena->free_next;
513         pArena = ARENA_PTR( ptr, arena );
514         if (arena == pArena->free_next) break;
515         if (pArena->size >= size) return arena;
516     }
517     dprintf_local( stddeb, "Local_FindFreeBlock: not enough space\n" );
518     if (debugging_local) LOCAL_PrintHeap(ds);
519     return 0;
520 }
521
522 /***********************************************************************
523  *           LOCAL_GetBlock
524  * The segment may get moved around in this function, so all callers
525  * should reset their pointer variables.
526  */
527 static HLOCAL LOCAL_GetBlock( WORD ds, WORD size, WORD flags )
528 {
529     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
530     LOCALHEAPINFO *pInfo;
531     LOCALARENA *pArena;
532     WORD arena;
533
534     if (!(pInfo = LOCAL_GetHeap( ds )))
535     {
536         fprintf( stderr, "Local_GetBlock: Local heap not found\n");
537         LOCAL_PrintHeap(ds);
538         return 0;
539     }
540     
541     size += ARENA_HEADER_SIZE;
542     size = LALIGN( MAX( size, sizeof(LOCALARENA) ) );
543
544       /* Find a suitable free block */
545     arena = LOCAL_FindFreeBlock( ds, size );
546     if (arena == 0) {
547         /* no space: try to make some */
548         LOCAL_Compact( ds, size, flags );
549         arena = LOCAL_FindFreeBlock( ds, size );
550     }
551     if (arena == 0) {
552         /* still no space: try to grow the segment */
553         LOCAL_GrowHeap( ds );
554         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
555         pInfo = LOCAL_GetHeap( ds );
556         arena = LOCAL_FindFreeBlock( ds, size );
557     }
558     if (arena == 0) {
559         fprintf( stderr, "Local_GetBlock: not enough space in heap %04x for %d bytes\n",
560                  ds, size );
561         return 0;
562     }
563
564       /* Make a block out of the free arena */
565     pArena = ARENA_PTR( ptr, arena );
566     dprintf_local( stddeb, "LOCAL_GetBlock size = %04x, arena at %04x size %04x\n", size,
567                    arena, pArena->size );
568     if (pArena->size > size + LALIGN(sizeof(LOCALARENA)))
569     {
570         LOCAL_AddBlock( ptr, arena, arena+size );
571         LOCAL_MakeBlockFree( ptr, arena+size );
572         pInfo->items++;
573     }
574     LOCAL_RemoveFreeBlock( ptr, arena );
575
576     if (flags & LMEM_ZEROINIT) {
577         memset( (char *)pArena + ARENA_HEADER_SIZE, 0, size - ARENA_HEADER_SIZE );
578     }
579
580     dprintf_local( stddeb, "Local_GetBlock: arena at %04x\n", arena );
581     return arena + ARENA_HEADER_SIZE;
582 }
583
584
585 /***********************************************************************
586  *           LOCAL_NewHTable
587  */
588 static BOOL LOCAL_NewHTable( WORD ds )
589 {
590     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
591     LOCALHEAPINFO *pInfo;
592     LOCALHANDLEENTRY *pEntry;
593     HLOCAL handle;
594     int i;
595
596     dprintf_local( stddeb, "Local_NewHTable\n" );
597     if (!(pInfo = LOCAL_GetHeap( ds )))
598     {
599         fprintf( stderr, "Local heap not found\n");
600         LOCAL_PrintHeap(ds);
601         return FALSE;
602     }
603
604     handle = LOCAL_GetBlock( ds, pInfo->hdelta * sizeof(LOCALHANDLEENTRY)
605                                   + 2 * sizeof(WORD), LMEM_FIXED );
606     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
607     pInfo = LOCAL_GetHeap( ds );
608     if (handle == 0) return FALSE;
609
610     /* Fill the entry table */
611
612     *(WORD *)(ptr + handle) = pInfo->hdelta;
613     pEntry = (LOCALHANDLEENTRY *)(ptr + handle + sizeof(WORD));
614     for (i = pInfo->hdelta; i > 0; i--) (pEntry++)->lock = 0xff;
615     *(WORD *)pEntry = pInfo->htable;
616     pInfo->htable = handle;
617     return TRUE;
618 }
619
620
621 /***********************************************************************
622  *           LOCAL_GetNewHandleEntry
623  */
624 static HLOCAL LOCAL_GetNewHandleEntry( WORD ds )
625 {
626     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
627     LOCALHEAPINFO *pInfo;
628     LOCALHANDLEENTRY *pEntry = NULL;
629     WORD table;
630
631     if (!(pInfo = LOCAL_GetHeap( ds )))
632     {
633         fprintf( stderr, "LOCAL_GetNewHandleEntry: Local heap not found\n");
634         LOCAL_PrintHeap(ds);
635         return 0;
636     }
637
638     /* Find a free slot in existing tables */
639
640     table = pInfo->htable;
641     while (table)
642     {
643         WORD count = *(WORD *)(ptr + table);
644         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
645         for (; count > 0; count--, pEntry++)
646             if (pEntry->lock == 0xff) break;
647         if (count) break;
648         table = *(WORD *)pEntry;
649     }
650
651     if (!table)  /* We need to create a new table */
652     {
653         if (!LOCAL_NewHTable( ds )) return 0;
654         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
655         pInfo = LOCAL_GetHeap( ds );
656         pEntry = (LOCALHANDLEENTRY *)(ptr + pInfo->htable + sizeof(WORD));
657     }
658
659     /* Now allocate this entry */
660
661     pEntry->lock = 0;
662     dprintf_local( stddeb, "LOCAL_GetNewHandleEntry(%04x): %04x\n",
663                    ds, ((char *)pEntry - ptr) );
664     return (HLOCAL)((char *)pEntry - ptr);
665 }
666
667
668 /***********************************************************************
669  *           LOCAL_FreeArena
670  */
671 static HLOCAL LOCAL_FreeArena( WORD ds, WORD arena )
672 {
673     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
674     LOCALHEAPINFO *pInfo;
675     LOCALARENA *pArena, *pPrev, *pNext;
676
677     dprintf_local( stddeb, "LocalFreeArena: %04x ds=%04x\n", arena, ds );
678     if (!(pInfo = LOCAL_GetHeap( ds ))) return arena;
679
680     pArena = ARENA_PTR( ptr, arena );
681     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
682     {
683         /* shouldn't happen */
684         fprintf( stderr, "LocalFreeArena: Trying to free block %04x twice!\n",
685                  arena );
686         LOCAL_PrintHeap( ds );
687         return arena;
688     }
689
690       /* Check if we can merge with the previous block */
691
692     pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
693     pNext = ARENA_PTR( ptr, pArena->next );
694     if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
695     {
696         arena  = pArena->prev & ~3;
697         pArena = pPrev;
698         LOCAL_RemoveBlock( ptr, pPrev->next );
699         pInfo->items--;
700     }
701     else  /* Make a new free block */
702     {
703         LOCAL_MakeBlockFree( ptr, arena );
704     }
705
706       /* Check if we can merge with the next block */
707
708     if ((pArena->next == pArena->free_next) &&
709         (pArena->next != pInfo->last))
710     {
711         LOCAL_RemoveBlock( ptr, pArena->next );
712         pInfo->items--;
713     }
714     return 0;
715 }
716
717
718 /***********************************************************************
719  *           LOCAL_FreeHandleEntry
720  *
721  * Free a handle table entry.
722  */
723 static void LOCAL_FreeHandleEntry( WORD ds, HLOCAL handle )
724 {
725     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
726     LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
727     LOCALHEAPINFO *pInfo;
728     WORD *pTable;
729     WORD table, count, i;
730
731     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
732
733     /* Find the table where this handle comes from */
734
735     pTable = &pInfo->htable;
736     while (*pTable)
737     {
738         WORD size = (*(WORD *)(ptr + *pTable)) * sizeof(LOCALHANDLEENTRY);
739         if ((handle >= *pTable + sizeof(WORD)) &&
740             (handle < *pTable + sizeof(WORD) + size)) break;  /* Found it */
741         pTable = (WORD *)(ptr + *pTable + sizeof(WORD) + size);
742     }
743     if (!*pTable)
744     {
745         fprintf(stderr, "LOCAL_FreeHandleEntry: invalid entry %04x\n", handle);
746         LOCAL_PrintHeap( ds );
747         return;
748     }
749
750     /* Make the entry free */
751
752     pEntry->addr = 0;  /* just in case */
753     pEntry->lock = 0xff;
754
755     /* Now check if all entries in this table are free */
756
757     pEntry = (LOCALHANDLEENTRY *)(ptr + *pTable + sizeof(WORD));
758     count = *(WORD *)(ptr + *pTable);
759     for (i = count; i > 0; i--, pEntry++) if (pEntry->lock != 0xff) return;
760     
761     /* Remove the table from the linked list and free it */
762
763     table = *pTable;
764     dprintf_local( stddeb, "LOCAL_FreeHandleEntry(%04x): freeing table %04x\n",
765                    ds, table);
766     *pTable = *((WORD *)(ptr + count * sizeof(*pEntry)) + 1);
767     LOCAL_FreeArena( ds, ARENA_HEADER( table ) );
768 }
769
770
771 /***********************************************************************
772  *           LOCAL_Free
773  *
774  * Implementation of LocalFree().
775  */
776 HLOCAL LOCAL_Free( HANDLE ds, HLOCAL handle )
777 {
778     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
779
780     dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
781     
782     if (!handle) { fprintf( stderr, "LOCAL_Free: handle is 0.\n" ); return 0; }
783     if (HANDLE_FIXED( handle ))
784     {
785         if (!LOCAL_FreeArena( ds, ARENA_HEADER( handle ) )) return 0;  /* OK */
786         else return handle;  /* couldn't free it */
787     }
788     else
789     {
790         WORD arena = ARENA_HEADER( *(WORD *)(ptr + handle) );
791         dprintf_local( stddeb, "LocalFree: real block at %04x\n", arena );
792         if (LOCAL_FreeArena( ds, arena )) return handle; /* couldn't free it */
793         LOCAL_FreeHandleEntry( ds, handle );
794         return 0;  /* OK */
795     }
796 }
797
798
799 /***********************************************************************
800  *           LOCAL_Alloc
801  *
802  * Implementation of LocalAlloc().
803  */
804 HLOCAL LOCAL_Alloc( HANDLE ds, WORD flags, WORD size )
805 {
806     char *ptr;
807     HLOCAL handle;
808     
809     dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
810
811     if (flags & LMEM_MOVEABLE)
812     {
813         LOCALHANDLEENTRY *plhe;
814         HLOCAL hmem;
815         
816         if (!(hmem = LOCAL_GetBlock( ds, size, flags ))) return 0;
817         if (!(handle = LOCAL_GetNewHandleEntry( ds )))
818         {
819             fprintf( stderr, "LocalAlloc: couldn't get handle\n");
820             LOCAL_FreeArena( ds, ARENA_HEADER(hmem) );
821             return 0;
822         }
823         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
824         plhe = (LOCALHANDLEENTRY *)(ptr + handle);
825         plhe->addr = hmem;
826         plhe->flags = (BYTE)(flags >> 8);
827         plhe->lock = 0;
828     }
829     else handle = LOCAL_GetBlock( ds, size, flags );
830
831     return handle;
832 }
833
834
835 /***********************************************************************
836  *           LOCAL_ReAlloc
837  *
838  * Implementation of LocalReAlloc().
839  */
840 HLOCAL LOCAL_ReAlloc( HANDLE ds, HLOCAL handle, WORD size, WORD flags )
841 {
842     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
843     LOCALHEAPINFO *pInfo;
844     LOCALARENA *pArena, *pNext;
845     WORD arena, newhandle, blockhandle;
846     LONG nextarena;
847
848     if (!handle) return LOCAL_Alloc( ds, size, flags );
849
850     dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
851                    handle, size, flags, ds );
852     if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
853     
854     if (HANDLE_FIXED( handle )) blockhandle = handle;
855     else blockhandle = *(WORD *)(ptr + handle);
856
857     arena = ARENA_HEADER( blockhandle );
858     dprintf_local( stddeb, "LocalReAlloc: arena is %04x\n", arena );
859     pArena = ARENA_PTR( ptr, arena );
860     
861     if (flags & LMEM_MODIFY) {
862       dprintf_local( stddeb, "LMEM_MODIFY set\n");
863       return handle;
864     }
865     if (!size) size = 1;
866     size = LALIGN( size );
867     nextarena = LALIGN(blockhandle + size);
868
869       /* Check for size reduction */
870
871     if (nextarena < pArena->next)
872     {
873         if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
874         {
875             dprintf_local( stddeb, "size reduction, making new free block\n");
876               /* It is worth making a new free block */
877             LOCAL_AddBlock( ptr, arena, nextarena );
878             LOCAL_MakeBlockFree( ptr, nextarena );
879             pInfo->items++;
880         }
881         dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
882         return handle;
883     }
884
885       /* Check if the next block is free */
886
887     pNext = ARENA_PTR( ptr, pArena->next );
888     if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
889         (nextarena <= pNext->next))
890     {
891         LOCAL_RemoveBlock( ptr, pArena->next );
892         if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
893         {
894             dprintf_local( stddeb, "size increase, making new free block\n");
895               /* It is worth making a new free block */
896             LOCAL_AddBlock( ptr, arena, nextarena );
897             LOCAL_MakeBlockFree( ptr, nextarena );
898             pInfo->items++;
899         }
900         dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
901         return handle;
902     }
903
904       /* Now we have to allocate a new block */
905
906     newhandle = LOCAL_GetBlock( ds, size, flags );
907     if (newhandle == 0) return 0;
908     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
909     memcpy( ptr + newhandle, ptr + (arena + ARENA_HEADER_SIZE), size );
910     LOCAL_FreeArena( ds, arena );
911     if (HANDLE_MOVEABLE( handle ))
912     {
913         dprintf_local( stddeb, "LocalReAlloc: fixing handle\n");
914         *(WORD *)(ptr + handle) = newhandle;
915         newhandle = handle;
916     }
917     dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
918     return newhandle;
919 }
920
921
922 /***********************************************************************
923  *           LOCAL_InternalLock
924  */
925 static HLOCAL LOCAL_InternalLock( LPSTR heap, HLOCAL handle )
926 {
927     dprintf_local( stddeb, "LocalLock: %04x ", handle );
928     if (HANDLE_MOVEABLE(handle))
929     {
930         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(heap + handle);
931         if (pEntry->lock < 0xfe) pEntry->lock++;
932         handle = pEntry->addr;
933     }
934     dprintf_local( stddeb, "returning %04x\n", handle );
935     return handle;
936 }
937
938
939 /***********************************************************************
940  *           LOCAL_Lock
941  */
942 LPSTR LOCAL_Lock( HANDLE ds, HLOCAL handle )
943 {
944     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
945     return handle ? ptr + LOCAL_InternalLock( ptr, handle ) : NULL;
946 }
947
948
949 /***********************************************************************
950  *           LOCAL_Unlock
951  */
952 BOOL LOCAL_Unlock( WORD ds, HLOCAL handle )
953 {
954     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
955
956     dprintf_local( stddeb, "LocalUnlock: %04x\n", handle );
957     if (HANDLE_MOVEABLE(handle))
958     {
959         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
960         if (!pEntry->lock || (pEntry->lock == 0xff)) return FALSE;
961         /* For moveable block, return the new lock count */
962         /* (see _Windows_Internals_ p. 197) */
963         return --pEntry->lock;
964     }
965     else return FALSE;
966 }
967
968
969 /***********************************************************************
970  *           LOCAL_Size
971  *
972  * Implementation of LocalSize().
973  */
974 WORD LOCAL_Size( WORD ds, HLOCAL handle )
975 {
976     char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
977     LOCALARENA *pArena;
978
979     dprintf_local( stddeb, "LocalSize: %04x ds=%04x\n", handle, ds );
980
981     if (HANDLE_MOVEABLE( handle )) handle = *(WORD *)(ptr + handle);
982     pArena = ARENA_PTR( ptr, ARENA_HEADER(handle) );
983     return pArena->next - handle;
984 }
985
986
987 /***********************************************************************
988  *           LOCAL_Flags
989  *
990  * Implementation of LocalFlags().
991  */
992 WORD LOCAL_Flags( WORD ds, HLOCAL handle )
993 {
994     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
995
996     if (HANDLE_MOVEABLE(handle))
997     {
998         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
999         dprintf_local( stddeb, "LOCAL_Flags(%04x,%04x): returning %04x\n",
1000                        ds, handle, pEntry->lock | (pEntry->flags << 8) );
1001         return pEntry->lock | (pEntry->flags << 8);
1002     }
1003     else
1004     {
1005         dprintf_local( stddeb, "LOCAL_Flags(%04x,%04x): returning 0\n",
1006                        ds, handle );
1007         return 0;
1008     }
1009 }
1010
1011
1012 /***********************************************************************
1013  *           LOCAL_HeapSize
1014  *
1015  * Implementation of LocalHeapSize().
1016  */
1017 WORD LOCAL_HeapSize( WORD ds )
1018 {
1019     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1020     if (!pInfo) return 0;
1021     return pInfo->last - pInfo->first;
1022 }
1023
1024
1025 /***********************************************************************
1026  *           LOCAL_CountFree
1027  *
1028  * Implementation of LocalCountFree().
1029  */
1030 WORD LOCAL_CountFree( WORD ds )
1031 {
1032     WORD arena, total;
1033     LOCALARENA *pArena;
1034     LOCALHEAPINFO *pInfo;
1035     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1036
1037     if (!(pInfo = LOCAL_GetHeap( ds )))
1038     {
1039         fprintf( stderr, "LOCAL_Handle(%04x): Local heap not found\n", ds );
1040         LOCAL_PrintHeap( ds );
1041         return 0;
1042     }
1043
1044     total = 0;
1045     arena = pInfo->first;
1046     pArena = ARENA_PTR( ptr, arena );
1047     for (;;)
1048     {
1049         arena = pArena->free_next;
1050         pArena = ARENA_PTR( ptr, arena );
1051         if (arena == pArena->free_next) break;
1052         total += pArena->size;
1053     }
1054     dprintf_local( stddeb, "LOCAL_CountFree(%04x): returning %d\n", ds, total);
1055     return total;
1056 }
1057
1058
1059 /***********************************************************************
1060  *           LOCAL_Handle
1061  *
1062  * Implementation of LocalHandle().
1063  */
1064 HLOCAL LOCAL_Handle( WORD ds, WORD addr )
1065
1066     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1067     LOCALHEAPINFO *pInfo;
1068     WORD table;
1069
1070     if (!(pInfo = LOCAL_GetHeap( ds )))
1071     {
1072         fprintf( stderr, "LOCAL_Handle(%04x): Local heap not found\n", ds );
1073         LOCAL_PrintHeap( ds );
1074         return 0;
1075     }
1076
1077     /* Find the address in the entry tables */
1078
1079     table = pInfo->htable;
1080     while (table)
1081     {
1082         WORD count = *(WORD *)(ptr + table);
1083         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY*)(ptr+table+sizeof(WORD));
1084         for (; count > 0; count--, pEntry++)
1085             if (pEntry->addr == addr) return (HLOCAL)((char *)pEntry - ptr);
1086         table = *(WORD *)pEntry;
1087     }
1088
1089     return (HLOCAL)addr;  /* Fixed block handle is addr */
1090 }
1091
1092
1093 /***********************************************************************
1094  *           LocalAlloc   (KERNEL.5)
1095  */
1096 HLOCAL LocalAlloc( WORD flags, WORD size )
1097 {
1098     return LOCAL_Alloc( CURRENT_DS, flags, size );
1099 }
1100
1101
1102 /***********************************************************************
1103  *           LocalReAlloc   (KERNEL.6)
1104  */
1105 HLOCAL LocalReAlloc( HLOCAL handle, WORD size, WORD flags )
1106 {
1107     return LOCAL_ReAlloc( CURRENT_DS, handle, size, flags );
1108 }
1109
1110
1111 /***********************************************************************
1112  *           LocalFree   (KERNEL.7)
1113  */
1114 HLOCAL LocalFree( HLOCAL handle )
1115 {
1116     return LOCAL_Free( CURRENT_DS, handle );
1117 }
1118
1119
1120 /***********************************************************************
1121  *           LocalLock   (KERNEL.8)
1122  */
1123 NPVOID LocalLock( HLOCAL handle )
1124 {
1125     char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
1126     return (NPVOID)LOCAL_InternalLock( ptr, handle );
1127 }
1128
1129
1130 /***********************************************************************
1131  *           LocalUnlock   (KERNEL.9)
1132  */
1133 BOOL LocalUnlock( HLOCAL handle )
1134 {
1135     return LOCAL_Unlock( CURRENT_DS, handle );
1136 }
1137
1138
1139 /***********************************************************************
1140  *           LocalSize   (KERNEL.10)
1141  */
1142 WORD LocalSize( HLOCAL handle )
1143 {
1144     return LOCAL_Size( CURRENT_DS, handle );
1145 }
1146
1147
1148 /***********************************************************************
1149  *           LocalHandle   (KERNEL.11)
1150  */
1151 HLOCAL LocalHandle( WORD addr )
1152
1153     return LOCAL_Handle( CURRENT_DS, addr );
1154 }
1155
1156
1157 /***********************************************************************
1158  *           LocalFlags   (KERNEL.12)
1159  */
1160 WORD LocalFlags( HLOCAL handle )
1161 {
1162     return LOCAL_Flags( CURRENT_DS, handle );
1163 }
1164
1165
1166 /***********************************************************************
1167  *           LocalCompact   (KERNEL.13)
1168  */
1169 WORD LocalCompact( WORD minfree )
1170 {
1171     dprintf_local( stddeb, "LocalCompact: %04x\n", minfree );
1172     return LOCAL_Compact( CURRENT_DS, minfree, 0 );
1173 }
1174
1175
1176 /***********************************************************************
1177  *           LocalNotify   (KERNEL.14)
1178  */
1179 FARPROC LocalNotify( FARPROC func )
1180 {
1181     LOCALHEAPINFO *pInfo;
1182     FARPROC oldNotify;
1183     WORD ds = CURRENT_DS;
1184
1185     if (!(pInfo = LOCAL_GetHeap( ds )))
1186     {
1187         fprintf( stderr, "LOCAL_Notify(%04x): Local heap not found\n", ds );
1188         LOCAL_PrintHeap( ds );
1189         return 0;
1190     }
1191     dprintf_local( stddeb, "LocalNotify(%04x): %08lx\n", ds, func );
1192     oldNotify = pInfo->notify;
1193     pInfo->notify = func;
1194     return oldNotify;
1195 }
1196
1197
1198 /***********************************************************************
1199  *           LocalShrink   (KERNEL.121)
1200  */
1201 WORD LocalShrink( HLOCAL handle, WORD newsize )
1202 {
1203     dprintf_local( stddeb, "LocalShrink: %04x %04x\n", handle, newsize );
1204     return 0;
1205 }
1206
1207
1208 /***********************************************************************
1209  *           GetHeapSpaces   (KERNEL.138)
1210  */
1211 DWORD GetHeapSpaces( HMODULE module )
1212 {
1213     NE_MODULE *pModule;
1214     WORD ds;
1215
1216     module = GetExePtr( module );
1217     if (!(pModule = MODULE_GetPtr( module ))) return 0;
1218     ds = (NE_SEG_TABLE( pModule ) + pModule->dgroup - 1)->selector;
1219     return MAKELONG( LOCAL_CountFree( ds ), LOCAL_HeapSize( ds ) );
1220 }
1221
1222
1223 /***********************************************************************
1224  *           LocalCountFree   (KERNEL.161)
1225  */
1226 WORD LocalCountFree(void)
1227 {
1228     return LOCAL_CountFree( CURRENT_DS );
1229 }
1230
1231
1232 /***********************************************************************
1233  *           LocalHeapSize   (KERNEL.162)
1234  */
1235 WORD LocalHeapSize()
1236 {
1237     dprintf_local( stddeb, "LocalHeapSize:\n" );
1238     return LOCAL_HeapSize( CURRENT_DS );
1239 }
1240
1241
1242 /***********************************************************************
1243  *           LocalHandleDelta   (KERNEL.310)
1244  */
1245 WORD LocalHandleDelta( WORD delta )
1246 {
1247     LOCALHEAPINFO *pInfo;
1248
1249     if (!(pInfo = LOCAL_GetHeap( CURRENT_DS )))
1250     {
1251         fprintf( stderr, "LocalHandleDelta: Local heap not found\n");
1252         LOCAL_PrintHeap( CURRENT_DS );
1253         return 0;
1254     }
1255     if (delta) pInfo->hdelta = delta;
1256     dprintf_local(stddeb, "LocalHandleDelta: returning %04x\n", pInfo->hdelta);
1257     return pInfo->hdelta;
1258 }
1259
1260
1261 /***********************************************************************
1262  *           LocalInfo   (TOOLHELP.56)
1263  */
1264 BOOL LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL handle )
1265 {
1266     LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle)));
1267     if (!pInfo) return FALSE;
1268     pLocalInfo->wcItems = pInfo->items;
1269     return TRUE;
1270 }
1271
1272
1273 /***********************************************************************
1274  *           LocalFirst   (TOOLHELP.57)
1275  */
1276 BOOL LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL handle )
1277 {
1278     WORD ds = SELECTOROF( WIN16_GlobalLock( handle ) );
1279     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1280     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1281     if (!pInfo) return FALSE;
1282
1283     pLocalEntry->hHandle   = pInfo->first + ARENA_HEADER_SIZE;
1284     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1285     pLocalEntry->wFlags    = LF_FIXED;
1286     pLocalEntry->wcLock    = 0;
1287     pLocalEntry->wType     = LT_NORMAL;
1288     pLocalEntry->hHeap     = handle;
1289     pLocalEntry->wHeapType = NORMAL_HEAP;
1290     pLocalEntry->wNext     = ARENA_PTR(ptr,pInfo->first)->next;
1291     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1292     return TRUE;
1293 }
1294
1295
1296 /***********************************************************************
1297  *           LocalNext   (TOOLHELP.58)
1298  */
1299 BOOL LocalNext( LOCALENTRY *pLocalEntry )
1300 {
1301     WORD ds = SELECTOROF( pLocalEntry->hHeap );
1302     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1303     LOCALARENA *pArena;
1304
1305     if (!LOCAL_GetHeap( ds )) return FALSE;
1306     if (!pLocalEntry->wNext) return FALSE;
1307     pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
1308
1309     pLocalEntry->hHandle   = pLocalEntry->wNext + ARENA_HEADER_SIZE;
1310     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1311     pLocalEntry->wFlags    = (pArena->prev & 3) + 1;
1312     pLocalEntry->wcLock    = 0;
1313     pLocalEntry->wType     = LT_NORMAL;
1314     if (pArena->next != pLocalEntry->wNext)  /* last one? */
1315         pLocalEntry->wNext = pArena->next;
1316     else
1317         pLocalEntry->wNext = 0;
1318     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1319     return TRUE;
1320 }
1321
1322 #endif  /* WINELIB */