/*
** FILE: input.c
**
** DESC: doskey-like read() function
**
** (C) Rainer Schnitker 1994
**
** 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 (at your option) any later version.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <malloc.h>

/*
** the read function, arguments:
** input buffer, size of buffer, boolean (newline after <return> pressed)
** return size of input
*/

int get_input(char *, int, int);

/*
** this function reads a key from the console
** low byte: ascii
** high byte: scan code, if ascii = 0
*/
static int _read_keybrd(void)
{
    int c = getch();
    if (c == 0 || c == 0xE0)
	c = getch() << 8;
    return c;
}

/* history strings can contain 256 entries */
static char * oldstr[256];
static int istr = 0;

/* get previus string */
static void cpy_up_string(char *str)
{
    if (istr == 0 || !str)
	return;

    istr--;
    if (istr == 0) {
	if (oldstr[255])
	    istr = 255;
    }
    if (oldstr[istr])
	strcpy(str, oldstr[istr]);
    else
	istr ++;
}

static void cpy_down_string(char *str)
{
    if (istr == 0 || !str)
	return;

    istr++;
    if (istr == 256)
	istr = 1;

    if (oldstr[istr])
	strcpy(str, oldstr[istr]);
    else
	istr--;
}

/* put next string */
static void cpy_new_strcpy(char *str)
{
    static int line_index = 0;

    line_index++;
    if (line_index >= 256)
	line_index = 1;
    if (! oldstr[line_index])
	oldstr[line_index] = malloc(strlen(str)+1);
    else
	oldstr[line_index] = realloc(oldstr[line_index], strlen(str)+1);
    if (oldstr[line_index])
	strcpy(oldstr[line_index], str);
    istr = line_index + 1;
}

int get_input(char *str, int size, int opt_newline)
{
    char ins_flag;
    int strpos;
    int strend;

    unsigned int key, scan;
    unsigned char ascii = 0;
    int i;

    strpos = 0;
    strend = 0;
    ins_flag = 1;
    memset(str, 0, size);

    for (;;) {
	key = _read_keybrd();

	scan = (key >> 8) & 0xFF;
	ascii = (unsigned char) (key & 0xFF);

	if (ascii == 0xE0)
	    ascii = 0;

	if (ascii >= 32) {	/* show char */
	    if (strpos == strend) {
		putchar(ascii);
		*(str + strpos) = ascii;
		strend++;
	    } else if (ins_flag) {	/* shift right chars */
		for (i = strend; i >= strpos; --i)
		    *(str + i + 1) = *(str + i);
		*(str + strpos) = ascii;
		printf("%s",str + strpos);
		for (i = strend; i > strpos; i--)
		    putchar('\b');
		strend++;
	    } else {
		putchar(ascii);
		*(str + strpos) = ascii;
	    }
	    strpos++;
	    fflush(stdout);
	    continue;
	}
	/* backspace or delete */
	else if ((ascii == '\b' && strpos != 0)
		 || (scan == 0x53 && strend != strpos)) {
	    if (ascii == '\b') {
		putchar('\b');
		strpos--;
	    }
	    for (i = strpos; i < strend; ++i)
		*(str + i) = *(str + i + 1);
	    strend--;
	    *(str + strend) = '\0';
	    printf("%s",str + strpos);
	    putchar(' ');
	    putchar('\b');
	    for (i = strend; i > strpos; i--)
		putchar('\b');
	    fflush(stdout);
	    continue;
	} else if (ascii == 13) {   /* return */
	    if (opt_newline)
		putchar('\n');
	    while (strend > 0 && *(str + strend - 1) == ' ')
		strend--;
	    *(str + strend) = 0;
	    if (strend>0)
		cpy_new_strcpy(str);
	    break;
	} else if (ascii == 27) {	/* ESC */
	    for (; strpos < strend; strpos++)
		putchar(' ');
	    for (; strpos != 0; strpos--) {
		putchar('\b');
		putchar(' ');
		putchar('\b');
	    }
	    memset(str, 0, size);
	    strpos = strend = 0;
	    fflush(stdout);
	    continue;
	} else if (scan == 0x4b && strpos != 0) {
	    putchar('\b');
	    strpos--;
	    fflush(stdout);
	    continue;
	} else if (scan == 0x48) {	/* up */
	    for (; strpos != 0; strpos--) {
		putchar('\b');
		putchar(' ');
		putchar('\b');
	    }
	    cpy_up_string(str);
	    printf("%s",str);
	    strpos = strend = strlen(str);
	    fflush(stdout);
	    continue;
	} else if (scan == 0x50) {	/* down */
	    for (; strpos != 0; strpos--) {
		putchar('\b');
		putchar(' ');
		putchar('\b');
	    }
	    cpy_down_string(str);
	    printf("%s",str);
	    strpos = strend = strlen(str);
	    fflush(stdout);
	    continue;
	} else if (scan == 0x4d && strpos < strend) {	/* -> */
	    putchar(*(str + strpos));
	    strpos++;
	    fflush(stdout);
	    continue;
	} else if (scan == 0x47) {	/* home */
	    for (; strpos != 0; strpos--)
		putchar('\b');
	    strpos = 0;
	    fflush(stdout);
	    continue;
	} else if (scan == 0x4F) {	/* end */
	    printf("%s",str + strpos);
	    strpos = strend;
	    fflush(stdout);
	    continue;
	} else if (scan == 0x52)/* ins */
	    ins_flag ^= 1;
	fflush(stdout);
    }
    return strend;
}
