/*
 * Libraries and headers for PDC release 3.3 (C) 1989 Lionel Hummel.
 * PDC Software Distribution (C) 1989 Lionel Hummel and Paul Petersen.
 * PDC I/O Library (C) 1987 by J.A. Lydiatt.
 *
 * This code is freely redistributable upon the conditions that this
 * notice remains intact and that modified versions of this file not
 * be included as part of the PDC Software Distribution without the
 * express consent of the copyright holders.  No warrantee of any
 * kind is provided with this code.  For further information, contact:
 *
 *  PDC Software Distribution    Internet:                     BIX:
 *  P.O. Box 4006             or hummel@cs.uiuc.edu            lhummel
 *  Urbana, IL  61801-8801       petersen@uicsrd.csrd.uiuc.edu
 */

/* malloc - routines for managing memory
 *
 * calloc     allocates a cleared array of blocks
 * free       frees a previously allocated block of memory
 * malloc     allocates a block of memory
 * realloc    alter buffer size; same contents, new ptr
 */

/* (ryb) Added typecasts in malloc() and free() to make GCC happy.
   Other changes. */

#include <exec/types.h>
#include <exec/memory.h>
#include <functions.h>
#include <stdlib.h>
#include <string.h>

/* According to X3J11, the response to a request for zero bytes is
   implementation defined.  For PDC, the intention is to return a
   successful result, but one that will cause a trap if it is ever
   dereferenced.  Under most present-day Amiga's, 0xFFFFFFFF (-1L)
   fills the bill just fine.  This may have problems, though, on
   systems with MMU's and very high memory addresses.

   (ryb) Removed special zero request code because it causes crashes when
   free() is called on the result.  If a buggy program wants zero bytes
   of memory, give it zero bytes of memory. ;-)  Maybe ifdef'd code could be
   added to help find that bug. */

typedef struct memchunk
{
  struct memchunk *next;
  struct memchunk *prev;
  long size;
} MEMCHUNK;

extern void (*__freeall) ();

static MEMCHUNK sentinel = {&sentinel, &sentinel, 0};

/* Called by exit() to free any allocated memory. */

static void
freeall ()
{
  /* These are volatile as a temporary fix for a problem with inline
     function definitions of shared library calls. */ 
  volatile MEMCHUNK *mp, *mp1;

  for (mp = sentinel.prev; mp != &sentinel;)
    {
      mp1 = mp->prev;
      FreeMem ((APTR) mp, (ULONG) mp->size);
      mp = mp1;
    }
}

void *
malloc (size_t size)
{
  MEMCHUNK *mp;
  ULONG chunksize;

  chunksize = size + sizeof (MEMCHUNK);
  mp = (MEMCHUNK *) AllocMem (chunksize, MEMF_PUBLIC);
  if (mp == NULL)
    return NULL;

  /* Keep the forward and backward links. */
  sentinel.prev->next = mp;
  mp->prev = sentinel.prev;
  sentinel.prev = mp;

  mp->next = &sentinel;
  mp->size = chunksize;
  __freeall = &freeall;

  return (void *) (mp + 1);
}

void
free (void *p)
{
  MEMCHUNK *mp, *prevmp, *nextmp;

	if(!p) return;

  mp = (MEMCHUNK *) ((char *) p - sizeof (MEMCHUNK));

  /* Sanity check: the prev link should point to us. Do nothing if bad. */
  prevmp = mp->prev;
  nextmp = mp->next;
  if (prevmp->next != mp)
    return;
  FreeMem ((APTR) mp, (ULONG) mp->size);
  prevmp->next = nextmp;
  nextmp->prev = prevmp;

  return;
}

void *
calloc (size_t nobj, size_t size)
{
  void *newmem;
  int total_size;

  total_size = nobj * size;
  newmem = malloc (total_size);
  if (newmem)
    memset (newmem, 0, total_size);

  return newmem;
}

void *
realloc (void *ptr, size_t size)
{
  MEMCHUNK *mp;
  void *newmem;

  mp = (MEMCHUNK *) ((char *)ptr - sizeof (MEMCHUNK));

  /* Sanity check: the prev link should point to us. Do nothing if bad. */
  if (mp->prev->next != mp)
    return NULL;

  newmem = malloc (size);
  if (newmem)
    {
      size = ((size > mp->size) ? mp->size : size);
      memcpy (newmem, ptr, size);
      free (ptr);
    }
  return newmem;
}
