Don't load user32 too early on for 16-bit apps, so that app-specific
[wine] / memory / global.c
1 /*
2  * Global heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6 /* 0xffff sometimes seems to mean: CURRENT_DS */
7
8 #include <sys/types.h>
9 #include <stdlib.h>
10 #include <time.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <string.h>
14
15 #include "wine/winbase16.h"
16 #include "wine/exception.h"
17 #include "wine/port.h"
18 #include "global.h"
19 #include "heap.h"
20 #include "toolhelp.h"
21 #include "selectors.h"
22 #include "miscemu.h"
23 #include "stackframe.h"
24 #include "module.h"
25 #include "debugtools.h"
26 #include "winerror.h"
27
28 DEFAULT_DEBUG_CHANNEL(global);
29
30   /* Global arena block */
31 typedef struct
32 {
33     DWORD     base;          /* Base address (0 if discarded) */
34     DWORD     size;          /* Size in bytes (0 indicates a free block) */
35     HGLOBAL16 handle;        /* Handle for this block */
36     HGLOBAL16 hOwner;        /* Owner of this block */
37     BYTE      lockCount;     /* Count of GlobalFix() calls */
38     BYTE      pageLockCount; /* Count of GlobalPageLock() calls */
39     BYTE      flags;         /* Allocation flags */
40     BYTE      selCount;      /* Number of selectors allocated for this block */
41 } GLOBALARENA;
42
43   /* Flags definitions */
44 #define GA_MOVEABLE     0x02  /* same as GMEM_MOVEABLE */
45 #define GA_DGROUP       0x04
46 #define GA_DISCARDABLE  0x08
47 #define GA_IPCSHARE     0x10  /* same as GMEM_DDESHARE */
48 #define GA_DOSMEM       0x20
49
50   /* Arena array */
51 static GLOBALARENA *pGlobalArena = NULL;
52 static int globalArenaSize = 0;
53
54 #define GLOBAL_MAX_ALLOC_SIZE 0x00ff0000  /* Largest allocation is 16M - 64K */
55
56 #define VALID_HANDLE(handle) (((handle)>>__AHSHIFT)<globalArenaSize)
57 #define GET_ARENA_PTR(handle)  (pGlobalArena + ((handle) >> __AHSHIFT))
58
59 /* filter for page-fault exceptions */
60 /* It is possible for a bogus global pointer to cause a */
61 /* page zero reference, so I include EXCEPTION_PRIV_INSTRUCTION too. */
62
63 static WINE_EXCEPTION_FILTER(page_fault)
64 {
65     switch (GetExceptionCode()) {
66         case (EXCEPTION_ACCESS_VIOLATION):
67         case (EXCEPTION_PRIV_INSTRUCTION):
68            return EXCEPTION_EXECUTE_HANDLER;
69         default:
70            return EXCEPTION_CONTINUE_SEARCH;
71     }
72 }
73
74 /***********************************************************************
75  *           GLOBAL_GetArena
76  *
77  * Return the arena for a given selector, growing the arena array if needed.
78  */
79 static GLOBALARENA *GLOBAL_GetArena( WORD sel, WORD selcount )
80 {
81     if (((sel >> __AHSHIFT) + selcount) > globalArenaSize)
82     {
83         int newsize = ((sel >> __AHSHIFT) + selcount + 0xff) & ~0xff;
84         GLOBALARENA *pNewArena = realloc( pGlobalArena,
85                                           newsize * sizeof(GLOBALARENA) );
86         if (!pNewArena) return 0;
87         pGlobalArena = pNewArena;
88         memset( pGlobalArena + globalArenaSize, 0,
89                 (newsize - globalArenaSize) * sizeof(GLOBALARENA) );
90         globalArenaSize = newsize;
91     }
92     return pGlobalArena + (sel >> __AHSHIFT);
93 }
94
95 void debug_handles(void)
96 {
97     int printed=0;
98     int i;
99     for (i = globalArenaSize-1 ; i>=0 ; i--) {
100         if (pGlobalArena[i].size!=0 && (pGlobalArena[i].handle & 0x8000)){
101             printed=1;
102             DPRINTF("0x%08x, ",pGlobalArena[i].handle);
103         }
104     }
105     if (printed)
106         DPRINTF("\n");
107 }
108
109
110 /***********************************************************************
111  *           GLOBAL_CreateBlock
112  *
113  * Create a global heap block for a fixed range of linear memory.
114  */
115 HGLOBAL16 GLOBAL_CreateBlock( WORD flags, const void *ptr, DWORD size,
116                               HGLOBAL16 hOwner, unsigned char selflags )
117 {
118     WORD sel, selcount;
119     GLOBALARENA *pArena;
120
121       /* Allocate the selector(s) */
122
123     sel = SELECTOR_AllocBlock( ptr, size, selflags );
124     if (!sel) return 0;
125     selcount = (size + 0xffff) / 0x10000;
126
127     if (!(pArena = GLOBAL_GetArena( sel, selcount )))
128     {
129         SELECTOR_FreeBlock( sel );
130         return 0;
131     }
132
133       /* Fill the arena block */
134
135     pArena->base = (DWORD)ptr;
136     pArena->size = GetSelectorLimit16(sel) + 1;
137     pArena->handle = (flags & GMEM_MOVEABLE) ? sel - 1 : sel;
138     pArena->hOwner = hOwner;
139     pArena->lockCount = 0;
140     pArena->pageLockCount = 0;
141     pArena->flags = flags & GA_MOVEABLE;
142     if (flags & GMEM_DISCARDABLE) pArena->flags |= GA_DISCARDABLE;
143     if (flags & GMEM_DDESHARE) pArena->flags |= GA_IPCSHARE;
144     if (!(selflags & (WINE_LDT_FLAGS_CODE^WINE_LDT_FLAGS_DATA))) pArena->flags |= GA_DGROUP;
145     pArena->selCount = selcount;
146     if (selcount > 1)  /* clear the next arena blocks */
147         memset( pArena + 1, 0, (selcount - 1) * sizeof(GLOBALARENA) );
148
149     return pArena->handle;
150 }
151
152
153 /***********************************************************************
154  *           GLOBAL_FreeBlock
155  *
156  * Free a block allocated by GLOBAL_CreateBlock, without touching
157  * the associated linear memory range.
158  */
159 BOOL16 GLOBAL_FreeBlock( HGLOBAL16 handle )
160 {
161     WORD sel;
162     GLOBALARENA *pArena;
163
164     if (!handle) return TRUE;
165     sel = GlobalHandleToSel16( handle ); 
166     if (!VALID_HANDLE(sel))
167         return FALSE;
168     pArena = GET_ARENA_PTR(sel);
169     SELECTOR_FreeBlock( sel );
170     memset( pArena, 0, sizeof(GLOBALARENA) );
171     return TRUE;
172 }
173
174 /***********************************************************************
175  *           GLOBAL_MoveBlock
176  */
177 BOOL16 GLOBAL_MoveBlock( HGLOBAL16 handle, const void *ptr, DWORD size )
178 {
179     WORD sel;
180     GLOBALARENA *pArena;
181
182     if (!handle) return TRUE;
183     sel = GlobalHandleToSel16( handle ); 
184     if (!VALID_HANDLE(sel))
185         return FALSE;
186     pArena = GET_ARENA_PTR(sel);
187     if (pArena->selCount != 1)
188         return FALSE;
189
190     pArena->base = (DWORD)ptr;
191     pArena->size = size;
192     SELECTOR_ReallocBlock( sel, ptr, size );
193     return TRUE;
194 }
195
196 /***********************************************************************
197  *           GLOBAL_Alloc
198  *
199  * Implementation of GlobalAlloc16()
200  */
201 HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner, unsigned char selflags )
202 {
203     void *ptr;
204     HGLOBAL16 handle;
205
206     TRACE("%ld flags=%04x\n", size, flags );
207
208     /* If size is 0, create a discarded block */
209
210     if (size == 0) return GLOBAL_CreateBlock( flags, NULL, 1, hOwner, selflags );
211
212     /* Fixup the size */
213
214     if (size >= GLOBAL_MAX_ALLOC_SIZE - 0x1f) return 0;
215     size = (size + 0x1f) & ~0x1f;
216
217     /* Allocate the linear memory */
218     ptr = HeapAlloc( GetProcessHeap(), 0, size );
219       /* FIXME: free discardable blocks and try again? */
220     if (!ptr) return 0;
221
222       /* Allocate the selector(s) */
223
224     handle = GLOBAL_CreateBlock( flags, ptr, size, hOwner, selflags );
225     if (!handle)
226     {
227         HeapFree( GetProcessHeap(), 0, ptr );
228         return 0;
229     }
230
231     if (flags & GMEM_ZEROINIT) memset( ptr, 0, size );
232     return handle;
233 }
234
235 /***********************************************************************
236  *           GlobalAlloc16   (KERNEL.15)
237  * RETURNS
238  *      Handle: Success
239  *      NULL: Failure
240  */
241 HGLOBAL16 WINAPI GlobalAlloc16(
242                  UINT16 flags, /* [in] Object allocation attributes */
243                  DWORD size    /* [in] Number of bytes to allocate */
244 ) {
245     HANDLE16 owner = GetCurrentPDB16();
246
247     if (flags & GMEM_DDESHARE)
248         owner = GetExePtr(owner);  /* Make it a module handle */
249     return GLOBAL_Alloc( flags, size, owner, WINE_LDT_FLAGS_DATA );
250 }
251
252
253 /***********************************************************************
254  *           GlobalReAlloc16   (KERNEL.16)
255  * RETURNS
256  *      Handle: Success
257  *      NULL: Failure
258  */
259 HGLOBAL16 WINAPI GlobalReAlloc16(
260                  HGLOBAL16 handle, /* [in] Handle of global memory object */
261                  DWORD size,       /* [in] New size of block */
262                  UINT16 flags      /* [in] How to reallocate object */
263 ) {
264     WORD selcount;
265     DWORD oldsize;
266     void *ptr;
267     GLOBALARENA *pArena, *pNewArena;
268     WORD sel = GlobalHandleToSel16( handle );
269
270     TRACE("%04x %ld flags=%04x\n",
271                     handle, size, flags );
272     if (!handle) return 0;
273     
274     if (!VALID_HANDLE(handle)) {
275         WARN("Invalid handle 0x%04x!\n", handle);
276         return 0;
277     }
278     pArena = GET_ARENA_PTR( handle );
279
280       /* Discard the block if requested */
281
282     if ((size == 0) && (flags & GMEM_MOVEABLE) && !(flags & GMEM_MODIFY))
283     {
284         if (!(pArena->flags & GA_MOVEABLE) ||
285             !(pArena->flags & GA_DISCARDABLE) ||
286             (pArena->lockCount > 0) || (pArena->pageLockCount > 0)) return 0;
287         HeapFree( GetProcessHeap(), 0, (void *)pArena->base );
288         pArena->base = 0;
289
290         /* Note: we rely on the fact that SELECTOR_ReallocBlock won't 
291          * change the selector if we are shrinking the block.
292          * FIXME: shouldn't we keep selectors until the block is deleted?
293          */
294         SELECTOR_ReallocBlock( sel, 0, 1 );
295         return handle;
296     }
297
298       /* Fixup the size */
299
300     if (size > GLOBAL_MAX_ALLOC_SIZE - 0x20) return 0;
301     if (size == 0) size = 0x20;
302     else size = (size + 0x1f) & ~0x1f;
303
304       /* Change the flags */
305
306     if (flags & GMEM_MODIFY)
307     {
308           /* Change the flags, leaving GA_DGROUP alone */
309         pArena->flags = (pArena->flags & GA_DGROUP) | (flags & GA_MOVEABLE);
310         if (flags & GMEM_DISCARDABLE) pArena->flags |= GA_DISCARDABLE;
311         return handle;
312     }
313
314       /* Reallocate the linear memory */
315
316     ptr = (void *)pArena->base;
317     oldsize = pArena->size;
318     TRACE("oldsize %08lx\n",oldsize);
319     if (ptr && (size == oldsize)) return handle;  /* Nothing to do */
320
321     if (pArena->flags & GA_DOSMEM)
322         ptr = DOSMEM_ResizeBlock(ptr, size, NULL);
323     else
324         ptr = HeapReAlloc( GetProcessHeap(), 0, ptr, size );
325     if (!ptr)
326     {
327         SELECTOR_FreeBlock( sel );
328         memset( pArena, 0, sizeof(GLOBALARENA) );
329         return 0;
330     }
331
332       /* Reallocate the selector(s) */
333
334     sel = SELECTOR_ReallocBlock( sel, ptr, size );
335     if (!sel)
336     {
337         HeapFree( GetProcessHeap(), 0, ptr );
338         memset( pArena, 0, sizeof(GLOBALARENA) );
339         return 0;
340     }
341     selcount = (size + 0xffff) / 0x10000;
342
343     if (!(pNewArena = GLOBAL_GetArena( sel, selcount )))
344     {
345         HeapFree( GetProcessHeap(), 0, ptr );
346         SELECTOR_FreeBlock( sel );
347         return 0;
348     }
349
350       /* Fill the new arena block */
351
352     if (pNewArena != pArena) memcpy( pNewArena, pArena, sizeof(GLOBALARENA) );
353     pNewArena->base = (DWORD)ptr;
354     pNewArena->size = GetSelectorLimit16(sel) + 1;
355     pNewArena->selCount = selcount;
356     pNewArena->handle = (pNewArena->flags & GA_MOVEABLE) ? sel - 1 : sel;
357
358     if (selcount > 1)  /* clear the next arena blocks */
359         memset( pNewArena + 1, 0, (selcount - 1) * sizeof(GLOBALARENA) );
360
361     if ((oldsize < size) && (flags & GMEM_ZEROINIT))
362         memset( (char *)ptr + oldsize, 0, size - oldsize );
363     return pNewArena->handle;
364 }
365
366
367 /***********************************************************************
368  *           GlobalFree16   (KERNEL.17)
369  * RETURNS
370  *      NULL: Success
371  *      Handle: Failure
372  */
373 HGLOBAL16 WINAPI GlobalFree16(
374                  HGLOBAL16 handle /* [in] Handle of global memory object */
375 ) {
376     void *ptr;
377
378     if (!VALID_HANDLE(handle)) {
379         WARN("Invalid handle 0x%04x passed to GlobalFree16!\n",handle);
380         return 0;
381     }
382     ptr = (void *)GET_ARENA_PTR(handle)->base;
383
384     TRACE("%04x\n", handle );
385     if (!GLOBAL_FreeBlock( handle )) return handle;  /* failed */
386     if (ptr) HeapFree( GetProcessHeap(), 0, ptr );
387     return 0;
388 }
389
390
391 /***********************************************************************
392  *           WIN16_GlobalLock16   (KERNEL.18)
393  *
394  * This is the GlobalLock16() function used by 16-bit code.
395  */
396 SEGPTR WINAPI WIN16_GlobalLock16( HGLOBAL16 handle )
397 {
398     WORD sel = GlobalHandleToSel16( handle );
399     TRACE("(%04x) -> %08lx\n", handle, MAKELONG( 0, sel ) );
400
401     if (handle)
402     {
403         if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
404
405         if (!VALID_HANDLE(handle)) {
406             WARN("Invalid handle 0x%04x passed to WIN16_GlobalLock16!\n",handle);
407             sel = 0;
408         }
409         else if (!GET_ARENA_PTR(handle)->base) 
410             sel = 0;
411         else
412             GET_ARENA_PTR(handle)->lockCount++;
413     }
414
415     CURRENT_STACK16->ecx = sel;  /* selector must be returned in CX as well */
416     return MAKESEGPTR( sel, 0 );
417 }
418
419
420 /**********************************************************************
421  *           K32WOWGlobalLock16         (WOW32.11) (KERNEL32.60)
422  */
423 SEGPTR WINAPI K32WOWGlobalLock16( HGLOBAL16 hMem )
424 {
425     return WIN16_GlobalLock16( hMem );
426 }
427
428
429 /***********************************************************************
430  *           GlobalLock16   (KERNEL.18)
431  *
432  * This is the GlobalLock16() function used by 32-bit code.
433  * 
434  * RETURNS
435  *      Pointer to first byte of memory block
436  *      NULL: Failure
437  */
438 LPVOID WINAPI GlobalLock16(
439               HGLOBAL16 handle /* [in] Handle of global memory object */
440 ) {
441     if (!handle) return 0;
442     if (!VALID_HANDLE(handle))
443         return (LPVOID)0;
444     GET_ARENA_PTR(handle)->lockCount++;
445     return (LPVOID)GET_ARENA_PTR(handle)->base;
446 }
447
448
449 /***********************************************************************
450  *           GlobalUnlock16   (KERNEL.19)
451  * NOTES
452  *      Should the return values be cast to booleans?
453  *
454  * RETURNS
455  *      TRUE: Object is still locked
456  *      FALSE: Object is unlocked
457  */
458 BOOL16 WINAPI GlobalUnlock16(
459               HGLOBAL16 handle /* [in] Handle of global memory object */
460 ) {
461     GLOBALARENA *pArena = GET_ARENA_PTR(handle);
462     if (!VALID_HANDLE(handle)) {
463         WARN("Invalid handle 0x%04x passed to GlobalUnlock16!\n",handle);
464         return 0;
465     }
466     TRACE("%04x\n", handle );
467     if (pArena->lockCount) pArena->lockCount--;
468     return pArena->lockCount;
469 }
470
471 /***********************************************************************
472  *     GlobalChangeLockCount               (KERNEL.365)
473  *
474  * This is declared as a register function as it has to preserve
475  * *all* registers, even AX/DX !
476  *
477  */
478 void WINAPI GlobalChangeLockCount16( HGLOBAL16 handle, INT16 delta,
479                                      CONTEXT86 *context )
480 {
481     if ( delta == 1 )
482         GlobalLock16( handle );
483     else if ( delta == -1 )
484         GlobalUnlock16( handle );
485     else
486         ERR("(%04X, %d): strange delta value\n", handle, delta );
487 }
488
489 /***********************************************************************
490  *           GlobalSize16   (KERNEL.20)
491  * RETURNS
492  *      Size in bytes of object
493  *      0: Failure
494  */
495 DWORD WINAPI GlobalSize16(
496              HGLOBAL16 handle /* [in] Handle of global memory object */
497 ) {
498     TRACE("%04x\n", handle );
499     if (!handle) return 0;
500     if (!VALID_HANDLE(handle))
501         return 0;
502     return GET_ARENA_PTR(handle)->size;
503 }
504
505
506 /***********************************************************************
507  *           GlobalHandle16   (KERNEL.21)
508  * NOTES
509  *      Why is GlobalHandleToSel used here with the sel as input?
510  *
511  * RETURNS
512  *      Handle: Success
513  *      NULL: Failure
514  */
515 DWORD WINAPI GlobalHandle16(
516              WORD sel /* [in] Address of global memory block */
517 ) {
518     TRACE("%04x\n", sel );
519     if (!VALID_HANDLE(sel)) {
520         WARN("Invalid handle 0x%04x passed to GlobalHandle16!\n",sel);
521         return 0;
522     }
523     return MAKELONG( GET_ARENA_PTR(sel)->handle, GlobalHandleToSel16(sel) );
524 }
525
526 /***********************************************************************
527  *           GlobalHandleNoRIP   (KERNEL.159)
528  */
529 DWORD WINAPI GlobalHandleNoRIP16( WORD sel )
530 {
531     int i;
532     for (i = globalArenaSize-1 ; i>=0 ; i--) {
533         if (pGlobalArena[i].size!=0 && pGlobalArena[i].handle == sel)
534                 return MAKELONG( GET_ARENA_PTR(sel)->handle, GlobalHandleToSel16(sel) );
535     }
536     return 0;
537 }
538
539
540 /***********************************************************************
541  *           GlobalFlags16   (KERNEL.22)
542  * NOTES
543  *      Should this return GMEM_INVALID_HANDLE instead of 0 on invalid
544  *      handle?
545  *
546  * RETURNS
547  *      Value specifying flags and lock count
548  *      GMEM_INVALID_HANDLE: Invalid handle
549  */
550 UINT16 WINAPI GlobalFlags16(
551               HGLOBAL16 handle /* [in] Handle of global memory object */
552 ) {
553     GLOBALARENA *pArena;
554
555     TRACE("%04x\n", handle );
556     if (!VALID_HANDLE(handle)) {
557         WARN("Invalid handle 0x%04x passed to GlobalFlags16!\n",handle);
558         return 0;
559     }
560     pArena = GET_ARENA_PTR(handle);
561     return pArena->lockCount |
562            ((pArena->flags & GA_DISCARDABLE) ? GMEM_DISCARDABLE : 0) |
563            ((pArena->base == 0) ? GMEM_DISCARDED : 0);
564 }
565
566
567 /***********************************************************************
568  *           LockSegment16   (KERNEL.23)
569  */
570 HGLOBAL16 WINAPI LockSegment16( HGLOBAL16 handle )
571 {
572     TRACE("%04x\n", handle );
573     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
574     if (!VALID_HANDLE(handle)) {
575         WARN("Invalid handle 0x%04x passed to LockSegment16!\n",handle);
576         return 0;
577     }
578     GET_ARENA_PTR(handle)->lockCount++;
579     return handle;
580 }
581
582
583 /***********************************************************************
584  *           UnlockSegment16   (KERNEL.24)
585  */
586 void WINAPI UnlockSegment16( HGLOBAL16 handle )
587 {
588     TRACE("%04x\n", handle );
589     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
590     if (!VALID_HANDLE(handle)) {
591         WARN("Invalid handle 0x%04x passed to UnlockSegment16!\n",handle);
592         return;
593     }
594     GET_ARENA_PTR(handle)->lockCount--;
595     /* FIXME: this ought to return the lock count in CX (go figure...) */
596 }
597
598
599 /***********************************************************************
600  *           GlobalCompact16   (KERNEL.25)
601  */
602 DWORD WINAPI GlobalCompact16( DWORD desired )
603 {
604     return GLOBAL_MAX_ALLOC_SIZE;
605 }
606
607
608 /***********************************************************************
609  *           GlobalFreeAll   (KERNEL.26)
610  */
611 void WINAPI GlobalFreeAll16( HGLOBAL16 owner )
612 {
613     DWORD i;
614     GLOBALARENA *pArena;
615
616     pArena = pGlobalArena;
617     for (i = 0; i < globalArenaSize; i++, pArena++)
618     {
619         if ((pArena->size != 0) && (pArena->hOwner == owner))
620             GlobalFree16( pArena->handle );
621     }
622 }
623
624
625 /***********************************************************************
626  *           GlobalWire16   (KERNEL.111)
627  */
628 SEGPTR WINAPI GlobalWire16( HGLOBAL16 handle )
629 {
630     return WIN16_GlobalLock16( handle );
631 }
632
633
634 /***********************************************************************
635  *           GlobalUnWire16   (KERNEL.112)
636  */
637 BOOL16 WINAPI GlobalUnWire16( HGLOBAL16 handle )
638 {
639     return !GlobalUnlock16( handle );
640 }
641
642
643 /***********************************************************************
644  *           SetSwapAreaSize16   (KERNEL.106)
645  */
646 LONG WINAPI SetSwapAreaSize16( WORD size )
647 {
648     FIXME("(%d) - stub!\n", size );
649     return MAKELONG( size, 0xffff );
650 }
651
652
653 /***********************************************************************
654  *           GlobalLRUOldest   (KERNEL.163)
655  */
656 HGLOBAL16 WINAPI GlobalLRUOldest16( HGLOBAL16 handle )
657 {
658     TRACE("%04x\n", handle );
659     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
660     return handle;
661 }
662
663
664 /***********************************************************************
665  *           GlobalLRUNewest   (KERNEL.164)
666  */
667 HGLOBAL16 WINAPI GlobalLRUNewest16( HGLOBAL16 handle )
668 {
669     TRACE("%04x\n", handle );
670     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
671     return handle;
672 }
673
674
675 /***********************************************************************
676  *           GetFreeSpace16   (KERNEL.169)
677  */
678 DWORD WINAPI GetFreeSpace16( UINT16 wFlags )
679 {
680     MEMORYSTATUS ms;
681     GlobalMemoryStatus( &ms );
682     return ms.dwAvailVirtual;
683 }
684
685 /***********************************************************************
686  *           GlobalDOSAlloc   (KERNEL.184)
687  * RETURNS
688  *      Address (HW=Paragraph segment; LW=Selector)
689  */
690 DWORD WINAPI GlobalDOSAlloc16(
691              DWORD size /* [in] Number of bytes to be allocated */
692 ) {
693    UINT16    uParagraph;
694    LPVOID    lpBlock = DOSMEM_GetBlock( size, &uParagraph );
695
696    if( lpBlock )
697    {
698        HMODULE16 hModule = GetModuleHandle16("KERNEL");
699        WORD      wSelector;
700        GLOBALARENA *pArena;
701    
702        wSelector = GLOBAL_CreateBlock(GMEM_FIXED, lpBlock, size, hModule, WINE_LDT_FLAGS_DATA );
703        pArena = GET_ARENA_PTR(wSelector);
704        pArena->flags |= GA_DOSMEM;
705        return MAKELONG(wSelector,uParagraph);
706    }
707    return 0;
708 }
709
710
711 /***********************************************************************
712  *           GlobalDOSFree      (KERNEL.185)
713  * RETURNS
714  *      NULL: Success
715  *      sel: Failure
716  */
717 WORD WINAPI GlobalDOSFree16(
718             WORD sel /* [in] Selector */
719 ) {
720    DWORD   block = GetSelectorBase(sel);
721
722    if( block && block < 0x100000 ) 
723    {
724        LPVOID lpBlock = DOSMEM_MapDosToLinear( block );
725        if( DOSMEM_FreeBlock( lpBlock ) )
726            GLOBAL_FreeBlock( sel );
727        sel = 0;
728    }
729    return sel;
730 }
731
732
733 /***********************************************************************
734  *           GlobalPageLock   (KERNEL.191)
735  */
736 WORD WINAPI GlobalPageLock16( HGLOBAL16 handle )
737 {
738     TRACE("%04x\n", handle );
739     if (!VALID_HANDLE(handle)) {
740         WARN("Invalid handle 0x%04x passed to GlobalPageLock!\n",handle);
741         return 0;
742     }
743     return ++(GET_ARENA_PTR(handle)->pageLockCount);
744 }
745
746
747 /***********************************************************************
748  *           GlobalPageUnlock   (KERNEL.192)
749  */
750 WORD WINAPI GlobalPageUnlock16( HGLOBAL16 handle )
751 {
752     TRACE("%04x\n", handle );
753     if (!VALID_HANDLE(handle)) {
754         WARN("Invalid handle 0x%04x passed to GlobalPageUnlock!\n",handle);
755         return 0;
756     }
757     return --(GET_ARENA_PTR(handle)->pageLockCount);
758 }
759
760
761 /***********************************************************************
762  *           GlobalFix16   (KERNEL.197)
763  */
764 WORD WINAPI GlobalFix16( HGLOBAL16 handle )
765 {
766     TRACE("%04x\n", handle );
767     if (!VALID_HANDLE(handle)) {
768         WARN("Invalid handle 0x%04x passed to GlobalFix16!\n",handle);
769         return 0;
770     }
771     GET_ARENA_PTR(handle)->lockCount++;
772
773     return GlobalHandleToSel16(handle);
774 }
775
776
777 /***********************************************************************
778  *           GlobalUnfix16   (KERNEL.198)
779  */
780 void WINAPI GlobalUnfix16( HGLOBAL16 handle )
781 {
782     TRACE("%04x\n", handle );
783     if (!VALID_HANDLE(handle)) {
784         WARN("Invalid handle 0x%04x passed to GlobalUnfix16!\n",handle);
785         return;
786     }
787     GET_ARENA_PTR(handle)->lockCount--;
788 }
789
790
791 /***********************************************************************
792  *           FarSetOwner   (KERNEL.403)
793  */
794 void WINAPI FarSetOwner16( HGLOBAL16 handle, HANDLE16 hOwner )
795 {
796     if (!VALID_HANDLE(handle)) {
797         WARN("Invalid handle 0x%04x passed to FarSetOwner!\n",handle);
798         return;
799     }
800     GET_ARENA_PTR(handle)->hOwner = hOwner;
801 }
802
803
804 /***********************************************************************
805  *           FarGetOwner   (KERNEL.404)
806  */
807 HANDLE16 WINAPI FarGetOwner16( HGLOBAL16 handle )
808 {
809     if (!VALID_HANDLE(handle)) {
810         WARN("Invalid handle 0x%04x passed to FarGetOwner!\n",handle);
811         return 0;
812     }
813     return GET_ARENA_PTR(handle)->hOwner;
814 }
815
816
817 /***********************************************************************
818  *           GlobalHandleToSel   (TOOLHELP.50)
819  */
820 WORD WINAPI GlobalHandleToSel16( HGLOBAL16 handle )
821 {
822     if (!handle) return 0;
823     if (!VALID_HANDLE(handle)) {
824         WARN("Invalid handle 0x%04x passed to GlobalHandleToSel!\n",handle);
825         return 0;
826     }
827     if (!(handle & 7))
828     {
829         WARN("Program attempted invalid selector conversion\n" );
830         return handle - 1;
831     }
832     return handle | 7;
833 }
834
835
836 /***********************************************************************
837  *           GlobalFirst   (TOOLHELP.51)
838  */
839 BOOL16 WINAPI GlobalFirst16( GLOBALENTRY *pGlobal, WORD wFlags )
840 {
841     if (wFlags == GLOBAL_LRU) return FALSE;
842     pGlobal->dwNext = 0;
843     return GlobalNext16( pGlobal, wFlags );
844 }
845
846
847 /***********************************************************************
848  *           GlobalNext   (TOOLHELP.52)
849  */
850 BOOL16 WINAPI GlobalNext16( GLOBALENTRY *pGlobal, WORD wFlags)
851 {
852     GLOBALARENA *pArena;
853
854     if (pGlobal->dwNext >= globalArenaSize) return FALSE;
855     pArena = pGlobalArena + pGlobal->dwNext;
856     if (wFlags == GLOBAL_FREE)  /* only free blocks */
857     {
858         int i;
859         for (i = pGlobal->dwNext; i < globalArenaSize; i++, pArena++)
860             if (pArena->size == 0) break;  /* block is free */
861         if (i >= globalArenaSize) return FALSE;
862         pGlobal->dwNext = i;
863     }
864
865     pGlobal->dwAddress    = pArena->base;
866     pGlobal->dwBlockSize  = pArena->size;
867     pGlobal->hBlock       = pArena->handle;
868     pGlobal->wcLock       = pArena->lockCount;
869     pGlobal->wcPageLock   = pArena->pageLockCount;
870     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
871     pGlobal->wHeapPresent = FALSE;
872     pGlobal->hOwner       = pArena->hOwner;
873     pGlobal->wType        = GT_UNKNOWN;
874     pGlobal->wData        = 0;
875     pGlobal->dwNext++;
876     return TRUE;
877 }
878
879
880 /***********************************************************************
881  *           GlobalInfo   (TOOLHELP.53)
882  */
883 BOOL16 WINAPI GlobalInfo16( GLOBALINFO *pInfo )
884 {
885     int i;
886     GLOBALARENA *pArena;
887
888     pInfo->wcItems = globalArenaSize;
889     pInfo->wcItemsFree = 0;
890     pInfo->wcItemsLRU = 0;
891     for (i = 0, pArena = pGlobalArena; i < globalArenaSize; i++, pArena++)
892         if (pArena->size == 0) pInfo->wcItemsFree++;
893     return TRUE;
894 }
895
896
897 /***********************************************************************
898  *           GlobalEntryHandle   (TOOLHELP.54)
899  */
900 BOOL16 WINAPI GlobalEntryHandle16( GLOBALENTRY *pGlobal, HGLOBAL16 hItem )
901 {
902     GLOBALARENA *pArena = GET_ARENA_PTR(hItem);
903
904     pGlobal->dwAddress    = pArena->base;
905     pGlobal->dwBlockSize  = pArena->size;
906     pGlobal->hBlock       = pArena->handle;
907     pGlobal->wcLock       = pArena->lockCount;
908     pGlobal->wcPageLock   = pArena->pageLockCount;
909     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
910     pGlobal->wHeapPresent = FALSE;
911     pGlobal->hOwner       = pArena->hOwner;
912     pGlobal->wType        = GT_UNKNOWN;
913     pGlobal->wData        = 0;
914     pGlobal->dwNext++;
915     return TRUE;
916 }
917
918
919 /***********************************************************************
920  *           GlobalEntryModule   (TOOLHELP.55)
921  */
922 BOOL16 WINAPI GlobalEntryModule16( GLOBALENTRY *pGlobal, HMODULE16 hModule,
923                                  WORD wSeg )
924 {
925     FIXME("(%p, 0x%04x, 0x%04x), stub.\n", pGlobal, hModule, wSeg);
926     return FALSE;
927 }
928
929
930 /***********************************************************************
931  *           MemManInfo   (TOOLHELP.72)
932  */
933 BOOL16 WINAPI MemManInfo16( MEMMANINFO *info )
934 {
935     MEMORYSTATUS status;
936
937     /*
938      * Not unsurprisingly although the documention says you 
939      * _must_ provide the size in the dwSize field, this function
940      * (under Windows) always fills the structure and returns true.
941      */
942     GlobalMemoryStatus( &status );
943     info->wPageSize            = getpagesize();
944     info->dwLargestFreeBlock   = status.dwAvailVirtual;
945     info->dwMaxPagesAvailable  = info->dwLargestFreeBlock / info->wPageSize;
946     info->dwMaxPagesLockable   = info->dwMaxPagesAvailable;
947     info->dwTotalLinearSpace   = status.dwTotalVirtual / info->wPageSize;
948     info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
949     info->dwFreePages          = info->dwMaxPagesAvailable;
950     info->dwTotalPages         = info->dwTotalLinearSpace;
951     info->dwFreeLinearSpace    = info->dwMaxPagesAvailable;
952     info->dwSwapFilePages      = status.dwTotalPageFile / info->wPageSize;
953     return TRUE;
954 }
955
956 /***********************************************************************
957  *           GetFreeMemInfo   (KERNEL.316)
958  */
959 DWORD WINAPI GetFreeMemInfo16(void)
960 {
961     MEMMANINFO info;
962     MemManInfo16( &info );
963     return MAKELONG( info.dwTotalLinearSpace, info.dwMaxPagesAvailable );
964 }
965
966 /*
967  * Win32 Global heap functions (GlobalXXX).
968  * These functions included in Win32 for compatibility with 16 bit Windows
969  * Especially the moveable blocks and handles are oldish. 
970  * But the ability to directly allocate memory with GPTR and LPTR is widely
971  * used.
972  *
973  * The handle stuff looks horrible, but it's implemented almost like Win95
974  * does it. 
975  *
976  */
977
978 #define MAGIC_GLOBAL_USED 0x5342
979 #define GLOBAL_LOCK_MAX   0xFF
980 #define HANDLE_TO_INTERN(h)  ((PGLOBAL32_INTERN)(((char *)(h))-2))
981 #define INTERN_TO_HANDLE(i)  ((HGLOBAL) &((i)->Pointer))
982 #define POINTER_TO_HANDLE(p) (*(((HGLOBAL *)(p))-1))
983 #define ISHANDLE(h)          (((DWORD)(h)&2)!=0)
984 #define ISPOINTER(h)         (((DWORD)(h)&2)==0)
985
986 typedef struct __GLOBAL32_INTERN
987 {
988    WORD         Magic;
989    LPVOID       Pointer WINE_PACKED;
990    BYTE         Flags;
991    BYTE         LockCount;
992 } GLOBAL32_INTERN, *PGLOBAL32_INTERN;
993
994
995 /***********************************************************************
996  *           GlobalAlloc   (KERNEL32.315)
997  * RETURNS
998  *      Handle: Success
999  *      NULL: Failure
1000  */
1001 HGLOBAL WINAPI GlobalAlloc(
1002                  UINT flags, /* [in] Object allocation attributes */
1003                  DWORD size    /* [in] Number of bytes to allocate */
1004 ) {
1005    PGLOBAL32_INTERN     pintern;
1006    DWORD                hpflags;
1007    LPVOID               palloc;
1008
1009    if(flags&GMEM_ZEROINIT)
1010       hpflags=HEAP_ZERO_MEMORY;
1011    else
1012       hpflags=0;
1013    
1014    TRACE("() flags=%04x\n",  flags );
1015    
1016    if((flags & GMEM_MOVEABLE)==0) /* POINTER */
1017    {
1018       palloc=HeapAlloc(GetProcessHeap(), hpflags, size);
1019       return (HGLOBAL) palloc;
1020    }
1021    else  /* HANDLE */
1022    {
1023       /* HeapLock(heap); */
1024
1025       pintern=HeapAlloc(GetProcessHeap(), 0,  sizeof(GLOBAL32_INTERN));
1026       if (!pintern) return 0;
1027       if(size)
1028       {
1029          if (!(palloc=HeapAlloc(GetProcessHeap(), hpflags, size+sizeof(HGLOBAL)))) {
1030             HeapFree(GetProcessHeap(), 0, pintern);
1031             return 0;
1032          }
1033          *(HGLOBAL *)palloc=INTERN_TO_HANDLE(pintern);
1034          pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1035       }
1036       else
1037          pintern->Pointer=NULL;
1038       pintern->Magic=MAGIC_GLOBAL_USED;
1039       pintern->Flags=flags>>8;
1040       pintern->LockCount=0;
1041       
1042       /* HeapUnlock(heap); */
1043        
1044       return INTERN_TO_HANDLE(pintern);
1045    }
1046 }
1047
1048
1049 /***********************************************************************
1050  *           GlobalLock   (KERNEL32.326)
1051  * RETURNS
1052  *      Pointer to first byte of block
1053  *      NULL: Failure
1054  */
1055 LPVOID WINAPI GlobalLock(
1056               HGLOBAL hmem /* [in] Handle of global memory object */
1057 ) {
1058    PGLOBAL32_INTERN pintern;
1059    LPVOID           palloc;
1060
1061    if(ISPOINTER(hmem))
1062       return (LPVOID) hmem;
1063
1064    /* HeapLock(GetProcessHeap()); */
1065    
1066    pintern=HANDLE_TO_INTERN(hmem);
1067    if(pintern->Magic==MAGIC_GLOBAL_USED)
1068    {
1069       if(pintern->LockCount<GLOBAL_LOCK_MAX)
1070          pintern->LockCount++;
1071       palloc=pintern->Pointer;
1072    }
1073    else
1074    {
1075       WARN("invalid handle\n");
1076       palloc=(LPVOID) NULL;
1077       SetLastError(ERROR_INVALID_HANDLE);
1078    }
1079    /* HeapUnlock(GetProcessHeap()); */;
1080    return palloc;
1081 }
1082
1083
1084 /***********************************************************************
1085  *           GlobalUnlock   (KERNEL32.332)
1086  * RETURNS
1087  *      TRUE: Object is still locked
1088  *      FALSE: Object is unlocked
1089  */
1090 BOOL WINAPI GlobalUnlock(
1091               HGLOBAL hmem /* [in] Handle of global memory object */
1092 ) {
1093    PGLOBAL32_INTERN       pintern;
1094    BOOL                 locked;
1095
1096    if(ISPOINTER(hmem))
1097       return FALSE;
1098
1099    /* HeapLock(GetProcessHeap()); */
1100    pintern=HANDLE_TO_INTERN(hmem);
1101    
1102    if(pintern->Magic==MAGIC_GLOBAL_USED)
1103    {
1104       if((pintern->LockCount<GLOBAL_LOCK_MAX)&&(pintern->LockCount>0))
1105          pintern->LockCount--;
1106
1107       locked = (pintern->LockCount != 0);
1108       if (!locked) SetLastError(NO_ERROR);
1109    }
1110    else
1111    {
1112       WARN("invalid handle\n");
1113       SetLastError(ERROR_INVALID_HANDLE);
1114       locked=FALSE;
1115    }
1116    /* HeapUnlock(GetProcessHeap()); */
1117    return locked;
1118 }
1119
1120
1121 /***********************************************************************
1122  *           GlobalHandle   (KERNEL32.325)
1123  * Returns the handle associated with the specified pointer.
1124  *
1125  * RETURNS
1126  *      Handle: Success
1127  *      NULL: Failure
1128  */
1129 HGLOBAL WINAPI GlobalHandle(
1130                  LPCVOID pmem /* [in] Pointer to global memory block */
1131 ) {
1132     HGLOBAL handle;
1133     PGLOBAL32_INTERN  maybe_intern;
1134     LPCVOID test;
1135
1136     if (!pmem)
1137     {
1138         SetLastError( ERROR_INVALID_PARAMETER );
1139         return 0;
1140     }
1141
1142     __TRY
1143     {
1144         handle = 0;
1145
1146         /* note that if pmem is a pointer to a a block allocated by        */
1147         /* GlobalAlloc with GMEM_MOVEABLE then magic test in HeapValidate  */
1148         /* will fail.                                                      */
1149         if (ISPOINTER(pmem)) {
1150             if (HeapValidate( GetProcessHeap(), 0, pmem )) {
1151                 handle = (HGLOBAL)pmem;  /* valid fixed block */
1152                 break;
1153             }
1154             handle = POINTER_TO_HANDLE(pmem);
1155         } else
1156             handle = (HGLOBAL)pmem;
1157
1158         /* Now test handle either passed in or retrieved from pointer */
1159         maybe_intern = HANDLE_TO_INTERN( handle );
1160         if (maybe_intern->Magic == MAGIC_GLOBAL_USED) {
1161             test = maybe_intern->Pointer;
1162             if (HeapValidate( GetProcessHeap(), 0, ((HGLOBAL *)test)-1 ) && /* obj(-handle) valid arena? */
1163                 HeapValidate( GetProcessHeap(), 0, maybe_intern ))  /* intern valid arena? */
1164                 break;  /* valid moveable block */
1165         }
1166         handle = 0;
1167         SetLastError( ERROR_INVALID_HANDLE );
1168     }
1169     __EXCEPT(page_fault)
1170     {
1171         SetLastError( ERROR_INVALID_HANDLE );
1172         return 0;
1173     }
1174     __ENDTRY
1175
1176     return handle;
1177 }
1178
1179
1180 /***********************************************************************
1181  *           GlobalReAlloc   (KERNEL32.328)
1182  * RETURNS
1183  *      Handle: Success
1184  *      NULL: Failure
1185  */
1186 HGLOBAL WINAPI GlobalReAlloc(
1187                  HGLOBAL hmem, /* [in] Handle of global memory object */
1188                  DWORD size,     /* [in] New size of block */
1189                  UINT flags    /* [in] How to reallocate object */
1190 ) {
1191    LPVOID               palloc;
1192    HGLOBAL            hnew;
1193    PGLOBAL32_INTERN     pintern;
1194    DWORD heap_flags = (flags & GMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0;
1195
1196    hnew = 0;
1197    /* HeapLock(heap); */
1198    if(flags & GMEM_MODIFY) /* modify flags */
1199    {
1200       if( ISPOINTER(hmem) && (flags & GMEM_MOVEABLE))
1201       {
1202          /* make a fixed block moveable
1203           * actually only NT is able to do this. But it's soo simple
1204           */
1205          if (hmem == 0)
1206          {
1207              ERR("GlobalReAlloc32 with null handle!\n");
1208              SetLastError( ERROR_NOACCESS );
1209              return 0;
1210          }
1211          size=HeapSize(GetProcessHeap(), 0, (LPVOID) hmem);
1212          hnew=GlobalAlloc( flags, size);
1213          palloc=GlobalLock(hnew);
1214          memcpy(palloc, (LPVOID) hmem, size);
1215          GlobalUnlock(hnew);
1216          GlobalFree(hmem);
1217       }
1218       else if( ISPOINTER(hmem) &&(flags & GMEM_DISCARDABLE))
1219       {
1220          /* change the flags to make our block "discardable" */
1221          pintern=HANDLE_TO_INTERN(hmem);
1222          pintern->Flags = pintern->Flags | (GMEM_DISCARDABLE >> 8);
1223          hnew=hmem;
1224       }
1225       else
1226       {
1227          SetLastError(ERROR_INVALID_PARAMETER);
1228          hnew = 0;
1229       }
1230    }
1231    else
1232    {
1233       if(ISPOINTER(hmem))
1234       {
1235          /* reallocate fixed memory */
1236          hnew=(HGLOBAL)HeapReAlloc(GetProcessHeap(), heap_flags, (LPVOID) hmem, size);
1237       }
1238       else
1239       {
1240          /* reallocate a moveable block */
1241          pintern=HANDLE_TO_INTERN(hmem);
1242
1243 #if 0
1244 /* Apparently Windows doesn't care whether the handle is locked at this point */
1245 /* See also the same comment in GlobalFree() */
1246          if(pintern->LockCount>1) {
1247             ERR("handle 0x%08lx is still locked, cannot realloc!\n",(DWORD)hmem);
1248             SetLastError(ERROR_INVALID_HANDLE);
1249          } else
1250 #endif
1251          if(size!=0)
1252          {
1253             hnew=hmem;
1254             if(pintern->Pointer)
1255             {
1256                if((palloc = HeapReAlloc(GetProcessHeap(), heap_flags,
1257                                    (char *) pintern->Pointer-sizeof(HGLOBAL),
1258                                    size+sizeof(HGLOBAL))) == NULL)
1259                    return 0; /* Block still valid */
1260                pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1261             }
1262             else
1263             {
1264                 if((palloc=HeapAlloc(GetProcessHeap(), heap_flags, size+sizeof(HGLOBAL)))
1265                    == NULL)
1266                     return 0;
1267                *(HGLOBAL *)palloc=hmem;
1268                pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1269             }
1270          }
1271          else
1272          {
1273             if(pintern->Pointer)
1274             {
1275                HeapFree(GetProcessHeap(), 0, (char *) pintern->Pointer-sizeof(HGLOBAL));
1276                pintern->Pointer=NULL;
1277             }
1278          }
1279       }
1280    }
1281    /* HeapUnlock(heap); */
1282    return hnew;
1283 }
1284
1285
1286 /***********************************************************************
1287  *           GlobalFree   (KERNEL32.322)
1288  * RETURNS
1289  *      NULL: Success
1290  *      Handle: Failure
1291  */
1292 HGLOBAL WINAPI GlobalFree(
1293                  HGLOBAL hmem /* [in] Handle of global memory object */
1294 ) {
1295    PGLOBAL32_INTERN pintern;
1296    HGLOBAL        hreturned = 0;
1297
1298    if(ISPOINTER(hmem)) /* POINTER */
1299    {
1300       if(!HeapFree(GetProcessHeap(), 0, (LPVOID) hmem)) hmem = 0;
1301    }
1302    else  /* HANDLE */
1303    {
1304       /* HeapLock(heap); */
1305       pintern=HANDLE_TO_INTERN(hmem);
1306       
1307       if(pintern->Magic==MAGIC_GLOBAL_USED)
1308       {  
1309
1310 /* WIN98 does not make this test. That is you can free a */
1311 /* block you have not unlocked. Go figure!!              */
1312       /* if(pintern->LockCount!=0)  */
1313       /*    SetLastError(ERROR_INVALID_HANDLE);  */
1314
1315          if(pintern->Pointer)
1316             if(!HeapFree(GetProcessHeap(), 0, (char *)(pintern->Pointer)-sizeof(HGLOBAL)))
1317                hreturned=hmem;
1318          if(!HeapFree(GetProcessHeap(), 0, pintern))
1319             hreturned=hmem;
1320       }      
1321       /* HeapUnlock(heap); */
1322    }
1323    return hreturned;
1324 }
1325
1326
1327 /***********************************************************************
1328  *           GlobalSize   (KERNEL32.329)
1329  * RETURNS
1330  *      Size in bytes of the global memory object
1331  *      0: Failure
1332  */
1333 DWORD WINAPI GlobalSize(
1334              HGLOBAL hmem /* [in] Handle of global memory object */
1335 ) {
1336    DWORD                retval;
1337    PGLOBAL32_INTERN     pintern;
1338
1339    if(ISPOINTER(hmem)) 
1340    {
1341       retval=HeapSize(GetProcessHeap(), 0,  (LPVOID) hmem);
1342    }
1343    else
1344    {
1345       /* HeapLock(heap); */
1346       pintern=HANDLE_TO_INTERN(hmem);
1347       
1348       if(pintern->Magic==MAGIC_GLOBAL_USED)
1349       {
1350         if (!pintern->Pointer) /* handle case of GlobalAlloc( ??,0) */
1351             return 0;
1352          retval=HeapSize(GetProcessHeap(), 0,
1353                          (char *)(pintern->Pointer)-sizeof(HGLOBAL))-4;
1354          if (retval == 0xffffffff-4) retval = 0;
1355       }
1356       else
1357       {
1358          WARN("invalid handle\n");
1359          retval=0;
1360       }
1361       /* HeapUnlock(heap); */
1362    }
1363    /* HeapSize returns 0xffffffff on failure */
1364    if (retval == 0xffffffff) retval = 0;
1365    return retval;
1366 }
1367
1368
1369 /***********************************************************************
1370  *           GlobalWire   (KERNEL32.333)
1371  */
1372 LPVOID WINAPI GlobalWire(HGLOBAL hmem)
1373 {
1374    return GlobalLock( hmem );
1375 }
1376
1377
1378 /***********************************************************************
1379  *           GlobalUnWire   (KERNEL32.330)
1380  */
1381 BOOL WINAPI GlobalUnWire(HGLOBAL hmem)
1382 {
1383    return GlobalUnlock( hmem);
1384 }
1385
1386
1387 /***********************************************************************
1388  *           GlobalFix   (KERNEL32.320)
1389  */
1390 VOID WINAPI GlobalFix(HGLOBAL hmem)
1391 {
1392     GlobalLock( hmem );
1393 }
1394
1395
1396 /***********************************************************************
1397  *           GlobalUnfix   (KERNEL32.331)
1398  */
1399 VOID WINAPI GlobalUnfix(HGLOBAL hmem)
1400 {
1401    GlobalUnlock( hmem);
1402 }
1403
1404
1405 /***********************************************************************
1406  *           GlobalFlags   (KERNEL32.321)
1407  * Returns information about the specified global memory object
1408  *
1409  * NOTES
1410  *      Should this return GMEM_INVALID_HANDLE on invalid handle?
1411  *
1412  * RETURNS
1413  *      Value specifying allocation flags and lock count
1414  *      GMEM_INVALID_HANDLE: Failure
1415  */
1416 UINT WINAPI GlobalFlags(
1417               HGLOBAL hmem /* [in] Handle to global memory object */
1418 ) {
1419    DWORD                retval;
1420    PGLOBAL32_INTERN     pintern;
1421    
1422    if(ISPOINTER(hmem))
1423    {
1424       retval=0;
1425    }
1426    else
1427    {
1428       /* HeapLock(GetProcessHeap()); */
1429       pintern=HANDLE_TO_INTERN(hmem);
1430       if(pintern->Magic==MAGIC_GLOBAL_USED)
1431       {               
1432          retval=pintern->LockCount + (pintern->Flags<<8);
1433          if(pintern->Pointer==0)
1434             retval|= GMEM_DISCARDED;
1435       }
1436       else
1437       {
1438          WARN("Invalid handle: %04x", hmem);
1439          retval=0;
1440       }
1441       /* HeapUnlock(GetProcessHeap()); */
1442    }
1443    return retval;
1444 }
1445
1446
1447 /***********************************************************************
1448  *           GlobalCompact   (KERNEL32.316)
1449  */
1450 DWORD WINAPI GlobalCompact( DWORD minfree )
1451 {
1452     return 0;  /* GlobalCompact does nothing in Win32 */
1453 }
1454
1455
1456 /***********************************************************************
1457  *           GlobalMemoryStatus   (KERNEL32.327)
1458  * RETURNS
1459  *      None
1460  */
1461 VOID WINAPI GlobalMemoryStatus(
1462             LPMEMORYSTATUS lpmem
1463 ) {
1464     static MEMORYSTATUS cached_memstatus;
1465     static int cache_lastchecked = 0;
1466     SYSTEM_INFO si;
1467 #ifdef linux
1468     FILE *f;
1469 #endif
1470
1471     if (time(NULL)==cache_lastchecked) {
1472         memcpy(lpmem,&cached_memstatus,sizeof(MEMORYSTATUS));
1473         return;
1474     }
1475     cache_lastchecked = time(NULL);
1476
1477 #ifdef linux
1478     f = fopen( "/proc/meminfo", "r" );
1479     if (f)
1480     {
1481         char buffer[256];
1482         int total, used, free, shared, buffers, cached;
1483
1484         lpmem->dwLength = sizeof(MEMORYSTATUS);
1485         lpmem->dwTotalPhys = lpmem->dwAvailPhys = 0;
1486         lpmem->dwTotalPageFile = lpmem->dwAvailPageFile = 0;
1487         while (fgets( buffer, sizeof(buffer), f ))
1488         {
1489             /* old style /proc/meminfo ... */
1490             if (sscanf( buffer, "Mem: %d %d %d %d %d %d", &total, &used, &free, &shared, &buffers, &cached ))
1491             {
1492                 lpmem->dwTotalPhys += total;
1493                 lpmem->dwAvailPhys += free + buffers + cached;
1494             }
1495             if (sscanf( buffer, "Swap: %d %d %d", &total, &used, &free ))
1496             {
1497                 lpmem->dwTotalPageFile += total;
1498                 lpmem->dwAvailPageFile += free;
1499             }
1500
1501             /* new style /proc/meminfo ... */
1502             if (sscanf(buffer, "MemTotal: %d", &total))
1503                 lpmem->dwTotalPhys = total*1024;
1504             if (sscanf(buffer, "MemFree: %d", &free))
1505                 lpmem->dwAvailPhys = free*1024;
1506             if (sscanf(buffer, "SwapTotal: %d", &total))
1507                 lpmem->dwTotalPageFile = total*1024;
1508             if (sscanf(buffer, "SwapFree: %d", &free))
1509                 lpmem->dwAvailPageFile = free*1024;
1510             if (sscanf(buffer, "Buffers: %d", &buffers))
1511                 lpmem->dwAvailPhys += buffers*1024;
1512             if (sscanf(buffer, "Cached: %d", &cached))
1513                 lpmem->dwAvailPhys += cached*1024;
1514         }
1515         fclose( f );
1516
1517         if (lpmem->dwTotalPhys)
1518         {
1519             DWORD TotalPhysical = lpmem->dwTotalPhys+lpmem->dwTotalPageFile;
1520             DWORD AvailPhysical = lpmem->dwAvailPhys+lpmem->dwAvailPageFile;
1521             lpmem->dwMemoryLoad = (TotalPhysical-AvailPhysical)
1522                                       / (TotalPhysical / 100);
1523         }
1524     } else
1525 #endif
1526     {
1527         /* FIXME: should do something for other systems */
1528         lpmem->dwMemoryLoad    = 0;
1529         lpmem->dwTotalPhys     = 16*1024*1024;
1530         lpmem->dwAvailPhys     = 16*1024*1024;
1531         lpmem->dwTotalPageFile = 16*1024*1024;
1532         lpmem->dwAvailPageFile = 16*1024*1024;
1533     }
1534     GetSystemInfo(&si);
1535     lpmem->dwTotalVirtual  = si.lpMaximumApplicationAddress-si.lpMinimumApplicationAddress;
1536     /* FIXME: we should track down all the already allocated VM pages and substract them, for now arbitrarily remove 64KB so that it matches NT */
1537     lpmem->dwAvailVirtual  = lpmem->dwTotalVirtual-64*1024;
1538     memcpy(&cached_memstatus,lpmem,sizeof(MEMORYSTATUS));
1539
1540     /* it appears some memory display programs want to divide by these values */
1541     if(lpmem->dwTotalPageFile==0)
1542         lpmem->dwTotalPageFile++;
1543
1544     if(lpmem->dwAvailPageFile==0)
1545         lpmem->dwAvailPageFile++;
1546 }
1547
1548 /***********************************************************************
1549  *           A20Proc16   (KERNEL.165)
1550  */
1551 void WINAPI A20Proc16( WORD unused )
1552 {
1553     /* this is also a NOP in Windows */
1554 }
1555
1556 /***********************************************************************
1557  *           LimitEMSPages16   (KERNEL.156)
1558  */
1559 DWORD WINAPI LimitEMSPages16( DWORD unused )
1560 {
1561     return 0;
1562 }