/* vim: set filetype=c:
 *
 * conio.c86  -  Console I/O routines for target system
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: conio.c86,v 1.6 2007/01/06 18:30:51 gkminix Exp $
 */


/* Include target system C headers */
#include <common.h86>
#include <i386/ctype.h86>
#include <i386/conio.h86>



/*
 * Pointer to a routine which prints a character using ANSI conversion
 */
void (* altout)(char) = NULL;



/*
 * Define input buffer
 */
#define INBUFSIZ	80

static char inbuf[INBUFSIZ];
static size_t inbufread = 0;
static size_t inbufpos = 0;




/*
 * Clear the screen
 */
static inline void clrscn(void)
{
  __u16 d0;

  /* We use the assembler to call the BIOS to do the work */
  __asm__ __volatile__(
	"movb $0x0F,%%ah\n\t"
	"int $0x10\n\t"
	"xorb %%ah,%%ah\n\t"
	"int $0x10"
	: "=&a" (d0)
	: /* no input */
	: "memory", "cc");
}



/*
 * Print a character onto the console
 */
char cputc(char ch)
{
  static size_t col = 0;

  if (altout != NULL)
	(*altout)(ch);
  else {
	if (ch == '\t') {
		while ((col & 7) != 0) {
			charout(' ');
			col++;
		}
	} else if (ch == '\n') {
		crlfout();
		col = 0;
	} else if (ch == '\r') {
		charout(ch);
		col = 0;
	} else if (ch == '\b' || ch == CHR_BELL) {
		charout(ch);
	} else if (ch == '\f') {
		clrscn();
		col = 0;
	} else {
		charout(ch);
		col++;
	}
  }
  return(ch);
}



/*
 * Print a string onto the console with a terminal CR/LF
 */
size_t cputs(char *s)
{
  size_t count = 0;

  while (*s) {
	count++;
	cputc(*s);
  }
  cputc('\n');
  return(count);
}



/*
 * Print a buffer onto the console
 */
size_t cwrite(char *buf, size_t len)
{
  size_t count = 0;

  while (count < len)
	cputc(buf[count++]);
  return(len);
}



/*
 * Read a line from the keyboard
 */
size_t cread(char *buf, size_t len)
{
  short ch = 0;
  size_t pos = 0;

  /* Check if buffer is valid */
  if (len == 0)
	return(0);

  /* Read a line from the keyboard with backspace editing */
  while (TRUE) {
	if (ch != CHR_BREAK) {
		ch = getkey(0);
		if (pos == 0 && (ch & 0xFF) == 0) {
			buf[0] = (char)((ch >> 8) | 0x80);
			return(1);
		}
		ch &= 0x7F;
	}
	if (ch == CHR_CR) {
		cputc('\n');
		buf[pos++] = '\n';
		return(pos);
	}
	if (!iscntrl((char)ch) && pos < (len - 1)) {
		buf[pos++] = (char)ch;
		cputc((char)ch);
	} else if ((ch == CHR_BREAK || ch == CHR_BS) && pos > 0) {
		cputc('\b');
		cputc(' ');
		cputc('\b');
		pos--;
	} else if (ch == CHR_BREAK)
		return(0);
	else
		cputc(CHR_BELL);
   }
}



/*
 * Read a character from the console (buffered)
 */
char cgetc(void)
{
  char ch;

  /* Read next line from keyboard */
  if (inbufpos >= inbufread) {
	inbufread = cread(inbuf, INBUFSIZ);
	inbufpos = 0;
  }

  /* Check if EOF */
  if (inbufread <= inbufpos)
	return(CHR_BREAK);

  /* Return next character or EOL */
  ch = inbuf[inbufpos++];
  if (inbufpos == inbufread)
	return('\r');
  return(ch);
}



/*
 * Put a character back into the input buffer
 */
char cungetc(char ch)
{
  if (inbufpos > 0)
	inbuf[--inbufpos] = ch;
  else if (inbufread == 0)
	inbuf[inbufread++] = ch;
  else
	ch = CHR_BREAK;
  return(ch);
}



/*
 * Flush the input buffer
 */
void cflush(void)
{
  inbufread = 0;
  inbufpos = 0;
}



/*
 * Read a string from the console
 */
char *cgets(char *s, size_t count)
{
  char *ret, ch;
  size_t i;

  ret = s;
  for (i = count - 1; i > 0; i--) {
	ch = cgetc();
	if (ch == CHR_BREAK) {
		if (s == ret)
			return(NULL);
		break;
	}
	*s++ = ch;
	if (ch == '\n')
		break;
  }
  *s = '\0';
  return(ret);
}

