#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <osbind.h>
#include <memory.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include "symdir.h"
#include <unistd.h>
#include "lib.h"
#include <ctype.h>

int	errno;
int	__mint;		/* 0 for TOS, MiNT version number otherwise */
int	_console_dev;

FILE	_iob[_NFILE];	/* stream buffers initialized below */

/* functions registered by user for calling at exit */
#ifdef __STDC__
typedef void (*ExitFn)(void);
#else
typedef void (*ExitFn)();
#endif
static ExitFn *_at_exit;
static int num_at_exit;	/* number of functions registered - 1 */
extern clock_t	_start_time;	/* defined in clock.c */
extern clock_t	_child_runtime;	/* defined in spawnve.c */
extern clock_t	_sys_runtime;	/* defined in spawnve.c */

/*
 * get MiNT version number
 */
static void
getMiNT(void)
{
	long *cookie;

	cookie = *((long **) 0x5a0L);
	if (!cookie)
		__mint = 0;
	else {
		while (*cookie) {
			if (*cookie == 0x4d694e54L) {
				__mint = cookie[1];
				return;
			}
			cookie += 2;
		}
	}
	__mint = 0;
}

/* supplied by the user */
__EXTERN int main __PROTO((int, char **, char **));

/* in getbuf.c */
__EXTERN void _getbuf __PROTO((FILE *));

void
_main(_argc, _argv, _envp)
	long _argc;
	char **_argv, **_envp;
{
	char *tty;
	register FILE *f;
	register int i;
 	char *s, *t;
	extern int __default_mode__; /* in defmode.c or defined by user */
	int main(int, char **, char **);

	_start_time = 0;	/* for dumped prgs */
	_start_time = clock();
	_child_runtime = _sys_runtime = 0;
	num_at_exit = 0;
/*
 * check for MiNT
 */
 	(void)Supexec(getMiNT);

/*
 * check environment for various things
 */
	_set_unixmode(getenv("UNIXMODE"));

	if (tty = getenv("TTY")) {
		if (!strcmp(tty, "AUX:") || !strcmp(tty, "aux:"))
			_console_dev = 1;
		else
			_console_dev = 2;
	}
	else
		_console_dev = 2;

/* if stderr is not re-directed to a file, force 2 to console
 * (UNLESS we've been run from a shell we trust, i.e. one that supports
 *  the official ARGV scheme, in which case we leave stderr be).
 */
	if(!*_argv[0] && isatty(2))
	    (void)Fforce(2, _console_dev - 3);

	stdin->_flag = _IOREAD|_IOFBF|__default_mode__;
	stdout->_flag = _IOWRT|_IOLBF|__default_mode__;
	stderr->_flag = _IORW|_IONBF|__default_mode__;
			 /* some brain-dead people read from stderr */

	for(i = 0, f = _iob; i < 3; ++i, ++f) {	/* flag device streams */
	    if(isatty(f->_file = i))
			f->_flag |= _IODEV;
	    else
	        if(f == stdout)	{ /* stderr is NEVER buffered */
	        /* if stdout re-directed, make it full buffered */
	            f->_flag &= ~(_IOLBF | _IONBF);
	            f->_flag |=  _IOFBF;
	        }
	    _getbuf(f);	/* get a buffer */
	}

	for(i = 0; i <= 2; i++) {
	    __open_stat[__OPEN_INDEX(i)].filename = isatty(i) ? 
			((_console_dev == 2) ? "/dev/console" : "/dev/tty1")
		        : (char *)NULL; /* if it was re-dir */
	}

 	for(i = 3; i < _NFILE; i++, f++) {
	  f->_flag = 0;		/* clear flags, if this is a dumped program */
	}

	/* Fix up environment, if necessary. At present, the only variable
	 * affected is PATH; the "standard" path separators for PATH are
	 * ',' and ';' in the Atari world, but POSIX mandates ':'. This
	 * conflicts with the use of ':' as a drive separator, so we
	 * also convert names like A:\foo to /dev/A/foo
	 * NOTE: this conversion must be undone in spawn.c so that
	 * old fashioned programs will understand us!
	 */
	for (i = 0; s = _envp[i]; i++) {	/* '=', NOT '==' */
		if (s[0] == 'P' && s[1] == 'A' && s[2] == 'T' &&
		    s[3] == 'H' && s[4] == '=') {
			_envp[i] = (char *)malloc(4 * strlen(s));
			strncpy(_envp[i], s, 5);
			t = _envp[i]+5;
			s += 5;
			while (*s) {
				if (s[1] == ':' && isalpha(*s)) {	/* drive letter */
					*t++ = '/';
					*t++ = 'd'; *t++ = 'e'; *t++ = 'v';
					*t++ = '/'; *t++ = *s++; s++;
				} else if (*s == ';' || *s == ',') {
					*t++ = ':'; s++;
				} else {
					*t++ = *s++;
				}
			}
			*t++ = 0;
			break;
		}
	}

	errno = 0;

	/* ANSI-Draft: A return from the initial call to the main      */
	/* function is equivalent to calling the exit function with    */
	/* the value returned by the main function as its argument. If */
	/* the main function executes a return that specifies no       */
	/* value, the termination status returned to the host 	       */
	/* environment is undefined. [section 2.1.2.2]		       */
	exit(main((int) _argc, _argv, _envp));	/* if main() returns exit */
}

__EXITING _exit(status)
	int status;
{
	__exit((long)status);
}

__EXITING exit(status)
	int status;
{
	register int i, f;


	for(i=0; i<_NFILE; ++i) {
		f = _iob[i]._flag;
		if(f & (_IORW | _IOREAD | _IOWRT))
			if (_iob[i]._file <= 2) /* only flush std. streams */
			    fflush(&_iob[i]);
			else
			    fclose(&_iob[i]);
	}

	for(i = num_at_exit - 1; i >= 0; --i)
		(*_at_exit[i])();

	_exit(status);
}

/* register a function for execution on termination */
/* Ansi requires atleast 32 entries, we make it dynamic and hope
   it meets the ansi requirement */
 
int atexit(func)
	ExitFn func;
{
        ExitFn *new_at_exit = _at_exit;
    
	if (num_at_exit == 0)
		new_at_exit = (ExitFn *)malloc((size_t)sizeof(ExitFn));
	else
		new_at_exit = (ExitFn *)realloc(new_at_exit,
			(size_t)((num_at_exit + 1) * sizeof(ExitFn)));
	if(new_at_exit == (ExitFn *)NULL)
		return -1;	/* failure */

        _at_exit = new_at_exit;
	_at_exit[num_at_exit++] = func;
	return 0;		/* success */
}
