/* m4memory.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

#ifdef S4OS2
   #ifdef __DLL__
      #define  INCL_DOSMEMMGR
   #endif
#endif
#include "d4all.h"

#ifdef S4VB_DOS
   #include "malloc.h"
#endif

#ifdef __TURBOC__
   #pragma hdrstop
#endif

#ifdef S4OS2
   #ifdef __DLL__
      #include <bsememf.h>
   #endif
#endif

#ifdef S4SERVER
   /* for 32 bit servers, allow more than 10000 memory allocations... */
   #ifndef S4WIN32
      #ifdef S4WINTEL
         #ifndef S4OS2
            #define MEM4MAX_POINTERS 10000
         #endif
      #endif
   #endif
   #ifndef MEM4MAX_POINTERS
      #define MEM4MAX_POINTERS 100000
   #endif
#else
   #define MEM4MAX_POINTERS 10000
#endif

#define mem4numTypes 10

#ifdef S4MEM_PRINT
   #ifdef S4WINDOWS
      #ifdef S4TESTING
         #include "t4test.h"
      #endif
   #endif
   int v4print = 0 ; /* if v4print == 0 then stdout, else stdprn, no output if -1 */
   /* use globals to hold the current file name and line number */
   const char *m4fileName = 0 ;
   int m4lineNo = 0 ;
   #ifdef S4WINDOWS
      int  S4FUNCTION code4memFileName( void )
      {
         return m4lineNo ;
      }

      const char *S4FUNCTION code4memFileNo( void )
      {
         return m4fileName ;
      }

      void S4FUNCTION code4memLineNoSet( int val )
      {
         m4lineNo = val ;
      }

      void S4FUNCTION code4memFileNameSet( const char *ptr )
      {
         m4fileName = ptr ;
      }
   #endif
#endif

typedef struct
{
   LINK4  link ;
   MEM4  types[mem4numTypes] ;
} MEMORY4GROUP ;

#ifdef S4WIN32
   #ifdef S4SEMAPHORE
      /* multi-thread support */
      CRITICAL_SECTION critical4memory, critical4expression ;
   #endif
#endif

#ifndef S4CBPP
   static LIST4  avail = { 0, 0, 0 } ;   /* A list of available MEM4 entries */
   static LIST4  used = { 0, 0, 0 } ;   /* A list of used MEM4 entries */
   static LIST4  groups = { 0, 0, 0 } ; /* A list of Allocated MEM4 groups */
#else
   /* initialization with class and union structures */
   static LIST4  avail  = { {0}, 0, {0} } ;   /* A list of available MEM4 entries */
   static LIST4  used   = { {0}, {0}, {0} } ;   /* A list of used MEM4 entries */
   static LIST4  groups = { {0}, 0, {0} } ;   /* A list of Allocated MEM4 groups */
#endif
#ifdef S4SEMAPHORE
   #ifdef S4WIN32
      static int memoryInitialized = 0 ;
   #endif
#endif

#ifdef S4SEMAPHORE
#ifdef S4WIN32
int mem4start( CODE4 *c4 )
{
   if ( memoryInitialized == 0 )
      return -1 ;

   EnterCriticalSection( &critical4memory ) ;

   return 0 ;
}

void mem4stop( CODE4 *c4 )
{
   if ( memoryInitialized == 0 )
      return ;

   LeaveCriticalSection( &critical4memory ) ;
}
#else
static int mem4start( CODE4 *c4 )
{
   APIRET rc ;

   #ifdef E4ANALYZE
      if ( c4 == 0 )
         return error4( c4, e4info, "OS/2 Semaphore Failure" ) ;
   #endif

   rc = DosRequestMutexSem( c4->hmtxMem, -1 ) ;
   if ( rc != 0 )
      return error4( c4, e4info, "OS/2 Semaphore Failure" ) ;
   return 0 ;
}

static void mem4stop( CODE4 *c4 )
{
   #ifdef E4ANALYZE
      if ( c4 == 0 )
      {
         error4( c4, e4info, "OS/2 Semaphore Failure" ) ;
         return ;
      }
   #endif

   DosReleaseMutexSem( c4->hmtxMem ) ;
}
#endif /* S4WIN32 */
#endif /* S4SEMAPHORE */

#ifdef E4MISC

static char **mem4testPointers ;
static int mem4numPointer = -1 ;
static int mem4numUsed = 0 ;

#ifdef S4DATA_ALIGN
   #define mem4extraChars 12
#else
   #define mem4extraChars 10
#endif
#define mem4extraTot (mem4extraChars*2 + sizeof(unsigned))
#define mem4checkChar 0x55

#define MEM4INC 500

/* Returns the pointer to be returned; is passed the pointer allocated by malloc ... */
static char *mem4fixPointer( char *startPtr, unsigned largeLen )
{
   char *returnPtr ;
   unsigned pos ;

   memset( startPtr, mem4checkChar, mem4extraChars ) ;
   returnPtr = startPtr + mem4extraChars ;

   memcpy( returnPtr, (void *)&largeLen, sizeof(largeLen) ) ;
   pos = largeLen - mem4extraChars ;
   memset( startPtr+ pos, mem4checkChar, mem4extraChars ) ;

   return returnPtr + sizeof(unsigned) ;
}

/* Returns the pointer allocated by malloc; */
/* passed by pointer returned by 'mem4fixPointer' */
static char *mem4checkPointer( char *returnPtr, int clear )
{
   unsigned *largeLenPtr ;
   char *mallocPtr, *testPtr ;
   int i, j ;

   largeLenPtr = (unsigned *)(returnPtr - sizeof(unsigned)) ;
   mallocPtr = returnPtr - sizeof(unsigned) - mem4extraChars ;

   for ( j = 0; j < 2; j++ )
   {
      if (j == 0)
         testPtr = mallocPtr ;
      else
         testPtr = mallocPtr + *largeLenPtr - mem4extraChars ;

      for ( i = 0 ; i < mem4extraChars ; i++ )
         if ( testPtr[i] != mem4checkChar )
         {
            #ifdef S4MEM_PRINT
               if ( v4print != -1 )
               {
                  #ifdef S4WINDOWS
                     #ifdef S4TESTING
                        d4displayStr( mem4displayPtr, "corrupt memory: ", 1 ) ;
                        d4displayPtr( mem4displayPtr, mallocPtr- sizeof(unsigned) - mem4extraChars, 0 ) ;
                     #endif
                  #else
                     if ( v4print )
                        fprintf( stdprn, "\r\ncorrupt memory: %p", mallocPtr- sizeof(unsigned) - mem4extraChars ) ;
                     else
                        printf( "\r\ncorrupt memory: %p", mallocPtr- sizeof(unsigned) - mem4extraChars ) ;
                  #endif
               }
            #endif
            error4( 0, e4result, E85901 ) ;
            return 0 ;
         }
   }
   if ( clear == 1 ) /* null the memory to potentially detect re-use, including clearing check chars */
      memset( mallocPtr, 0, *largeLenPtr ) ;
   return mallocPtr ;
}

static int mem4pushPointer( char *ptr )
{
   #ifdef S4WINDOWS
      HANDLE handle, *hPtr, *oldHPtr ;
      hPtr = (HANDLE *)0 ;
   #endif

   if ( mem4numPointer < 0 )
   {
      #ifdef S4WINDOWS
         #ifdef __DLL__
            handle = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, (DWORD) sizeof(char *) * MEM4INC + sizeof(HANDLE) ) ;
         #else
            handle = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD) sizeof(char *) * MEM4INC + sizeof(HANDLE) ) ;
         #endif

         if ( handle == (HANDLE) 0 )
            return error4( 0, e4memory, E95901 ) ;

         hPtr = (HANDLE *)GlobalLock( handle ) ;
         *hPtr++ = handle ;
         mem4testPointers = (char **)hPtr ;
      #else
         mem4testPointers = (char **)malloc( sizeof(char *) * MEM4INC ) ;
      #endif
      mem4numPointer = MEM4INC ;
   }
   if ( mem4numPointer == mem4numUsed )
   {
      mem4numPointer += MEM4INC ;
      if ( mem4numPointer > MEM4MAX_POINTERS )
         return error4( 0, e4result, E95901 ) ;

      #ifdef S4WINDOWS
         oldHPtr = (HANDLE *)(mem4testPointers) ;
         oldHPtr-- ;  /* get the actual handle */

         #ifdef __DLL__
            handle = GlobalReAlloc( *oldHPtr, (DWORD)sizeof(char *) * mem4numPointer + sizeof( HANDLE ), GMEM_MOVEABLE ) ;
         #else
            handle = GlobalReAlloc( *oldHPtr, (DWORD)sizeof(char *) * mem4numPointer + sizeof( HANDLE ), GMEM_MOVEABLE ) ;
         #endif

         if ( handle == (HANDLE) 0 )
            return error4( 0, e4memory, E95901 ) ;

         hPtr = (HANDLE *)GlobalLock( handle ) ;
         *hPtr++ = handle ;
         mem4testPointers = (char **)hPtr ;
      #else
         mem4testPointers = (char **)realloc( (void *)mem4testPointers, sizeof(char *)*mem4numPointer ) ;
      #endif
   }

   if ( mem4testPointers == 0 )
      return error4( 0, e4memory, E95901 ) ;

   mem4testPointers[mem4numUsed++] = ptr ;

   return 0 ;
}

static int mem4popPointer( char *ptr )
{
   int i ;

   if ( mem4testPointers == 0 )   /* failure in allocating tracking memory, so allow shutdown */
   {
      mem4numUsed-- ;
      return 0 ;
   }

   for ( i = mem4numUsed - 1 ; i >= 0 ; i-- )
      if ( mem4testPointers[i] == ptr )
      {
         /* This 'memmove' may create compile warning */
         c4memmove( mem4testPointers+i, mem4testPointers+i+1, (size_t) (sizeof(char *) * (mem4numUsed-i-1))) ;
         mem4numUsed-- ;
         return 0 ;
      }

   return error4( 0, e4result, E95902 ) ;
}

int S4FUNCTION mem4checkMemory()
{
   int i ;
   char *ptr ;

   if ( code4numCodeBase() == 0 )
      return 0 ;

   #ifdef S4SEMAPHORE
      mem4start( 0 ) ;
   #endif

   for ( i = 0; i < mem4numUsed; i++ )
   {
      ptr = mem4checkPointer( mem4testPointers[i], 0 ) ;
      if ( ptr == 0 )
         return error4( 0, e4result, E95903 ) ;
   }

   #ifdef S4SEMAPHORE
      mem4stop( 0 ) ;
   #endif

   return 0 ;
}

int S4FUNCTION mem4freeCheck( const int maxLeft )
{
   #ifdef S4MEM_PRINT
      int i ;
      if ( v4print != -1 )
      {
         #ifdef S4WINDOWS
            #ifdef S4TESTING
               for ( i = 0; i < mem4numUsed; i++ )
               {
                  d4displayStr( mem4displayPtr, "mem4freeCheck: ", 1 ) ;
                  d4displayPtr( mem4displayPtr, mem4testPointers[i], 0 ) ;
               }
            #endif
         #else
            if ( v4print )
               for ( i = 0; i < mem4numUsed; i++ )
                  fprintf( stdprn, "\r\nmem4freeCheck: %p", mem4testPointers[i] ) ;
            else
               for ( i = 0; i < mem4numUsed; i++ )
                  printf( "\r\nmem4freeCheck: %p", mem4testPointers[i] ) ;
         #endif
      }
   #endif

   if ( mem4numUsed > maxLeft )
      return error4( 0, e4result, 95904 ) ;

   return ( mem4numUsed ) ;
}
#endif

void *S4FUNCTION mem4allocDefault( MEM4 *memoryType )
{
   #ifdef E4PARM_HIGH
      if ( memoryType == 0 )
      {
         error4( 0, e4parm_null, E95905 ) ;
         return 0 ;
      }
   #endif

   return mem4alloc2Default( memoryType, 0 ) ;
}

Y4CHUNK *mem4allocChunkDefault( MEM4 *typePtr )
{
   Y4CHUNK *chunkPtr ;
   int  nAllocate, i ;
   char *ptr ;
   long allocSize ;

   nAllocate = typePtr->unitExpand ;
   if ( l4last( &typePtr->chunks ) == 0 )
      nAllocate = typePtr->unitStart ;

   for ( ;; )
   {
      allocSize = (long)sizeof( LINK4 ) + (long)nAllocate * typePtr->unitSize ;
      if ( allocSize < UINT_MAX )
         break ;
      if ( nAllocate <= 1 )
         return 0 ;
      nAllocate = nAllocate / 2 ;
   }

   #ifdef S4DOS    /* no memory sharing under DOS, so can use a CODE4 for limited memory */
      chunkPtr = (Y4CHUNK *)u4allocFreeDefault( typePtr->codeBase, allocSize ) ;
   #else
      chunkPtr = (Y4CHUNK *)u4alloc( allocSize ) ;
   #endif
   if ( chunkPtr == 0 )
      return 0 ;
   ptr = (char *)&chunkPtr->data ;
   for ( i = 0 ; i < nAllocate ; i++ )
      l4add( &typePtr->pieces, (LINK4 *)( ptr + i * typePtr->unitSize ) ) ;

   return  chunkPtr ;
}

static void *mem4allocLow( MEM4 *memoryType )
{
   LINK4 *nextPiece ;
   Y4CHUNK *newChunk ;
   #ifdef E4MISC
      char *ptr ;
   #endif
   #ifdef S4OS2_SEMAPHORE
      #ifdef __DLL__
         ULONG flags ;
      #endif
   #endif

   if ( memoryType == 0 )
      return 0 ;
   nextPiece = (LINK4 *)l4pop( &memoryType->pieces ) ;

   if ( nextPiece != 0 )
   {
      #ifdef S4SEMAPHORE
         #ifdef S4OS2
            #ifdef __DLL__
               /* get access to the memory */
               flags = PAG_WRITE | PAG_READ  ;
               if ( DosGetSharedMem( nextPiece, flags ) != 0 )
                  return 0 ;
            #endif
         #endif
      #endif
      #ifdef E4MISC
         memoryType->nUsed++ ;
         ptr = mem4fixPointer( (char *)nextPiece, memoryType->unitSize ) ;

         if ( mem4pushPointer( ptr ) == e4memory )
         {
            memoryType->nUsed-- ;
            l4add( &memoryType->pieces, nextPiece ) ;
            return 0 ;
         }

         #ifdef S4MEM_PRINT
            if ( v4print != -1 )
            {
               #ifdef S4WINDOWS
                  #ifdef S4TESTING
                     {
                        d4displayStr( mem4displayPtr, "  Y4ALLOC:  ", 1 ) ;
                        d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                        d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                        d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                        d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                        d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                     }
                  #endif
               #else
                  if ( v4print )
                     fprintf( stdprn, "\r\n  Y4ALLOC:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo ) ;
                  else
                     printf( "\r\n  Y4ALLOC:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  ) ;
               #endif
            }
         #endif

         return (void *)ptr ;
      #else
         return nextPiece ;
      #endif
   }

   if ( (newChunk = mem4allocChunkDefault( memoryType )) == 0 )
      return 0 ;
   l4add( &memoryType->chunks, &newChunk->link ) ;

   memoryType->nUsed++ ;
   #ifdef E4MISC
      ptr = mem4fixPointer( (char *)l4pop(&memoryType->pieces),
                            memoryType->unitSize ) ;
      #ifdef S4MEM_PRINT
         if ( v4print != -1 )
         {
            #ifdef S4WINDOWS
               #ifdef S4TESTING
                  {
                     d4displayStr( mem4displayPtr, "  Y4ALLOC:  ", 1 ) ;
                     d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                     d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                     d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                     d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                     d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                  }
               #endif
            #else
               if ( v4print )
                  fprintf( stdprn, "\r\n  Y4ALLOC:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo ) ;
               else
                  printf( "\r\n  Y4ALLOC:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  ) ;
            #endif
         }
      #endif

      mem4pushPointer( ptr ) ;
      return (void *)ptr ;
   #else
      return l4pop( &memoryType->pieces ) ;
   #endif
}

void *S4FUNCTION mem4alloc2Default( MEM4 *memoryType, CODE4 *c4 )
{
   void *ptr ;

   if ( code4numCodeBase() == 0 )
      return 0 ;

   if ( c4 )
      if ( error4code( c4 ) < 0 )
         return 0 ;

   #ifdef S4SEMAPHORE
      mem4start( c4 ) ;
   #endif

   ptr = mem4allocLow( memoryType ) ;

   #ifdef S4SEMAPHORE
      #ifdef S4WIN32
         mem4stop( c4 ) ;
      #else
         mem4stop( memoryType->codeBase ) ;
      #endif
   #endif

   if ( ptr == 0 )
   {
      if ( c4 )
         error4set( c4, e4memory ) ;
      return 0 ;
   }
   #ifdef E4MISC
      memset( ptr, 0, memoryType->unitSize - mem4extraTot ) ;
   #else
      memset( ptr, 0, memoryType->unitSize ) ;
   #endif
   return ptr ;
}

MEM4 *S4FUNCTION mem4createDefault( CODE4 *c4, int start, const unsigned int uSize, int expand, const int isTemp )
{
   MEM4 *onType ;
   unsigned int unitSize ;
   #ifdef S4SEMAPHORE
      #ifdef S4OS2
         #ifdef __DLL__
            ULONG flags ;
         #endif
      #endif
   #endif

   #ifdef E4PARM_HIGH
      /* c4 == 0 is allowable */
      if (  start < 1 || expand < 1 )
      {
         error4( c4, e4parm, E95906 ) ;
         return 0 ;
      }
   #endif

   if ( code4numCodeBase() == 0 )
      return 0 ;

   if ( uSize < sizeof( LINK4 ) )   /* ensure enough so that we can keep track of the memory */
      unitSize = sizeof( LINK4 ) ;
   else
      unitSize = uSize ;

   #ifdef E4MISC
      unitSize += 2 * mem4extraChars + sizeof( unsigned ) ;
   #endif

   if ( c4 )
      if ( error4code( c4 ) < 0 )
         return 0 ;

   #ifdef S4SEMAPHORE
      mem4start( c4 ) ;
   #endif

   if ( !isTemp )
      for( onType = 0 ;; )
      {
         onType = (MEM4 *)l4next( &used, onType ) ;
         if ( onType == 0 )
            break ;

         #ifdef S4SEMAPHORE
            #ifdef S4OS2
               #ifdef __DLL__
                  /* get access to the memory */
                  flags = PAG_WRITE | PAG_READ  ;
                  if ( DosGetSharedMem( onType, flags ) != 0 )
                     return 0 ;
               #endif
            #endif
         #endif

         #ifdef S4DOS    /* no memory sharing under DOS, so can use a CODE4 for limited memory */
            if ( onType->codeBase == c4 || onType->codeBase == 0 )
         #endif
         if ( onType->unitSize == unitSize && onType->nRepeat > 0 )
         {
            /* Match */
            if ( start > onType->unitStart )
               onType->unitStart = start ;
            if ( expand > onType->unitExpand)
               onType->unitExpand = expand ;
            onType->nRepeat++ ;
            #ifdef S4SEMAPHORE
               mem4stop( c4 ) ;
            #endif
            #ifdef S4DOS
               if ( onType->codeBase == 0 )  /* set to avoid code4initUndo() errors */
                  onType->codeBase = c4 ;
            #endif
            return onType ;
         }
      }

   /* Allocate memory for another MEM4 */

   onType = (MEM4 *)l4last( &avail ) ;
   if ( onType == 0 )
   {
      MEMORY4GROUP *group ;
      int i ;

      #ifdef S4MEM_PRINT
         char *nullPtr = 0 ;

         if ( v4print != -1 )
         {
            #ifdef S4WINDOWS
               #ifdef S4TESTING
                  {
                     d4displayStr( mem4displayPtr, "  MEM4:  ", 1 ) ;
                     d4displayPtr( mem4displayPtr, nullPtr, 0 ) ;
                     d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                     d4displayStr( mem4displayPtr, "ignoreNextLine", 0 ) ;
                     d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                     d4displayNum( mem4displayPtr, 0, 0 ) ;
                     d4displayStr( mem4displayPtr, "  num bytes:  ", 0 ) ;
                     d4displayNum( mem4displayPtr, 0, 0 ) ;
                  }
               #endif
            #else
               if ( v4print )
                  fprintf( stdprn, "\r\n  MEM4:  %lx  file:  ignoreNextLine  line:  %12d  num bytes:  %12d", nullPtr, 0, 0 ) ;
               else
                  printf("\r\n  MEM4:  %lx  file:  ignoreNextLine  line:  %12d  num bytes:  %12d", nullPtr, 0, 0 ) ;
            #endif
         }
      #endif

      group = (MEMORY4GROUP *)u4allocFreeDefault( c4, (long)sizeof( MEMORY4GROUP ) ) ;
      if ( group == 0 )
      {
         if ( c4 )
            error4set( c4, e4memory ) ;
         #ifdef S4SEMAPHORE
            mem4stop( c4 ) ;
         #endif
         return 0 ;
      }

      for ( i = 0 ; i < mem4numTypes ; i++ )
         l4add( &avail, group->types + i ) ;
      onType = (MEM4 *)l4last( &avail ) ;
      l4add( &groups, group ) ;
   }

   l4remove( &avail, onType ) ;
   memset( (void *)onType, 0, sizeof( MEM4 ) ) ;
   l4add( &used, onType ) ;

   onType->unitStart = start ;
   onType->unitSize = unitSize ;
   onType->unitExpand= expand ;
   onType->nRepeat = 1 ;
   onType->nUsed = 0 ;
   if ( isTemp )
      onType->nRepeat = -1 ;

   #ifdef S4SEMAPHORE
      mem4stop( c4 ) ;
   #endif

   #ifdef S4MEM_PRINT
      if ( v4print != -1 )
      {
         #ifdef S4WINDOWS
            #ifdef S4TESTING
               {
                  d4displayStr( mem4displayPtr, "  MEM4:     ", 1 ) ;
                  d4displayPtr( mem4displayPtr, onType, 0 ) ;
                  d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                  d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                  d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                  d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
               }
            #endif
         #else
            if ( v4print )
               fprintf( stdprn, "\r\n  MEM4:     %lx  file:  %s  line:  %12d", onType, m4fileName, m4lineNo ) ;
            else
               printf( "\r\n  MEM4:     %lx  file:  %s  line:  %12d", onType, m4fileName, m4lineNo ) ;
         #endif
      }
   #endif

   #ifdef S4DOS    /* no memory sharing under DOS, so can use a CODE4 for limited memory */
      onType->codeBase = c4 ;
   #endif
   return onType ;
}

void *S4FUNCTION mem4createAllocDefault( CODE4 *c4, MEM4 **typePtrPtr, int start, const unsigned int unitSize, int expand, const int isTemp)
{
   if ( *typePtrPtr == 0 )
   {
      *typePtrPtr = mem4createDefault( c4, start, unitSize, expand, isTemp ) ;
      if ( *typePtrPtr == 0 )
         return 0 ;
   }

   return mem4alloc2Default( *typePtrPtr, c4 ) ;
}

int S4FUNCTION mem4freeDefault( MEM4 *memoryType, void *freePtr )
{
   #ifdef E4MISC
      int rc ;
      LINK4 *holdPtr ;
   #endif

   if ( freePtr == 0 )  /* as documented */
      return 0 ;

   if ( memoryType == 0 )
      return error4( 0, e4parm_null, E95907 ) ;

   if ( code4numCodeBase() == 0 )
      return 0 ;

   #ifdef S4SEMAPHORE
      mem4start( 0 ) ;
   #endif

   memoryType->nUsed-- ;
   #ifdef E4MISC
      if ( memoryType->nUsed < 0 )
      {
         #ifdef S4SEMAPHORE
            mem4stop( 0 ) ;
         #endif
         return error4( 0, e4result, E95907 ) ;
      }

      rc = mem4popPointer( (char *)freePtr ) ;
      holdPtr = (LINK4 *)mem4checkPointer( (char *)freePtr, 0 ) ;
      if ( holdPtr != 0 )
      {
         l4add( &memoryType->pieces, holdPtr ) ;
         #ifdef S4MEM_PRINT
            if ( v4print != -1 )
            {
               #ifdef S4WINDOWS
                  #ifdef S4TESTING
                     {
                        d4displayStr( mem4displayPtr, "  Y4FREE:   ", 1 ) ;
                        d4displayPtr( mem4displayPtr, freePtr, 0 ) ;
                        d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                        d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                        d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                        d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                     }
                  #endif
               #else
                  if ( v4print )
                     fprintf(stdprn, "\r\n  Y4FREE:   %lx  file:  %s  line:  %12d", freePtr, m4fileName, m4lineNo ) ;
                  else
                     printf( "\r\n  Y4FREE:   %lx  file:  %s  line:  %12d", freePtr, m4fileName, m4lineNo ) ;
               #endif
            }
         #endif
      }

      memset( ((char *)freePtr), 0, memoryType->unitSize - 2 * mem4extraChars - sizeof( unsigned ) ) ;
      #ifdef S4SEMAPHORE
         mem4stop( 0 ) ;
      #endif
      return rc ;
   #else
      l4add( &memoryType->pieces, (LINK4 *)freePtr ) ;
      #ifdef S4SEMAPHORE
         mem4stop( 0 ) ;
      #endif
      return 0 ;
   #endif
}

void S4FUNCTION mem4release( MEM4 *memoryType )
{
   void *ptr ;

   if ( memoryType == 0 )
      return ;

   if ( code4numCodeBase() == 0 )
      return ;

   #ifdef S4SEMAPHORE
      mem4start( 0 ) ;
   #endif

   memoryType->nRepeat-- ;
   if ( memoryType->nRepeat <= 0 )
   {
      for(;;)
      {
         ptr = l4pop( &memoryType->chunks) ;
         if ( ptr == 0 )
            break ;
         u4freeDefault( ptr ) ;
      }

      l4remove( &used, memoryType ) ;
      l4add( &avail, memoryType ) ;

      #ifdef S4MEM_PRINT
         if ( v4print != -1 )
         {
            #ifdef S4WINDOWS
               #ifdef S4TESTING
                  {
                     d4displayStr( mem4displayPtr, "  Y4RELEASE:  ", 1 ) ;
                     d4displayPtr( mem4displayPtr, memoryType, 0 ) ;
                  }
               #endif
            #else
               if ( v4print )
                  fprintf( stdprn, "\r\n  Y4RELEASE:  %lx", memoryType ) ;
               else
                  printf( "\r\n  Y4RELEASE:  %lx", memoryType ) ;
            #endif
         }
      #endif
   }

   #ifdef S4SEMAPHORE
      mem4stop( 0 ) ;
   #endif
}

#ifdef S4TESTING
   int s4allocOff = 0 ;
#endif

#ifdef S4MAX
   static long mem4maxMemory = 16384 ;
   static long mem4allocated = 0L ;

   long u4allocated( void )
   {
      return mem4allocated ;
   }

   long u4max( void )
   {
      return mem4maxMemory ;
   }
#endif

void *S4FUNCTION u4allocDefault( long n )
{
   size_t s ;
   char *ptr ;
   #ifdef S4SEMAPHORE
      #ifdef S4OS2
         #ifdef __DLL__
            ULONG    flags;
            APIRET   rc;
         #endif
      #endif
   #endif
   #ifdef S4WINDOWS
      HANDLE  handle, *hPtr ;
   #endif

   #ifdef S4TESTING
      if ( s4allocOff == 1 )   /* for testing, emulate out of memory conditions */
         return 0 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( n == 0L )
      {
         error4( 0, e4parm, E85903 ) ;
         return 0 ;
      }
      #ifdef E4MISC
         n += mem4extraChars*2 + sizeof(unsigned) ;
      #endif
   #endif

   #ifdef S4MAX
      n += sizeof( long ) ;   /* room for length */
      if ( mem4allocated + n > mem4maxMemory )
         return 0 ;
   #endif

   s = (size_t) n ;
   if ( n > (long) s )
      return 0 ;

   #ifdef S4WINDOWS
      #ifdef __DLL__
         handle = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, (DWORD) s+ sizeof(HANDLE) ) ;
      #else
         handle = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD) s+ sizeof(HANDLE) ) ;
      #endif

      if ( handle == (HANDLE)0 )
         return 0 ;

      hPtr = (HANDLE *)GlobalLock( handle ) ;
      *hPtr++ = handle ;
      ptr = (char *)hPtr ;
   #else
      #ifdef __DLL__
         #ifdef S4OS2_SEMAPHORE
            flags = PAG_WRITE | PAG_READ | OBJ_GETTABLE ;
            rc = DosAllocSharedMem( (void *)&ptr, 0, s, flags ) ;

            if (rc != 0)
               return 0 ;
            flags = PAG_WRITE | PAG_READ ;
            rc = DosGetSharedMem( ptr, flags ) ;
            if ( rc != 0 )
               return 0 ;
         #else
            ptr = (char *)malloc( s ) ;

            if ( ptr == 0 )
               return 0 ;

            #ifndef S4PASCAL_WIN
               memset( ptr, 0, s ) ;
            #endif
         #endif
      #else
         ptr = (char *)malloc( s ) ;

         if ( ptr == 0 )
            return 0 ;

                              /* Borland malloc of 64K (or close to) will */
         #ifdef __TURBOC__    /* result in corruption of segment memory due */
            if ( (ptr+s-1 <= ptr) && (s > 1 ))    /* to wrap-around problems  */
            {
               free( ptr ) ;
               return 0 ;
            }
         #endif

         #ifndef S4PASCAL_WIN
            memset( ptr, 0, s ) ;
         #endif
      #endif
   #endif

   #ifdef S4MEM_PRINT
      if ( v4print != -1 )
      {
         #ifdef S4WINDOWS
            #ifdef S4TESTING
               {
                  d4displayStr( mem4displayPtr, "  U4ALLOC:  ", 1 ) ;
                  d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                  d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                  d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                  d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                  d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                  d4displayStr( mem4displayPtr, "  num bytes:  ", 0 ) ;
                  d4displayNum( mem4displayPtr, n, 0 ) ;
               }
            #endif
         #else
            if ( v4print )
               fprintf( stdprn, "\r\n  U4ALLOC:  %lx  file:  %s  line:  %12d  num bytes:  %12ld", ptr, m4fileName, m4lineNo, n ) ;
            else
               printf("\r\n  U4ALLOC:  %lx  file:  %s  line:  %12d  num bytes:  %12ld", ptr, m4fileName, m4lineNo, n ) ;
         #endif
      }
   #endif

   #ifdef E4MISC
      #ifdef S4SEMAPHORE
         mem4start( 0 ) ;
      #endif
      ptr = mem4fixPointer( ptr, s ) ;
      mem4pushPointer( ptr ) ;
      memset( ptr, 0, s-mem4extraChars*2 - sizeof(unsigned) ) ;
      #ifdef S4SEMAPHORE
         mem4stop( 0 ) ;
      #endif
   #endif

   #ifdef S4MAX
      #ifdef S4SEMAPHORE
         mem4start( 0 ) ;
      #endif
      mem4allocated += n ;
      #ifdef S4SEMAPHORE
         mem4stop( 0 ) ;
      #endif
      *((long *)ptr) = n ;
      ptr += sizeof( long ) ;
   #endif

   return (void *)ptr ;
}

void S4FUNCTION u4freeFixedDefault( FIXED4MEM fixedMem )
{
   #ifdef S4USE_LOW_MEMORY
      if ( ( GetWinFlags() & WF_ENHANCED ) == WF_ENHANCED )
         GlobalPageUnlock( (HGLOBAL)s4protected( fixedMem ) ) ;
      GlobalDosFree( (HGLOBAL)s4protected( fixedMem ) ) ;
   #else
      u4freeDefault( fixedMem ) ;
   #endif
}

/* u4alloc_fixed() allocates fixed memory blocks in the lower 1 MEG of ram.
   It is used for allocating memory that is to be used with DOS interrupts.
*/
FIXED4MEM S4FUNCTION u4allocFixedDefault( CODE4 *c4, long n )
{
   #ifdef S4USE_LOW_MEMORY
      size_t s ;
      FIXED4MEM fixedMem ;

      memset( &fixedMem, 0, sizeof( FIXED4MEM ) ) ;

      #ifdef E4PARM_LOW
         if ( c4 == 0 )
         {
            error4( 0, e4parm_null, E95908 ) ;
            return fixedMem ;
         }

         if ( n == 0L )
         {
            error4( c4, e4parm, E85903 ) ;
            return fixedMem ;
         }
         n += mem4extraChars*2 + sizeof(unsigned) ;
      #endif

      s = (size_t)n ;
      if ( n > (long)s )
         return fixedMem ;

      #ifdef __DLL__
         fixedMem.block = GlobalDosAlloc( (DWORD)s + sizeof(HANDLE) ) ;
      #else
         fixedMem.block = GlobalDosAlloc( (DWORD)s + sizeof(HANDLE) ) ;
      #endif

      if ( fixedMem.block == 0 )
         return fixedMem ;

      if ( ( GetWinFlags() & WF_ENHANCED ) == WF_ENHANCED )
         GlobalPageLock( (HGLOBAL)s4protected( fixedMem ) ) ;

      memset( (void *)s4protected( fixedMem ), 0, (size_t)n ) ;
      return fixedMem ;
   #else
      return u4allocFreeDefault( c4, n ) ;
   #endif
}

void *S4FUNCTION u4allocErDefault( CODE4 *c4, long n )
{
   void *ptr = (void *)u4allocFreeDefault( c4, n ) ;
   if ( ptr == 0 && c4 )
      error4( c4, e4memory, E95909 ) ;

   return ptr ;
}

int S4FUNCTION u4freeDefault( void *ptr )
{
   int rc ;
   #ifdef S4WINDOWS
      HANDLE  hand ;
   #endif

   #ifdef S4MAX
      long amount ;
   #endif

   rc = 0 ;

   if ( code4numCodeBase() == 0 )
      return 0 ;

   if ( ptr == 0 )
      return 0 ;

   #ifdef S4MAX
      ptr = ((char *)ptr) - sizeof( long ) ;
      amount = *((long *)ptr) ;
      #ifdef S4SEMAPHORE
         mem4start( 0 ) ;
      #endif
      mem4allocated -= amount ;
      #ifdef S4SEMAPHORE
         mem4stop( 0 ) ;
      #endif
   #endif

   #ifdef S4WINDOWS
      #ifdef E4MISC
         rc = mem4popPointer( (char *)ptr ) ;
         ptr = mem4checkPointer( (char *)ptr, 1 ) ;
         if ( (char *)ptr == 0 )  /* error during mem check */
            return -1 ;
         hand = ((HANDLE *)(char *)ptr)[-1] ;
      #else
         hand = ((HANDLE *)ptr)[-1] ;
      #endif

      #ifdef S4MEM_PRINT
         if ( v4print != -1 )
         {
            #ifdef S4WINDOWS
               #ifdef S4TESTING
                  d4displayStr( mem4displayPtr, "  U4FREE:   ", 1 ) ;
                  d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                  d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                  d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                  d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                  d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
               #endif
            #endif
         }
      #endif
      GlobalUnlock( hand ) ;
      hand = GlobalFree( hand ) ;

      if ( hand != (HANDLE) 0 )
         return error4( 0, e4memory, E95910 ) ;
   #else
      #ifdef E4MISC
         #ifdef S4SEMAPHORE
            mem4start( 0 ) ;
         #endif
         rc = mem4popPointer( (char *)ptr ) ;
         ptr = mem4checkPointer( (char *)ptr, 1 ) ;
         #ifdef S4SEMAPHORE
            mem4stop( 0 ) ;
         #endif

         #ifdef S4MEM_PRINT
            if ( v4print != -1 )
            {
               #ifdef S4WINDOWS
                  #ifdef S4TESTING
                     {
                        d4displayStr( mem4displayPtr, "  U4FREE:  ", 1 ) ;
                        d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                        d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                        d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                        d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                        d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                     }
                  #endif
               #else
                  if ( v4print )
                     fprintf( stdprn, "\r\n  U4FREE:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  ) ;
                  else
                     printf( "\r\n U4FREE:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  );
               #endif
            }
         #endif

         free( ptr ) ;
      #else
         #ifdef S4MEM_PRINT
            if ( v4print != -1 )
            {
               #ifdef S4WINDOWS
                  #ifdef S4TESTING
                     {
                        d4displayStr( mem4displayPtr, "  U4FREE:  ", 1 ) ;
                        d4displayPtr( mem4displayPtr, ptr, 0 ) ;
                        d4displayStr( mem4displayPtr, "  file:  ", 0 ) ;
                        d4displayStr( mem4displayPtr, m4fileName, 0 ) ;
                        d4displayStr( mem4displayPtr, "  line:  ", 0 ) ;
                        d4displayNum( mem4displayPtr, m4lineNo, 0 ) ;
                     }
                  #endif
               #else
                  if ( v4print )
                     fprintf( stdprn, "\r\n  U4FREE:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  ) ;
                  else
                     printf( "\r\n  U4FREE:  %lx  file:  %s  line:  %12d", ptr, m4fileName, m4lineNo  );
               #endif
            }
         #endif

         free( ptr ) ;
      #endif
   #endif

   return rc ;
}

int mem4reset( void )
{
   MEM4 *onType ;
   LINK4 *onChunk, *onGroup ;
   #ifdef S4WINDOWS
      #ifdef E4MISC
         HANDLE hand ;
      #endif
   #endif
   #ifdef S4LOCK_CHECK
      return error4( 0, e4result, E85904 ) ;
   #endif

   for( onType = 0 ;; )
   {
      onType = (MEM4 *)l4next(&used,onType) ;
      if ( onType == 0 )
         break ;
      do
      {
         onChunk = (LINK4 *)l4pop( &onType->chunks) ;
         u4freeDefault( onChunk ) ;  /* free of 0 still succeeds */
      } while ( onChunk ) ;
   }

   for( ;; )
   {
      onGroup = (LINK4 *)l4pop( &groups ) ;
      if ( onGroup == 0 )
         break ;
      u4freeDefault( onGroup ) ;
   }

   #ifdef E4MISC
      if ( mem4numPointer > 0 )
      {
         #ifdef S4WINDOWS
            hand = ((HANDLE *)mem4testPointers)[-1] ;

            GlobalUnlock( hand ) ;
            hand = GlobalFree( hand ) ;

            if ( hand != (HANDLE)0 )
               return error4( 0, e4memory, E95911 ) ;
         #else
            #ifdef S4MEM_PRINT
               if ( v4print != -1 )
               {
                  #ifdef S4WINDOWS
                     #ifdef S4TESTING
                        {
                           d4displayStr( mem4displayPtr, "  MEM4RESET:  ", 1 ) ;
                           d4displayPtr( mem4displayPtr, mem4testPointers, 0 ) ;
                     #endif
                  #else
                     if ( v4print )
                        fprintf( stdprn, "\r\n  MEM4RESET:  %lx", mem4testPointers ) ;
                     else
                        printf( "\r\n  MEM4RESET:  %lx", mem4testPointers );
                  #endif
               }
            #endif

            free( (void *)mem4testPointers ) ;
         #endif

         mem4testPointers = 0 ;
         mem4numPointer = -1 ;
         mem4numUsed = 0 ;
      }
   #endif

   mem4init() ;

   #ifdef S4SEMAPHORE
      #ifdef S4WIN32
         DeleteCriticalSection( &critical4memory ) ;
         DeleteCriticalSection( &critical4expression ) ;
      #endif
   #endif

   #ifdef S4SEMAPHORE
      #ifdef S4WIN32
         memoryInitialized = 0 ;
      #endif
   #endif

   return 0 ;
}

void mem4init( void )
{
   if ( code4numCodeBase() != 0 )
      return ;

   #ifdef S4SEMAPHORE
      #ifdef S4WIN32
         memoryInitialized = 1 ;
      #endif
   #endif

   #ifdef S4SEMAPHORE
      #ifdef S4WIN32
         InitializeCriticalSection( &critical4memory ) ;
         InitializeCriticalSection( &critical4expression ) ;
      #endif
      mem4start( 0 ) ;
   #endif
   memset( (void *)&avail, 0, sizeof( avail ) ) ;
   memset( (void *)&used, 0, sizeof( used ) ) ;
   memset( (void *)&groups, 0, sizeof( groups ) ) ;
   #ifdef S4SEMAPHORE
      mem4stop( 0 ) ;
   #endif
}