#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct gcentry {
 void *varaddr;
 char varname[50];
 struct gcentry *next;
};

struct gcallocation {
 void *start;
 void *end;
 size_t size;
 struct gcallocation *prev, *next;
};

struct gcentry *root = NULL;
struct gcallocation *allocations = NULL;

void append_variable (void *varaddr, char *varname) {
 struct gcentry *e;

 e = malloc (sizeof (*e));
 if (NULL == e) {
  perror ("allocating gcentry in append_variable");
  exit (EXIT_FAILURE);
 }
 e->varaddr = varaddr;
 strcpy (e->varname, varname);
 e->next = root;
 root = e;
}

void release (struct gcentry *e) {
 struct gcallocation *a = allocations;
 struct gcentry *etmp;
 int found;

 while (a) {
  etmp = e;
  found = 0;
  while (etmp) {
   char *bptr = *(char **)etmp->varaddr;
   /* 
    * Check if the pointer still points to a valid range.
    */
   if (bptr >= (char *)a->start && bptr <= (char *)a->end) {
    found = 1;
    printf ("found a->start %p\n", a->start);
    break;
   }
   etmp = etmp->next;
  }
  if (!found) {
   /*
    * The memory is no longer used by any pointers.  We can safely free it.
    */
   printf ("!found a->start %p\n", a->start);

   if (a->prev)
    a->prev->next = a->next;
   else 
    allocations = a->next;

   if (a->next) 
    a->next->prev = a->prev;

   free (a);
  }
  a = a->next;
 }
}

void *alloc (size_t s) {
 struct gcallocation *a;
 release (root);

 a = malloc (sizeof (*a) + s);

 if (NULL == a) {
  perror ("unable to alloc");
  exit (EXIT_FAILURE);
 }

 a->start = a + 1;
 a->end = ((char *)a->start) + s;
 a->size = s;
 a->prev = NULL;

 if (allocations)
  allocations->prev = a;
 
 a->next = allocations;
 allocations = a;

 return a->start;
}

#define variable(type,var) type volatile var = NULL; append_variable(&var, #var)

/* Test code */

int main (int argc, char *argv[]) {
 variable (char *, p);
 variable (char *, p2);

 while (1) {
  p = alloc (5);
  strcpy (p, "abcd");
  printf ("p %s\n", p); 
  p2 = p;
 }

 return EXIT_SUCCESS;
}
