/* 
 * console input/output routines.
 * written by Eric R. Smith and placed in the public domain
 */

/*
 * BUGS: assumes that all tty handles >= 0 are console handles.
 * If we have some handles >= 0 not attached to the console,
 * and some other handles attached to a different device, we might
 * get characters mixed up. This will generally not be a problem,
 * since earlier versions of the library forbid any non-console
 * devices entirely.
 */

#include <osbind.h>
#include <support.h>
#include <stdlib.h>
#include <ioctl.h>
#include <tchars.h>
#include <signal.h>
#include <unistd.h>
#ifndef _COMPILER_H
#include <compiler.h>
#endif

extern int _console_dev;	/* in main.c */
int __check_signals = 0;

#define KBUFSIZ 80
#define NUMDEV	3
	/* 0 == prn:, 1 == aux:, 2 == con: */

typedef struct _buffer {
	short head, tail;
	long  buffer[KBUFSIZ];
} k_buf;

k_buf in_buf[NUMDEV];

#define in_dev(dev) ((dev)>2 ? _console_dev : (dev))

#define IN_BUF(dev) (&in_buf[in_dev(dev)])

/*
 * what handle means:: 0-2: BIOS handle, 3: stdout, 4: stderr
 */
static short LOOKUP __PROTO((short handle));
static long raw_in __PROTO((int dev));
static long raw_instat __PROTO((int dev));
void flush_key_buff __PROTO((int dev));
#if 0
static void raw_out __PROTO((int dev, int c));
#endif

static short
#ifdef __STDC__
LOOKUP(short handle)
#else
LOOKUP(handle)
short handle;
#endif
{
	switch(handle) {
	case -3:
	case -2:
	case -1:
	case 0:
		return handle + 3;
	case 2:
		return 4;
	case 1:
		if (isatty(0)) return 3;
		if (isatty(2)) return 4;
		/* else fall through */
	default:
		return _console_dev;
	}
}

/*
 * raw i/o routines
 */

static long
raw_in(dev)
int dev;
{
  if (dev < 3)
    return Bconin (dev);
  else if (dev == 3)
    return Crawcin ();
  else
    return Cauxin ();
}

#if 0
static void
raw_out(dev, c)
int dev, c;
{
	if (dev < 3)
		Bconout(dev, c);
	else if (dev == 3)
		(void)Crawio(c);
	else
		Cauxout(c);
}
#endif

static long
raw_instat(dev)
int dev;
{
	return Bconstat(in_dev(dev));
}

/*
 * somewhat less raw i/o routines. The main difference is that these ones
 * will check for pending input before doing output, to see if a signal
 * needs to be raised. This is only done if __check_signals is non-zero;
 * signal() should set __check_signals when the user attempts to catch
 * SIGINT or SIGQUIT. We don't do this checking all the time because
 * the user may be typing ahead input for another program (e.g. if this
 * is a little utility of some sort) and we shouldn't steal keystrokes
 * unless necessary.
 */

unsigned int console_read_byte(handle)
int handle;
{
	k_buf *p;
	short i, j, dev;
	long r;

	dev = LOOKUP(handle);

	p = IN_BUF(dev);
	if ( p->head != (i = p->tail)) {
		j = p->tail + 1;
		if (j >= KBUFSIZ)
			j = 0;
		p->tail = j;
		r = p->buffer[i];
	}
	else
		r = raw_in(dev);

	return r;
}

int console_input_status(handle)
int handle;
{
	short dev;
	k_buf *p;

	dev = LOOKUP(handle);
	p = IN_BUF(dev);
	return (p->head != p->tail) || raw_instat(dev);
}

void
console_write_byte(handle, n)
int handle;
int n;
{
	char ch;

	if (__check_signals)
		flush_key_buff(handle);
	ch = n;
	(void)Fwrite(handle, 1L, &ch);
}

void flush_key_buff(fd)  
int fd;
{ 
    long c;
    short i, j, waiting = 0;
    unsigned char ch;
    k_buf *p;
    short dev;
    short handle;
    
    handle = fd;
    
    dev = LOOKUP(handle);
    p = IN_BUF(dev);
    
    while (raw_instat(dev) || waiting) 
    { 
	c = raw_in(dev);
	if (!(__ttymode & RAW)) 
	{
	    ch = c & 0xff;
	    if (ch == ('S'-'@')) { waiting = 1; }
	    else if (ch == ('Q'-'@')) { waiting = 0; }
	    else if (ch == (unsigned char)__tchars[TC_INTRC]) 
	    { 
		p->head = p->tail;
		raise(SIGINT);
	    }
	    else if (ch == (unsigned char)__tchars[TC_QUITC]) 
	    { 
		p->head = p->tail;
		raise(SIGQUIT);
	    }
	    else if (!waiting) 
	    { 
		i = p->head;
		j = i + 1;
		if (j >= KBUFSIZ) j = 0;
		if (j != p->tail) 
		{ 
		    p->buffer[i] = c;
		    p->head = j;
		}
	    }
	}
    }
}
