#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>
#include    <jctype.h>
#include    <dos.h>

#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)

#define BUF_MAX 1024			/* 256,512,1024,2048... */
#define BUF_MSK (BUF_MAX-1)
#define LIN_MAX (128-2)

static  int     his_pos=0;
static  int     his_top=0;
static  int     his_old=0;
static  int     his_blk=0;
static  char    *his_lin;
static  char    his_buf[BUF_MAX];
static  int     his_len[2]={ 0,0 };
static  char    his_tmp[2][LIN_MAX+2];

static	int	ungetbuf=ERR;

#define	PUTC	putch

static	void	BEEP()
{
    PUTC(0x07);
}
static	int	GETCH()
{
    int     ch;

    if ( (ch = ungetbuf) != ERR ) {
	ungetbuf = ERR;
	return ch;
    }
    return getch();
}
static	void	UNGETCH(int ch)
{
    ungetbuf = ch;
}
static	void    FLUSH()
{
}
static	void    REPCHR(ch,n)
int     ch,n;
{
    while ( n-- > 0 )
        PUTC(ch);
}
static	void    BAKSPC(n)
int     n;
{
    while ( n-- > 0 )
        PUTC(0x08);
}
static	void	PUTS(str,n)
char	*str;
int	n;
{
    while ( n-- > 0 )
	PUTC(*(str++));
}
static	int     iskan(p)
char    *p;
{
    if ( iskanji(*p) && iskanji2(*(p+1)) )
        return TRUE;
    else
        return FALSE;
}
static	int     kan_pos(p,len)
register char *p;
int     len;
{
    int     i,n;

    for ( i = 0 ; *p != '\0' && i < len ; ) {
        n = (iskan(p) ? 2:1);
        if ( (i + n) > len )
            break;
        i += n;
        p += n;
    }
    return i;
}
static  void    his_set(str,len)
char    *str;
int     len;
{
    int     c;

    while ( len-- > 0 && *str != '\0' ) {
        his_buf[his_pos++] = *(str++);
        his_pos &= BUF_MSK;
        if ( his_pos == his_top ) {
            do {
                c = his_buf[his_top++];
                his_top &= BUF_MSK;
            } while ( his_top != his_pos && c != '\n' );
        }
    }
}
static  int     his_next(pos)
int     pos;
{
    int     c;

    while ( pos != his_pos ) {
        c = his_buf[pos++];
        pos &= BUF_MSK;
        if ( c == '\n' )
            break;
    }
    return pos;
}
static  int     his_back(pos)
int     pos;
{
    if ( pos == his_top )
        return pos;

    pos = (pos - 1) & BUF_MSK;
    while ( pos != his_top ) {
        pos = (pos - 1) & BUF_MSK;
        if ( his_buf[pos] == '\n' ) {
            pos = (pos + 1) & BUF_MSK;
            break;
        }
    }
    return pos;
}
static  int     his_get(str,pos,off,max)
char    *str;
int     pos;
int     off;
int     max;
{
    int     i,n;

    i = 0;
    while ( i < max && pos != his_pos ) {
        if ( his_buf[pos] == '\n' )
            break;
        else if ( iskanji(his_buf[pos]) &&
                  iskanji2(his_buf[(pos+1)&BUF_MSK]) )
            n = 2;
        else
            n = 1;

        if ( (i + n) > max )
            break;

        if ( i >= off ) {
            i += n;
            while ( n-- > 0 ) {
                *(str++) = his_buf[pos++];
                pos &= BUF_MSK;
            }
        } else {
            pos = (pos + n) & BUF_MSK;
            while ( n-- > 0 ) {
                if ( ++i > off )
                    *(str++) = ' ';
                else
                    str++;
            }
        }
    }
    return (i > off ? i:off);
}
static  int     his_cmp(pos,str,len)
int     pos;
char    *str;
int     len;
{
    while ( len > 0 && *str != '\0' ) {
        if ( pos == his_pos || his_buf[pos++] != *(str++) )
            return FALSE;
        pos &= BUF_MSK;
        len--;
    }
    return TRUE;
}
char    *input(buf, max)
char	*buf;
int	max;
{
    int     ch,och,n,i;
    int     pos,len,his,cps;
    char    *p;

    och = ch = 0;
    pos = len = cps = 0;
    his = his_pos;
    his_lin = his_tmp[his_blk];

    if ( max > LIN_MAX )
	max = LIN_MAX;

    if ( --max < 0 ) {
	his_lin[0] = '\n';
	len = 1;
	goto ENDOF;
    }

    for ( ; ; ) {
	och = ch;

        if ( (ch = GETCH()) == 0x1B )
	    ch = (ch << 8) | GETCH();

        switch(ch) {
        case 0x08:
            if ( pos > 0 ) {
                pos = kan_pos(his_lin,pos - 1);
                p = &(his_lin[pos]);
                n = (iskan(p) ? 2:1);
                memcpy(p,p + n,len - pos - n);
                len -= n;

                BAKSPC(n);
                PUTS(p,len - pos);
                REPCHR(' ',n);
                BAKSPC(len - pos + n);
            }
            break;

        case 0x1B56:
            if ( pos < len ) {
                p = &(his_lin[pos]);
                n = (iskan(p) ? 2:1);
                memcpy(p,p + n,len - pos - n);
                len -= n;

                PUTS(p,len - pos);
                REPCHR(' ',n);
                BAKSPC(len - pos + n);
            }
            break;

        case 0x1C:
            if ( pos < len ) {
                p = &(his_lin[pos]);
                n = (iskan(p) ? 2:1);
                pos += n;

                while ( n-- > 0 )
                    PUTC(*(p++));
            }
            break;

        case 0x1D:
            if ( pos > 0 ) {
                pos = kan_pos(his_lin,pos - 1);
                p = &(his_lin[pos]);
                n = (iskan(p) ? 2:1);

                BAKSPC(n);
            }
            break;

	case 0x1E:
	    for ( ; ; ) {
		if ( his == his_top ) {
		    BEEP();
		    break;
		}

		his = his_back(his);
		cps = ((och == 0x1F || och == 0x1E) ? cps:pos);
		if ( !his_cmp(his,his_lin,cps) )
		    continue;

		BAKSPC(pos);
                REPCHR(' ',len);
                BAKSPC(len);

                pos = len = his_get(his_lin,his,0,max);

                PUTS(his_lin,len);
                BAKSPC(len - pos);
		break;
            }
            break;

        case 0x1F:
	    for ( ; ; ) {
		if ( his == his_pos ) {
		    BEEP();
		    break;
		}

		his = his_next(his);
		cps = ((och == 0x1F || och == 0x1E) ? cps:pos);
		if ( !his_cmp(his,his_lin,cps) )
		    continue;

                BAKSPC(pos);
                REPCHR(' ',len);
                BAKSPC(len);

                pos = len = his_get(his_lin,his,0,max);

                PUTS(his_lin,len);
                BAKSPC(len - pos);
		break;
            }
            break;

	case 0x16:
        case 'R'-'@':
            if ( his != his_top ) {
                his = his_back(his);

                REPCHR(' ',len - pos);
                BAKSPC(len - pos);

                len = his_get(his_lin,his,pos,max);

                PUTS(&(his_lin[pos]),len - pos);
                BAKSPC(len - pos);
            } else
		BEEP();
            break;

	case 0x17:
        case 'E'-'@':
            if ( his != his_pos ) {
                his = his_next(his);

                REPCHR(' ',len - pos);
                BAKSPC(len - pos);

                len = his_get(his_lin,his,pos,max);

                PUTS(&(his_lin[pos]),len - pos);
                BAKSPC(len - pos);
            } else
		BEEP();
            break;

	case 0x1B55:
        case 'A'-'@':
            p = &(his_lin[len]);
            while ( len < max && *p != '\n' && *p != '\0' ) {
                len++;
                p++;
            }
            PUTS(&(his_lin[pos]),len - pos);
            pos = len;
            break;

        case 0x0D:
            his_lin[len++] = '\n';
            if ( len > 1 && !his_cmp(his_old,his_lin,len) ) {
		his_old = his_pos;
                his_set(his_lin,len);
	    }

            PUTS(&(his_lin[pos]),len - pos);
	    PUTC('\r');
            goto ENDOF;

        case 'X'-'@':
            BAKSPC(pos);
            REPCHR(' ',len);
            BAKSPC(len);
            pos = len = 0;
            break;

	case 0x0B:
        case 'D'-'@':
            BAKSPC(pos);
            REPCHR(' ',len);
            BAKSPC(len);

            his_len[his_blk] = len;
            his_blk ^= 1;
            his_lin = his_tmp[his_blk];
            pos = len = his_len[his_blk];

            PUTS(his_lin,len);
            break;

        default:
            if ( iskanji(ch) ) {
                n = GETCH();
                if ( !iskanji2(n) ) {
                    UNGETCH(n);
                    n = 1;
                } else {
                    ch = (ch << 8) | n;
                    n = 2;
                }
            } else
                n = 1;

            if ( (len + n ) >= max ) {
                BEEP();
                break;
            }

            if ( pos < len ) {
                p = &(his_lin[len + (n - 1)]);
                for ( i = len - pos ; i > 0 ; i--,p-- )
                    *p = *(p - n);
            }

            p = &(his_lin[pos]);
            if ( n == 1 ) {
                his_lin[pos++] = ch;
            } else {
                his_lin[pos++] = ch >> 8;
                his_lin[pos++] = ch;
            }
            len += n;

            PUTS(p,len - pos + n);
            BAKSPC(len - pos);
            break;
        }
        FLUSH();
    }

ENDOF:
    his_len[his_blk] = len;
    his_lin[len] = '\0';
    strcpy(buf, his_lin);
    return buf;
}
