/* sys/sbrk.c (emx+gcc) -- Copyright (c) 1992-1993 by Eberhard Mattes */

#include <sys/emx.h>
#define INCL_DOSSEMAPHORES
#define INCL_DOSEXCEPTIONS
#define INCL_DOSERRORS
#include <os2emx.h>
#include <errno.h>
#include "syscalls.h"

#if defined (__MT__)
static void *__sbrk1 (int incr)
#else
void *__sbrk (int incr)
#endif
{
  ULONG rc;
  void *old;
  unsigned old_size, new_size;
  unsigned addr, rest, high, size;

  old = _sys_heap_brk;
  old_size = (char *)_sys_heap_brk - (char *)_sys_heap_base;
  new_size = old_size + incr;
  if (incr < 0)
    {
      if (-incr > old_size)
        {
          errno = ENOMEM;
          return ((void *)(-1));
        }

      addr = (unsigned)_sys_heap_brk + incr;
      addr = (addr + 0xfff) & ~0xfff; /* Round up */
      high = (unsigned)_sys_heap_brk;
      high = (high + 0xfff) & ~0xfff; /* Round up */
      if (high > addr)
        {
          /* Decommit at least one page */
          size = high - addr;
          rc = DosSetMem ((void *)addr, size, PAG_DECOMMIT);
          if (rc != 0)
            {
              _sys_set_errno (rc);
              return ((void *)(-1));
            }
        }
      _sys_heap_brk = (char *)_sys_heap_brk + incr;
      return (old);
    }
  else if (new_size < old_size) /* overflow */
    {
      errno = ENOMEM;
      return ((void *)(-1));
    }
  else if (new_size > _sys_heap_size)
    {
      errno = ENOMEM;
      return ((void *)(-1));
    }
  else
    {
      addr = (unsigned)_sys_heap_brk;
      size = incr;
      if (addr & 0xfff)
        {
          rest = 0x1000 - (addr & 0xfff);
          if (incr <= rest)
            size = 0;
          else
            {
              size -= rest;
              addr += rest;
            }
        }
      if (size != 0)
        {
          rc = DosSetMem ((void *)addr, size, PAG_DEFAULT | PAG_COMMIT);
          if (rc != 0)
            {
              _sys_set_errno (rc);
              return ((void *)(-1));
            }
        }
      _sys_heap_brk = (char *)_sys_heap_brk + incr;
      return (old);
    }
}


#if defined (__MT__)

void *__sbrk (int incr)
{
  void *p;
  ULONG nesting, rc;

  DosEnterMustComplete (&nesting);
  do
    {
      rc = DosRequestMutexSem (_sbrk_mutex, SEM_INDEFINITE_WAIT);
    } while (rc == ERROR_SEM_OWNER_DIED || rc == ERROR_INTERRUPT);
  p = __sbrk1 (incr);
  DosReleaseMutexSem (_sbrk_mutex);
  DosExitMustComplete (&nesting);
  return (p);
}

#endif
