/*****************************************************************************/
/* TEXT GETLINE SYSTEM for X680x0 V6.50 Copyright 1989-92,94,98 by AIG-SOFT */
/*****************************************************************************/
/* V5.0 : 複数行、自動改行対応 */
/* V6.0 : バッファー構成一新（個別オープン方式）、一部キー変更 */
/*	  入力可能文字、insertモード、Word削除/上書き処理バグとり */
/*	  ^Lコントロールコード入力制御反転 */
// V6.5 : ASK on/off対応のため、テンプレートをF1/F3からCTRL-B,UNDOへ移行 98/07/04

#include <stdio.h>
#include <string.h>
#include <stdlib.h>	// free
#include <iocslib.h>
#include "defines.h"
#include "doslibm.h"
#include "getline.h"
#include "ascreen.h"
#include "acmp.h"	// is24,kstrchar
#include "akey.h"
#include "amacro.h"

/*****************************************************************************/
/* 内部変数 */

static	unsint	p=0,pe=0,pe2=0,pe3=0;	/* バッファー内ポインター */
static	unsint	MLEN;		/* 入力最大長さ */
static	unsint	pmode;		/* 動作モード */
static	unsint	fins;		/* インサートモードフラグ  */
static	unsint	fsrch,fsrch2;	/* ヒストリーサーチモードフラグ */
static	unsint	floop;		/* ヒストリーループモードフラグ */
static	unsint	ftop;		/* ヒストリー内部ポインター */
static	unsint	*buff2,*buff3;	/* 内部バッファー */
static	_GETLNDT *g;		/* getline構造体アドレス */
static	int	stx,sty,endx;	/* V5.0 : 複数行対応 */

/*****************************************************************************/
/* GETLINE 内部処理関数群
   内部では全ての文字を２バイトで１文字として扱う。これにより２バイト文字、
   １バイト文字どちらであっても同じ処理でいいことになり処理が簡単になる。
   また内部処理と表示を分けることで1/2、1/4角文字のように内部２バイト表示
   １バイト、またコントロール文字のように内部１バイト表示２バイトの文字にも
   簡単に対応出来るようになった。ただ、内部バッファーから外部表現への変換が
   必要なため、エディター等で使うには若干の問題があるかもしれない。
*/
/*****************************************************************************/
/* チェック */
/*****************************************************************************/

static unsint CheckSPC()
/* check point space/tab */
{
unsint c;
	c=g->buffer[p];
	return(c==SPC||c==TAB);
}

static short BLen(unsint c)
/* バッファー内バイト数＋この文字のサイズ */
{
unsint i;
short len;
	len=pe+1+(c>=256); /* 基本長さ＋この文字のサイズ */
	for (i=0;i<pe;i++) {
		if (g->buffer[i]>=256) len++;
	}
	return(len);
}

static unsint CheckSize(unsint c)
/* 文字表示幅 */
/* 半角、１／２角、１／４角文字＝１ */
/* 全角文字、コントロール文字＝２ */
{
	return(2-((c<256 && c>=' ')||is24(c>>8)));
}

#define BCheckSize(point)	CheckSize(g->buffer[point])

static short BKlen2(unsint ps)
/* ポイント以降バッファー内容の表示長さを計算する */
{
unsint i;
short len=0;
	for (i=ps;i<pe;i++) len+=BCheckSize(i);
	return(len);
}

/*****************************************************************************/
/* カーソル移動 */
/*****************************************************************************/

static unsint PHome()
/* point home */
{
	if (p==0) return(1); /* 既に行頭にいる */
	p=0;
	return(0);
}

static unsint HomeSub()
/* 行頭へ */
{
unsint pb;
	pb=p;
	if (!PHome()) B_LOCATE(stx,sty);
	return(pb);
}

static unsint PEnd()
/* point end */
{
	if (p==pe) return(1); /* 既に行末にいる */
	p=pe;
	return(0);
}

static unsint PRight()
/* point right */
{
	if (p==pe) return(1); /* 移動不可：ポイントが行末にある */
	p++;
	return(0);
}

static unsint PLeft()
/* point left */
{
	if (p==0) return(1); /* 移動不可：ポイントが行頭にある */
	p--;
	return(0);
}

static unsint CsrCrlf()
/* 改行処理 */
{
int y,yy;
	y=csrlin();
	crlf();
	yy=csrlin();
	if (yy==y) { /* スクロールが起こった */
		sty--;
		#if 0
		/* スクロールで始点が消えるので強制リターンするの処理 */
		if (sty<0) return(-1);
		#endif
	}
	B_LOCATE(stx,yy);
	return(0);
}

static void CsrRight(unsint point)
/* point=移動前のポイント */
{
unsint l;
	l=BCheckSize(point);
	if (pos()+l>endx) CsrCrlf();
	else {
		curright();
		if (l==2) curright();
		if (pos()==endx && BCheckSize(point+1)==2) B_LOCATE(stx,csrlin()+1);
	}
}

static void CsrLeft(unsint point)
/* point=移動後のポイント */
{
unsint i;
	if (pos()!=stx) {
		curleft();
		if (BCheckSize(point)==2) curleft();
	} else { /* 行戻りをするとき：先頭から動かし直し */
		curoff();
		B_LOCATE(stx,sty); /* home */
		for (i=0;i<point;i++) CsrRight(i);
		curon();
	}
}

static void MoveCursor(unsint mode,unsint pb)
/* cursor move */
/* 動けることを確認してから呼び出すこと */
{
short i;
	if (mode==1) { /* front move : home , before word */
		for (i=(int)pb-1;i>=p;i--) CsrLeft(i);
	} else { /* end move : end , next word */
		for (i=pb;i<p;i++) CsrRight(i);
	}
}

static unsint EndSub()
/* 行末へ */
{
unsint pb;
	pb=p;
	if (!PEnd()) MoveCursor(2,pb);
	return(pb);
}

static unsint SkipSPWord(unsint dir,unsint mode)
/* skip word/space */
/* dir =0:left , 1:right */
/* mode=0:word , 1=space */
{
	while (CheckSPC()==mode) {
		if (dir==0? PLeft():PRight()) return(1); /* 移動不可：行末、行頭にいる */
	}
	return(0);
}

static unsint BeforeWord()
/* point move to before word */
{
unsint i=0;
	if (p==pe) i++; /* 今行末にいる時はワードスキップしない */
	for (;i<3;i++) {
		/* (skip word 1 ->) skip space -> skip word 2 */
		/* i%2 =  0     ->     1       ->    0        */
		if (SkipSPWord(0,i%2)) return(1);
		/* スキップしている間に行頭にきてしまった */
	}
	/* skipした後は単語の１文字前にポイントがあるから */
	p++;
	return(0);
}

static unsint AfterWord()
/* point move to after word */
{
unsint i;
	for (i=0;i<=1;i++) {
		/* skip word 1 -> skip space */
		/* i   =  0    ->     1      */
		if (SkipSPWord(1,i)) return(1);
		/* スキップしている間に行末にきてしまった */
	}
	return(0);
}

static unsint PDown()
/* カーソル下移動 */
{
unsint i;
int x,y;
	curoff();
	cursor(&x,&y);
	i=p;
	do {
		CsrRight(i++);
		if (csrlin()==y+1 && pos()>=x) {
			p=i;
			goto DNS;
		}
	} while (i<=pe);
	B_LOCATE(x,y);
    DNS:;
	curon();
	return(1);
}

static unsint PUp()
/* カーソル上移動 */
{
sint i;
int x,y;
	curoff();
	cursor(&x,&y);
	i=p;
	do {
		CsrLeft(--i);
		if (csrlin()==y-1 && pos()<=x) {
			p=i;
			goto UPS;
		}
	} while (i>=0);
	B_LOCATE(x,y);
    UPS:;
	curon();
	return(0);
}

/*****************************************************************************/
/* 初期化 */
/*****************************************************************************/

static unsint OpenBuffer(unchar *lines)
/* 先行入力文字列を内部バッファーに展開する */
/* klen()文字数を返す */
{
unsint c,len;
	len=pe=0;
	while ((c=*lines++)!=0) {
		if (check_kanji1(c)) c=(c<<8)|*lines++;
		len+=(1+c>=256);
		if (len>MLEN) break;
		g->buffer[pe++]=c;
	}
	PHome();
	return(pe);
}

static unsint CloseBuffer(unchar *lines)
/* 内部バッファーを文字列にする */
{
unsint c,i=0,j=0;
	while (i<pe) {
		c=g->buffer[i++];
		if (c>=256) lines[j++]=(c>>8);
		lines[j++]=(unchar)(c);
	}
	lines[j]=0;
	return(j); /* 文字数を返す */
}

static void SaveBuffer(unsint no)
/* copy buffer to buff2/3 */
{
unsint i;
unsint *buf;
	if (no==2) {
		pe2=pe;
		buf=buff2;
	} else {
		pe3=pe;
		buf=buff3;
	}
	for (i=0;i<pe;i++) buf[i]=g->buffer[i];
}

static void InsSet()
/* insert mode set from pmode-BIT7 */
{
	if (pmode&BIT7) { /* insert mode */
		fins=1;
	} else { /* overwrite mode */
		fins=0;
	}
	LEDMOD(4,fins); /* insert LED on/off */
}

static void Init()
/* 初期化 */
{
	InsSet();
	fsrch=0;
	fsrch2=1; /* 区別しないが初期 */
}

static unchar pkeys[12][6];	/* 全再定義キー待避エリア */

static void KeySet()
/* 再定義可能キー待避＆セット */
{
int i;
	for (i=21;i<=32;i++) { // RollUP〜UNDO
		FNCKEYGT(i,&pkeys[i-21][0]);
	}
	/* キー定義 */
	keysetc(K_RUP,  0x16);	/* ROLL-UP   */
	keysetc(K_RDOWN,0x17);	/* ROLL-DOWN */
	keysetc(K_INS,  0x09);	/* INS   */
	keysetc(K_DEL,  0x07);	/* DEL   */
	keysetc(K_HOME, 0x11);	/* HOME  */
	keysetc(K_UP,   0x1e);	/* UP    */
	keysetc(K_LEFT, 0x1d); 	/* LEFT  */
	keysetc(K_RIGHT,0x1c);	/* RIGHT */
	keysetc(K_DOWN, 0x1f);	/* DOWN  */
	keysetc(K_CLR,  0x19);	/* CLR   */
	keysetc(K_UNDO, 0x0e23);/* UNDO : F3相当 */
//	keysetc(K_F1,   0x0e21);/* F1    */// -> CTRL-B
//	keysetc(K_F3,   0x0e23);/* F3    */
}

static void KeyReset()
/* 全再定義可能キー復帰 */
{
int i;
	for (i=21;i<=32;i++) { // RollUP〜UNDO
		FNCKEYST(i,&pkeys[i-21][0]);
	}
}

/*****************************************************************************/
/* ポイント変更 */
/*****************************************************************************/

static void PInc(unsint *point)
/* pointer increment */
{
	(*point)++;
	if (*point==g->bsize) *point=0;
}

static void PDec(unsint *point)
/* pointer decrement */
{
	if (*point==0) *point=g->bsize;
	(*point)--;
}

static void PInc2(unsint *point)
{
	PInc(point);
	if (*point==g->top) ftop=1;
}

/*****************************************************************************/
/* 挿入 */
/*****************************************************************************/

static unsint Ins(unsint mode)
/* １文字挿入 */
/* mode=0:空白挿入、1:ポイント文字挿入 */
{
unsint i;
	if (p==pe) return(1); /* 行末にいる：挿入する必要無し */
	if (BLen(mode==0? ' ':g->buffer[p])>MLEN) return(2); /* line over */
	for (i=pe-1;i>=p;i--) g->buffer[i+1]=g->buffer[i];
	if (mode==0) g->buffer[p]=' '; /* 空白が挿入される */
	/* そうでない時はポイントにあった文字がそのまま残る */
	pe++;
	return(0);
}

/*****************************************************************************/
/* 表示 */
/*****************************************************************************/

static void CPrint2(unint c)
/* １文字表示 */
{
short len;
int x;
	if (pmode&BIT0) { /* password enter mode */
		c='*';
		if (CheckSize(c)==2) c=0x2a2a; /* '*'２つ */
	}
	else
	if (c<' ') c=('^'<<8)|('@'+c); /* コントロール文字 */
	
	len=CheckSize(c);
	x=pos();
	if (x==endx && len==2) { /* 自動改行 */
		/* カーソルを動かなさいように今の１文字分を消す */
		/* Ｘ６８ ＢＩＯＳのバグを回避するためにこのようにする */
		/* （行末で１文字表示した時カーソルが次の行の先頭に来ない）*/
		fill_area(endx*8,csrlin()*16,8,16,B_COLOR(-1)); /* 強引な消し方 */
		CsrCrlf();
	} else {
		if (x+len>endx+1) CsrCrlf(); /* 実は＋１がミソ */
	}
	if (c>256) {
		if ((EX_PUTC16(c>>8)>>16)>endx) CsrCrlf(); /* これも結構重要 */
	}
	if ((EX_PUTC16(c&0xff)>>16)>endx) CsrCrlf(); /* これも結構重要 */
}

static void CPrint(unint c)
/* １文字表示＆バッファー書き込み */
{
	CPrint2(c);
	g->buffer[p++]=c; /* ポインターも進める */
}

static void LinePrint(unsint ps,short olen)
/* ポイント以降表示 */
{
short i;
	/* とりあえず表示できる分表示する */
	for (i=ps;i<pe;i++) {
		CPrint2(g->buffer[i]);
		olen-=BCheckSize(i); /* 前表示した分と今表示した分の差 */
	}
	/* 前の表示が今表示したものより長かった時、長い部分＝上書きされない部分を消す */
	if (olen>0) {
	    int x,y;
		cursor(&x,&y);
		for (i=0;i<olen;i++) {
			if (pos()+1>endx+1) CsrCrlf(); /* 実は＋１がミソ */
			/* これも結構重要 */
			if ((EX_PUTC16(' ')>>16)>endx) CsrCrlf();
		}
		B_LOCATE(x,y);
	} /* olen<0なら消さない */
	/* カーソルをポイントに戻す */
	MoveCursor(1,pe); /* pe -> p */
}

static void AllPrint(unsint olen)
/* １行全表示 */
/* homeへの移動はこのルーチン呼び出し前＝行の修正前に行うこと */
{
	InsSet();
	LinePrint(0,olen); /* 全行表示 */
}

/*****************************************************************************/
/* 削除 */
/*****************************************************************************/

static unsint Del()
/* １文字削除 */
{
unsint i;
	if (p==pe) return(1); /* 削除不可：行末にいる */
	for (i=p+1;i<=pe;i++) g->buffer[i-1]=g->buffer[i];
	pe--;
	return(0);
}

static void DelSub()
{
short len;
	len=BKlen2(p);
	if (!Del()) {
		LinePrint(p,len);
		g->rhist=1;
	}
}

static unsint DelBefore()
/* ポイント前全削除 */
{
unsint i;
	if (p==0) return(1); /* 削除不可：行頭にいる */
	for (i=p;i<=pe;i++) g->buffer[i-p]=g->buffer[i];
	pe-=p;
	PHome();
	return(0);
}

static unsint DelAfter()
/* ポイント後全削除 */
{
	if (p==pe) return(1); /* 削除不可：行末にいる */
	/* ポイントをpeにするだけ */
	pe=p;
	return(0);
}

static unsint DelWord()
/* 単語クリア */
{
unsint mode=0;
	if (p==pe) return(1); /* 削除不可：行末にいる */
	/* 最初の１文字で削除モードを決定する */
	mode=CheckSPC();
	/* on space = delete space */
	/* on word  = delete word  */
	while (CheckSPC()==mode) if (Del()) break;
	return(0);
}

static unsint DelLine()
/* １行クリア */
{
	if (pe==0) return(1); /* まだ１文字も入力されていない */
	pe=0;
	p=pe;
	return(0);
}

/*****************************************************************************/
/* ストア */
/*****************************************************************************/

static unsint WriteChar(unsint c,unchar *enter)
/* 文字書き込み */
/* return : 0=ok , 1=can't enter code , 2=line over */
{
unsint cb;
short len;
	if (*enter) { /* 入力文字制限があるとき */
	   int ret;
		ret=kstrchar(enter,c);
		if (pmode&BIT6) { /* 入力不可文字 */
			if (ret>=0) return(1); /* 入っていたら入力不可 */
		} else { /* 入力可能文字 */
			if (ret==-1) return(1);/* 入っていなかったら入力不可 */
		}
	}
	
	/* １文字書き込み */
	if (p==pe) { /* 新規書き込み */
		if (BLen(c)>MLEN) return(2); /* １行が一杯になる時 */
		pe++; /* エンドポインターも進める */
		CPrint(c); /* 表示 */
		return(0);
	}
	
	/* 上書き */
	if (fins) { /* insert mode */
		len=CheckSize(c);
		while (len--) { /* 表示幅分 */
			if (Ins(0)) {
				beep();
				InsSet();
				break;
				/* ret=2:insert disable(ret=1:p=peはなし) */
			}
		}
	}
	
	/* 前の文字と今の文字の表示長さの差を求める */
	cb=g->buffer[p]; /* 書込む前の文字 */
	len=CheckSize(cb)-CheckSize(c);
	if (!fins && len==0) { /* 差がない時はそのまま */
		CPrint(c);
		return(0);
	}
	
	if (len>=0) { /* 表示長さが短くなる時 */
		/* ポイント以降再表示のみ */
		CPrint(c); /* 表示 */
		LinePrint(p,BKlen2(p)+len); /*+lenが重要(後の残り文字を消す)*/
		return(0);
	}
	
	/* 表示長さが長くなる時 */
	if (p+1==pe) { /* となりが行末の時 */
		if (BLen(c)-1-(cb>=256)>MLEN) return(2); /* １行が一杯になる */
		CPrint(c); /* 表示（上書きするほうが長いのでこれだけでいい）*/
		return(0);
	}
	/* 行真ん中での上書き */
	/* となりの１文字もつぶして入れる */
	/* 今は文字の長さが１／２しかないのでこれでいい */
	len=BKlen2(p);
	p++; Del(); p--;
	CPrint(c); /* 表示 */
	LinePrint(p,len);
	return(0);
}

/*****************************************************************************/
/* ヒストリー：Type-3 */
/*****************************************************************************/

static void InitHist()
/* history buffer Initalize */
{
	g->wp=g->rp=g->top=g->fhist=0;
	g->rhist=1;
	beep();
}

static unsint Skip1Hist(unsint point)
/* １つ後のヒストリーへ */
{
unsint c;
	do {
		c=g->history[point];
		PInc(&point);
	} while (c!=0);
	return(point);
}

static unsint Back1Hist(unsint point)
/* １つ前のヒストリーへ */
{
	PDec(&point);
	while (point!=g->top) {
		PDec(&point);
		if (g->history[point]==0) {
			PInc(&point); /* skip 00 */
			break;
		}
	}
	return(point);
}

static unsint CReadHist(unsint *point)
/* ヒストリー１文字読みだし */
{
unsint c;
	c=g->history[*point];
	PInc(point);
	if (check_kanji1(c)) {
		c=(c<<8)|g->history[*point];
		PInc(point);
	}
	return(c);
}

static void ReadHist(unsint point)
/* １ヒストリー読みだし */
{
unsint c;
	pe=0;
	while ((c=CReadHist(&point))!=0) {
		if (BLen(c)>MLEN) break;
		g->buffer[pe++]=c;
	}
	g->rhist=0;
}

static unsint CmpHist(unsint mode,unsint point)
/* ヒストリー比較 */
/* return : 一致=1 , 不一致=0 */
{
unsint c,cc,j,pee;
unsint *buf;
	pee=pe;
	buf=g->buffer;
	if (mode) {
		pee=pe3;
		buf=buff3;
	}
	
	j=0;
	while (j<pee) {
		if ((c=CReadHist(&point))==0) return(0); /* not same */
		/* read from buffer */
		cc=buf[j++];
		if (fsrch2) {
			if ( c<256) c =toupper(c);
			if (cc<256) cc=toupper(cc);
		}
		if (c!=cc) return(0); /* not same */
	}
	if (mode==0) {
		if (g->history[point]!=0) return(0);
	}
	/* 一致 */
	return(1);
}

static unsint CheckDouble()
/* 多重入力チェック */
/* return : 0=same history is none , 1=あり */
{
unsint point;
	switch (g->fcheck) {
		case 1: /* 直前２重排除 */
			return(CmpHist(0,Back1Hist(g->wp)));
		
		case 2: /* 全２重排除 */
			point=g->top;
			while (CmpHist(0,point)==0) {
				point=Skip1Hist(point);
				if (point==g->wp) return(0);
			}
			return(1);
	}
	/* 排除なし */
	return(0);
}

static void PushHist()
/* line buffer push into history */
{
unsint i,c;
	if (g->fhist) { /* history あり */
		/* ２つ目以降：history 入力排除チェック */
		if (CheckDouble()) return; /* いれない */
	}
	
	g->fhist=1;
	g->rp=g->wp;	/* 書き込んだところが次の読みだし点になる */
	ftop=i=0;	/* top変更フラグクリア */
	while (i<pe) {
		c=g->buffer[i++];
		if (c>=256) { /* 漢字 */
			g->history[g->wp]=(c>>8);
			PInc2(&g->wp);
			c&=0xff;
		}
		g->history[g->wp]=c;
		PInc2(&g->wp);
	}
	/* store end code 00 */
	g->history[g->wp]=0;
	PInc2(&g->wp);
	
	/* top判定 */
	if (ftop) g->top=Skip1Hist(g->wp); /* top変更あり */
}

static unsint PopHist(unsint mode)
/* pop line from history */
/* mode=0:前 , =1:後 */
/* return : 0=ok , 1=can't pop */
{
unsint point;
	point=g->rp;
	if (g->rhist==0) {
		if (mode==1) {
			/* +1:next */
			point=Skip1Hist(point);
			if (point==g->wp) {
				/* これ以降には行けない */
				if (!floop) {
					beep();
					return(1);
				}
				point=g->top;
			}
		} else {
			/* -1:before */
			if (point==g->top) {
				/* これ以前には行けない */
				if (!floop) {
					beep();
					return(1);
				}
				point=g->wp;
			}
	    		point=Back1Hist(point);
		}
	    	g->rp=point;
	}
	/* 読みだし */
	HomeSub();
	ReadHist(point);
	return(0);
}

static unsint SearchHist(unsint first,unsint mode)
/* ヒストリーの内容で一致するものを捜し出す */
/* first:1=最初   , 0=２回目以降 */
/* mode :0=前(-1) , 1=後(+1) */
/* return : 1=same history none , 0=あり & set g->rp */
{
unsint point;
	if (first) { /* 一回目 */
		point=g->top; /* +1:next,from top */
		if (mode==0) { /* -1:before */
		    	if (g->wp==g->top) return(1); /* not found */
			point=Back1Hist(g->wp); /* from end */
		}
		goto SF;
	}
	
	/* ２回目以降 */
	point=g->rp;
	do {
		if (mode==0) { /* -1:before */
		    	if (point==g->top) return(1); /* not found */
			point=Back1Hist(point);
		} else { /* +1:next */
			point=Skip1Hist(point);
			if (point==g->wp) return(1); /* not found */
		}
	    SF:;
    	} while (CmpHist(1,point)==0); /* 見つかるまで */
	
	/* 一致 -> 読みだし */
	HomeSub();
	g->rp=point;
	ReadHist(point);
	return(0);
}

/*****************************************************************************/
/* メイン */
/*****************************************************************************/

int getline(_GETLNDT *gdt,unchar *line,int wx,int mode,unchar *pline,unchar *enter
								,int kexit,int *rkey)
/*	gdt   : getline構造体
	line  : 入力バッファー
	wx    : 画面上１行の幅
	pline : 先行入力文字列
	   	もし、文字列がMLENより長かった場合はMLEN以下になる。
        enter : 入力可能文字：全角文字も指定可
	kexit : 緊急脱出キーコード
	rkey  : リターンキーコード
      mode(8bitまで有効)
 	bit 0 : 0=normal input , 1=password input (echo back with '*')
	bit 1 : 先行入力した後 0=行末へ , 1=行頭へ
	bit 2 : ^L=コントロール文字入力 0=off , 1=on
	bit 3 : wx>0時のみ↑↓処理指定  0=上下移動 , 1=ヒストリー
	bit5,4: 初期ヒストリーモード
		%00 = 直前２重排除
		%01 = 全２重排除
		%10 = 排除なし
	bit 6 : enter制御 0=入力可能 , 1=入力不可
	bit 7 : insert mode 0=off , 1=on
*/
{
unsint ret,c,fesc=0,knj1=0,first,point;
	/* 初期設定 */
	if (!gdt->finit) return(-1); /* getlineが初期化されていない */
	
	g=gdt;
	MLEN=g->lmax;
	buff2=&g->buffer[MLEN+1];
	buff3=&buff2[MLEN+1];
	if ((kexit&0xff00)==0x1b00) kexit=0x0e00|(kexit&0xff);
	p=pe=0;
	
	Init();
	KeySet();
	pmode=mode;
	switch (mode&(BIT4|BIT5)) {
		case BIT4:     g->fcheck=1; break;
		case BIT5:     g->fcheck=2; break;
		case BIT4|BIT5:g->fcheck=0; break;
	}
	
	/* 複数行、自動改行対応初期化 */
	{int y;
	c=TWIDTH(&y);
	}
	cursor(&stx,&sty);
	if (wx==0) { /* 幅指定なし */
		stx=0;
		endx=c-1;
		pmode|=BIT3; /* ↑↓はヒストリーキー固定 */
	} else { /* 幅指定有り */
		endx=stx+wx-1;
		if (endx>=c) endx=c-1;
	}
	
	/* 先行入力：入力文字制限処理なし */
	if (OpenBuffer(pline)>0) {
		/* 初期表示 */
		AllPrint(0);
		if (!(pmode&BIT1)) EndSub(); /* 行末へカーソル移動 */
		g->rhist=1; /* remake line */
	}
	
	/* キー処理メイン */
	while (1) {
		/* キー入力 */
		c=getch3(0,kexit);
		if (fesc) c=(fesc|c); /* 取り合えずキーコードを作る */
		if (c==0x0d||c==kexit) break; /* [RET]/緊急脱出 */
		
		if (fesc) { /* ２ストロークファンクション */
			fesc=0;
			goto CTRL_CODE;
		}
		
		if (c>=' ') { /* 通常文字 / 表示コントロール文字(^L) */
		    NORMAL:
			fsrch=0; /* ヒストリーサーチモードカット */
			if (knj1==0) { /* 漢字１バイト目? */
				if (check_kanji1(c)) { /* yes */
					knj1=c;
					continue;
				}
			}
			/* １文字内部処理＆表示 */
			/* 1/2,1/4角文字入力不可の時 *追加*/
			if (!g->f24 && is24(knj1)) beep();
			else {
				if (knj1) c=(knj1<<8)|c; /* 漢字完成 */
				if (WriteChar(c,enter)) beep();
			}
			knj1=0;
			g->rhist=1; /* 入力ライン修正フラグ */
			continue;
		}
		
	    CTRL_CODE: /* コントロールコード */
		knj1=0; /* 漢字モード解除 */
		switch (c) {
			case 0x0e: /* CTRL-N(内部ESCコード) */
				/* これは速攻で処理する */
				fesc=(fesc==0? 0x0e00:0);
				continue;
			
			/*** 移動系コマンド ***/
			
			case 0x11: /* home : ^Q/HOME */
				InsSet();
				HomeSub();
				continue;
			
			case 0x10: /* end of line : ^P */
				InsSet();
				EndSub();
				continue;
			
			case 0x1c: /* cursor right */
				InsSet();
				point=p;
				if (!PRight()) CsrRight(point);
				continue;
			
			case 0x1d: /* cursor left */
				InsSet();
				if (!PLeft()) CsrLeft(p);
				continue;
			
			case 0x06: /* after word : ^F */
				InsSet();
				point=p;
				AfterWord();
				MoveCursor(2,point);
				continue;
			
			case 0x01: /* before word : ^A */
				InsSet();
				point=p;
				BeforeWord();
				MoveCursor(1,point);
				continue;
			
			/*** 削除系コマンド ***/
			
			case 0x08: /* back space : ^H/BS */
				if (PLeft()) continue; /* 行頭にいる時 */
				CsrLeft(p);
			case 0x07: /* Delete : ^G/DEL */
				DelSub();
				kflush(); /* キーリピートしないように */
				continue;
			
			case 0x19: /* clear line : ^Y/CLR */
			case 0x15: /* Delete home to cursor : ^U */
				InsSet();
				if (pe>0) { /* 入力文字があるときのみ */
					point=BKlen2(0);
					p=HomeSub(); /* カーソルだけ行頭に戻す */
					if (c==0x15) DelBefore();
					else {
						if (g->rhist) PushHist();
						DelLine();
					}
					AllPrint(point);
					kflush();
				}
				continue;
			
			case 0x14: /* Delete word : ^T */
				point=BKlen2(p);
				if (!DelWord()) {
					LinePrint(p,point);
					g->rhist=1;
				}
				continue;
			
			case 0x0b: /* erase line from cursor to end : ^K */
				InsSet();
				point=BKlen2(p);
				if (!DelAfter()) {
					LinePrint(p,point);
					g->rhist=1;
				}
				continue;
			
			/*** 挿入系コマンド ***/
			
			case 0x0f: /* insert : ^O */
			case 0x18: /* insert type 2 : ^X */
				InsSet();
				point=BKlen2(p);
				switch (Ins(c==0x18)) {
					case 0: /* ok */
						LinePrint(p,point);
						g->rhist=1;
						break;
					case 2: /* line overflow */
						beep();
				}
				continue;
			
			case 0x09: /* insert mode : TAB/INS */
				fins^=1;
				LEDMOD(4,fins);
				continue;
			
			/*** ヒストリーコマンド ***/
			
			case 0x0a: /* line enter to history : ^J */
				InsSet();
				if (pe>0) { /* [ret]のみの時はhistoryに入れない */
					PushHist();
					SaveBuffer(2);
					g->rhist=0; /* not remake history */
				}
				continue;
			
			case 0x1e: /* ↑ */
			case 0x1f: /* ↓ */
				if (pmode&BIT3) goto HIST_EXEC;
				/* 上下移動 */
				InsSet();
				if (c==0x1e) PUp(); else PDown();
				continue;
			
			case 0x16: /* ^V/ROLL-UP */
			case 0x17: /* ^W/ROLL-DOWN */
				/* 既にサーチモードにいる時は連続サーチを行う */
				if (fsrch==0) {
					if (pe>0) { /* 入力あり */
						first=fsrch=1; /* ｻｰﾁﾓｰﾄﾞ */
						SaveBuffer(3); /* ここの内容と比較される */
					} /* 入力が無い時はV4.0以前の↑↓と同じ */
				}
			    HIST_EXEC:
			    	if (!g->fhist) {
					/* ヒストリーなし */
					beep();
					continue;
				}
				
				point=BKlen2(0); /* 元の表示長さ */
				if (fsrch) {
					if (SearchHist(first,c==0x16)) {
						fsrch=0;
						beep();
						continue;
					}
				} else {
					if (PopHist(c==0x1e)) {
						beep();
						continue;
					}
				}
				
				/* 表示 */
				first=0;
				AllPrint(point); /* 全表示 */
				EndSub(); /* 行末へカーソル移動 */
				g->rhist=0; /* not remake history */
				continue;
			
			/*** フラグ系 ***/
			
			case 0x1a: /* history search mode : ^Z */
				fsrch2^=1; /* 大文字小文字区別 する/しない */
				continue;
			
			case 0x0e01: /* CTRL-N+^A */
			case 0x0e02: /* CTRL-N+^B */
				g->fcheck=c-0x0e01; /* 0/1 */
				continue;
			
			case 0x0e04: /* CTRL-N+^D */
				g->fcheck=2;
				continue;
			
			case 0x0e03: /* CTRL-N+^C */
				InitHist();
				continue;
			
			/*** テンプレートコマンド ***/
			
			case 0x0e21: /* １文字コピー : CTRL-B */
			case 0x02:
				if (p<pe2) {
					c=buff2[p];
					goto NORMAL;
				}
				continue;
			
			case 0x0e23: /* 残りコピー : UNDO */
				for (c=p;c<pe2;c++) {
					/* １文字内部処理＆表示 */
					if (WriteChar(buff2[c],enter)==1) break;
					/* line overになったら中断 */
				}
				g->rhist=1;
				continue;
			
			/*** 制御系コマンド ***/
			
			case 0x04: /* Init : ^D */
				Init();
				continue;
			
			case 0x0c: /* ctrl char enter : ^L */
				if (pmode&BIT2) {
					if ((c=getch3(0,0))!=0) goto NORMAL;
					/* ^@は入れられない */
				}
				beep();
				continue;
			
			/*** 終了系コマンド ***/
			
			case 0x1b: /* ESC */
			case 0x03: /* CTRL-C/BREAK */
				ret=line[0]=0; /* 入力を消す */
				goto BR_RET;
		}
	}
	/* CR/ENTER/^M */
	if (pe>0) { /* [RET]のみはﾋｽﾄﾘｰに入れない */
		PushHist();
		SaveBuffer(2);
	}
	ret=CloseBuffer(line);
    BR_RET:;
	*rkey=c; /* return key code */
	KeyReset();
	LEDMOD(4,0);
	return(ret);
}

/*****************************************************************************/
