Source to mint/main.c


Enter a symbol's name here to quickly find it.

/*
 * This file has been modified as part of the FreeMiNT project. See
 * the file Changes.MH for details and dates.
 */

/*
Copyright 1990,1991,1992 Eric R. Smith.
Copyright 1992,1993,1994 Atari Corporation.
All rights reserved.
*/

#include "mint.h"
#include "version.h"
#include "cookie.h"
#include "xbra.h"

/* the kernel's stack size */
#define STACK	8*1024L

/* if the user is holding down the magic shift key, we ask before booting */
#define MAGIC_SHIFT 0x2		/* left shift */

/* magic number to show that we have captured the reset vector */
#define RES_MAGIC 0x31415926L

static long getmch P_((void));
static void do_file P_((int));
#ifndef SYSUPDATE_DAEMON
 static void do_sync P_((void));
#endif
void shutdown P_((void));
#ifndef MAC
static long ARGS_ON_STACK mint_criticerr P_((long));
#endif
static void ARGS_ON_STACK do_exec_os P_((register long basepage));

static int gem_active;	/* 0 if AES has not started, nonzero otherwise */

#define EXEC_OS 0x4feL
static int  check_for_gem P_((void));
static void run_auto_prgs P_((void));

#ifdef LATTICE
/*
 * AGK: this is witchcraft to completely replace the startup code for
 * Lattice; doing so saves around 10K on the final binary and pulls only
 * long division & multitplication from the library (and not even those
 * if you compile for native '030). The drawback of this code is it
 * passes no environment or command line whatsoever. Since I always
 * set MiNT options & environment in 'mint.cnf' this is not a personal
 * downer, however at some point in the future we ought to have a kernel
 * parseargs() like call which sets these things up.
 */ 
BASEPAGE *_base;

static void
start(BASEPAGE *bp)
{
	long shrinklen;
	
	_base = bp;
	shrinklen = bp->p_tlen + bp->p_dlen + bp->p_blen + STACK + 0x100;
	if (bp->p_lowtpa + shrinklen <= bp->p_hitpa) {
		static char null[1] = {""};
		static char *argv[2] = {null, NULL};
		extern __builtin_putreg P_((int, long));	/* totally bogus */

		__builtin_putreg(15, bp->p_lowtpa + shrinklen);
		Mshrink((void *)bp->p_lowtpa, shrinklen);
		main(1, argv);
	}
	Pterm(ENSMEM);
}
#endif

#if defined(__GNUC__) || defined(__MINT__)
long _stksize = STACK;
#ifndef PROFILING
#include <minimal.h>
#endif
#endif

#ifdef MAC
struct sysvars *sysvar = 0;
int mac_frame = 0;
long mac_term = 0;
int mac_floplck = 0;
#endif

int curs_off = 0;	/* set if we should turn the cursor off when exiting */
int mint_errno = 0;	/* error return from open and creat filesystem calls */

/*
 * AGK: for proper co-processors we must consider saving their context.
 * This variable when non-zero indicates that the BIOS considers a true
 * coprocessor to be present. We use this variable in the context switch
 * code to decide whether to attempt an FPU context save.
 */
short fpu = 0;

/*
 * "mch" holds what kind of machine we are running on
 */
long mch = 0;

/*
 * "screen_boundary+1" tells us how screens must be positioned
 * (to a 256 byte boundary on STs, a 16 byte boundary on other
 * machines; actually, 16 bytes is conservative, 4 is probably
 * OK, but it doesn't hurt to be cautious). The +1 is because
 * we're using this as a mask in the ROUND() macro in mem.h.
 */
int screen_boundary = 255;

/*
 * variable holds processor type
 */
long mcpu = 0;
short ppc = 0;
/*
 * variable holds language preference
 */
int gl_lang = -1;

/*
 * variable set if someone has already installed an flk cookie
 */
int flk = 0;

/*
 * variable set to 1 if the _VDO cookie indicates Falcon style video
 */
int FalconVideo;

/* program to run at startup */
#ifdef MULTITOS
static int init_is_gem = 1;	/* set to 1 if init_prg is GEM */
#else
static int init_is_gem = 0;	/* set to 1 if init_prg is GEM */
BASEPAGE *gem_base = 0;
long gem_start = 0;
#endif
static const char *init_prg = 0;

/* note: init_tail is also used as a temporary stack for resets in
 * intr.spp
 */
char init_tail[256];

/* initial environment for that program */
static char *init_env = 0;
/* temporary pointer into that environment for setenv */
static char *env_ptr;
/* length of the environment */
static long env_len;

/* GEMDOS pointer to current basepage */
BASEPAGE **tosbp;

/* pointer to the BIOS keyboard shift variable */
extern char *kbshft;	/* see bios.c */
extern BCONMAP2_T *bconmap2;	/* bconmap struct, see bios.c */

/* version of TOS we're running over */
int tosvers;

/* structures for keyboard/MIDI interrupt vectors */
KBDVEC *syskey, oldkey;
xbra_vec old_ikbd;			/* old ikbd vector */

/* values the user sees for the DOS, BIOS, and XBIOS vectors */
long save_dos, save_bios, save_xbios;

/* values for original system vectors */
xbra_vec old_dos, old_bios, old_xbios, old_timer, old_vbl, old_5ms;
xbra_vec old_criticerr;
xbra_vec old_execos;

long old_term;

xbra_vec old_resvec;	/* old reset vector */
long old_resval;	/* old reset validation */

#ifdef EXCEPTION_SIGS
/* bus error, address error, illegal instruction, etc. vectors */
xbra_vec old_bus, old_addr, old_ill, old_divzero, old_trace, old_priv;
xbra_vec old_linef, old_chk, old_trapv, old_mmuconf, old_format, old_cpv;
xbra_vec old_uninit, old_spurious, old_fpcp[7], old_pmmuill, old_pmmuacc;
#endif

/* BIOS disk vectors */
xbra_vec old_mediach, old_getbpb, old_rwabs;

/* BIOS drive map */
long olddrvs;

extern Func bios_tab[], dos_tab[];

/* interval for syncing the filesystems */
#ifdef SYSUPDATE_DAEMON
extern long sync_time;
#else
static long sync_time = 5;
#endif

/* kernel info that is passed to loaded file systems and device drivers */

struct kerinfo kernelinfo = {
	MAJ_VERSION, MIN_VERSION,
	DEFAULT_MODE, 0,
	bios_tab, dos_tab,
	changedrv,
	Trace, Debug, ALERT, FATAL,
	kmalloc, kfree, umalloc, ufree,
	strnicmp, stricmp, strlwr, strupr, ksprintf,
	ms_time, unixtim, dostim,
	nap, sleep, wake, (void ARGS_ON_STACK (*)P_((void *))) wakeselect,
	denyshare, denylock, addtimeout, canceltimeout,
	addroottimeout, cancelroottimeout,
	ikill, iwake
};

/* table of processor frame sizes in _words_ (not used on MC68000) */
unsigned char framesizes[16] = {
/*0*/	0,	/* MC68010/M68020/M68030/M68040 short */
/*1*/	0,	/* M68020/M68030/M68040 throwaway */
/*2*/	2,	/* M68020/M68030/M68040 instruction error */
/*3*/	2,	/* M68040 floating point post instruction */
/*4*/	3,	/* MC68LC040/MC68EC040 unimplemented floating point instruction */
/*5*/	129,	/* NOTUSED */
/*6*/	129,	/* NOTUSED */
/*7*/	26,	/* M68040 access error */	
/*8*/	25,	/* MC68010 long */	
/*9*/	6,	/* M68020/M68030 mid instruction */
/*A*/	12,	/* M68020/M68030 short bus cycle */
/*B*/	42,	/* M68020/M68030 long bus cycle */
/*C*/	8,	/* CPU32 bus error */
/*D*/	129,	/* NOTUSED */
/*E*/	129,	/* NOTUSED */
/*F*/	13	/* 68070 and 9xC1xx microcontroller address error */
};

/* TOS and MiNT cookie jars, respectively. See the comments and code 
 * after main() for further details
 */

COOKIE *oldcookie, *newcookie;
MEMREGION *newjar_region;

/*
 * install a new vector for address "addr", using the XBRA protocol.
 * must run in supervisor mode!
 */

static void
xbra_install (xbra_vec *xv, long addr, long ARGS_ON_STACK (*func) ())
{
	xv->xbra_magic = XBRA_MAGIC;
	xv->xbra_id = MINT_MAGIC;
	xv->jump = JMP_OPCODE;
	xv->this = func;
	xv->next = *((struct xbra **)addr);
	*((short **)addr) = &xv->jump;
	cpush(&xv->jump, 6);
}

/*
 * MiNT critical error handler; all it does is to jump through
 * the vector for the current process
 */

#ifndef MAC
static long ARGS_ON_STACK
mint_criticerr (long error) /* high word is error, low is drive */
{
	return (*curproc->criticerr)(error);
}
#endif

/*
 * if we are MultiTOS, and if we are running from the AUTO folder,
 * then we grab the exec_os vector and use that to start GEM; that
 * way programs that expect exec_os to act a certain way will still
 * work.
 * NOTE: we must use Pexec instead of p_exec here, because we will
 * be running in a user context (that of process 1, not process 0)
 */

static void ARGS_ON_STACK
do_exec_os (long basepage)
{
	register long r;

/* if the user didn't specify a startup program, jump to the ROM */
	if (!init_prg) {
		register void ARGS_ON_STACK (*f) P_((long));
		f = (void ARGS_ON_STACK (*) P_((long))) old_execos.next;
		(*f)(basepage);
		Pterm0();
	} else {		

/* we have to set a7 to point to lower in our TPA; otherwise we would
 * bus error right after the Mshrink call!
 */
		setstack(basepage+500L);
#if defined(__TURBOC__) && !defined(__MINT__)
		Mshrink(0, (void *)basepage, 512L);
#else
		Mshrink((void *)basepage, 512L);
#endif
		r = Pexec(200, (char *)init_prg, init_tail, init_env);
		Pterm((int)r);
	}
}


/* initialize all interrupt vectors and new trap routines
 * we also get here any TOS variables that we're going to change
 * (e.g. the pointer to the cookie jar) so that rest_intr can
 * restore them.
 */

static void
init_intr (void)
{
	extern long ARGS_ON_STACK mint_bios();
	extern long ARGS_ON_STACK mint_dos();
	extern long ARGS_ON_STACK mint_timer();
	extern long ARGS_ON_STACK mint_vbl();
	extern long ARGS_ON_STACK mint_5ms();
	extern long ARGS_ON_STACK mint_xbios();
	extern long ARGS_ON_STACK reset();
  	extern long ARGS_ON_STACK new_ikbd();
  	extern long ARGS_ON_STACK new_bus();
  	extern long ARGS_ON_STACK new_addr();
  	extern long ARGS_ON_STACK new_ill();
  	extern long ARGS_ON_STACK new_divzero();
  	extern long ARGS_ON_STACK new_trace();
  	extern long ARGS_ON_STACK new_priv();
  	extern long ARGS_ON_STACK new_linef();
  	extern long ARGS_ON_STACK new_chk();
  	extern long ARGS_ON_STACK new_trapv();
  	extern long ARGS_ON_STACK new_fpcp();
  	extern long ARGS_ON_STACK new_mmu();
  	extern long ARGS_ON_STACK new_format();
  	extern long ARGS_ON_STACK new_cpv();
  	extern long ARGS_ON_STACK new_uninit();
  	extern long ARGS_ON_STACK new_spurious();
  	extern long ARGS_ON_STACK new_pmmuacc();
	short savesr;
#ifndef MAC
	int i;
#endif

	syskey = (KBDVEC *)Kbdvbase();
	oldkey = *syskey;

	xbra_install(&old_ikbd, (long)(&syskey->ikbdsys), new_ikbd);

#ifndef MAC
/* gratuitous (void *) for Lattice */
	old_term = (long)Setexc(0x102, (void *)-1UL);
#endif

	savesr = spl7();

	xbra_install(&old_dos, GEMDOS, mint_dos);
	save_dos = (long)old_dos.next;

	xbra_install(&old_bios, BIOS, mint_bios);
	save_bios = (long)old_bios.next;

	xbra_install(&old_xbios, XBIOS, mint_xbios);
	save_xbios = (long)old_xbios.next;

	xbra_install(&old_timer, TIMER, mint_timer);
#ifndef MAC
	xbra_install(&old_criticerr, CRIT, mint_criticerr);
#endif
	xbra_install(&old_5ms, FIVE, mint_5ms);
	xbra_install(&old_vbl, VBL, mint_vbl);
#ifndef MAC
	xbra_install(&old_resvec, RESVEC, reset);
	old_resval = *((long *)RESVAL);
	*((long *)RESVAL) = RES_MAGIC;
#endif


#ifdef EXCEPTION_SIGS
/* set up signal handlers */
	xbra_install(&old_bus, 8L, new_bus);
	xbra_install(&old_addr, 12L, new_addr);
	xbra_install(&old_ill, 16L, new_ill);
	xbra_install(&old_divzero, 20L, new_divzero);
#ifndef MACSBUG
	xbra_install(&old_trace, 36L, new_trace);
#endif
	xbra_install(&old_priv, 32L, new_priv);
	xbra_install(&old_chk, 24L, new_chk);
	xbra_install(&old_trapv, 28L, new_trapv);
#ifndef MAC
	if (tosvers >= 0x106)
		xbra_install(&old_linef, 44L, new_linef);
	for (i = (int)(sizeof(old_fpcp) / sizeof(old_fpcp[0])); i--; ) {
		xbra_install(&old_fpcp[i], 192L + i * 4, new_fpcp);
	}
	xbra_install(&old_mmuconf, 224L, new_mmu);
	xbra_install(&old_pmmuill, 228L, new_mmu);
	xbra_install(&old_pmmuacc, 232L, new_pmmuacc);
	xbra_install(&old_format, 56L, new_format);
	xbra_install(&old_cpv, 52L, new_cpv);
	xbra_install(&old_uninit, 60L, new_uninit);
	xbra_install(&old_spurious, 96L, new_spurious);
#endif
#endif

/* set up disk vectors */
	xbra_install(&old_mediach, MEDIACH, new_mediach);
	xbra_install(&old_rwabs, RWABS, new_rwabs);
	xbra_install(&old_getbpb, GETBPB, new_getbpb);
	olddrvs = *((long *)DRVMAP);

/* flush entire caches since physical and logical address may not be same */
/* and do this before turning interupts back on */
 	cpush(0,-1);
 	spl(savesr);
 
/* set up cookie jar */
	oldcookie = *CJAR;	/* CJAR defined in cookie.h */
	install_cookies();
}

/* restore all interrupt vectors and trap routines */
/*
 * NOTE: This is *not* the approved way of unlinking XBRA trap handlers.
 * Normally, one should trace through the XBRA chain. However, this is
 * a very unusual situation: when MiNT exits, any TSRs or programs running
 * under MiNT will no longer exist, and so any vectors that they have
 * caught will be pointing to never-never land! So we do something that
 * would normally be considered rude, and restore the vectors to
 * what they were before we ran.
 * BUG: we should restore *all* vectors, not just the ones that MiNT caught.
 */

void
restr_intr (void)
{
	short savesr;
#ifndef MAC
	int i;
#endif

	savesr = spl7();
	*syskey = oldkey;		/* restore keyboard vectors */
	*tosbp = _base;			/* restore GEMDOS basepage pointer */
	*CJAR = oldcookie;		/* restore old cookie jar */

#ifdef EXCEPTION_SIGS
	*((long *)0x08L) = (long) old_bus.next;
	*((long *)0x0cL) = (long) old_addr.next;
	*((long *)0x10L) = (long) old_ill.next;
	*((long *)0x14L) = (long) old_divzero.next;
#ifndef MACSBUG 
	*((long *)0x24L) = (long) old_trace.next;
#endif
	*((long *)0x20L) = (long) old_priv.next;
	*((long *)0x18L) = (long) old_chk.next;
	*((long *)0x1cL) = (long) old_trapv.next;
#ifndef MAC
	if (old_linef.next)
		*((long *)0x2cL) = (long) old_linef.next;
	for (i = (int)(sizeof(old_fpcp) / sizeof(old_fpcp[0])); i--; ) {
		((long *)0xc0L)[i] = (long) old_fpcp[i].next;
	}
	*((long *)0xe0L) = (long) old_mmuconf.next;
	*((long *)0xe4L) = (long) old_pmmuill.next;
	*((long *)0xe8L) = (long) old_pmmuacc.next;
	*((long *)0x38L) = (long) old_format.next;
	*((long *)0x34L) = (long) old_cpv.next;
	*((long *)0x3cL) = (long) old_uninit.next;
	*((long *)0x60L) = (long) old_spurious.next;
#endif
#endif
	*((long *)GEMDOS) = (long) old_dos.next;
	*((long *)BIOS) = (long) old_bios.next;
	*((long *)XBIOS) = (long) old_xbios.next;
	*((long *)TERM) = old_term;
#ifndef MAC
	*((long *)CRIT) = (long) old_criticerr.next;
#endif
	*((long *)FIVE) = (long) old_5ms.next;
	*((long *)TIMER) = (long) old_timer.next;
	*((long *)VBL) = (long) old_vbl.next;
#ifndef MAC
	*((long *)RESVAL) = old_resval;
	*((long *)RESVEC) = (long) old_resvec.next;
#endif
	*((long *)RWABS) = (long) old_rwabs.next;
	*((long *)MEDIACH) = (long) old_mediach.next;
	*((long *)GETBPB) = (long) old_getbpb.next;
	*((long *)DRVMAP) = olddrvs;

	spl(savesr);
}


/* we save the TOS supervisor stack pointer so that we can reset it when
   calling Pterm() (not that anyone will ever want to leave MiNT :-)).
 */

long tosssp;		/* TOS supervisor stack pointer */


/*
 * enter_kernel: called every time we enter the MiNT kernel via a trap
 * call. Sets up the GEMDOS and BIOS vectors to point to TOS, and
 * sets up other vectors and system variables appropriately. Note that
 * calling enter_kernel multiple times is probably NOT a good idea,
 * but the code will allow it.
 * The parameter is a flag telling us whether or not this is a GEMDOS
 * call; the BIOS uses this for checking security of Rwabs.
 */

short in_kernel = 0;

void ARGS_ON_STACK
enter_kernel (int isGEMDOS)
{
	short save_sr;

	if (in_kernel) return;

	save_sr = spl7();
	curproc->in_dos = isGEMDOS;
	save_dos = *((long *) GEMDOS);
	save_bios = *((long *) BIOS);
	save_xbios = *((long *) XBIOS);
	*((long *) GEMDOS) = (long)old_dos.next;
	*((long *) BIOS) = (long)old_bios.next;
	*((long *) XBIOS) = (long)old_xbios.next;
	*tosbp = _base;

	in_kernel = 1;
	spl(save_sr);
}

/*
 * leave_kernel: called before leaving the kernel, either back to
 * user mode or when calling a signal handler or the GEMDOS
 * terminate vector. Note that interrupts should be disabled before
 * this routine is called.
 */

void ARGS_ON_STACK
leave_kernel (void)
{
	*((long *) GEMDOS) = save_dos;
	*((long *) BIOS) = save_bios;
	*((long *) XBIOS) = save_xbios;
	*tosbp = curproc->base;
	in_kernel = 0;
	curproc->in_dos = 0;
}

#ifndef SYSUPDATE_DAEMON
/*
 * do_sync: sync all filesystems at regular intervals
 */
static void
do_sync()
{
	s_ync();
	addroottimeout(1000l*sync_time, do_sync, 0);
}
#endif


/*
 * shut down processes; this involves waking them all up, and sending
 * them SIGTERM to give them a chance to clean up after themselves
 */

static void ARGS_ON_STACK
shutmedown (PROC *p)
{
	UNUSED(p);
	curproc->wait_cond = 0;
}

void
shutdown (void)
{
	PROC *p;
	int proc_left = 0;

	curproc->sighandle[SIGCHLD] = SIG_IGN;

	for (p = proclist; p; p = p->gl_next) {
		if (p->pid == 0) continue;
		if (p->wait_q != ZOMBIE_Q && p->wait_q != TSR_Q) {
			short sr = spl7();
			if (p->wait_q != READY_Q) {
				rm_q(p->wait_q, p);
				add_q(READY_Q, p);
			}
			spl(sr);
			post_sig(p, SIGTERM);
			proc_left++;
		}
	}

	if (proc_left) {
		/* sleep a little while, to give the other processes a chance to
		   shut down
		 */

		addtimeout(5000, shutmedown);
		do {
			sleep(WAIT_Q, (long)shutdown);
		} while (curproc->wait_cond == (long)shutdown);
	}
 	/* uk: do an additional sync for the file systems */
 	s_ync();
}

#ifdef AUTO_FIX
static char *my_name = "MINT.PRG" ;

static void get_my_name(void)
{
	register BASEPAGE *bp ;
	register DTABUF *dta ;
	register char *p ;

	/*
	*	When executing AUTO folder programs, the ROM TOS locates
	*	all programs using Fsfirst/Fsnext; by peeking into our
	*	parent's (the AUTO-execute process's) DTA area, we can
	*	find out our own name...
	*	This works with all known (to me) TOS versions prior to
	*	MultiTOS; but I don't think any other programs should use
	*	this trick, since MiNT or another OS with memory protection
	*	could already be running! <mk>
	*/
	/* Some validity checks first... */
	if ( (bp = (_base)->p_parent)!=NULL && (dta = (DTABUF*)bp->p_dta)!=NULL )
	{
		p = dta->dta_name ;
		/* Test if "MINT*.PRG" or "MNT*.PRG" */
		if ( (strncmp(p,"MINT",4)==0 || strncmp(p,"MNT",3)==0 )
			 && strncmp(p+strlen(p)-4,".PRG",4)==0 )
		{
			my_name = p ;
#if 0
			/* DEBUGGING: */
			Cconws("[MiNT is named \\AUTO\\") ;
			Cconws(p) ;
			Cconws("]\r\n") ;
#endif
		}
	}
}
#endif

#ifndef MULTITOS
long GEM_memflags = F_FASTLOAD | F_ALTLOAD | F_ALTALLOC | F_PROT_S | F_ALLOCZERO;
#else
long GEM_memflags = F_FASTLOAD | F_ALTLOAD | F_ALTALLOC | F_PROT_S;
#endif

#ifdef MAC
#include <mintbind.h>
void macidle()
{
	(void)Psetpgrp(0, 0);
	while (1) {
		(void)Bconstat(0);
		(void)Fselect(50, 0L, 0L, 0L);
	}
}
#endif

#if defined(__GNUC__) || defined(__MINT__)
int
main(argc, argv, envp)
	int argc;
	char **argv, **envp;
#else
int
main(argc, argv)
	int argc;
	char **argv;
#endif
{
	long *sysbase;
	long r;
	extern int debug_level, debug_logging;	/* in debug.c */
	extern int no_mem_prot;		/* memprot.c */
	extern const char *greet1, *greet2;
					/* welcome.c */
	static char buf[SPRINTF_MAX];
	static char curpath[128];
	long yn;
	FILEPTR *f;

#ifdef SYSUPDATE_DAEMON
	extern void start_sysupdate();
#endif
	
#if defined(__GNUC__) || defined(__MINT__)
	UNUSED(envp);
#endif

/* greetings (placed here 19960610 cpbs to allow me to get version
 * info by starting MINT.PRG, even if MiNT's already installed.)  */
	Cconws(greet1);
	ksprintf(buf, VERS_STRING, MAJ_VERSION, MIN_VERSION);
	Cconws(buf);
	Cconws(greet2);

#ifdef __TURBOC__
	Cconws("PRELIMINARY PureC compiled version!\r\n");
#endif

/* figure out what kind of machine we're running on */
/* biosfs wants to know this; also sets no_mem_prot */
/* 920625 kbad put it here so memprot_warning can be intelligent */
	(void)Supexec(getmch);
#ifdef ONLY030
	if (mcpu != 30) {
		Cconws("\r\nThis version of MiNT requires a 68030.\r\n");
		Cconws("Hit any key to continue.\r\n");
		(void)Cconin();
		Pterm0();
	}
#endif

/* Ask the user if s/he wants to boot MiNT */
	if ((Kbshift(-1) & MAGIC_SHIFT) == MAGIC_SHIFT) {
		yn = boot_kernel_p();
		Cconws("\r\n");
		if (!yn)
			Pterm0();
	}

	if (argv[0][0] == 0) {	/* maybe started from the desktop */
		curs_off = 1;
	}

	yn = 0; /* by default, don't print basepage */
	--argc, ++argv;
	while (argc && **argv == '-') {
		if (argv[0][1] >= '0' && argv[0][1] <= '9') {
		/* a number sets out_device to that device */
			extern int out_device;
			out_device = (int)atol(&argv[0][1]);
		}
		else if (argv[0][1] == 'b') {
		/* print MiNT basepage */
			yn++;
		}
		else if (argv[0][1] == 'd') {
		/* -d increases debugging level */
			debug_level++;
		}
		else if (argv[0][1] == 'm' || argv[0][1] == 'p') {
			int givenotice = (argv[0][2] != 'w');
		/* -m and -p turn off memory protection */
		extern const char *memprot_notice, *memprot_warning;
			if (no_mem_prot) {
			    if (givenotice)
				Cconws(memprot_notice);
			}
			else {
			    no_mem_prot = 1;
			    if (givenotice)
				Cconws(memprot_warning);
			}
		}
		else if (argv[0][1] == 'l') {
		/* -l turns on debug logging */
			debug_logging = 1;
		}
		else {
			Cconws("Unknown argument (ignored): ");
			Cconws(*argv);
			Cconws("\r\n");
		}
		++argv, --argc;
	}
	if (argc) {
		Cconws("Unknown argument ignored: ");
		Cconws(*argv);
		Cconws(" (and all the rest)\r\n");
        }

	if (yn)
	{
    	    ksprintf(buf,"MiNT@%lx\r\nhit a key...",_base);
	    Cconws(buf);
	    (void)Crawcin();
	    Cconws("\r\033K");
	}

#ifdef notdef
/* if less than 1 megabyte free, turn off memory protection */
	if (Mxalloc(-1L, 3) < ONE_MEG && !no_mem_prot) {
		extern const char *insuff_mem_warning;
		Cconws(insuff_mem_warning);
		no_mem_prot = 1;
	}
#endif

#ifndef AUTO_FIX
/* look for ourselves as \AUTO\MINTNP.PRG; if so, we turn memory
 * protection off
 */
	if (!no_mem_prot && Fsfirst("\\AUTO\\MINTNP.PRG",0) == 0)
		no_mem_prot = 1;
#endif

/* check for GEM -- this must be done from user mode */
	gem_active = check_for_gem();

#ifdef AUTO_FIX
	if ( !gem_active )	/* only useful if we're in the AUTO folder... */
	{
		get_my_name() ;
		/* Turn off memory protection if our name matches either
			"MINTN*.PRG" or "MNTN*.PRG": */
		if (!no_mem_prot)
		{
				if ( strncmp(my_name, "MINTN", 5)==0
					|| strncmp(my_name, "MNTN", 4)==0 )
					no_mem_prot = 1;
		}
	}
#endif

/*
 * get the current directory, so that we can switch back to it after
 * the file systems are properly initialized
 */
/* set the current directory for the current process */
	Dgetpath(curpath, 0);
	if (!*curpath) {
		curpath[0] = '\\';
		curpath[1] = 0;
	}
	tosssp = (long)Super(0L);	/* enter supervisor mode */
	if (!no_mem_prot)
		save_mmu();		/* save current MMU setup */

/* get GEMDOS pointer to current basepage */
/* 0x4f2 points to the base of the OS; here we can find the OS compilation
   date, and (in newer versions of TOS) where the current basepage pointer
   is kept; in older versions of TOS, it's at 0x602c
 */
	sysbase = *((long **)(SYSBASE));	/* gets the RAM OS header */
	sysbase = (long *)sysbase[2];	/* gets the ROM one */

	tosvers = (int)(sysbase[0] & 0x0000ffff);
	if (tosvers == 0x100) {
		if ((sysbase[7] & 0xfffe0000L) == 0x00080000L)
			tosbp = (BASEPAGE **)0x873cL;	/* SPANISH ROM */
		else
			tosbp = (BASEPAGE **) 0x602cL;
		kbshft = (char *) 0x0e1bL;
	} else {
		tosbp = (BASEPAGE **) sysbase[10];
		kbshft = (char *) sysbase[9];
	}

	if (tosvers >= 0x0400 && tosvers <= 0x404) {
		bconmap2 = (BCONMAP2_T *)Bconmap(-2);
		if (bconmap2->maptabsize == 1) {
			/* Falcon BIOS Bconmap is busted */
			bconmap2->maptabsize = 3;
		}
		has_bconmap = 1;
	} else {
/* The TT TOS release notes are wrong... this is the real way to test
 * for Bconmap ability
 */
		has_bconmap = (Bconmap(0) == 0);
		if (has_bconmap)
			bconmap2 = (BCONMAP2_T *)Bconmap(-2);
	}

/* initialize memory */
	init_mem();

/* initialize the basic file systems */
	timestamp = Tgettime();
	datestamp = Tgetdate();
	init_filesys();

/* initialize processes */
	init_proc();

/* initialize system calls */
	init_dos();
	init_bios();
	init_xbios();

/* NOTE: there's a call to kmalloc embedded in install_cookies, which
 * is called by init_intr; so make sure this is the last of the
 * init_* things called!
 */
	init_intr();
	enter_kernel(1);	/* we'll be making GEMDOS calls */

#if 0
	if (!gem_active) {
/* make MiNT invisible in the basepage chain, so that
 * programs that rely on a certain basepage chain
 * structure to determine whether or not they were run
 * from the desktop will have a better chance of working.
 * NOTE THAT THIS IS ONLY DONE TO HELP OUT BRAIN-DAMAGED
 * SOFTWARE: do *not* try counting basepages to figure
 * out whether or not you were run from the desktop!!!
 */
		rootproc->base = _base->p_parent;
	} else
#endif
		rootproc->base = _base;

/* set up standard file handles for the current process
 * do this here, *after* init_intr has set the Rwabs vector,
 * so that AHDI doesn't get upset by references to drive U:
 */
	f = do_open("U:\\DEV\\CONSOLE", O_RDWR, 0, (XATTR *)0);
	if (!f) {
		FATAL("unable to open CONSOLE device");
	}
	curproc->control = f;
	curproc->handle[0] = f;
	curproc->handle[1] = f;
	f->links = 3;

	f = do_open("U:\\DEV\\MODEM1", O_RDWR, 0, (XATTR *)0);
	curproc->aux = f;
	((struct tty *)f->devinfo)->aux_cnt = 1;
	f->pos = 1;	/* flag for close to --aux_cnt */
	if (has_bconmap) {
	/* If someone has already done a Bconmap call, then
	 * MODEM1 may no longer be the default
	 */
		bconmap(curbconmap);
		f = curproc->aux;	/* bconmap can change curproc->aux */
	}
	if (f) {
		curproc->handle[2] = f;
		f->links++;
	}
	f = do_open("U:\\DEV\\CENTR", O_RDWR, 0, (XATTR *)0);
	if (f) {
		curproc->handle[3] = curproc->prn = f;
		f->links = 2;
	}
	f = do_open("U:\\DEV\\MIDI", O_RDWR, 0, (XATTR *)0);
	if (f) {
		curproc->midiin = curproc->midiout = f;
		((struct tty *)f->devinfo)->aux_cnt = 1;
		f->pos = 1;	/* flag for close to --aux_cnt */
		f->links = 2;
	}

/* load external file systems */
/* set path first to make sure that MiNT's directory matches
 * GEMDOS's
 */
	DEBUG(("About to load external device drivers"));
	d_setpath(curpath);
	
	load_devdriver();
	DEBUG(("About to load external file systems"));
#ifndef PROFILING
/* load_filesys causes media changes :-( */
	load_filesys();
#endif

/* note that load_filesys changed the
 * directory on us!!
 */
	d_setpath(curpath);
	DEBUG(("About to load mint.cnf"));	
/* load the configuration file */
	load_config();

/* start system update daemon */
	if (sync_time > 0) {
#ifdef SYSUPDATE_DAEMON
		start_sysupdate();
#else
		addroottimeout(1000l*sync_time, do_sync, 0);
#endif
	}

	*((long *)DRVMAP) |= PSEUDODRVS;

	if (init_env == 0)
		init_env = (char *)_base->p_env;

/* empty environment? Set the PATH variable to the root of the current drive */
	if (init_env[0] == 0) {
		static char path_env[] = "PATH=\0C:\0";
		path_env[6] = curproc->curdrv + 'A';
		init_env = path_env;
	}

/* if we are MultiTOS, we're running in the AUTO folder, and our INIT is
 * in fact GEM, take the exec_os() vector. (We know that INIT is GEM
 * if the user told us so by using GEM= instead of INIT=.)
 */
	if (!gem_active && init_is_gem) {
		xbra_install(&old_execos, EXEC_OS, (long ARGS_ON_STACK (*)())do_exec_os);
	}

/* run any programs appearing after us in the AUTO folder */
	run_auto_prgs();

/* prepare to run the init program as PID 1. */
	set_pid_1();

#ifdef MAC
/* run event handler process */
	{
		BASEPAGE *bp;

		bp = (BASEPAGE *)p_exec(5, (char *)0L, (char *)0L, 0L);
		m_shrink(0, (virtaddr)bp, 4*1024L);
		if ((long)bp > 0L) {
			bp->p_tbase = (long)macidle;
			r = p_exec(104, "IDLE", (char *)bp, 0L);
		}
	}
#endif

/* run the initial program */
/* if that program is in fact GEM, we start it via exec_os, otherwise
 * we do it with Pexec.
 * the logic is: if the user specified init_prg, and it is not
 * GEM, then we try to execute it; if it *is* GEM (e.g. gem.sys),
 * then we try to execute it if gem is already active, otherwise
 * we jump through the exec_os vector (which we grabbed above) in
 * order to start it. We *never* go through exec_os if we're not in
 * the AUTO folder.
 */
	if (init_prg && (!init_is_gem || gem_active)) {
		r = p_exec(0, (char *)init_prg, init_tail, init_env);
	} else if (!gem_active) {   
		BASEPAGE *bp; int pid;
		bp = (BASEPAGE *)p_exec(7,
		  (char *)GEM_memflags, (char *)"\0", init_env);
		bp->p_tbase = *((long *) EXEC_OS );
#ifndef MULTITOS
		if (((long *) sysbase[5])[0] == 0x87654321)
		  gem_start = ((long *) sysbase[5])[2];
		gem_base = bp;
#endif
		r = p_exec(106, (char *)"GEM", bp, 0L);
		pid = (int)r;
		if (pid > 0) {
			do {
				r = p_wait3(0, (long *)0);
			} while(pid != ((r & 0xffff0000L) >> 16));
			r &= 0x0000ffff;
		}
	} else {
Cconws("If MiNT is run after GEM starts, you must specify a program\r\n");
Cconws("to run initially in MINT.CNF, with an INIT= line\r\n");
			r = 0;
	}

	if (r < 0 && init_prg) {
		ksprintf(buf, "FATAL: couldn't run %s\r\n", init_prg);
		Cconws(buf);
	}

	if (r) {
		ksprintf(buf, "exit code: %ld\r\n", r);
		Cconws(buf);
	}

	rootproc->base = _base;

/* shut down all processes gracefully */
	shutdown();

/* put everything back and exit */
	if (!gem_active && init_is_gem) {
	/* we stole exec_os above */
		*((long *)EXEC_OS) = (long)old_execos.next;
	}
	restr_intr();
	close_filesys();
	if (!no_mem_prot)
		restr_mmu();
	restr_screen();

	(void)Super((void *)tosssp);	/* gratuitous (void *) for Lattice */
	Cconws("leaving MiNT\r\n");

	if (curs_off)
		Cconws("\033f");	/* disable cursor */

	return 0;
}


/*
 * cookie jar handling routines. The "cookie jar" is an area of memory
 * reserved by TOS for TSR's and utility programs; the idea is that
 * you put a cookie in the jar to notify people of available services.
 * The BIOS uses the cookie jar in TOS 1.6 and higher; for earlier versions
 * of TOS, the jar is always empty (unless someone added a cookie before
 * us; POOLFIX does, for example).
 * MiNT establishes an entirely new cookie jar (with the old cookies copied
 * over) and frees it on exit. That's because TSR's run under MiNT
 * will no longer be accessible after MiNT exits.
 * MiNT also puts a cookie in the jar, with tag field 'MiNT' (of course)
 * and with the major version of MiNT in the high byte of the low word,
 * and the minor version in the low byte.
 */

void
install_cookies (void)
{
	COOKIE *cookie;
	int i, ncookies;
	long ncsize;
	extern long rsvf;

	/* note that init_intr sets oldcookie to the old cookie jar */

	ncookies = 0;
	cookie = oldcookie;
	if (cookie) {
		while (cookie->tag != 0) {
		/* check for true FPU co-processor */
			if ((cookie->tag == COOKIE__FPU) &&
				 (cookie->value >> 16) >= 2)
				fpu = 1;
		/* check for _FLK cookie */
			else if (cookie->tag == COOKIE__FLK)
				flk = 1;
		/* ..and for RSVF */
			else if (cookie->tag == COOKIE_RSVF)
				rsvf = cookie->value;
			cookie++; ncookies++;
		}
	}

	/*
	 * We allocate the cookie jar in global memory so anybody can read
	 * it or write it. This code allocates at least 8 more cookies, and
	 * then rounds up to a QUANTUM boundary (that's what ROUND does). 
	 * Probably, nobody will have to allocate another cookie jar :-)
	 */

	/* NOTE: obviously, we can do this only if init_intr is called
	 * _after_ memory, processes, etc. have been initialized
	 */
	ncsize = (ncookies+8)*sizeof(COOKIE);
	ncsize = ROUND(ncsize);
#if 0
	newcookie = (COOKIE *)alloc_region(core, ncsize, PROT_G);
#else
	newjar_region = get_region (core, ncsize, PROT_G);
	newcookie = (COOKIE *) attach_region (rootproc, newjar_region);
#endif

/* copy the old cookies to the new jar */

	for (i = 0, cookie = oldcookie; i < ncookies;) {
/*
 * but don't copy RSVF, MiNTs /dev is for real...
 * (if you want to know whats in there use ls :)
 */
		if (cookie->tag == COOKIE_RSVF) {
			++cookie, --ncookies;
			continue;
		}
		newcookie[i++] = *cookie++;
	}

/* install MiNT cookie */
	newcookie[i].tag   = COOKIE_MiNT;
	newcookie[i].value = (MAJ_VERSION << 8) | MIN_VERSION;
	i++;

/* install _FLK cookie to indicate that file locking works */
	if (!flk) {
		newcookie[i].tag   = COOKIE__FLK;
		newcookie[i].value = 0;
		i++;
	}

/* jr: install PMMU cookie if memory protection is used */
	if (!no_mem_prot) {
		newcookie[i].tag   = COOKIE_PMMU;
		newcookie[i].value = 0;
		i++;
	}

/* the last cookie should have a 0 tag, and a value indicating the number
 * of slots, total
 */

	newcookie[i].tag   = 0;
	newcookie[i].value = ncsize/sizeof(COOKIE);

	*CJAR = newcookie;

}

/*
 * Get the value of the _MCH cookie, if one exists; also set no_mem_prot if
 * there's a _CPU cookie and you're not on an '030, or if there is none.
 * This must be done in a separate routine because the machine type and CPU
 * type are needed when initializing the system, whereas install_cookies is
 * not called until everything is practically up.
 * In fact, getmch() should be called before *anything* else is
 * initialized, so that if we find a MiNT cookie already in the
 * jar we can bail out early and painlessly.
 */

static long
getmch (void)
{
	COOKIE *jar;
	int foundcpu = 0;
	int i;
	long *sysbase;
	extern int no_mem_prot;

	mcpu = 0;
	jar = *CJAR;	/* CJAR defined in cookie.h */
	if (jar) {
		while (jar->tag != 0) {
		/* check for machine type */
			if (jar->tag == COOKIE__MCH) {
				mch = jar->value;
			} else if (jar->tag == COOKIE__CPU) {
	    			/* if not '030 then no memory protection */
				mcpu = jar->value;
	    			if (jar->value != 30) no_mem_prot = 1;
	    			foundcpu = 1;
			} else if (jar->tag == COOKIE__VDO) {
				FalconVideo = (jar->value == 0x00030000L);
				if (jar->value & 0xffff0000L)
					screen_boundary = 15;
			} else if (jar->tag == COOKIE_MiNT) {
				Cconws("MiNT is already installed!!\r\n");
				Pterm(2);
			} else if (jar->tag == COOKIE__AKP) {
				gl_lang = (int) ((jar->value >> 8) & 0x00ff);
			} else if (jar->tag == COOKIE_PMMU) {
				/* jr: if PMMU cookie exists, someone else is
				   already using the PMMU */
				Cconws ("MiNT: PMMU already in use, memory protection turned off.\r\n");
				no_mem_prot = 1;
#ifdef MAC
			} else if ((jar->tag == COOKIE_SVAR)) {
				sysvar = (struct sysvars *)jar->value;
#endif
			}
			jar++;
		}
	}
#ifdef MAC
	if (!sysvar) {
		Cconws("This version of MiNT is for the Macintosh.\r\n");
		Pterm(2);
	}
	if (sysvar->version != SVAR_VERS) {
		Cconws("MiNT's sysvar structure does not match JET's.\r\n");
		Pterm(2);
	}
	ppc = POWERPC;
	if (mcpu > 0)
		mac_frame = 1;
#endif
	if (!foundcpu) no_mem_prot = 1;
/*
 * if no preference found, look at the country code to decide
 */
	if (gl_lang < 0) {
		sysbase = *((long **)(SYSBASE)); /* gets the RAM OS header */
		sysbase = (long *)sysbase[2];	/* gets the ROM one */
		i = (int) ((sysbase[7] & 0x7ffe0000L) >> 17L);
		switch(i) {
		case 1:		/* Germany */
		case 8:		/* Swiss German */
			gl_lang = 1;
			break;
		case 2:		/* France */
		case 7:		/* Swiss French */
			gl_lang = 2;
			break;
		case 4:		/* Spain */
			gl_lang = 4;
			break;
		case 5:		/* Italy */
			gl_lang = 5;
			break;
		default:
			gl_lang = 0;
			break;
		}
	}
	

	if (gl_lang >= MAXLANG || gl_lang < 0)
		gl_lang = 0;
	return 0L;
}

/*
 * routines for reading the configuration file
 * we allow the following commands in the file:
 * # anything		-- comment
 * INIT=file		-- specify boot program
 * CON=file		-- specify initial file/device for handles -1, 0, 1
 * PRN=file		-- specify initial file for handle 3
 * BIOSBUF=[yn]		-- if 'n' or 'N' then turn off BIOSBUF feature
 * DEBUG_LEVEL=n	-- set debug level to (decimal number) n
 * DEBUG_DEVNO=n	-- set debug device number to (decimal number) n
 * HARDSCROLL=n		-- set hard-scroll size to n, range 0-99.
 * SLICES=nnn		-- set multitasking granularity
 * UPDATE=n   		-- set the sync time in seconds for the system update daemon
 * echo message		-- print a message on the screen
 * alias drive path	-- make a fake drive pointing at a path
 * cd dir		-- change directory/drive
 * exec cmd args	-- execute a program
 * setenv name val	-- set up environment
 * sln file1 file2	-- create a symbolic link
 * ren file1 file2	-- rename a file
 *
 * BUG: if you use setenv in mint.cnf, *none* of the original environment
 * gets passed to children. This is rarely a problem if mint.prg is
 * in the auto folder.
 */

extern short bconbdev, bconbsiz;	/* from bios.c */

static void
doset (char *name, char *val)
{
	char *t;

	if (!strcmp(name, "GEM")) {
		init_is_gem = 1;
		goto setup_init;
	} 
	if (!strcmp(name, "INIT")) {
		init_is_gem = 0;
setup_init:
		if (!*val) return;
		t = kmalloc(strlen(val)+1);
		if (!t) return;
		strcpy(t, val);
		init_prg = t;
		while (*t && !isspace(*t)) t++;
/* get the command tail, too */
		if (*t) {
			*t++ = 0;
			strncpy(init_tail+1, t, 125);
			init_tail[126] = 0;
			init_tail[0] = strlen(init_tail+1);
		}
		return;
	}
	if (!strcmp(name, "CON")) {
		FILEPTR *f;
		int i;

		f = do_open(val, O_RDWR, 0, (XATTR *)0);
		if (f) {
			for (i = -1; i < 2; i++) {
				do_close(curproc->handle[i]);
				curproc->handle[i] = f;
				f->links++;
			}
			f->links--;	/* correct for overdoing it */
		}
		return;
	}
	if (!strcmp(name, "PRN")) {
		FILEPTR *f;

		f = do_open(val, O_RDWR|O_CREAT|O_TRUNC, 0, (XATTR *)0);
		if (f) {
			do_close(curproc->handle[3]);
			do_close(curproc->prn);
			curproc->prn = curproc->handle[3] = f;
			f->links = 2;
		}
		return;
	}
	if (!strcmp(name, "AUX")) {
		FILEPTR *f;

		f = do_open(val, O_RDWR|O_CREAT|O_TRUNC, 0, (XATTR *)0);
		if (f) {
			extern FILESYS bios_filesys;
			extern DEVDRV bios_tdevice;

			do_close (curproc->handle[2]);
			do_close (curproc->aux);
			curproc->aux = curproc->handle[2] = f;
			f->links = 2;
			if (is_terminal(f) && f->fc.fs == &bios_filesys &&
			    f->dev == &bios_tdevice &&
			    (has_bconmap ? (f->fc.aux>=6) : (f->fc.aux==1))) {
				if (has_bconmap)
					curproc->bconmap = f->fc.aux;
				((struct tty *)f->devinfo)->aux_cnt++;
				f->pos = 1;
			}
		}
		return;
	}
	if (!strcmp(name, "BIOSBUF")) {
		if (*val == 'n' || *val == 'N') {
			if (bconbsiz) bflush();
			bconbdev = -1;
		}
		return;
	}
	if (!strcmp(name, "DEBUG_LEVEL")) {
		extern int debug_level;
		if (*val >= '0' && *val <= '9')
			debug_level = (int)atol(val);
		else ALERT("Bad arg to \"DEBUG_LEVEL\" in cnf file");
		return;
	}
	if (!strcmp(name, "DEBUG_DEVNO")) {
		extern int out_device;
		if (*val >= '0' && *val <= '9')
			out_device= (int)atol(val);
		else ALERT("Bad arg to \"DEBUG_DEVNO\" in cnf file");
		return;
	}

#ifdef FASTTEXT
	if (!strcmp(name, "HARDSCROLL")) {
		int i;
		extern int hardscroll;

		if (!strcmp(val, "AUTO")) {
			hardscroll = -1;
			return;
		}
		i = *val++;
		if (i < '0' || i > '9') return;
		hardscroll = i-'0';
		i = *val;
		if (i < '0' || i > '9') return;
		hardscroll = 10*hardscroll + i - '0';
		return;
	}
#endif
	if (!strcmp(name, "MAXMEM")) {
		long r;

		r = atol(val) * 1024L;
		if (r > 0)
			p_setlimit(2, r);
		return;
	}
	if (!strcmp(name, "SLICES")) {
		extern short time_slice;

		time_slice = atol(val);
		return;
	}

	if (!strcmp(name, "PSEUDODRIVES")) {
		FORCE("PSEUDODRIVES= no longer supported");
		return;
	}

	/* uk: set update time for system update daemon */
	if (!strcmp(name, "UPDATE")) {
		extern long sync_time;

		sync_time = atol(val);
		return;
	}
	FORCE("Unknown variable `%s'", name);
}

/* Execute a line from the config file */
static void
do_line (char *line)
{
	char *cmd, *arg1, *arg2;
	char *newenv;
	char *t;
	int i;
	char delim;

	while (*line == ' ') line++;	/* skip whitespace at start of line */
	if (*line == '#') return;	/* ignore comments */
	if (!*line) return;		/* and also blank lines */

	cmd = line;
/* check for variable assignments (e.g. INIT=, etc.) */
/*
 * AGK: note we check for spaces whilst scanning so that an environment
 * variable may include an =, this has the unfortunate side effect that
 * the '=' _has_ to be concatenated to the variable name (INIT etc.)
 */
	for (t = cmd; *t && *t != ' '; t++) {
		if (*t == '=') {
			*t++ = 0;
			doset(cmd, t);
			return;
		}
	}

/* OK, assume a regular command; break it up into 'cmd', 'arg1', arg2' */

	while (*line && *line != ' ') line++;
	delim = ' ';
	if (*line) {
		*line++ = 0;
		while (*line == ' ') line++;
		if (*line == '"') {
			delim = '"';
			line++;
		}
	}

	if (!strcmp(cmd, "echo")) {
		c_conws(line); c_conws("\r\n");
		return;
	}
	arg1 = line;
	while (*line && *line != delim) line++;
	delim = ' ';
	if (*line) {
		*line++ = 0;
		while (*line == ' ') line++;
		if (*line == '"') {
			delim = '"';
			line++;
		}
	}
	if (!strcmp(cmd, "cd")) {
		int drv;
		(void)d_setpath(arg1);
		drv = toupper(*arg1) - 'A';
		if (arg1[1] == ':') (void)d_setdrv(drv);
		return;
	}
	if (!strcmp(cmd, "exec")) {
		char cmdline[128];
		int i;

		i = strlen(line);
		if (i > 126) i = 126;
		cmdline[0] = i;
		strncpy(cmdline+1, line, i);
		cmdline[i+1] = 0;
		i = (int)p_exec(0, arg1, cmdline, init_env);
		if (i == -33) {
			FORCE("%s: file not found", arg1);
		} else if (i < 0) {
			FORCE("%s: error while attempting to execute", arg1);
		}
		return;
	}
	if (!strcmp(cmd, "setenv")) {
		if (strlen(arg1) + strlen(line) + 4 + (env_ptr - init_env) >
							 env_len) {
			long j;

			env_len += 1024;
			newenv = (char *)m_xalloc(env_len, 0x13);
			if (init_env) {
				t = init_env;
				j = env_ptr - init_env;
				env_ptr = newenv;
				for (i = 0; i < j; i++)
					*env_ptr++ = *t++;
				if (init_env)
					m_free((virtaddr)init_env);
			} else {
				env_ptr = newenv;
			}
			init_env = newenv;
		}
		while (*arg1) {
			*env_ptr++ = *arg1++;
		}
		*env_ptr++ = '=';
		while (*line) {
			*env_ptr++ = *line++;
		}
		*env_ptr++ = 0;
		*env_ptr = 0;
		return;
	}
	if (!strcmp (cmd, "include")) {
	    long fd = f_open (arg1, 0);
	    if (fd < 0) {
		ALERT ("include: cannot open file %s", arg1);
		return;
	    }
	    do_file ((int)fd);
	    f_close ((int)fd);
	    return;
	}
	arg2 = line;
	while (*line && *line != delim) line++;
	if (*line) {
		*line = 0;
	}
	if (!strcmp(cmd, "alias")) {
		int drv;
		long r;
		fcookie root_dir;
		extern int aliasdrv[];

		drv = toupper(*arg1) - 'A';
		if (drv < 0 || drv >= NUM_DRIVES) {
			ALERT("Bad drive (%c:) in alias", drv+'A');
			return;
		}
		r = path2cookie(arg2, NULL, &root_dir);
		if (r) {
			ALERT("alias: TOS error %ld while looking for %s",
				r, arg2);
			return;
		}
		aliasdrv[drv] = root_dir.dev + 1;
		*((long *)DRVMAP) |= (1L << drv);
		release_cookie(&curproc->curdir[drv]);
		dup_cookie(&curproc->curdir[drv], &root_dir);
		release_cookie(&curproc->root[drv]);
		curproc->root[drv] = root_dir;
		return;
	}
	if (!strcmp(cmd, "sln")) {
		(void)f_symlink(arg1, arg2);
		return;
	}
	if (!strcmp(cmd, "ren")) {
		(void)f_rename(0, arg1, arg2);
		return;
	}
	FORCE("syntax error in mint.cnf near: %s", cmd);
}

#undef BUF
#undef LINE

#define BUF 512
#define LINE 256

static void
do_file (int fd)
{
	long r;
	char buf[BUF+1], c;
	char line[LINE+1];
	char *from;
	int count = 0;

 	buf[BUF] = 0;
	from = &buf[BUF];
	line[LINE] = 0;

	for(;;) {
		c = *from++;
		if (!c) {
			r = f_read(fd, (long)BUF, buf);
			if (r <= 0) break;
			buf[r] = 0;
			from = buf;
		} else if (c == '\r') {
			continue;
		} else if (c == '\n') {
			line[count] = 0;
			do_line(line);
			count = 0;
		} else {
			if (count < LINE) {
				line[count++] = c;
			}
		}
	}
	if (count) {
		line[count] = 0;
		do_line(line);
	}
}

void
load_config (void)
{
	int fd;

	fd = (int) f_open("mint.cnf", 0);
	if (fd < 0)
		fd = (int) f_open("\\mint\\mint.cnf", 0);
	if (fd < 0)
		fd = (int) f_open("\\multitos\\mint.cnf", 0);
	if (fd < 0) return;
	do_file(fd);
	f_close(fd);
}

/*
 * run programs in the AUTO folder that appear after MINT.PRG
 * some things to watch out for:
 * (1) make sure GEM isn't active
 * (2) make sure there really is a MINT.PRG in the auto folder
 */

/*
 * some global variables used to see if GEM is active
 */
static short aes_intout[64];
static short aes_dummy[64];
static short aes_globl[15];
static short aes_cntrl[6] = { 10, 0, 1, 0, 0 };

short *aes_pb[6] = { aes_cntrl, aes_globl, aes_dummy, aes_intout,
		     aes_dummy, aes_dummy };

/* check for whether GEM is active; remember, this *must* be done in
 * user mode
 */

static int
check_for_gem (void)
{
	call_aes(aes_pb);	/* does an appl_init */
	return aes_globl[0];
}

static void
run_auto_prgs (void)
{
	DTABUF *dta;
	long r;
	static char pathspec[32] = "\\AUTO\\";
	short runthem = 0;	/* set to 1 after we find MINT.PRG */
	char	curpath[PATH_MAX];
 	int	curdriv,bootdriv;

/* if the AES is running, don't check AUTO */

	if (gem_active)	return;

/* OK, now let's run through \\AUTO looking for
 * programs...
 */
	d_getpath(curpath,0);
	curdriv = d_getdrv();
	/*
	*	We are in Supervisor mode, so we can do this
	*/
#ifdef MAC
	bootdriv = 'P' - 'A';
#else
	bootdriv=*((short*)0x446);
#endif
	d_setdrv(bootdriv);
	d_setpath("\\");

	dta = (DTABUF *)f_getdta();
	r = f_sfirst("\\AUTO\\*.PRG", 0);
	while (r >= 0) {
#ifdef AUTO_FIX
		if ( strcmp(dta->dta_name, my_name)==0 )
#else
		if (!strcmp(dta->dta_name, "MINT.PRG") ||
		    !strcmp(dta->dta_name, "MINTNP.PRG"))
#endif
			runthem = 1;
		else if (runthem) {
			strcpy(pathspec+6, dta->dta_name);
			p_exec(0, pathspec, (char *)"", init_env);
		}
		r = f_snext();
	}
 	d_setdrv(curdriv);
 	d_setpath(curpath);
}