/*
 * Note to mg porters: this is included on the off chance you don't have an
 * alloca, and want to use the regexp code. I seriously recommend not using
 * it if you can avoid it.
 */

/*
 * alloca -- (mostly) portable public-domain implementation -- D A Gwyn
 * 
 * This implementation of the PWB library alloca() function, which is used to
 * allocate space off the run-time stack so that it is automatically
 * reclaimed upon procedure exit, was inspired by discussions with J. Q.
 * Johnson of Cornell.
 * 
 * It should work under any C implementation that uses an actual procedure stack
 * (as opposed to a linked list of frames).  There are some preprocessor
 * constants that can be defined when compiling for your specific system, for
 * improved efficiency; however, the defaults should be okay.
 * 
 * The general concept of this implementation is to keep track of all
 * alloca()-allocated blocks, and reclaim any that are found to be deeper in
 * the stack than the current invocation.  This heuristic does not reclaim
 * storage as soon as it becomes invalid, but it will do so eventually.
 * 
 * As a special case, alloca(0) reclaims storage without allocating any.  It is
 * a good idea to use alloca(0) in your main control loop, etc. to force
 * garbage collection.
 */
#include "regexp.h"
#ifdef	REGEXP

#include "def.h"		/* Get me lotsa defines */

#ifdef ANSI
#include <stdlib.h>
#endif

typedef VOID   *pointer;	/* generic pointer type */

/*
 * Define STACK_DIRECTION if you know the direction of stack growth for your
 * system; otherwise it will be automatically deduced at run-time.
 * 
 * STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 =>
 * grows toward lower addresses STACK_DIRECTION undefined => direction of
 * growth unknown
 */


#ifdef STACK_DIRECTION

#define	STACK_DIR	STACK_DIRECTION	/* known at compile-time */

#else				/* STACK_DIRECTION undefined -> need to find
				 * it at compile time */

static int      stack_dir;	/* 1 or -1 once known */
#define	STACK_DIR	stack_dir

static          VOID
find_stack_direction PROTO(( VOID ))
{
	static char    *addr = NULL;	/* address of first `dummy', once
					 * known */
	auto char       dummy;	/* to get stack address */

	if (addr == NULL) {	/* initial entry */
		addr = &dummy;

		find_stack_direction();	/* recurse once */
	} else
	 /* second entry */ if (&dummy > addr)
		stack_dir = 1;	/* stack grew upward */
	else
		stack_dir = -1;	/* stack grew downward */
}

#endif				/* STACK_DIRECTION */

/*
 * An "alloca header" is used to: (a) chain together all alloca()ed blocks;
 * (b) keep track of stack depth.
 * 
 * It is very important that sizeof(header) agree with malloc() alignment chunk
 * size.  The following default should work okay.
 */

#ifndef	ALIGN_SIZE
#define	ALIGN_SIZE	sizeof(double)
#endif

typedef union hdr {
	char            align[ALIGN_SIZE];	/* to force sizeof(header) */
	struct {
		union hdr      *next;	/* for chaining headers */
		char           *deep;	/* for stack depth measure */
	}               h;
}               header;

/*
 * alloca( size ) returns a pointer to at least `size' bytes of storage which
 * will be automatically reclaimed upon exit from the procedure that called
 * alloca().  Originally, this space was supposed to be taken from the
 * current stack frame of the caller, but that method cannot be made to work
 * for some implementations of C, for example under Gould's UTX/32.
 */

static header  *last_alloca_header = NULL;	/* -> last alloca header */

pointer
alloca(size)			/* returns pointer to storage */
	unsigned        size;	/* # bytes to allocate */
{
	auto char       probe;	/* probes stack depth: */
	register char  *depth = &probe;

#ifndef STACK_DIRECTION
	if (STACK_DIR == 0)	/* unknown growth direction */
		find_stack_direction();
#endif
	/*
	 * Reclaim garbage, defined as all alloca()ed storage that was
	 * allocated from deeper in the stack than currently.
	 */

	{
		register header *hp;	/* traverses linked list */

		for (hp = last_alloca_header; hp != NULL;)
			if (STACK_DIR > 0 && hp->h.deep > depth
			    || STACK_DIR < 0 && hp->h.deep < depth) {
				register header *np = hp->h.next;

				free((pointer) hp);	/* collect garbage */

				hp = np;	/* -> next header */
			} else
				break;	/* rest are not deeper */

		last_alloca_header = hp;	/* -> last valid storage */
	}

	if (size == 0)
		return NULL;	/* no allocation required */

	/* Allocate combined header + user data storage. */

	{
		register pointer new = malloc(sizeof(header) + size);
		/* address of header */

		((header *) new)->h.next = last_alloca_header;
		((header *) new)->h.deep = depth;

		last_alloca_header = (header *) new;

		/* User storage begins just after header. */

		return (pointer) ((char *) new + sizeof(header));
	}
}
#else
#include "nullfile.h"
#endif
