/* $Id: rtxalloc.c,v 1.3 2009/01/04 15:42:15 kiesling Exp $ -*-c-*-*/

/*
  This file is part of Ctalk.
  Copyright © 2005-2009  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "list.h"

/* #define __ALLOC_FREE_CHECK__ */

LIST *new_list (void);
void list_add (LIST *, LIST *);
LIST *list_remove (LIST **, LIST **);

#ifdef __ALLOC_FREE_CHECK__
static LIST *_alloc_blocks = NULL;
static LIST *_free_blocks = NULL;

static int __block_is_allocated (unsigned long int b) {
  LIST *t;
  for (t = _alloc_blocks; t; t = t -> next) {
    if ((unsigned long int)t -> data == b)
      return 1;
  }
  return 0;
}

void __add_xalloc_block (void *p) {
  LIST *t;
  t = new_list ();
  t -> data = p;
  if (_alloc_blocks == NULL) {
    _alloc_blocks = t;
  } else {
    list_add (_alloc_blocks, t);
  }
}

void __delete_xalloc_block (void *p) {
  LIST *t, *t2;
  for (t = _alloc_blocks; t; t = t -> next) {
    if (t -> data == p) {
      t2 = list_remove (&_alloc_blocks, &t);
      free (t2);
      return;
    }
  }
}

#endif



/*
 * Prototypes from ctalk.h.
 */
#ifdef __GNUC__
void _error (char *, ...) __attribute__ ((noreturn));
#else
void _error (char *, ...);
#endif
void _warning (char *fmt, ...);

void *__xalloc (int size) {
  static void *p;
  if ((p = malloc (size)) == NULL)
    _error ("__xalloc: %s.\n", strerror (errno));
  memset (p, '\0', size);
#ifdef __ALLOC_FREE_CHECK__
  if (__block_is_allocated ((unsigned long int )p)) 
      fprintf (stderr, "Block %#x is already allocated.\n", p);
  __add_xalloc_block (p);
#endif
  return p;
}

void __xfree(void *__p) {
  if (__p == NULL) {
    _warning ("Attempt to free a NULL pointer.\n");
    return;
  }
#ifdef __ALLOC_FREE_CHECK__
  if (!__block_is_allocated ((unsigned long int )__p)) 
      fprintf (stderr, "Block %#x is not allocated before free ().\n", __p);
  __delete_xalloc_block (__p);
#endif
  free (__p);
}

void *__xstrdup (char *s) {
  void *p;
  if ((p = calloc (strlen(s) + 1, sizeof(char))) == NULL)
    _error ("__xalloc: %s.\n", strerror (errno));
  strcpy ((char *)p, s);
#ifdef __ALLOC_FREE_CHECK__
  if (__block_is_allocated ((unsigned long int )p)) 
      fprintf (stderr, "Block %#x is already allocated.\n", p);
  __add_xalloc_block (p);
#endif
  return p;
}

