/* Copyright (c) 1990,1991,1992,1993 Chris and John Downey */

/***

* program name:
    xvi
* function:
    Portable version of UNIX "vi" editor, with extensions.
* module name:
    tos.c
* module function:
    System interface module for the Atari ST.
* history:
    STEVIE - ST Editor for VI Enthusiasts, Version 3.10
    Originally by Tim Thompson (twitch!tjt)
    Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
    Heavily modified by Chris & John Downey
    Atari port modified by Steve Found for LATTICE C V5.0
    Last modified by Martin Guy

***/

#include "xvi.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 we 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 */

/*
 * These are globals which are set by the OS-specific module,
 * and used for various purposes throughout the rest of xvi.
 */
int	Rows;				/* Number of Rows and Columns */
int	Columns;			/* in the current window. */

static	char	tmpbuff[L_tmpnam];
static	char	*logscreen;

/*
 * 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.
 *
 * "timeout" parameter not handled at the moment.
 */
int
inchar(timeout)
long	timeout;
{
    int		k, s;

    for (;;) {
	long c;

	/*
	 * Get the next input character, either from the input
	 * buffer or directly from TOS.
	 */
	if (inptr != inbuf) {	/* input in the buffer, use it */
	    long	*p;

	    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();
	}

	k = (c & 0xFF);
	s = (c >> 16) & 0xFF;
	if (k != 0)
	    break;

	switch (s) {

	case 0x62: k = K_HELP; break;
	case 0x61: k = K_UNDO; break;
	case 0x52: k = K_INSERT; break;
	case 0x47: k = K_HOME; break;
	case 0x48: k = K_UARROW; break;
	case 0x50: k = K_DARROW; break;
	case 0x4b: k = K_LARROW; break;
	case 0x4d: k = K_RARROW; break;
	case 0x29: k = K_CGRAVE; break; /* 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:
	    k = 0;
	    alert();
	    break;
	}

	if (k != 0) {
	    break;
	}
    }
    return(k);
}

/*
 * 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 */
	    alert();			/* and sound the alarm */
	} else {
	    *inptr++ = Crawcin();
	}
    }
}

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

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

#define BGND	0
#define TEXT	1

/*
 * 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, (short) bgnd);		/* swap colors */
    Setcolor(BGND, (short) text);

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

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

void
alert()
{
    if (Pb(P_vbell))
	vbeep();
    else
	outchar('\007');
}

bool_t
can_write(file)
char	*file;
{
    if (access(file, 0) == -1 || access(file, 2) == 0) {
	return(TRUE);
    } else {
	return(FALSE);
    }
}

/*
 * remove(file) - remove a file
 * I don't know whether success is detectable here - cmd.
 */
#ifndef LATTICE
bool_t
remove(file)
char	*file;
{
    Fdelete(file);
    return(TRUE);
}
#endif

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

    Rows = 25;

    Cursconf(1, NULL);
}

void
sys_startv()
{
}

void
sys_endv()
{
}

void
sys_exit(r)
int	r;
{
    tty_goto(25, 0);
    outchar('\n');
    exit(r);
}

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

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

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

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

void
Wait200ms()
{
    long	n;

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

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

    arg[0] = '\0';		/* no arguments passed to the shell */

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

#ifdef	MEGAMAX
char *
strchr(s, c)
char	*s;
int	c;
{
    do {
	if (*s == c)
	    return(s);
    } while (*s++);

    return(NULL);
}
#endif /* MEGAMAX */

/*
 * 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".
 */
#ifndef LATTICE
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(NULL);
}
#endif

/*
 * Set the specified colour. Just does standout/standend mode for now.
 * Optimisation here to avoid setting standend when we aren't in
 * standout; assumes calling routines are well-behaved (i.e. only do
 * screen movement in P_colour) or some terminals will write garbage
 * all over the screen.
 */
void
set_colour(c)
int	c;
{
    static int	oldc = -1;

    if (c == oldc)
	return;

    if (c != 0)
	outstr("\033p");
    else
	outstr("\033q");

    oldc = c;
}

/*
 * tempfname - Create a temporary file name.
 */
char *
tempfname(srcname)
char *srcname;
{
    return(tmpnam(tmpbuff));
}

#ifndef ABS
#	define ABS(n) ((n) < 0 ? -(n) : (n))
#endif

/*
 * Scroll the ST Monochrome screen.
 */
void
st_scroll(start, end, nlines)
unsigned start, end;
int nlines;
{
    char *s, *e, *d;
    char *s2;
    size_t bytes, clr_bytes;

    if (ABS(nlines) > (end + 1 - start) || nlines == 0)
	return;

    invis_cursor();

    if (nlines > 0) {
	d = logscreen + (start * 1280);
	s = d + (nlines * 1280);
	s2 = d + bytes;
    } else /* (nlines < 0) */ {
	nlines = -nlines;
	s2 = s = logscreen + (start * 1280);
	d = s + (nlines * 1280);
    }

    /*
     * Move the appropriate lines up or down.
     */
    bytes = (end + 1 - start - nlines) * 1280;
    memmove(d, s, bytes);

    /*
     * Clear the ones left behind.
     */
    clr_bytes = ((end + 1 - start) * 1280) - bytes;
    (void) memset(s2, 0, clr_bytes);

    vis_cursor();
}
