/*
 *
 * Crtinit: C run-time initialization code.
 * Written by Eric R. Smith, and placed in the public domain.
 * Use at your own risk.
 *
 * 01/03/89 ++jrb
 *	The (new) meaning of _stksize: (thanks to allan pratt for the feedback)
 *
 *	_stksize			meaning
 *	  -1L	    keep all of memory (except MINFREE at top) and do
 *		    mallocs from own heap, with heap grown upwards towards
 *		    stack, and the stack growing down towards heap,
 *		    with a minimum slush between them so that they
 *		    dont meet (only checked while malloc'ing). With
 *		    this model, further spawning is not possible, but it is
 *		    well suited for programs such as gcc-cc1 etc.
 *		    Thanks to Piet van Oostrum & Atze Dijkstra for this idea
 *
 *	0L	    keep minimum amount of memory. this is also the
 *		    case when _stksize is undefined by the user.
 *	1L	    keep 1/4 of memory, free 3/4 ( as in Alcyon GEMSTART)
 *	2L	    keep 2/4 (1/2), free rest
 *	3L	    keep 3/4, free 1/4
 *	other	    keep that many bytes
 *	-other	    keep |other| bytes, use the heap for mallocs
 *
 * 02/14/90 ++jrb (thanks edgar)
 *	auto acc detect
 *	undump friendly
 *
 * NOTE: dumping applications should use _initial_stack instead: if
 *	 !=0, then _stksize is initialized from _initial_stack, and
 *	 mallocs are always from internal heap. (TeX works much better now),
 *	 thanks edgar!
 *
 * Acc convention:
 *	user sets _heapbase to bottom of stack + heap area
 *	     sets _stksize to the size of this area
 *	     at startup, sp will be set to top of this area
 *	     (_heapbase  + _stksize ) and malloc()'s will happen from heap.
 *		(note malloc() and *not* Malloc())
 *
 * 02/16/90 ++jrb
 *  - bug fix: dont get screwed by desktop launch when fast bit is set
 *             convert env string to format usable
 *		(atari get your act together!!)
 */

#include <basepage.h>
#include <osbind.h>
#include "lib.h"

#define isspace(c) ((c) == ' '||(c) == '\t')
#define MINFREE	(8L * 1024L)		/* free at least this much mem */
					/* on top */
#define MINKEEP (8L * 1024L)		/* keep at least this much mem */

BASEPAGE *_base;
char **environ;
static long argc;
static char **argv;

/*
 * initial stack is used primarily by dumping application,
 * if it is, malloc is always from heap, and _stksize is init
 * from initial_stack (to preserve the value in the undumped run)
 */
long _initial_stack;		/* .comm __initial_size, 4 */
extern long _stksize;		/* picked up from user or from stksiz.c */

/* set to heap base addr when _stksize == -1L || _initial_stack || When DA */
void *_heapbase;

/* default sizeof stdio buffers */
size_t __DEFAULT_BUFSIZ__;	/* .comm             */

/* are we an app? */
short _app;

static long parseargs();
static void setup_handlers	__PROTO((void));
__EXTERN void _main	__PROTO((long, char **, char **));
__EXTERN void _init_signal	__PROTO((void));
__EXTERN void monstartup __PROTO((void *lowpc, void *highpc));
__EXTERN void _mcleanup	 __PROTO((void));
__EXTERN void moncontrol __PROTO((long));


/*
 * accessories start here:
 */

static char	*acc_argv[] = {"", (char *) 0}; /* no name and no arguments */

void _acc_main()
{
	_app = 0;				/* this is an accessory */
	_main(1L, acc_argv, acc_argv);
	/*NOTREACHED*/
}

/* functions in crt0.s or gcrt0.s */
extern void _setstack(), _monstartup(), _moncontrol(), __mcleanup();

void _crtinit()
{
	register BASEPAGE *bp;
	register long m;
	register long freemem;

	_app = 1;	/* its an application */
	if(!__DEFAULT_BUFSIZ__)
	    __DEFAULT_BUFSIZ__ = BUFSIZ;

	bp = _base;
	m = parseargs(bp);	/* m = # bytes used by environment + args */

/* make m the total number of bytes required by program sans stack/heap */
	m += (bp->p_tlen + bp->p_dlen + bp->p_blen);
	m = (m + 3L) & (~3L);
/* freemem the amount of free mem accounting for MINFREE at top */
	if((freemem = (long)bp->p_hitpa - (long)bp->p_tbase - MINFREE - m) <= 0L)
	    goto notenough;
	
	if(_initial_stack)
	{
	    /* the primary use of _initial_stack will be in dumping */
	    /* applications where only a heap for malloc makes sense */
	    _heapbase = (void *) ((long)bp->p_tbase + m);
	    _stksize = _initial_stack;
	}
	
	switch(_stksize)
	{
	  case -1L:	/* keep all but MINFREE, and malloc from own heap */
	    _stksize = freemem;
	    _heapbase = (void *) ((long)bp->p_tbase + m);
	    break;
	    
	  case 0L:	/* free all but MINKEEP */
	    _stksize = MINKEEP;
	    break;

	  case 1L:	/* keep 1/4, free 3/4 */
	    _stksize = freemem >> 2;
	    break;

	  case 2L:	/* keep 1/2, free 1/2 */
	    _stksize = freemem >> 1;
	    break;

	  case 3L:	/* keep 3/4, free 1/4 */
	    _stksize = freemem - (freemem >> 2); 
	    break;
	    
	  default:
	    if(_stksize < -1L) { /* keep |_stksize|, use heap for mallocs */
		_stksize = -_stksize;
		_heapbase = (void *)((long)bp->p_tbase + m);
	    }
	}
	
/* make m the total number of bytes including stack */
	_stksize = _stksize & (~3L);
	m += _stksize;

/* make sure there's enough room for the stack */
	if ((((long)bp->p_tbase) + m) > ((long)bp->p_hitpa - MINFREE))
	    goto notenough;

/* set up the new stack to bp->p_tbase + m  */
	_setstack(bp->p_tbase + m);

/* shrink the TPA */
/* this is right only if bp == bp->p_tbase - sizeof(BASEPAGE) */
	(void)Mshrink(bp, m + sizeof(BASEPAGE));
	
/* establish handlers,  call the main routine */
	setup_handlers();

/* start profiling, if we were linked with crt0.o */
	_monstartup((void *)(bp->p_tbase), 
		   (void *)((long)bp->p_tbase +  bp->p_tlen));

	_main(argc, argv, environ);
	/* not reached normally */

notenough:
	Cconws("Fatal error: insufficient memory\r\n");
		Pterm(-1);
}


/*
 * parseargs(bp): parse the environment and arguments pointed to by the
 * basepage. Return the number of bytes of environment and arguments
 * that have been appended to the bss area (the environ and argv arrays
 * are put here, as is a temporary buffer for the command line, if
 * necessary).
 *
 * The MWC extended argument passing scheme is assumed.
 *
 */

static long parseargs(bp)
	BASEPAGE *bp;
{
	long count = 4;		/* compensate for aligning */
	long  i;
	char *from, *cmdln, *to;
	char **envp, **arg;
/* flag to indicate desktop-style arg. passing */
	long desktoparg = 0;

/* handle the environment first */

	environ = envp = (char **)(( (long)bp->p_bbase + bp->p_blen + 4) & (~3));
	from = bp->p_env;
	while (*from) {

/* if we find MWC arguments, tie off environment here */
		if (*from == 'A' && *(from+1) == 'R' && *(from+2) == 'G' &&
		    *(from+3) == 'V' && *(from+4) == '=') {
			*envp++ = (char *) 0; count += 4;
			*from++ = 0;
#ifdef STRICTLY_COMPATIBLE_WITH_STANDARD
			if (bp->p_cmdlin[0] != 127)
				goto old_cmdlin;
#endif
			while (*from++) ; /* skip ARGV= string */
			argv = arg = envp++;
			*arg++ = from; count+= 4;
			while (*from++) ; /* skip argv[0] */
			goto do_argc;
		}
		*envp++ = from;
		count += 4;
		desktoparg = 1;
		while (*from) {
			if (*from == '=') {
				desktoparg = 0;
			}
			from++;
		}
		from++;		/* skip 0 */

/* the desktop (and some shells) use the environment in the wrong
   way, putting in "PATH=\0C:\0" instead of "PATH=C:". so if we
   find an "environment variable" without an '=' in it, we
   see if the last environment variable ended with '=\0', and
   if so we append this one to the last one
 */
		if(desktoparg && envp > &environ[1]) 
		{
		/* launched from desktop -- fix up env */
		    char *p, *q;

		    q = envp[-2];	/* current one is envp[-1] */
		    while (*q) q++;
		    if (q[-1] == '=') {
			p = *--envp;
			while(*p)
			   *q++ = *p++;
		        *q = '\0';
		   }
		}
	}
	*envp++ = (char *)0;
	count += 4;

/* old_cmdlin: */
/* Allocate some room for the command line to be parsed */
	cmdln = bp->p_cmdlin;
	i = *cmdln++;
	from = to = (char *) envp;
	if (i > 0) {
		count += (i&(~3));
		envp = (char **) ( ((long) envp)  + (i&(~3)) );
	}
	envp += 2; count += 8;

/* Now parse the command line and put argv after the environment */

	argv = arg = envp;
	*arg++ = "";		/* argv[0] not available */
	count += 4;
	while(i > 0 && isspace(*cmdln) )
		cmdln++,--i;

	while (i > 0) {
		if (isspace(*cmdln)) {
			--i; cmdln++;
			while (i > 0 && isspace(*cmdln))
				--i,cmdln++;
			*to++ = 0;
		}
		else {
			if (!(*to++ = *cmdln++)) break;
			--i;
		}
	}
	*to++ = '\0';
	*to = '\0'; /* bug fix example:cmdln == '\3' 'a' ' ' 'b' '\0' */
	/* the loop below expects \0\0 at end to terminate! */
	/* the byte @ cmdln[i+2] != 0 when fast bit is set */
do_argc:
	argc = 1;		/* at this point argv[0] is done */
	while (*from) {
		*arg++ = from;
		argc++;
		count += 4;
		while(*from++) ;
	}
	*arg++ = (char *) 0;
	return count+4;
}

static void setup_handlers()
{
    _init_signal();
}

/*
 * _exit: does the actual exiting. Note that _moncontrol and __mcleanup are
 * dummies in crt0.s, but do something in gcrt0.s
 */

void _exit(status)
	int status;
{
    _moncontrol(0L);
    __mcleanup();
    Pterm(status);
}
