static	char	RCSid[] =
"$Header: tos.c,v 1.3 88/10/27 08:18:07 tony Exp $";

/*
 * System-dependent routines for the Atari ST.
 *
 * $Log:	tos.c,v $
 * Revision 1.3  88/10/27  08:18:07  tony
 * Added a null flushbuf() for compatibility with versions that buffer
 * output. Added fixname() to do appropriate name truncation. Added
 * a quick doshell() that supports commands, but not interactive shells.
 * Removed support for Megamax.
 * 
 * Revision 1.2  88/09/23  19:35:44  tony
 * Added mktemp() for use with the '!' operator. Also added support for
 * the Sozobon C compiler.
 * 
 * Revision 1.1  88/03/20  21:10:42  tony
 * Initial revision
 * 
 *
 */

#include "stevie.h"

#include <osbind.h>

/*
 * The following buffer is used to work around a bug in TOS. It appears that
 * unread console input can cause a crash, but only if console output is
 * going on. The solution is to always grab any unread input before putting
 * out a character. The following buffer holds any characters read in this
 * fashion. The problem can be easily produced because STEVIE can't yet keep
 * up with the normal auto-repeat rate in insert mode.
 */
#define	IBUFSZ	128

static long inbuf[IBUFSZ];	/* buffer for unread input */
static long *inptr = inbuf;	/* where to put next character */

/*
 * inchar() - get a character from the keyboard
 *
 * Certain special keys are mapped to values above 0x80. These
 * mappings are defined in keymap.h. If the key has a non-zero
 * ascii value, it is simply returned. Otherwise it may be a
 * special key we want to map.
 *
 * The ST has a bug involving keyboard input that seems to occur
 * when typing quickly, especially typing capital letters. Sometimes
 * a value of 0x02540000 is read. This doesn't correspond to anything
 * on the keyboard, according to my documentation. My solution is to
 * loop when any unknown key is seen. Normally, the bell is rung to
 * indicate the error. If the "bug" value is seen, we ignore it completely.
 */
int
inchar()
{
	for (;;) {
		long c, *p;

		/*
		 * Get the next input character, either from the input
		 * buffer or directly from TOS.
		 */
		if (inptr != inbuf) {	/* input in the buffer, use it */
			c = inbuf[0];
			/*
			 * Shift everything else in the buffer down. This
			 * would be cleaner if we used a circular buffer,
			 * but it really isn't worth it.
			 */
			inptr--;
			for (p = inbuf; p < inptr ;p++)
				*p = *(p+1);
		} else
			c = Crawcin();
	
		if ((c & 0xff) != 0)
			return ((int) c);
	
		switch ((int) (c >> 16) & 0xff) {
	
		case 0x62: return K_HELP;
		case 0x61: return K_UNDO;
		case 0x52: return K_INSERT;
		case 0x47: return K_HOME;
		case 0x48: return K_UARROW;
		case 0x50: return K_DARROW;
		case 0x4b: return K_LARROW;
		case 0x4d: return K_RARROW;
		case 0x29: return K_CGRAVE;	/* control grave accent */
		
		/*
		 * Occurs due to a bug in TOS.
		 */
		case 0x54:
			break;
		/*
		 * Add the function keys here later if we put in support
		 * for macros.
		 */
	
		default:
			beep();
			break;
	
		}
	}
}

/*
 * get_inchars - snarf away any pending console input
 *
 * If the buffer overflows, we discard what's left and ring the bell.
 */
static void
get_inchars()
{
	while (Cconis()) {
		if (inptr >= &inbuf[IBUFSZ]) {	/* no room in buffer? */
			Crawcin();		/* discard the input */
			beep();			/* and sound the alarm */
		} else
			*inptr++ = Crawcin();
	}
}

void
outchar(c)
char	c;
{
	get_inchars();
	Cconout(c);
}

void
outstr(s)
register char	*s;
{
	get_inchars();
	Cconws(s);
}

/*
 * flushbuf() - a no-op for TOS
 */
void
flushbuf()
{
}

#define	BGND	0
#define	TEXT	3

/*
 * vbeep() - visual bell
 */
static void
vbeep()
{
	int	text, bgnd;		/* text and background colors */
	long	l;

	text = Setcolor(TEXT, -1);
	bgnd = Setcolor(BGND, -1);

	Setcolor(TEXT, bgnd);		/* swap colors */
	Setcolor(BGND, text);

	for (l=0; l < 5000 ;l++)	/* short pause */
		;

	Setcolor(TEXT, text);		/* restore colors */
	Setcolor(BGND, bgnd);
}

void
beep()
{
	if (P(P_VB))
		vbeep();
	else
		outchar('\007');
}

/*
 * remove(file) - remove a file
 */
void
remove(file)
char *file;
{
	Fdelete(file);
}

/*
 * rename(of, nf) - rename existing file 'of' to 'nf'
 */
void
rename(of, nf)
char	*of, *nf;
{
	Fdelete(nf);		/* if 'nf' exists, remove it */
	Frename(0, of, nf);
}

void
windinit()
{
	if (Getrez() == 0)
		Columns = 40;		/* low resolution */
	else
		Columns = 80;		/* medium or high */

	P(P_LI) = Rows = 25;

	Cursconf(1,NULL);
}

void
windexit(r)
int r;
{
	exit(r);
}

void
windgoto(r, c)
int	r, c;
{
	outstr("\033Y");
	outchar(r + 040);
	outchar(c + 040);
}

/*
 * System calls or library routines missing in TOS.
 */

void
sleep(n)
int n;
{
	int k;

	k = Tgettime();
	while ( Tgettime() <= k+n )
		;
}

void
delay()
{
	long	n;

	for (n = 0; n < 8000 ;n++)
		;
}

int
system(cmd)
char	*cmd;
{
	char	arg[1];

	arg[0] = (char) 0;	/* no arguments passed to the shell */

	if (Pexec(0, cmd, arg, 0L) < 0)
		return -1;
	else
		return 0;
}

#ifdef	SOZOBON

FILE *
fopenb(fname, mode)
char	*fname;
char	*mode;
{
	char	modestr[10];

	sprintf(modestr, "%sb", mode);

	return fopen(fname, modestr);
}

#endif

#ifndef	SOZOBON
/*
 * getenv() - get a string from the environment
 *
 * Both Alcyon and Megamax are missing getenv(). This routine works for
 * both compilers and with the Beckemeyer and Gulam shells. With gulam,
 * the env_style variable should be set to either "mw" or "gu".
 */
char *
getenv(name)
char *name;
{
	extern long _base;
	char *envp, *p;

	envp = *((char **) (_base + 0x2c));

	for (; *envp ;envp += strlen(envp)+1) {
		if (strncmp(envp, name, strlen(name)) == 0) {
			p = envp + strlen(name);
			if (*p++ == '=')
				return p;
		}
	}
	return (char *) 0;
}
#endif

/*
 * mktemp() - quick hack since there isn't one here
 */
char *
mktemp(name)
char	*name;
{
	int	num;		/* pasted into the string to make it unique */
	char	cbuf[7];
	char	*s;		/* where the X's start in name */
	int	fd;

	if ((s = strchr(name, 'X')) == NULL)	/* needs to be an X */
		return (char *) NULL;

	if (strlen(s) != 6)			/* should be 6 X's */
		return (char *) NULL;

	for (num = 0; num < 1000 ;num++) {
		sprintf(cbuf, "%06d", num);
		strcpy(s, cbuf);
		if ((fd = open(name, 0)) < 0)
			return name;
		close(fd);
	}
	return (char *) NULL;
}

void
doshell(cmd)
char	*cmd;
{
	if (cmd == NULL) {
		emsg("Sorry, no interactive shell");
		return;
	}
	system(cmd);
	wait_return();
}

#define	PSIZE	128

/*
 * fixname(s) - fix up a dos name
 *
 * Takes a name like:
 *
 *	\x\y\z\base.ext
 *
 * and trims 'base' to 8 characters, and 'ext' to 3.
 */
char *
fixname(s)
char	*s;
{
	char	*strchr(), *strrchr();
	static	char	f[PSIZE];
	char	base[32];
	char	ext[32];
	char	*p;
	int	i;

	strcpy(f, s);

	for (i=0; i < PSIZE ;i++)
		if (f[i] == '/')
			f[i] = '\\';

	/*
	 * Split the name into directory, base, extension.
	 */
	if ((p = strrchr(f, '\\')) != NULL) {
		strcpy(base, p+1);
		p[1] = '\0';
	} else {
		strcpy(base, f);
		f[0] = '\0';
	}

	if ((p = strchr(base, '.')) != NULL) {
		strcpy(ext, p+1);
		*p = '\0';
	} else
		ext[0] = '\0';

	/*
	 * Trim the base name if necessary.
	 */
	if (strlen(base) > 8)
		base[8] = '\0';
	
	if (strlen(ext) > 3)
		ext[3] = '\0';

	/*
	 * Paste it all back together
	 */
	strcat(f, base);
	strcat(f, ".");
	strcat(f, ext);

	return f;
}
