/*
 *	input.c
 *
 *	input module
 */

#include "fd.h"
#include "wait.h"
#include "parse.h"
#include "kconv.h"
#include "func.h"
#include "kanji.h"

#ifdef	DEP_ORIGSHELL
#include "system.h"
#endif
#ifdef	DEP_PTY
#include "termemu.h"
#endif

#define	LIMITSELECTWARN		100
#define	YESNOSTR		"[Y/N]"
#define	YESNOSIZE		strsize(YESNOSTR)
#define	WAITAFTERWARN		360	/* msec */
#define	maxscr()		(maxcol * (maxline - minline) \
				- plen - (n_column - n_lastcolumn))
#define	within(n)		((n) < maxscr())
#define	overflow(n)		((n) > maxscr())
#define	ptr2col(n)		(((n) + plen) % maxcol)
#define	ptr2line(n)		(((n) + plen) / maxcol)
#define	iseol(n)		(within(n) && !(ptr2col(n)))
#define	LEFTMARGIN		0
#define	RIGHTMARGIN		2

extern char **history[];
extern short histsize[];
extern int curcolumns;
extern int minfilename;
extern int fnameofs;
extern int hideclock;
extern CONST char *promptstr;
#ifdef	DEP_ORIGSHELL
extern int fdmode;
extern CONST char *promptstr2;
#endif
#ifdef	DEP_IME
extern int ime_cont;
extern int ime_line;
extern int *ime_xposp;
extern VOID (*ime_locate)__P_((int, int));
#endif

#ifdef	DEP_KCONV
static int NEAR getinkcode __P_((VOID_A));
#else
#define	getinkcode()		NOCNV
#endif
#ifndef	_NOEDITMODE
static int NEAR getemulatekey __P_((int, CONST char []));
#endif
#ifdef	DEP_IME
static int NEAR getimebuf __P_((CONST char *, int *));
static int NEAR getime __P_((int, int *, int));
#endif
#ifdef	DEP_KCONV
static int NEAR getkey4 __P_((int, int));
#else
#define	getkey4(s, t)		getkey3(s, getinkcode(), t)
#endif
static char *NEAR trquote __P_((CONST char *, int, int *));
static int NEAR vlen __P_((CONST char *, int));
static int NEAR rlen __P_((CONST char *, int));
static int NEAR vonkanji1 __P_((CONST char *, int));
static int NEAR attrkanjiputs2 __P_((CONST char *, int, int));
#if	FD >= 2
static VOID NEAR kanjiputs3 __P_((CONST char *, int, int, int, int));
#else
#define	kanjiputs3(s, n, l, p, m) \
				VOID_C kanjiputs2(s, l, p)
#endif
static VOID NEAR putcursor __P_((int, int));
static VOID NEAR rightcursor __P_((VOID_A));
static VOID NEAR leftcursor __P_((VOID_A));
static VOID NEAR upcursor __P_((VOID_A));
static VOID NEAR downcursor __P_((VOID_A));
#ifdef	DEP_ORIGSHELL
static VOID NEAR forwcursor __P_((int));
static VOID NEAR backcursor __P_((int));
static VOID NEAR forwline __P_((int));
static VOID NEAR dumbputs __P_((CONST char *, int, int, int, int));
static VOID NEAR rewritecursor __P_((int, int));
static int NEAR checkcursor __P_((int, int));
#endif
static VOID NEAR scrollup __P_((VOID_A));
static VOID NEAR locate2 __P_((int, int));
#ifdef	DEP_IME
static VOID locate3 __P_((int, int));
#endif
static VOID NEAR setcursor __P_((int, int));
static VOID NEAR putstr __P_((int *, int *, int));
static VOID NEAR ringbell __P_((VOID_A));
static VOID NEAR clearline __P_((VOID_A));
static VOID NEAR newline __P_((int));
static VOID NEAR rightchar __P_((VOID_A));
static VOID NEAR leftchar __P_((VOID_A));
static VOID NEAR insertchar __P_((int, int, int));
static VOID NEAR deletechar __P_((int, int, int));
static VOID NEAR insshift __P_((int, int));
static VOID NEAR delshift __P_((int, int));
static VOID NEAR truncline __P_((VOID_A));
static VOID NEAR displaystr __P_((VOID_A));
static VOID NEAR insertbuf __P_((int));
static int NEAR preparestr __P_((int, int));
static int NEAR insertcursor __P_((int *, int *, int, int));
static int NEAR quotemeta __P_((int *, int *, int, int *, int *, int *));
static int NEAR insertstr __P_((CONST char *, int, int, int *, int *));
#ifndef	_NOCOMPLETE
static VOID NEAR selectfile __P_((int, char *CONST *));
static int NEAR completestr __P_((int, int, int));
#endif
#ifdef	DEP_KCONV
static u_int NEAR getucs2 __P_((int));
static VOID NEAR ungetch3 __P_((int));
#endif
static int NEAR getch3 __P_((VOID_A));
static int NEAR getkanjikey __P_((char *, int));
static VOID NEAR copyhist __P_((CONST char *, int));
static VOID NEAR _inputstr_up __P_((int *, int, char **));
static VOID NEAR _inputstr_down __P_((int *, int, char **));
static VOID NEAR _inputstr_delete __P_((VOID_A));
static VOID NEAR _inputstr_enter __P_((VOID_A));
#if	FD >= 2
static VOID NEAR _inputstr_case __P_((int));
static int NEAR search_matchlen __P_((VOID_A));
static char *NEAR search_up __P_((int, int *, int, char **));
static char *NEAR search_down __P_((int, int *, int, char **));
#endif
static VOID NEAR _inputstr_input __P_((CONST char *, int));
static int NEAR _inputstr __P_((int, int, int));
static VOID NEAR dispprompt __P_((CONST char *, int));
static int NEAR truncstr __P_((CONST char *));
static int NEAR yesnomes __P_((CONST char *));
static int NEAR selectcnt __P_((int, char *CONST *, int));
static int NEAR selectadj __P_((int, int, CONST char *CONST *,
		char **, int *, int));
static VOID NEAR selectmes __P_((int, int, int,
		char *CONST [], int [], int *, int));

int subwindow = 0;
int win_x = 0;
int win_y = 0;
char *curfilename = NULL;
#ifndef	_NOEDITMODE
char *editmode = NULL;
#endif
int lcmdline = 0;
int maxcmdline = 0;
#ifdef	DEP_ORIGSHELL
int dumbshell = 0;
#endif
#ifdef	DEP_IME
int imekey = 0;
#endif
#ifdef	DEP_URLPATH
int hidepasswd = 0;
#endif

#ifndef	_NOEDITMODE
static CONST int emulatekey[] = {
	K_UP, K_DOWN, K_RIGHT, K_LEFT,
	K_IC, K_DC, K_IL, K_DL,
	K_HOME, K_END, K_BEG, K_EOL,
	K_PPAGE, K_NPAGE, K_ENTER, K_ESC
};
#define	EMULATEKEYSIZ		arraysize(emulatekey)
static CONST char emacskey[] = {
	K_CTRL('P'), K_CTRL('N'), K_CTRL('F'), K_CTRL('B'),
	K_ESC, K_CTRL('D'), K_CTRL('Q'), K_CTRL('K'),
	K_ESC, K_ESC, K_CTRL('A'), K_CTRL('E'),
	K_CTRL('V'), K_CTRL('Y'), K_CTRL('O'), K_CTRL('G')
};
static CONST char vikey[] = {
	'k', 'j', 'l', 'h',
	K_ESC, 'x', K_ESC, 'D',
	'g', 'G', '0', '$',
	K_CTRL('B'), K_CTRL('F'), 'o', K_ESC
};
static CONST char wordstarkey[] = {
	K_CTRL('E'), K_CTRL('X'), K_CTRL('D'), K_CTRL('S'),
	K_CTRL('V'), K_CTRL('G'), K_CTRL(']'), K_CTRL('Y'),
	K_CTRL('W'), K_CTRL('Z'), K_CTRL('A'), K_CTRL('F'),
	K_CTRL('R'), K_CTRL('C'), K_CTRL('N'), K_ESC
};
static int vistat = 0;
#define	VI_NEXT			001
#define	VI_INSERT		002
#define	VI_ONCE			004
#define	VI_VIMODE		010
#define	isvimode()		((vistat & (VI_VIMODE | VI_INSERT)) \
				== VI_VIMODE)
#endif	/* !_NOEDITMODE */
static int plen = 0;
static int maxcol = 0;
static int minline = 0;
static int maxline = 0;
static char *inputbuf = NULL;
static ALLOC_T inputsize = (ALLOC_T)0;
static int rptr = 0;
static int vptr = 0;
static int inputlen = 0;
#ifndef	_NOCOMPLETE
static namelist *selectlist = NULL;
static int maxselect = 0;
static int tmpfileperrow = -1;
static int tmpfilepos = -1;
static int tmpcolumns = -1;
#endif
static int xpos = 0;
static int ypos = 0;
static int overwritemode = 0;
#ifdef	DEP_ORIGSHELL
static int dumbmode = 0;
static int lastofs2 = 0;
#endif
#if	FD >= 2
static int searchmode = 0;
static char *searchstr = NULL;
#endif
#ifdef	DEP_IME
static int imemode = 0;
#endif
#ifdef	DEP_KCONV
static u_char ungetbuf3[MAXUTF8LEN * MAXNFLEN];
static int ungetnum3 = 0;
#endif


#ifdef	DEP_KCONV
static int NEAR getinkcode(VOID_A)
{
	int code;

	code = (inputkcode != NOCNV) ? inputkcode : DEFCODE;
# ifdef	DEP_PTY
	if (parentfd >= 0 && ptyinkcode != NOCNV) code = ptyinkcode;
# endif

	return(code);
}
#endif	/* DEP_KCONV */

int intrkey(key)
int key;
{
	int c, duperrno;

	duperrno = errno;
	if (!kbhit2(0L)) c = EOF;
	else if ((c = Xgetch()) == EOF) /*EMPTY*/;
	else if (c != K_ESC) /*EMPTY*/;
	else if (kbhit2(WAITKEYPAD * 1000L)) {
		VOID_C ungetkey2(c, 0);
		c = EOF;
	}

	if (c == EOF) /*EMPTY*/;
	else if (c == cc_intr || (key >= 0 && c == key)) {
		if (isttyiomode) warning(0, INTR_K);
		else VOID_C Xfprintf(Xstderr, "%k\n", INTR_K);
	}
	else {
		VOID_C ungetkey2(c, 0);
		c = EOF;
	}
	errno = duperrno;

	return((c == EOF) ? 0 : 1);
}

#ifndef	_NOEDITMODE
static int NEAR getemulatekey(ch, table)
int ch;
CONST char table[];
{
	int i;

	for (i = 0; i < EMULATEKEYSIZ; i++) {
		if (table[i] == K_ESC) continue;
		if (ch == table[i]) return(emulatekey[i]);
	}

	return(ch);
}
#endif	/* !_NOEDITMODE */

#ifdef	DEP_IME
static int NEAR getimebuf(buf, ptrp)
CONST char *buf;
int *ptrp;
{
	int c;

	if (getinkcode() != EUC || !isekana(buf, *ptrp))
		c = (u_char)(buf[*ptrp]);
	else c = mkekana((u_char)(buf[++(*ptrp)]));
	(*ptrp)++;

	return(c);
}

static int NEAR getime(sig, chp, nowait)
int sig, *chp, nowait;
{
	static char buf[MAXKANJIBUF + 1] = "";
	static int next = 0;
	static int lastline = 0;
	int x, y, line;

	if (!ime_cont) next = lastline = 0;
	else if (next > 0) {
		if (next >= sizeof(buf) || !(*chp = getimebuf(buf, &next)))
			next = 0;
		else return(0);
	}

	if (nowait) return(-1);

	line = ptr2line(inputlen) + 1;
	if (line < lastline) line = lastline;
	ime_locate = locate3;
	ime_line = lastline = line;
	x = win_x - xpos;
	y = win_y - ypos;
# ifdef	DEP_ORIGSHELL
	if (shellmode) {
		ime_xposp = &win_x;
		x += xpos;
		y += ypos;
	}
# endif
	locate2(0, line);
	setcursor(inputlen, -1);
	Xputterm(L_CLEAR);
# ifdef	DEP_ORIGSHELL
	if (shellmode) /*EMPTY*/;
	else
# endif
	ime_line += ypos;
	locate2(x, y);

	*chp = ime_inputkanji(sig, buf);
	if (*chp < 0 || !ime_cont) {
		locate2(0, line);
		Xputterm(L_CLEAR);
		locate2(x, y);
		Xtflush();
	}
	if (*chp != K_ESC) {
		if (*chp < 0) {
			next = lastline = 0;
			ime_inputkanji(0, NULL);
		}
		else if (!*chp) *chp = getimebuf(buf, &next);
		return(0);
	}

	imemode = lastline = 0;
	return(-1);
}
#endif	/* DEP_IME */

#ifdef	DEP_KCONV
static int NEAR getkey4(sig, timeout)
int sig, timeout;
{
	int n;

	for (n = 0; n < ungetnum3; n++) VOID_C ungetkey2((int)ungetbuf3[n], 0);
# ifdef	DEP_IME
	if (imemode && !ungetnum3 && getime(sig, &n, 0) >= 0) /*EMPTY*/;
	else
# endif
	n = getkey3(sig, getinkcode(), timeout);

	ungetnum3 = 0;
	return(n);
}
#endif	/* DEP_KCONV */

int Xgetkey(sig, eof, timeout)
int sig, eof, timeout;
{
	static int prev = -1;
	int ch;

	if (sig < 0) {
#ifndef	_NOEDITMODE
		vistat = eof;
#endif
#ifdef	DEP_IME
		imemode = 0;
		ime_inputkanji(0, NULL);
#endif
		prev = -1;
		return('\0');
	}

	sig = sigalrm(sig);
	ch = getkey4(sig, timeout);
	if (eof && (ch != cc_eof || prev == ch)) eof = 0;
	prev = ch;
	if (eof) return(-1);

#ifndef	_NOEDITMODE
	if (!editmode) /*EMPTY*/;
	else if (!strcmp(editmode, "emacs"))
		ch = getemulatekey(ch, emacskey);
	else if (!strcmp(editmode, "vi")) do {
		vistat |= VI_VIMODE;
		vistat &= ~VI_NEXT;
		if (vistat & VI_INSERT) switch (ch) {
			case K_CTRL('V'):
				if (vistat & VI_ONCE)
					vistat &= ~(VI_INSERT | VI_ONCE);
				ch = K_IL;
				break;
			case K_ESC:
				if (vistat & VI_ONCE) vistat |= VI_NEXT;
				vistat &= ~(VI_INSERT | VI_ONCE);
				ch = K_LEFT;
				break;
			case K_CR:
				vistat &= ~(VI_INSERT | VI_ONCE);
				break;
			case K_BS:
				if (vistat & VI_ONCE)
					vistat &= ~(VI_INSERT | VI_ONCE);
				break;
			default:
				if (vistat & VI_ONCE)
					vistat &= ~(VI_INSERT | VI_ONCE);
				if (ch < K_MIN) break;
				ringbell();
				vistat |= VI_NEXT;
				break;
		}
		else if ((ch = getemulatekey(ch, vikey)) < K_MIN) switch (ch) {
			case ':':
				vistat |= (VI_NEXT | VI_INSERT);
				overwritemode = 0;
				break;
			case 'I':
				vistat |= VI_INSERT;
				overwritemode = 0;
				ch = K_BEG;
				break;
			case 'A':
				vistat |= VI_INSERT;
				overwritemode = 0;
				ch = K_EOL;
				break;
			case 'i':
				vistat |= (VI_NEXT | VI_INSERT);
				overwritemode = 0;
				break;
			case 'a':
				vistat |= VI_INSERT;
				overwritemode = 0;
				ch = K_RIGHT;
				break;
# if	FD >= 2
			case 'R':
				vistat |= (VI_NEXT | VI_INSERT);
				overwritemode = 1;
				break;
			case 'r':
				vistat |= (VI_NEXT | VI_INSERT | VI_ONCE);
				overwritemode = 1;
				break;
# endif	/* FD >= 2 */
			case K_BS:
				ch = K_LEFT;
				break;
			case ' ':
				ch = K_RIGHT;
				break;
			case K_ESC:
			case K_CR:
			case '\t':
				break;
			default:
				ringbell();
				vistat |= VI_NEXT;
				break;
		}
	} while ((vistat & VI_NEXT) && (ch = getkey4(sig, timeout)) > 0);
	else if (!strcmp(editmode, "wordstar"))
		ch = getemulatekey(ch, wordstarkey);
#endif	/* !_NOEDITMODE */

	return((ch >= 0) ? ch : K_ESC);
}

static char *NEAR trquote(s, len, widthp)
CONST char *s;
int len, *widthp;
{
	char *cp, *buf;
	int vw;

	cp = Xstrndup(s, len);
	buf = Xmalloc(len * MAXCHARWID + 1);

	vw = Xsnprintf(buf, len * MAXCHARWID + 1, "%^s", cp);
	Xfree(cp);
	if (widthp) *widthp = vw;

	return(buf);
}

static int NEAR vlen(s, cx)
CONST char *s;
int cx;
{
	int v, r, rw, vw;

	v = r = 0;
	while (r < cx) {
		getcharwidth(s, r, &rw, &vw);
		if (r + rw > cx) break;
		r += rw;
		v += vw;
	}

	return(v);
}

static int NEAR rlen(s, cx2)
CONST char *s;
int cx2;
{
	int v, r, rw, vw;

	v = r = 0;
	while (v < cx2) {
		getcharwidth(s, r, &rw, &vw);
		if (v + vw > cx2) break;
		r += rw;
		v += vw;
	}

	return(r);
}

static int NEAR vonkanji1(s, cx2)
CONST char *s;
int cx2;
{
	int v, r, rw, vw;

	v = r = 0;
	while (v < cx2) {
		getcharwidth(s, r, &rw, &vw);
		if (v + vw > cx2) return(0);
		r += rw;
		v += vw;
	}

	return(iskanji1(s, r) ? r + 1 : 0);
}

int kanjiputs2(s, len, ptr)
CONST char *s;
int len, ptr;
{
	char *buf;
	int n;

	if (len < 0) return(0);
	buf = Xmalloc(len * KANAWID + 1);
	VOID_C strncpy2(buf, s, &len, ptr);
#ifdef	DEP_KCONV
	n = Xkanjiputs(buf);
#else
	XXcputs(buf);
	n = strlen2(buf);
#endif
	Xfree(buf);

	return(n);
}

static int NEAR attrkanjiputs2(s, len, ptr)
CONST char *s;
int len, ptr;
{
	Xputterm(T_STANDOUT);
	len = kanjiputs2(s, len, ptr);
	Xputterm(END_STANDOUT);

	return(len);
}

#if	FD >= 2
static VOID NEAR kanjiputs3(s, cx2, len2, ptr, top)
CONST char *s;
int cx2, len2, ptr, top;
{
	int n, width;

	if (!searchmode || ptr + len2 <= top || ptr >= cx2) {
		VOID_C kanjiputs2(s, len2, ptr);
		return;
	}

	width = cx2 - top;
	n = top - ptr;
	if (n <= 0) width += n;
	else {
		VOID_C kanjiputs2(s, n, ptr);
		ptr += n;
		len2 -= n;
	}
	VOID_C attrkanjiputs2(s, width, ptr);
	ptr += width;
	len2 -= width;
	if (len2 > 0) VOID_C kanjiputs2(s, len2, ptr);
}
#endif	/* FD >= 2 */

VOID cputspace(n)
int n;
{
	VOID_C XXcprintf("%*s", n, nullstr);
}

VOID cputstr(n, s)
int n;
CONST char *s;
{
	if (!s) cputspace(n);
	else VOID_C XXcprintf("%^-*.*k", n, n, s);
}

VOID attrputstr(n, s, isstandout)
int n;
CONST char *s;
int isstandout;
{
	if (isstandout) putterm(T_STANDOUT);
	cputstr(n, s);
	if (isstandout) putterm(END_STANDOUT);
}

static VOID NEAR putcursor(c, n)
int c, n;
{
	win_x += n;
	while (n--) VOID_C XXputch(c);
#ifdef	DEP_ORIGSHELL
	if (dumbmode);
	else if (shellmode && win_x >= n_column) {
		VOID_C XXputch(' ');
		VOID_C XXputch('\r');
		win_x = 0;
		win_y++;
	}
#endif
}

static VOID NEAR rightcursor(VOID_A)
{
	win_x++;
	Xputterm(C_RIGHT);
}

static VOID NEAR leftcursor(VOID_A)
{
	win_x--;
	Xputterm(C_LEFT);
}

static VOID NEAR upcursor(VOID_A)
{
	win_y--;
	Xputterm(C_UP);
}

static VOID NEAR downcursor(VOID_A)
{
	win_y++;
	Xputterm(C_DOWN);
}

#ifdef	DEP_ORIGSHELL
static VOID NEAR forwcursor(x)
int x;
{
	while (win_x < x) {
		VOID_C XXputch(' ');
		win_x++;
	}
}

static VOID NEAR backcursor(x)
int x;
{
	while (win_x > x) {
		VOID_C XXputch(C_BS);
		win_x--;
	}
}

static VOID NEAR forwline(x)
int x;
{
	if (win_x < n_column) {
		Xcputnl();
		win_y++;
	}
	else {
		while (win_x >= n_column) {
			win_x -= n_column;
			win_y++;
		}
		VOID_C XXputch(' ');
		VOID_C XXputch('\r');
	}
	win_x = 0;
	forwcursor(x);
}

static VOID NEAR dumbputs(s, cx2, len2, max, ptr)
CONST char *s;
int cx2, len2, max, ptr;
{
	len2 -= ptr;
	if (max <= 0) clearline();
	else {
		if (len2 < max) {
			VOID_C kanjiputs2(s, len2, ptr);
			win_x += len2;
		}
		else {
			if (vonkanji1(s, ptr + max - 1)) max++;
			VOID_C kanjiputs2(s, max, ptr);
			win_x += max;
			putcursor('>', 1);
		}
		clearline();
		backcursor(xpos + cx2 - lastofs2);
	}
}

static VOID NEAR rewritecursor(cx, cx2)
int cx, cx2;
{
	char *dupl;
	int i, dx, ocx, ocx2;

	i = (lastofs2) ? 1 : plen;
	dx = cx2 - lastofs2;
	ocx2 = win_x - xpos - i + lastofs2;
	if (checkcursor(cx, cx2) < 0) /*EMPTY*/;
	else if (cx2 <= ocx2) backcursor(xpos + i + dx);
	else {
		ocx = rlen(inputbuf, ocx2);
		dupl = trquote(&(inputbuf[ocx]), cx - ocx, NULL);
		win_x += Xkanjiputs(dupl);
		Xfree(dupl);
	}
}

static int NEAR checkcursor(cx, cx2)
int cx, cx2;
{
	int i, dx, orptr, ovptr;

	i = (lastofs2) ? 1 : plen;
	dx = cx2 - lastofs2;
	if (dx >= 0 && i + dx < maxcol) return(0);

	orptr = rptr;
	ovptr = vptr;
	rptr = cx;
	vptr = cx2;
	displaystr();
	rptr = orptr;
	vptr = ovptr;

	return(-1);
}
#endif	/* DEP_ORIGSHELL */

static VOID NEAR scrollup(VOID_A)
{
#ifdef	DEP_PTY
	regionscroll(C_SCROLLFORW, 1, 0, maxline - 1, minline, maxline - 1);
#else
	Xlocate(0, maxline - 1);
	Xputterms(C_SCROLLFORW);
#endif
	ypos--;
	hideclock = 1;
}

static VOID NEAR locate2(x, y)
int x, y;
{
#ifdef	DEP_ORIGSHELL
	if (shellmode) {
		x += xpos;
		if (win_x >= n_column) {
			Xmovecursor(C_NLEFT, C_LEFT, win_x);
			win_x = 0;
		}
		if (y < win_y) Xmovecursor(C_NUP, C_UP, win_y - y);
		else if (y > win_y) {
			Xmovecursor(-1, C_SCROLLFORW, y - win_y);
			VOID_C XXputch('\r');
			win_x = 0;
		}
		if (x < win_x) Xmovecursor(C_NLEFT, C_LEFT, win_x - x);
		else if (x > win_x) Xmovecursor(C_NRIGHT, C_RIGHT, x - win_x);

		win_x = x;
		win_y = y;
		return;
	}
#endif	/* DEP_ORIGSHELL */
	while (ypos + y >= maxline) scrollup();
	win_x = xpos + x;
	win_y = ypos + y;
	Xlocate(win_x, win_y);
}

#ifdef	DEP_IME
static VOID locate3(x, y)
int x, y;
{
# ifdef	DEP_ORIGSHELL
	if (shellmode) locate2(x, y);
	else
# endif
	Xlocate(win_x = x, win_y = y);
}
#endif	/* DEP_IME */

static VOID NEAR setcursor(cx, cx2)
int cx, cx2;
{
	int f;

	if (cx < 0) cx = rlen(inputbuf, cx2);
	if (cx2 < 0) cx2 = vlen(inputbuf, cx);
#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		rewritecursor(cx, cx2);
		return;
	}
#endif
	f = within(cx2) ? 0 : 1;
	cx2 -= f;
	locate2(ptr2col(cx2), ptr2line(cx2));
	if (f) rightcursor();
}

static VOID NEAR putstr(cxp, cxp2, ins)
int *cxp, *cxp2, ins;
{
	char *cp, tmp[MAXCHARWID + 1];
	int rw, vw;

	while (ins > 0) {
		VOID_C getprintable(tmp, sizeof(tmp), inputbuf, *cxp, &rw);
		*cxp += rw;
		ins -= rw;
		cp = tmp;
		while (*cp) {
			getcharwidth(cp, (ALLOC_T)0, &rw, &vw);
			VOID_C XXcprintf("%.*k", vw, cp);
			cp += rw;
			win_x += vw;
			*cxp2 += vw;
#ifdef	DEP_ORIGSHELL
			if (dumbmode) VOID_C checkcursor(*cxp, *cxp2);
			else
#endif
			if (within(*cxp2) && ptr2col(*cxp2) < vw)
				setcursor(*cxp, *cxp2);
		}
	}
}

static VOID NEAR ringbell(VOID_A)
{
#ifdef	DEP_ORIGSHELL
	if (dumbmode && dumbterm <= 2) VOID_C XXputch('\007');
	else
#endif
	Xputterm(T_BELL);
	Xtflush();
}

static VOID NEAR clearline(VOID_A)
{
#ifdef	DEP_ORIGSHELL
	int x;
#endif

#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		/* abandon last 1 char because of auto newline */
		for (x = win_x; x < n_column - 1; x++) VOID_C XXputch(' ');
		for (x = win_x; x < n_column - 1; x++) VOID_C XXputch(C_BS);
	}
	else
#endif
	Xputterm(L_CLEAR);
}

static VOID NEAR newline(y)
int y;
{
#ifdef	DEP_ORIGSHELL
	if (dumbmode);
	else if (shellmode) forwline(xpos);
	else
#endif
	locate2(0, y);
}

static VOID NEAR rightchar(VOID_A)
{
	int rw, vw;

	getcharwidth(inputbuf, rptr, &rw, &vw);
	if (rptr + rw > inputlen) {
		ringbell();
		return;
	}
	rptr += rw;
	vptr += vw;
#ifdef	DEP_ORIGSHELL
	if (dumbmode) rewritecursor(rptr, vptr);
	else
#endif
	if (within(vptr) && ptr2col(vptr) < vw) setcursor(rptr, vptr);
	else while (vw--) rightcursor();
}

static VOID NEAR leftchar(VOID_A)
{
	int rw, vw, ovptr;

	rw = vw = 1;
	if (rptr >= 2 && onkanji1(inputbuf, rptr - 2)) rw = vw = 2;
#ifdef	CODEEUC
	else if (rptr >= 2 && isekana(inputbuf, rptr - 2)) rw++;
#else
	else if (isskana(inputbuf, rptr - 1)) /*EMPTY*/;
#endif
	else if (Xiscntrl(inputbuf[rptr - 1])) vw++;
	else if (ismsb(inputbuf[rptr - 1])) vw = 4;

	ovptr = vptr;
	rptr -= rw;
	vptr -= vw;
#ifdef	DEP_ORIGSHELL
	if (dumbmode) rewritecursor(rptr, vptr);
	else
#endif
	if (ptr2col(ovptr) < vw) setcursor(rptr, vptr);
	else while (vw--) leftcursor();
}

static VOID NEAR insertchar(cx, cx2, ins)
int cx, cx2, ins;
{
	char *dupl;
	int dx, dy, i, j, l, f1, ptr, len2, nlen;
#if	!MSDOS
	int f2;
#endif

	dupl = trquote(&(inputbuf[cx]), inputlen - cx, &l);
	len2 = cx2 + l;
	nlen = len2 + ins;
	dy = ptr2line(cx2);
	i = (dy + 1) * maxcol - plen;	/* prev. chars including cursor line */
	j = maxcol - ptr2col(cx2);
	if (j > ins) j = ins;		/* inserted columns in cursor line */

#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		i = (lastofs2) ? 1 : plen;
		dx = cx2 + ins - lastofs2;
		if (i + dx < maxcol) {
			putcursor(' ', ins);
			dumbputs(dupl, i + cx2, l, maxcol - (i + dx), 0);
		}
	}
	else
#endif	/* DEP_ORIGSHELL */
#if	!MSDOS
	if (*termstr[C_INSERT]) {
# ifdef	DEP_ORIGSHELL
		if (shellmode);
		else
# endif
		if (ypos + dy >= maxline - 1
		&& xpos + ptr2col(cx2) + (len2 - cx2) + j >= n_column) {
			/*
			 * In the case that current line is the last line,
			 * and the end of string will reach over the last line.
			 */
			while (ypos + dy >= maxline - 1) scrollup();
			setcursor(cx, cx2);
		}
		while (j--) Xputterm(C_INSERT);

		if (i < nlen) {
			while (i < nlen) {
				ptr = i - 1 - cx2;
					/* rest columns in cursor line */
				/*
				 * Whether if the end of line is kanji 1st byte
				 * f1: after inserted
				 * f2: before inserted
				 */
				if (ptr < ins) f1 = 0;
				else f1 = vonkanji1(dupl, ptr - ins);
				f2 = (vonkanji1(dupl, ptr)) ? 1 : 0;
				if (xpos + maxcol < n_column) {
					locate2(maxcol, dy);
					j = n_column - (xpos + maxcol);
					if (j > ins) j = ins;
					if (f2) j++;
					if (f1 && j-- > 0) rightcursor();
					if (j > 0) putcursor(' ', j);
				}

				dy++;
# ifdef	DEP_ORIGSHELL
				if (shellmode);
				else
# endif
				if (ypos + dy >= maxline - 1
				&& xpos + nlen - i >= n_column)
					while (ypos + dy >= maxline - 1)
						scrollup();
				locate2(0, dy);
				for (j = 0; j < ins; j++) Xputterm(C_INSERT);
				l = ins;
				if (ptr < ins) {
					ptr++;
					locate2(ins - ptr, dy);
					l = ptr + f2;
					ptr = 0;
				}
				else {
					ptr -= ins - 1;
					if (f1) dupl[f1 - 1] = dupl[f1] = ' ';
					if (f2) l++;
				}
				if (ptr + l > nlen - cx2) l = nlen - cx2 - ptr;
				VOID_C kanjiputs2(dupl, l, ptr);
				win_x += l;
				i += maxcol;
			}
			setcursor(cx, cx2);
		}
	}
	else
#endif	/* !MSDOS */
	{
		putcursor(' ', j);
		j = 0;
		l = i - cx2 - ins;	/* rest chars following inserted str */
		dx = ptr2col(cx2 + ins);
					/* cursor column after inserted */

		while (i < nlen) {
			ptr = i - 1 - cx2;
					/* rest columns in cursor line */
			/*
			 * Whether if the end of line is kanji 1st byte
			 * f1: after inserted
			 */
			if (ptr < ins) f1 = l = 0;
			else f1 = vonkanji1(dupl, ptr - ins);
#ifdef	DEP_ORIGSHELL
			if (shellmode);
			else
#endif
			if (ypos + dy >= maxline - 1) {
				while (ypos + dy >= maxline - 1) scrollup();
				locate2(dx, dy);
			}
			if (f1) l++;
			if (l > 0) {
				VOID_C kanjiputs2(dupl, l, j);
				win_x += l;
			}

			dy++;
			if (f1) newline(dy);
			else {
				putcursor(' ', 1);
				locate2(0, dy);
			}

			l = maxcol;
			if (ptr < ins - 1) {
				putcursor(' ', 1);
				ptr++;
				locate2(ins - ptr, dy);
				l -= ins - ptr;
				ptr = 0;
			}
			else {
				ptr -= ins - 1;
				if (f1) dupl[f1 - 1] = dupl[f1] = ' ';
			}
			if (ptr + l > len2 - cx2) l = len2 - cx2 - ptr;
			j = ptr;
			i += maxcol;
			dx = 0;
		}

		l = len2 - cx2 - j;
		if (l > 0) {
			VOID_C kanjiputs2(dupl, l, j);
			win_x += l;
		}
		setcursor(cx, cx2);
	}
#ifdef	DEP_ORIGSHELL
	if (dumbmode);
	else if (shellmode) {
		if (ptr2col(nlen) == 1) {
			setcursor(-1, nlen);
			Xputterm(L_CLEAR);
			setcursor(cx, cx2);
		}
	}
	else
#endif
	if (i == nlen && within(i) && ypos + dy >= maxline - 1) {
		while (ypos + dy >= maxline - 1) scrollup();
		setcursor(cx, cx2);
	}
	Xfree(dupl);
}

static VOID NEAR deletechar(cx, cx2, del)
int cx, cx2, del;
{
	char *dupl;
	int dy, i, j, l, f1, ptr, len2, nlen;
#if	!MSDOS
	int f2;
#endif

	dupl = trquote(&(inputbuf[cx]), inputlen - cx, &l);
	len2 = cx2 + l;
	nlen = len2 - del;
	dy = ptr2line(cx2);
	i = (dy + 1) * maxcol - plen;	/* prev. chars including cursor line */

#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		i = (lastofs2) ? 1 : plen;
		j = cx2 - lastofs2;
		dumbputs(dupl, i + cx2, l, maxcol - (i + j), del);
	}
	else
#endif	/* DEP_ORIGSHELL */
#if	!MSDOS
	if (*termstr[C_DELETE]) {
		j = maxcol - ptr2col(cx2);
		if (j > del) j = del;	/* deleted columns in cursor line */
		while (j--) Xputterm(C_DELETE);

		if (i < len2) {
			while (i < len2) {
				ptr = i - 1 - cx2;
					/* rest columns in cursor line */
				/*
				 * Whether if the end of line is kanji 1st byte
				 * f1: after deleted
				 */
				if (ptr >= nlen - cx2) f1 = 0;
				else f1 = (vonkanji1(dupl, ptr + del)) ? 1 : 0;
				f2 = (vonkanji1(dupl, ptr)) ? 1 : 0;
				j = maxcol - del + f2;
				if (j < maxcol - (++ptr)) {
					j = maxcol - ptr;
					l = ptr + f1;
					ptr = del;
				}
				else {
					l = del - f2 + f1;
					ptr += f2;
				}
				locate2(j, dy);
				if (ptr + l > len2) l = len2 - ptr;
				if (l > 0) {
					VOID_C kanjiputs2(dupl, l, ptr);
					win_x += l;
				}
				if (!f1) putcursor(' ', 1);
				locate2(0, ++dy);
				for (j = 0; j < del; j++) Xputterm(C_DELETE);
				if (f1) putcursor(' ', 1);
				i += maxcol;
			}
			setcursor(cx, cx2);
		}
	}
	else
#endif	/* !MSDOS */
	{
		j = del;
		l = i - cx2;	/* rest chars following cursor */

		while (i < len2) {
			ptr = i - 1 - cx2;
				/* rest columns in cursor line */
			/*
			 * Whether if the end of line is kanji 1st byte
			 * f1: after deleted
			 */
			if (ptr >= nlen - cx2) f1 = 0;
			else f1 = vonkanji1(dupl, ptr + del);
			if (f1) l++;
			if (l > 0) {
				VOID_C kanjiputs2(dupl, l, j);
				win_x += l;
			}

			dy++;
			if (f1) newline(dy);
			else {
				putcursor(' ', 1);
				locate2(0, dy);
			}

			l = maxcol;
			ptr += del + 1;
			if (f1) dupl[f1 - 1] = dupl[f1] = ' ';
			if (ptr + l > len2 - cx2) l = len2 - cx2 - ptr;
			j = ptr;
			i += maxcol;
		}

		l = len2 - cx2 - j;
		if (l > 0) {
			VOID_C kanjiputs2(dupl, l, j);
			win_x += l;
		}
		clearline();
		setcursor(cx, cx2);
	}
	Xfree(dupl);
}

static VOID NEAR insshift(cx, ins)
int cx, ins;
{
	if (cx >= inputlen) return;
	memmove(&(inputbuf[cx + ins]), &(inputbuf[cx]), inputlen - cx);
}

static VOID NEAR delshift(cx, del)
int cx, del;
{
	if (cx >= inputlen - del) return;
	memmove(&(inputbuf[cx]), &(inputbuf[cx + del]), inputlen - del - cx);
}

static VOID NEAR truncline(VOID_A)
{
	int dy, i, len2;

	len2 = vlen(inputbuf, inputlen);
#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		clearline();
		return;
	}
#endif
	Xputterm(L_CLEAR);

	dy = ptr2line(vptr);
	i = (dy + 1) * maxcol - plen;
	if (i < len2) {
#ifdef	DEP_ORIGSHELL
		if (shellmode) while (i < len2) {
			locate2(0, ++dy);
			Xputterm(L_CLEAR);
			i += maxcol;
		}
		else
#endif
		while (i < len2) {
			if (ypos + ++dy >= maxline) break;
			Xlocate(xpos, ypos + dy);
			Xputterm(L_CLEAR);
			i += maxcol;
		}
		setcursor(rptr, vptr);
	}
}

static VOID NEAR displaystr(VOID_A)
{
#if	FD >= 2
	int top;
#endif
	char *dupl;
	int i, x, y, len2, max, vi, width, f;

	dupl = trquote(inputbuf, inputlen, &len2);
#if	FD >= 2
	top = vlen(inputbuf, search_matchlen());
#endif

#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		backcursor(0);
		forwcursor(xpos);
		dispprompt(NULL, -1);
		i = plen;
		x = vptr - maxcol;
		y = (maxcol + 1) / 2;
		for (lastofs2 = 0; lastofs2 <= i + x; lastofs2 += y) {
# if	FD >= 2
			if (searchmode) i = plen + 1;
			else
# endif
			i = 1;
		}
		if (!lastofs2) dispprompt(NULL, 0);
		else {
# if	FD >= 2
			if (searchmode) dispprompt(NULL, 0);
# endif
			putcursor('<', 1);
			if ((f = vonkanji1(dupl, lastofs2 - 1)))
				dupl[f - 1] = dupl[f] = ' ';
		}
		dumbputs(dupl, i + vptr, len2, maxcol - i, lastofs2);
		Xtflush();
		Xfree(dupl);
		return;
	}
#endif	/* DEP_ORIGSHELL */

	dispprompt(NULL, 0);
	locate2(plen, 0);
	max = maxscr();
	if (len2 > max) {
		len2 = max;
		inputlen = rlen(inputbuf, max);
		if (vptr >= len2) {
			vptr = len2;
			rptr = inputlen;
		}
	}
	vi = (termstr[T_NOCURSOR] && *termstr[T_NOCURSOR]
		&& termstr[T_NORMALCURSOR] && *termstr[T_NORMALCURSOR]);
	if (vi) Xputterm(T_NOCURSOR);
	i = x = y = 0;
	width = maxcol - plen;
	while (i + width < len2) {
		/*
		 * Whether if the end of line is kanji 1st byte
		 */
		f = (vonkanji1(dupl, i + width - 1)) ? 1 : 0;
		clearline();
		if (stable_standout) Xputterm(END_STANDOUT);

#ifdef	DEP_ORIGSHELL
		if (shellmode);
		else
#endif
		if (ypos + y >= maxline - 1) {
			while (ypos + y >= maxline - 1) scrollup();
			locate2(x, y);
		}
		if (width + f > 0) {
			kanjiputs3(dupl, vptr, width + f, i, top);
			win_x += width + f;
		}

		i += width + f;
		width = maxcol - f;
		x = f;
		y++;

		if (f) {
			newline(y);
			putcursor(' ', 1);
		}
		else {
			putcursor(' ', 1);
			locate2(0, y);
		}
	}
	clearline();
	if (stable_standout) Xputterm(END_STANDOUT);
	kanjiputs3(dupl, vptr, len2 - i, i, top);
	win_x += len2 - i;
#ifdef	DEP_ORIGSHELL
	if (shellmode);
	else
#endif
	while (win_y < maxline - 1) {
		Xlocate(xpos, ++win_y);
		Xputterm(L_CLEAR);
	}
	if (vi) Xputterm(T_NORMALCURSOR);
	setcursor(rptr, vptr);
	Xtflush();
	Xfree(dupl);
}

static VOID NEAR insertbuf(ins)
int ins;
{
	inputbuf = c_realloc(inputbuf, inputlen + ins, &inputsize);
}

static int NEAR preparestr(rins, vins)
int rins, vins;
{
	int rw, vw;

	if (overflow(vlen(inputbuf, inputlen) + vins)) return(-1);

	if (!overwritemode) {
		insertchar(rptr, vptr, vins);
		insertbuf(rins);
		insshift(rptr, rins);
		inputlen += rins;
		return(0);
	}

	if (rptr >= inputlen) rw = vw = 0;
	else getcharwidth(inputbuf, rptr, &rw, &vw);

	if (!vw) /*EMPTY*/;
	else if (vins > vw) insertchar(rptr, vptr, vins - vw);
	else if (vins < vw) deletechar(rptr, vptr, vw - vins);

	if (!rw) /*EMPTY*/;
	else if (rins > rw) {
		insertbuf(rins - rw);
		insshift(rptr, rins - rw);
	}
	else if (rins < rw) delshift(rptr, rw - rins);

	inputlen += rins - rw;

	return(0);
}

static int NEAR insertcursor(cxp, cxp2, ins, ch)
int *cxp, *cxp2, ins, ch;
{
	if (overflow(vlen(inputbuf, inputlen) + ins)) return(-1);
	setcursor(*cxp, *cxp2);
	insertchar(*cxp, *cxp2, ins);
	insertbuf(ins);
	insshift(*cxp, ins);
	inputlen += ins;
	if (ch) {
		inputbuf[*cxp] = ch;
		putstr(cxp, cxp2, 1);
	}

	return(0);
}

static int NEAR quotemeta(cxp, cxp2, ch, qtopp, qp, qedp)
int *cxp, *cxp2, ch, *qtopp, *qp, *qedp;
{
	int cx2, ocx, ocx2;

	ocx = *cxp;
	ocx2 = *cxp2;
	if (*qtopp < 0) return(0);
	if (*qp) {
		if (*qp != '\'' || ch != '\'') return(0);
		if (insertcursor(cxp, cxp2, 1, *qp) < 0) return(-1);
		*qtopp = *cxp;
	}
	else if (qedp && *qedp > 0 && *qedp < *cxp) {
		if (inputbuf[*qedp] != '\'' || ch != '\'') {
			*qp = inputbuf[*qedp];
			cx2 = vlen(inputbuf, *qedp);
			setcursor(*qedp, cx2);
			deletechar(*qedp, cx2, 1);
			delshift(*qedp, 1);
			(*cxp)--;
			(*cxp2)--;
			if (cxp != &rptr && rptr > *qedp) rptr--;
			if (cxp2 != &vptr && vptr > cx2) vptr--;
			inputlen--;
			*qedp = -1;
			return(0);
		}
		*qtopp = *cxp;
	}

	cx2 = vlen(inputbuf, *qtopp);
	if (insertcursor(qtopp, &cx2, 1, *qp = '"') < 0) return(-1);
	(*cxp)++;
	(*cxp2)++;
	if (cxp != &rptr && rptr >= *qtopp) rptr += *cxp - ocx;
	if (cxp2 != &vptr && vptr >= cx2) vptr += *cxp2 - ocx2;
	*qtopp = -1;
	if (qedp) *qedp = -1;
	return(0);
}

static int NEAR insertstr(strins, ins, qtop, qp, qedp)
CONST char *strins;
int ins, qtop, *qp, *qedp;
{
	int i, n, ch, pc, max;

	n = vlen(inputbuf, inputlen);
	max = maxscr();
	if (n + vlen(strins, ins) > max) ins = rlen(strins, max - n);

	if (ins > 0) {
		if (onkanji1(strins, ins - 1)) ins--;
#ifdef	CODEEUC
		else if (isekana(strins, ins - 1)) ins--;
#endif
	}
	if (ins <= 0) return(ins);

	insertchar(rptr, vptr, vlen(strins, ins));
	insertbuf(ins);
	insshift(rptr, ins);
	for (i = 0; i < ins; i++) inputbuf[rptr + i] = ' ';
	inputlen += ins;
	for (i = 0; i < ins; i++) {
		if (*qp < '\0') pc = PC_NORMAL;
		else pc = parsechar(&(strins[i]), ins - i,
			'!', EA_FINDMETA, qp, NULL);
		if (pc == PC_WCHAR) {
			inputbuf[rptr] = strins[i++];
			inputbuf[rptr + 1] = strins[i];
			putstr(&rptr, &vptr, 2);
		}
#ifndef	FAKEESCAPE
		else if (pc == PC_EXMETA || pc == '!') {
			if (*qp) {
				n = 2;
				ch = *qp;
				*qp = '\0';
			}
			else {
				n = 1;
				ch = '\0';
			}
			if (insertcursor(&rptr, &vptr, n, ch) < 0) return(0);
			inputbuf[rptr] = PESCAPE;
			inputbuf[rptr + 1] = strins[i];
			putstr(&rptr, &vptr, 2);
			qtop = rptr;
		}
#endif	/* !FAKEESCAPE */
		else if (pc == PC_META) {
			n = quotemeta(&rptr, &vptr,
				strins[i], &qtop, qp, qedp);
			if (n < -1) return(0);
			setcursor(rptr, vptr);
			if (*qp == '\'' || !Xstrchr(DQ_METACHAR, strins[i])) {
				inputbuf[rptr] = strins[i];
				putstr(&rptr, &vptr, 1);
			}
			else {
#ifdef	FAKEESCAPE
				ch = strins[i];
#else
				ch = PESCAPE;
#endif
				if (insertcursor(&rptr, &vptr, 1, ch) < 0)
					return(0);
				inputbuf[rptr] = strins[i];
				putstr(&rptr, &vptr, 1);
			}
		}
		else {
			inputbuf[rptr] = strins[i];
			putstr(&rptr, &vptr, 1);
		}
	}

	return(0);
}

#ifndef	_NOCOMPLETE
static VOID NEAR selectfile(argc, argv)
int argc;
char *CONST *argv;
{
	char *cp;
	int duprow, dupminfilename, dupcolumns, dupdispmode, dupfnameofs;
	int i, j, n, len, maxlen;

	duprow = FILEPERROW;
	dupminfilename = minfilename;
	dupcolumns = curcolumns;
	dupdispmode = dispmode;
	dupfnameofs = fnameofs;
	minfilename = n_column;
	dispmode = F_FILETYPE;
	fnameofs = 0;

	if (argv) {
		selectlist = (namelist *)Xmalloc(argc * sizeof(namelist));
		maxlen = 0;
		for (i = 0; i < argc; i++) {
			memset((char *)&(selectlist[i]), 0, sizeof(namelist));
			cp = strrdelim(argv[i], 0);
			if (cp && !cp[1]) len = cp - argv[i];
			else {
				len = strlen(argv[i]);
				cp = NULL;
			}

			selectlist[i].name = Xstrndup(argv[i], len);
			selectlist[i].flags = (F_ISRED | F_ISWRI);
			selectlist[i].st_mode = (cp) ? S_IFDIR : S_IFREG;
			selectlist[i].tmpflags = F_STAT;
			len = strlen2(argv[i]);
			if (maxlen < len) maxlen = len;
		}
		i = sorton;
		sorton = 1;
		qsort(selectlist, argc, sizeof(namelist), cmplist);
		sorton = i;

		if (lcmdline < 0) {
			n = 1;
			Xcputnl();
			if (argc < LIMITSELECTWARN) i = 'Y';
			else {
				VOID_C XXcprintf("There are %d possibilities.",
					argc);
				XXcputs("  Do you really");
				Xcputnl();
				XXcputs("wish to see them all? (y or n)");
				Xtflush();
				for (;;) {
					i = getkey3(0, getinkcode(), 0);
					if (i < K_MIN) {
						i = Xtoupper(i);
						if (i == 'Y' || i == 'N')
							break;
					}
					ringbell();
				}
				Xcputnl();
				n += 2;
			}

			if (i == 'N') /*EMPTY*/;
			else if (n_column < maxlen + 2) {
				n += argc;
				for (i = 0; i < argc; i++) {
					VOID_C Xkanjiputs(selectlist[i].name);
					Xcputnl();
				}
			}
			else {
				tmpcolumns = n_column / (maxlen + 2);
				len = (argc + tmpcolumns - 1) / tmpcolumns;
				n += len;
				for (i = 0; i < len; i++) {
					for (j = i; j < argc; j += len)
						cputstr(maxlen + 2,
							selectlist[j].name);
					Xcputnl();
				}
			}

# ifdef	DEP_ORIGSHELL
			if (dumbmode || shellmode) {
				win_x = xpos;
				win_y = 0;
			}
			else
# endif
			if (ypos + n < maxline - 1) ypos += n;
			else ypos = maxline - 1;
			for (i = 0; i < argc; i++) Xfree(selectlist[i].name);
			Xfree(selectlist);
			selectlist = NULL;
			FILEPERROW = duprow;
			minfilename = dupminfilename;
			curcolumns = dupcolumns;
			dispmode = dupdispmode;
			fnameofs = dupfnameofs;

			return;
		}

		i = filetop(win);
		if (maxcmdline) tmpfileperrow = maxcmdline;
		else if (ypos >= i + FILEPERROW) tmpfileperrow = FILEPERROW;
		else {
			tmpfileperrow = ypos - i;
			if (tmpfileperrow < WFILEMIN) tmpfileperrow = WFILEMIN;
		}
		if ((n_column / 5) - 2 - 1 >= maxlen) tmpcolumns = 5;
		else if ((n_column / 3) - 2 - 1 >= maxlen) tmpcolumns = 3;
		else if ((n_column / 2) - 2 - 1 >= maxlen) tmpcolumns = 2;
		else tmpcolumns = 1;
		FILEPERROW = tmpfileperrow;
		curcolumns = tmpcolumns;
		tmpfilepos = listupfile(selectlist, argc, NULL, 1);
		maxselect = argc;
	}
	else if (argc < 0) {
		for (i = 0; i < maxselect; i++) Xfree(selectlist[i].name);
		Xfree(selectlist);
		selectlist = NULL;
	}
	else {
		FILEPERROW = tmpfileperrow;
		curcolumns = tmpcolumns;
		if (tmpfilepos >= maxselect) tmpfilepos %= (tmpfilepos - argc);
		else if (tmpfilepos < 0)
			tmpfilepos = maxselect - 1
				- ((maxselect - 1 - argc)
				% (argc - tmpfilepos));
		if (argc / FILEPERPAGE != tmpfilepos / FILEPERPAGE)
			tmpfilepos = listupfile(selectlist, maxselect,
				selectlist[tmpfilepos].name, 1);
		else if (argc != tmpfilepos) {
			putname(selectlist, argc, -1);
			putname(selectlist, tmpfilepos, 1);
		}
	}

	FILEPERROW = duprow;
	minfilename = dupminfilename;
	curcolumns = dupcolumns;
	dispmode = dupdispmode;
	fnameofs = dupfnameofs;
}

/*ARGSUSED*/
static int NEAR completestr(comline, cont, h)
int comline, cont, h;
{
# ifdef	DEP_ORIGSHELL
	int vartop;
# endif
	char *cp, *tmp, **argv;
	int i, i2, rw, n, ch, pc, ins, top, fix, argc, qtop, quote, quoted;

	if (selectlist && cont > 0) {
		selectfile(tmpfilepos++, NULL);
		setcursor(rptr, vptr);
		ringbell();
		return(-1);
	}

	quote = '\0';
# ifdef	DEP_ORIGSHELL
	vartop =
# endif
	top = 0;
# ifndef	NOUID
	if (h == HST_USER || h == HST_GROUP) quote = -1;
	else
# endif
	for (i = 0; i < rptr; i++) {
# ifdef	FAKEESCAPE
		pc = parsechar(&(inputbuf[i]), rptr - i,
			'$', EA_NOEVALQ, &quote, NULL);
# else
		pc = parsechar(&(inputbuf[i]), rptr - i,
			'$', EA_BACKQ, &quote, NULL);
# endif
		if (pc == PC_WCHAR) i++;
# ifdef	DEP_ORIGSHELL
		else if (pc == '$') vartop = i + 1;
# endif
# ifndef	FAKEESCAPE
		else if (pc == PC_ESCAPE) i++;
		else if (pc == PC_OPQUOTE) {
			if (inputbuf[i] == '`') top = i + 1;
		}
# endif
		else if (pc != PC_NORMAL) /*EMPTY*/;
		else if (i == top
		&& (n = getpathtop(&(inputbuf[i]), NULL, NULL)))
			i += n - 1;
		else if (inputbuf[i] == ':' || inputbuf[i] == '='
		|| Xstrchr(CMDLINE_DELIM, inputbuf[i]))
			top = i + 1;
	}
	if (comline && top > 0) {
		for (i = top - 1; i >= 0; i--)
			if (!Xisblank(inputbuf[i])) break;
		if (i >= 0 && !Xstrchr(SHELL_OPERAND, inputbuf[i]))
			comline = 0;
	}
# ifdef	DEP_ORIGSHELL
	if (vartop) {
		i = top + 1;
		if (quote) i++;
		if (vartop != i
		|| (vartop < rptr && !isidentchar(inputbuf[vartop])))
			vartop = 0;
		else {
			for (i = vartop; i < rptr; i++)
				if (!isidentchar2(inputbuf[i])) break;
			if (i < rptr) vartop = 0;
			else top = vartop;
		}
	}
# endif	/* DEP_ORIGSHELL */

	cp = Xstrndup(&(inputbuf[top]), rptr - top);
# ifdef	DEP_ORIGSHELL
	if (vartop) /*EMPTY*/;
	else
# endif
# ifndef	NOUID
	if (h == HST_USER || h == HST_GROUP) /*EMPTY*/;
	else
# endif
	cp = evalpath(cp, 0);

	if (selectlist && cont < 0) {
		argv = (char **)Xmalloc(1 * sizeof(char *));
		n = strlen(selectlist[tmpfilepos].name);
		i = (s_isdir(&(selectlist[tmpfilepos]))) ? 1 : 0;
		argv[0] = (char *)Xmalloc(n + i + 1);
		memcpy(argv[0], selectlist[tmpfilepos].name, n);
		if (i) argv[0][n] = _SC_;
		argv[0][n + i] = '\0';
		argc = 1;
	}
# ifndef	NOUID
#  ifndef	NOGETPWENT
	else if (h == HST_USER) {
		argv = NULL;
		n = strlen(cp);
		argc = completeuser(cp, n, 0, &argv, 0);
	}
#  endif
#  ifndef	NOGETGRENT
	else if (h == HST_GROUP) {
		argv = NULL;
		n = strlen(cp);
		argc = completegroup(cp, n, 0, &argv);
	}
#  endif
# endif	/* !NOUID */
# ifdef	DEP_ORIGSHELL
	else if (vartop) {
		argv = NULL;
		n = strlen(cp);
		argc = completeshellvar(cp, n, 0, &argv);
	}
# endif
	else {
		argv = NULL;
		argc = 0;
		n = strlen(cp);
		if (comline && !strdelim(cp, 1)) {
# ifdef	DEP_ORIGSHELL
			argc = completeshellcomm(cp, n, argc, &argv);
# else
			argc = completeuserfunc(cp, n, argc, &argv);
			argc = completealias(cp, n, argc, &argv);
			argc = completebuiltin(cp, n, argc, &argv);
			argc = completeinternal(cp, n, argc, &argv);
# endif
		}
# ifndef	_NOARCHIVE
		if (archivefile && !comline && *cp != _SC_)
			argc = completearch(cp, n, argc, &argv);
		else
# endif
		argc = completepath(cp, n, argc, &argv, comline);
		if (!argc && comline)
			argc = completepath(cp, n, argc, &argv, 0);
	}

	ins = strlen(getbasename(cp));
	Xfree(cp);
	if (!argc) {
		Xfree(argv);
		ringbell();
		return(-1);
	}

	cp = findcommon(argc, argv);
	if (argc != 1 || !cp) fix = '\0';
# ifndef	NOUID
	else if (h == HST_USER || h == HST_GROUP) fix = ' ';
# endif
	else fix = ((tmp = strrdelim(cp, 0)) && !tmp[1]) ? _SC_ : ' ';

	if (!cp || ((ins = (int)strlen(cp) - ins) <= 0 && fix != ' ')) {
		if (cont <= 0) ringbell();
		else {
			selectfile(argc, argv);
			if (lcmdline < 0) displaystr();
			setcursor(rptr, vptr);
		}
		for (i = 0; i < argc; i++) Xfree(argv[i]);
		Xfree(argv);
		Xfree(cp);
		return(-1);
	}
	for (i = 0; i < argc; i++) Xfree(argv[i]);
	Xfree(argv);

	qtop = top;
	if (!quote && inputbuf[top] == '~') {
		tmp = Xstrchr(&(inputbuf[top + 1]), _SC_);
		if (tmp) qtop = tmp - inputbuf + 1;
	}

	tmp = cp + (int)strlen(cp) - ins;
	if (fix == _SC_) ins--;
# ifndef	NOUID
	if (h == HST_USER || h == HST_GROUP) {
		VOID_C insertstr(tmp, ins, qtop, &quote, NULL);
		Xfree(cp);
		return(0);
	}
# endif

	quote = '\0';
	quoted = 0;
	i = top;
	i2 = vlen(inputbuf, top);
	while (i < rptr) {
		rw = 1;
		pc = parsechar(&(inputbuf[i]), rptr - i, '!', 0, &quote, NULL);
		if (pc == PC_WCHAR) rw++;
		else if (pc == PC_CLQUOTE) {
			quoted = i;
			qtop = i + 1;
		}
# ifndef	FAKEESCAPE
		else if (pc == PC_ESCAPE) {
			rw++;
			if (inputbuf[i + 1] == '!') qtop = i + 2;
			else if (Xstrchr(DQ_METACHAR, inputbuf[i + 1])) {
				n = quotemeta(&i, &i2,
					'\0', &qtop, &quote, &quoted);
				if (n < 0) {
					Xfree(cp);
					return(0);
				}
			}
			else if (inputbuf[i + 1] != '\'') {
				setcursor(i, i2);
				deletechar(i, i2, 1);
				delshift(i, 1);
				rptr--;
				vptr--;
				inputlen--;
				rw = 0;
			}
		}
# endif	/* !FAKEESCAPE */
		else if (pc == '!') {
			if (quote) {
				n = 2;
				ch = quote;
				quote = '\0';
			}
			else {
				n = 1;
				ch = '\0';
			}
			if (insertcursor(&i, &i2, n, ch) < 0) {
				Xfree(cp);
				return(0);
			}
			inputbuf[i] = PESCAPE;
			inputbuf[i + 1] = '!';
			putstr(&i, &i2, 2);
			rptr += n;
			vptr += n;
			qtop = i;
			rw = 0;
		}
		else if (pc == PC_OPQUOTE || pc == PC_SQUOTE) /*EMPTY*/;
		else if (Xstrchr(DQ_METACHAR, inputbuf[i])) {
			if (insertcursor(&i, &i2, 1, PESCAPE) < 0) {
				Xfree(cp);
				return(0);
			}
			rptr++;
			vptr++;
			qtop = i + 1;
		}
		else if (pc != PC_NORMAL) /*EMPTY*/;
		else if (Xstrchr(METACHAR, inputbuf[i])) {
			n = quotemeta(&i, &i2,
				inputbuf[i], &qtop, &quote, &quoted);
			if (n < 0) {
				Xfree(cp);
				return(0);
			}
		}
		i += rw;
		i2 += vlen(&(inputbuf[i]), rw);
	}
	setcursor(rptr, vptr);

	n = insertstr(tmp, ins, qtop, &quote, &quoted);
	Xfree(cp);
	if (n < 0) return(0);
	if (fix) {
		if (quote > '\0' && (fix != _SC_ || quoted < 0)) {
			if (insertcursor(&rptr, &vptr, 1, quote) < 0)
				return(0);
		}
		if (insertcursor(&rptr, &vptr, 1, fix) < 0) return(0);
	}

	return(0);
}
#endif	/* !_NOCOMPLETE */

#ifdef	DEP_KCONV
static u_int NEAR getucs2(ch)
int ch;
{
	char buf[MAXUTF8LEN + 1];
	int n;

	n = 0;
	if (ch & 0xff00) return((u_int)-1);
	buf[n++] = ch;
	if (!ismsb(ch)) /*EMPTY*/;
	else if ((ch = getch3()) == EOF) return((u_int)-1);
	else {
		buf[n++] = ch;
		if (isutf2(buf[0], buf[1])) /*EMPTY*/;
		else if ((ch = getch3()) == EOF) {
			ungetch3(buf[1]);
			return((u_int)-1);
		}
		else buf[n++] = ch;
	}
	buf[n] = '\0';

	return(ucs2fromutf8((u_char *)buf, NULL));
}

static VOID NEAR ungetch3(c)
int c;
{
	if (ungetnum3 >= arraysize(ungetbuf3)) return;
	memmove((char *)&(ungetbuf3[1]), (char *)&(ungetbuf3[0]),
		ungetnum3 * sizeof(u_char));
	ungetbuf3[0] = c;
	ungetnum3++;
}
#endif	/* DEP_KCONV */

static int NEAR getch3(VOID_A)
{
#ifdef	DEP_IME
	int c;
#endif

#ifdef	DEP_KCONV
	if (ungetnum3 > 0) return((int)ungetbuf3[--ungetnum3]);
#endif
#ifdef	DEP_IME
	if (imemode) return((getime(0, &c, 1) >= 0) ? c : EOF);
#endif
	if (!kbhit2(WAITKANJI * 1000L)) return(EOF);
	return(Xgetch());
}

static int NEAR getkanjikey(buf, ch)
char *buf;
int ch;
{
#ifdef	DEP_KCONV
	u_short ubuf[MAXNFLEN];
	char tmp[MAXUTF8LEN + 1];
	u_int u;
	int i, n, len, code;
#endif
	int ch2;

#ifdef	DEP_KCONV
	code = getinkcode();
	if (code == EUC && isekana2(ch)) {
		tmp[0] = (char)C_EKANA;
		tmp[1] = (ch & 0xff);
		tmp[2] = '\0';
		kanjiconv(buf, tmp, MAXKLEN, code, DEFCODE, L_INPUT);
		return(1);
	}
	if (code == SJIS && Xiskana(ch)) {
		tmp[0] = ch;
		tmp[1] = '\0';
		kanjiconv(buf, tmp, MAXKLEN, code, DEFCODE, L_INPUT);
		return(1);
	}
	if (code >= UTF8) {
		i = 0;
		for (;;) {
			if ((u = getucs2(ch)) == (u_int)-1) {
				if (i) ungetch3(ch);
				buf[0] = '\0';
				return(-1);
			}
			ubuf[i++] = u;
			if (i >= MAXNFLEN) break;
			if (code != M_UTF8 || (ch = getch3()) == EOF) break;
		}
		len = i;
		i = 0;
		if (!len) u = ch;
		else if (code != M_UTF8) u = ubuf[i++];
		else u = ucs2denormalization(ubuf, &i, code - UTF8);

		while (i < len) {
			tmp[ucs2toutf8(tmp, 0, ubuf[i++])] = '\0';
			for (n = 0; tmp[n]; n++) ungetch3(tmp[n]);
		}
		tmp[ucs2toutf8(tmp, 0, u)] = '\0';
		n = kanjiconv(buf, tmp, MAXKLEN, code, DEFCODE, L_INPUT);
# ifdef	CODEEUC
		if (isekana(buf, 0)) n = 1;
# endif
		return(n);
	}
#else	/* !DEP_KCONV */
# ifdef	CODEEUC
	if (isekana2(ch)) {
		buf[0] = (char)C_EKANA;
		buf[1] = (ch * 0xff);
		buf[2] = '\0';
		return(1);
	}
# else
	if (Xiskana(ch)) {
		buf[0] = ch;
		buf[1] = '\0';
		return(1);
	}
# endif
#endif	/* !DEP_KCONV */

	if (isinkanji1(ch, code)) {
		if ((ch2 = getch3()) == EOF || !isinkanji2(ch2, code)) {
			buf[0] = '\0';
			return(-1);
		}
#ifdef	DEP_KCONV
		tmp[0] = ch;
		tmp[1] = ch2;
		tmp[2] = '\0';
		kanjiconv(buf, tmp, MAXKLEN, code, DEFCODE, L_INPUT);
#else
		buf[0] = ch;
		buf[1] = ch2;
		buf[2] = '\0';
#endif
		return(2);
	}

	if (ch >= K_MIN) {
		buf[0] = '\0';
		return(-1);
	}

	buf[0] = ch;
	buf[1] = '\0';

	return((Xiscntrl(ch) || ismsb(ch)) ? 0 : 1);
}

static VOID NEAR copyhist(hist, keep)
CONST char *hist;
int keep;
{
#ifdef	DEP_ORIGSHELL
	int y1, y2, len2;

	len2 = vlen(inputbuf, inputlen);
	y1 = ptr2line(len2);
	if (dumbmode) {
		setcursor(0, 0);
		clearline();
	}
#endif	/* DEP_ORIGSHELL */

	if (!keep) {
		inputlen = (hist) ? strlen(hist) : 0;
		insertbuf(0);
		if (hist) memcpy(inputbuf, hist, inputlen + 1);
		else *inputbuf = '\0';
		if (rptr < 0 || rptr > inputlen) {
			rptr = inputlen;
			vptr = vlen(inputbuf, rptr);
		}
	}
	displaystr();

#ifdef	DEP_ORIGSHELL
	if (dumbmode) /*EMPTY*/;
	else if (shellmode) {
		y2 = ptr2line(vlen(inputbuf, inputlen));
		if (y1 > y2) {
			while (y1 > y2) {
				locate2(0, y1--);
				Xputterm(L_CLEAR);
			}
			setcursor(rptr, vptr);
		}
	}
#endif	/* DEP_ORIGSHELL */
}

static VOID NEAR _inputstr_up(histnop, h, tmp)
int *histnop, h;
char **tmp;
{
	keyflush();
#ifndef	_NOCOMPLETE
	if (completable(h) && selectlist) {
		selectfile(tmpfilepos--, NULL);
		setcursor(rptr, vptr);
	}
	else
#endif
#ifdef	DEP_ORIGSHELL
	if (dumbmode || vptr < maxcol)
#else
	if (vptr < maxcol)
#endif
	{
		if (nohist(h) || !history[h] || *histnop >= (int)histsize[h]
		|| !history[h][*histnop]) {
			ringbell();
			return;
		}

		if (!*tmp) {
			inputbuf[inputlen] = '\0';
			*tmp = Xstrdup(inputbuf);
		}
		rptr = -1;
		copyhist(history[h][(*histnop)++], 0);
	}
	else {
		if (!within(vptr) && !ptr2col(vptr)) {
			leftcursor();
			vptr--;
		}
		vptr -= maxcol;
		rptr = rlen(inputbuf, vptr);
		upcursor();
		if (onkanji1(inputbuf, rptr - 1)) {
			leftcursor();
			vptr--;
			rptr--;
		}
	}
}

static VOID NEAR _inputstr_down(histnop, h, tmp)
int *histnop, h;
char **tmp;
{
	int len2;

	len2 = vlen(inputbuf, inputlen);
	keyflush();
#ifndef	_NOCOMPLETE
	if (completable(h) && selectlist) {
		selectfile(tmpfilepos++, NULL);
		setcursor(rptr, vptr);
	}
	else
#endif
#ifdef	DEP_ORIGSHELL
	if (dumbmode || (vptr + maxcol > len2 || (vptr + maxcol == len2
	&& !within(len2) && !ptr2col(len2))))
#else
	if (vptr + maxcol > len2 || (vptr + maxcol == len2
	&& !within(len2) && !ptr2col(len2)))
#endif
	{
		if (nohist(h) || !history[h] || *histnop <= 0) {
			ringbell();
			return;
		}

		rptr = -1;
		if (--(*histnop) > 0) copyhist(history[h][*histnop - 1], 0);
		else {
			copyhist(*tmp, 0);
			Xfree(*tmp);
			*tmp = NULL;
		}
	}
	else {
		vptr += maxcol;
		rptr = rlen(inputbuf, vptr);
		downcursor();
		if (onkanji1(inputbuf, rptr - 1)) {
			leftcursor();
			vptr--;
			rptr--;
		}
	}
}

static VOID NEAR _inputstr_delete(VOID_A)
{
	int rw, vw;

	if (rptr >= inputlen) {
		ringbell();
		return;
	}

	getcharwidth(inputbuf, rptr, &rw, &vw);
	if (rptr + rw > inputlen) {
		ringbell();
		return;
	}

	deletechar(rptr, vptr, vw);
	delshift(rptr, rw);
	inputlen -= rw;
}

static VOID NEAR _inputstr_enter(VOID_A)
{
	int len, quote;

	if (!curfilename) {
		ringbell();
		return;
	}

	quote = '\0';
	keyflush();
	len = strlen(curfilename);
	if (insertstr(curfilename, len, rptr, &quote, NULL) < 0) {
		ringbell();
		return;
	}
	if (quote > '\0') VOID_C insertcursor(&rptr, &vptr, 1, quote);
	if (iseol(vptr)) setcursor(rptr, vptr);

	return;
}

#if	FD >= 2
static VOID NEAR _inputstr_case(upper)
int upper;
{
	int ch;

	keyflush();
	if (rptr >= inputlen) {
		ringbell();
		return;
	}
	if (!iswchar(inputbuf, rptr)) {
		ch = (upper)
			? Xtoupper(inputbuf[rptr]) : Xtolower(inputbuf[rptr]);
		if (ch != inputbuf[rptr]) {
			inputbuf[rptr++] = ch;
			vptr++;
			putcursor(ch, 1);
# ifdef	DEP_ORIGSHELL
			if (dumbmode) VOID_C checkcursor(rptr, vptr);
			else
# endif
			if (within(vptr) && ptr2col(vptr) < 1)
				setcursor(rptr, vptr);
			return;
		}
	}

	rightchar();
}

static int NEAR search_matchlen(VOID_A)
{
	int n;

	if (searchstr) {
		n = rptr - strlen(searchstr);
		if (n < 0) n = 0;
		for (; n < rptr; n++)
			if (!memcmp(searchstr, &(inputbuf[n]), rptr - n))
				return(n);
	}

	return(rptr);
}

static char *NEAR search_up(bias, histnop, h, tmp)
int bias, *histnop, h;
char **tmp;
{
	int n, cx, hlen, slen;

	searchmode = -1;
	if (!searchstr) return(NULL);

	slen = strlen(searchstr);
	cx = search_matchlen() - bias;
	if (cx > inputlen - slen) cx = inputlen - slen;
	for (; cx >= 0; cx--) {
		if (strncmp(&(inputbuf[cx]), searchstr, slen)) continue;
		rptr = cx + slen;
		vptr = vlen(inputbuf, rptr);
		return(NULL);
	}

	if (nohist(h) || !history[h]) {
		ringbell();
		searchmode = -2;
		return(NULL);
	}

	for (n = *histnop; n < (int)histsize[h]; n++) {
		if (!history[h][n]) break;
		hlen = strlen(history[h][n]);
		for (cx = hlen - slen; cx >= 0; cx--) {
			if (strncmp(&(history[h][n][cx]), searchstr, slen))
				continue;

			if (!*tmp) {
				inputbuf[inputlen] = '\0';
				*tmp = Xstrdup(inputbuf);
			}
			rptr = cx + slen;
			vptr = vlen(history[h][n], rptr);
			*histnop = n + 1;
			return(history[h][n]);
		}
	}

	ringbell();
	searchmode = -2;

	return(NULL);
}

static char *NEAR search_down(bias, histnop, h, tmp)
int bias, *histnop, h;
char **tmp;
{
	int n, cx, hlen, slen;

	searchmode = 1;
	if (!searchstr) return(NULL);

	slen = strlen(searchstr);
	cx = search_matchlen() + bias;
	for (; cx <= inputlen - slen; cx++) {
		if (strncmp(&(inputbuf[cx]), searchstr, slen)) continue;
		rptr = cx + slen;
		vptr = vlen(inputbuf, rptr);
		return(NULL);
	}

	if (nohist(h) || !history[h]) {
		ringbell();
		searchmode = 2;
		return(NULL);
	}

	for (n = *histnop - 2; n >= 0; n--) {
		hlen = strlen(history[h][n]);
		for (cx = 0; cx <= hlen - slen; cx++) {
			if (strncmp(&(history[h][n][cx]), searchstr, slen))
				continue;

			rptr = cx + slen;
			vptr = vlen(history[h][n], rptr);
			*histnop = n + 1;
			return(history[h][n]);
		}
	}

	if (*tmp && *histnop) {
		hlen = strlen(*tmp);
		for (cx = 0; cx <= hlen - slen; cx++) {
			if (strncmp(&((*tmp)[cx]), searchstr, slen)) continue;

			rptr = cx + slen;
			vptr = vlen(*tmp, rptr);
			*histnop = 0;
			return(*tmp);
		}
	}

	ringbell();
	searchmode = 2;

	return(NULL);
}
#endif	/* FD >= 2 */

static VOID NEAR _inputstr_input(buf, vw)
CONST char *buf;
int vw;
{
	int rw;

	rw = strlen(buf);
	if (vw <= 0 || preparestr(rw, vw) < 0) {
		ringbell();
		keyflush();
		return;
	}
	memcpy(&(inputbuf[rptr]), buf, rw);
	rptr += rw;
	VOID_C XXcprintf("%.*k", vw, buf);
	win_x += vw;
	vptr = vlen(inputbuf, rptr);
#ifdef	DEP_ORIGSHELL
	if (dumbmode) VOID_C checkcursor(rptr, vptr);
	else
#endif
	if (within(vptr) && ptr2col(vptr) < vw) setcursor(rptr, vptr);
}

static int NEAR _inputstr(def, comline, h)
int def, comline, h;
{
#if	!MSDOS
	keyseq_t key;
#endif
#if	FD >= 2
	ALLOC_T searchsize;
	CONST char *cp;
#endif
	char *tmphist, buf[MAXKLEN + 1];
	int i, n, ch, ch2, ovptr, hist, quote, sig;

	subwindow = 1;

#ifdef	DEP_ORIGSHELL
	if (dumbmode) maxcol -= RIGHTMARGIN;
	if (shellmode) sig = 0;
	else
#endif
	sig = 1;
	Xgetkey(-1, VI_INSERT, 0);
#ifndef	_NOCOMPLETE
	tmpfilepos = -1;
#endif
	rptr = inputlen;
	if (def >= 0 && def < maxcol) {
		while (def > inputlen) {
			insertbuf(0);
			inputbuf[inputlen++] = ' ';
		}
		rptr = def;
	}
	vptr = vlen(inputbuf, rptr);
	displaystr();
	keyflush();
	hist = 0;
	tmphist = NULL;
	quote = 0;
	ch = -1;
#if	FD >= 2
	searchmode = 0;
	searchstr = NULL;
	searchsize = (ALLOC_T)0;
#endif

	do {
		Xtflush();
		ch2 = ch;
		ovptr = vptr;
		if (quote) {
			i = ch = getkey2(sigalrm(sig), getinkcode(), 0);
			quote = 0;
#if	MSDOS
			switch (i) {
				case K_BS:
					i = C_BS;
					break;
				case K_DC:
					i = C_DEL;
					break;
				default:
					break;
			}
#else
			key.code = i;
			if (getkeyseq(&key) >= 0 && key.len == 1)
				i = *(key.str);
#endif

			n = getkanjikey(buf, i);
			if (n) {
				_inputstr_input(buf, n);
				ovptr = vptr;
				continue;
			}

			keyflush();
			ch = '\0';
			if (!*buf) continue;

			n = (Xiscntrl(buf[0])) ? 2 : 4;
			if (preparestr(1, n) < 0) {
				ringbell();
				continue;
			}

			inputbuf[rptr] = buf[0];
			putstr(&rptr, &vptr, 1);
			continue;
		}

#ifdef	DEP_ORIGSHELL
		if (shellmode && !inputlen) {
			if ((ch = Xgetkey(sig, 1, 0)) < 0) {
				ch = K_ESC;
				break;
			}
		}
		else
#endif
		ch = Xgetkey(sig, 0, 0);
#ifdef	DEP_ORIGSHELL
		if (shellmode && !comline && ch == cc_intr) break;
#endif

#if	FD >= 2
		if (searchmode) {
			cp = NULL;
			n = 0;
			switch (ch) {
				case K_CTRL('S'):
					keyflush();
					searchmode = 1;
					n++;
					break;
				case K_CTRL('R'):
					keyflush();
					searchmode = -1;
					n++;
					break;
				case K_BS:
					keyflush();
					if (searchstr
					&& (i = strlen(searchstr)) > 0)
						searchstr[i - 1] = '\0';
					else ringbell();
					break;
				default:
					i = getkanjikey(buf, ch);
					if (i <= 0) {
						keyflush();
						searchmode = 0;
						Xfree(searchstr);
						searchstr = NULL;
						searchsize = (ALLOC_T)0;
						break;
					}

					i = (searchstr)
						? strlen(searchstr) : 0;
					searchstr = c_realloc(searchstr,
						i + 1, &searchsize);
					searchstr[i++] = buf[0];
					if (buf[1]) searchstr[i++] = buf[1];
					searchstr[i] = '\0';
					break;
			}

			if (searchmode < 0)
				cp = search_up(n, &hist, h, &tmphist);
			else if (searchmode > 0)
				cp = search_down(n, &hist, h, &tmphist);
			copyhist(cp, (cp) ? 0 : 1);
			ovptr = vptr;

			if (searchmode) continue;
		}
#endif	/* FD >= 2 */

#ifdef	DEP_IME
		if (!imemode && !selectlist
		&& imekey >= 0 && ch == imekey && ch != K_ESC) {
				keyflush();
# ifdef	DEP_ORIGSHELL
				if (dumbmode) ringbell();
				else
# endif
				imemode = 1;
				continue;
		}
#endif	/* DEP_IME */

		switch (ch) {
			case K_RIGHT:
				keyflush();
#ifndef	_NOCOMPLETE
				if (completable(h) && selectlist) {
					i = tmpfilepos;
					tmpfilepos += tmpfileperrow;
					selectfile(i, NULL);
					ovptr = -1;
				}
				else
#endif
				if (rptr >= inputlen) ringbell();
#ifndef	_NOEDITMODE
				else if (isvimode()
				&& inputlen && rptr >= inputlen - 1)
					ringbell();
#endif
				else {
					rightchar();
					ovptr = vptr;
				}
				break;
			case K_LEFT:
				keyflush();
#ifndef	_NOCOMPLETE
				if (completable(h) && selectlist) {
					i = tmpfilepos;
					tmpfilepos -= tmpfileperrow;
					selectfile(i, NULL);
					ovptr = -1;
				}
				else
#endif
				if (rptr <= 0) ringbell();
				else {
					leftchar();
					ovptr = vptr;
				}
				break;
			case K_BEG:
				keyflush();
				rptr = vptr = 0;
				break;
			case K_EOL:
				keyflush();
				rptr = inputlen;
#ifndef	_NOEDITMODE
				if (isvimode() && inputlen) rptr--;
#endif
				vptr = vlen(inputbuf, rptr);
				break;
			case K_BS:
				keyflush();
				if (rptr <= 0) {
					ringbell();
					break;
				}
				leftchar();
				ovptr = vptr;
				_inputstr_delete();
				break;
			case K_DC:
				keyflush();
				_inputstr_delete();
#ifndef	_NOEDITMODE
				if (isvimode()
				&& inputlen && rptr >= inputlen) {
					leftchar();
					ovptr = vptr;
				}
#endif
				break;
			case K_DL:
				keyflush();
				if (rptr < inputlen) truncline();
				inputlen = rptr;
#ifndef	_NOEDITMODE
				if (isvimode() && inputlen) {
					leftchar();
					ovptr = vptr;
				}
#endif
				break;
			case K_CTRL('L'):
				keyflush();
#ifdef	DEP_ORIGSHELL
				if (dumbmode) rewritecursor(0, 0);
				else if (shellmode) {
					locate2(0, 0);
					Xputterm(L_CLEAR);
				}
				else
#endif
				{
#ifdef	DEP_PTY
					if (minline > 0) {
						i = filetop(win) + FILEPERROW;
						if (maxline != i) {
							ypos += i - maxline;
							maxline = i;
						}
					}
					else
#endif
					if (maxline != n_line) {
						ypos += n_line - maxline;
						maxline = n_line;
					}

					for (i = 0; i < WCMDLINE; i++) {
						if (ypos + i >= maxline) break;
						Xlocate(xpos, ypos + i);
						Xputterm(L_CLEAR);
					}
				}
				displaystr();
				break;
			case K_UP:
				_inputstr_up(&hist, h, &tmphist);
				ovptr = vptr;
				break;
			case K_DOWN:
				_inputstr_down(&hist, h, &tmphist);
				ovptr = vptr;
				break;
			case K_IL:
				keyflush();
				quote = 1;
				break;
			case K_ENTER:
				_inputstr_enter();
				ovptr = vptr;
				break;
#if	FD >= 2
			case K_IC:
				keyflush();
				overwritemode = 1 - overwritemode;
				break;
			case K_PPAGE:
				_inputstr_case(0);
				ovptr = vptr;
				break;
			case K_NPAGE:
				_inputstr_case(1);
				ovptr = vptr;
				break;
			case K_CTRL('S'):
				keyflush();
				if (nohist(h) || !history[h]) {
					ringbell();
					break;
				}
				searchmode = 1;
				Xfree(searchstr);
				searchstr = NULL;
				searchsize = (ALLOC_T)0;
				copyhist(NULL, 1);
				break;
			case K_CTRL('R'):
				keyflush();
				if (nohist(h) || !history[h]) {
					ringbell();
					break;
				}
				searchmode = -1;
				Xfree(searchstr);
				searchstr = NULL;
				searchsize = (ALLOC_T)0;
				copyhist(NULL, 1);
				break;
#endif	/* FD >= 2 */
#ifndef	_NOCOMPLETE
			case '\t':
				keyflush();
				if (!completable(h) || selectlist) {
					ringbell();
					break;
				}
				i = completestr(comline,
					(ch2 == ch) ? 1 : 0, h);
				if (i < 0) break;
				if (iseol(vptr)) ovptr = -1;
				break;
#endif	/* !_NOCOMPLETE */
			case K_CR:
				keyflush();
#ifndef	_NOCOMPLETE
				if (!completable(h) || !selectlist) break;
				ch = '\0';
				i = completestr(0, -1, h);
				if (i >= 0 && iseol(vptr)) ovptr = -1;
#endif	/* !_NOCOMPLETE */
				break;
			case K_ESC:
				keyflush();
				break;
			default:
				i = getkanjikey(buf, ch);
				_inputstr_input(buf, i);
				ovptr = vptr;
				break;
		}
#ifndef	_NOCOMPLETE
		if (completable(h) && selectlist && ch != '\t'
		&& ch != K_RIGHT && ch != K_LEFT
		&& ch != K_UP && ch != K_DOWN) {
			selectfile(-1, NULL);
			if (!maxcmdline) rewritefile(0);
			else {
				n = filetop(win);
				for (i = 0; i < maxcmdline; i++) {
					Xlocate(0, n + i);
					Xputterm(L_CLEAR);
				}
			}
			n = ypos;
			if (!lcmdline) ypos = L_CMDLINE + maxline - n_line;
			else if (lcmdline > 0) ypos = lcmdline;
			else ypos = maxline - 1;
			while (ypos > n) scrollup();
			displaystr();
			ovptr = -1;
		}
		if (ovptr != vptr) setcursor(rptr, vptr);
#endif	/* !_NOCOMPLETE */
		if (ch == K_ESC) {
#ifdef	DEP_ORIGSHELL
			if (shellmode);
			else
#endif
			break;
		}
	} while (ch != K_CR);

#ifndef	_NOCOMPLETE
	if (selectlist) selectfile(-1, NULL);
#endif
	setcursor(inputlen, -1);
	subwindow = 0;
	Xgetkey(-1, 0, 0);
	Xfree(tmphist);

	i = 0;
#ifdef	DEP_ORIGSHELL
	if (shellmode && ch == cc_intr) inputlen = 0;
	else
#endif
	if (ch == K_ESC) {
		inputlen = 0;
		if (maxcmdline) i = 0;
#ifdef	DEP_PTY
		else if (minline > 0) i = 1;
#endif
		else if (hideclock) i = 1;
	}
	inputbuf[inputlen] = '\0';

	Xtflush();
	hideclock = 0;
	if (i) rewritefile(1);

	return(ch);
}

static VOID NEAR dispprompt(s, set)
CONST char *s;
int set;
{
	static CONST char *prompt = NULL;
	char *buf;

	if (set > 0) prompt = s;
#if	FD >= 2
	else if (searchmode > 0) s = (searchmode > 1) ? SEAFF_K : SEAF_K;
	else if (searchmode < 0) s = (searchmode < -1) ? SEABF_K : SEAB_K;
#endif
	else if (!s) s = prompt;

	if (set < 0) {
		if (s && *s) {
			plen = strlen2(s);
#ifdef	DEP_ORIGSHELL
			if (dumbmode || shellmode) /*EMPTY*/;
			else
#endif
			plen++;
		}
		else {
			plen = evalprompt(&buf, promptstr);
			Xfree(buf);
#ifndef	DEP_ORIGSHELL
			plen++;
#endif
		}
		return;
	}

#ifdef	DEP_ORIGSHELL
	if (dumbmode) {
		backcursor(0);
		forwcursor(xpos);
		win_y = 0;
	}
	else if (shellmode) locate2(0, 0);
	else
#endif
	Xlocate(xpos, ypos);
	if (s && *s) {
#ifdef	DEP_ORIGSHELL
		if (dumbmode || shellmode) plen = Xkanjiputs(s);
		else
#endif
		{
			VOID_C XXputch(' ');
			plen = 1 + Xattrkanjiputs(s, 1);
		}
	}
	else {
#ifndef	DEP_ORIGSHELL
		VOID_C XXputch(' ');
		Xputterm(T_STANDOUT);
#endif
		plen = evalprompt(&buf, promptstr);
		VOID_C Xkanjiputs(buf);
		Xfree(buf);
#ifdef	DEP_ORIGSHELL
		if (dumbmode);
		else
#else
		plen++;
		Xputterm(END_STANDOUT);
#endif
		Xputterm(T_NORMAL);
	}

	win_x += plen;
}

char *inputstr(prompt, delsp, ptr, def, h)
CONST char *prompt;
int delsp, ptr;
CONST char *def;
int h;
{
	int i, len, ch, pc, qtop, quote, comline, dupwin_x, dupwin_y;

	dupwin_x = win_x;
	dupwin_y = win_y;
	win_x = 0;
	xpos = LEFTMARGIN;
#ifdef	DEP_ORIGSHELL
	dumbmode = (dumbterm || dumbshell) ? !Xtermmode(-1) : 0;
	if (dumbmode || shellmode) lcmdline = -1;
#endif

	if (!lcmdline) ypos = L_CMDLINE;
	else if (lcmdline > 0) ypos = lcmdline;
	else ypos = n_line - 1;

	maxcol = n_column - 1 - xpos;
	if (maxcmdline) {
		minline = ypos;
		maxline = ypos + maxcmdline;
	}
	else {
		minline = 0;
		maxline = n_line;
	}

#ifdef	DEP_PTY
# ifdef	DEP_ORIGSHELL
	if (!fdmode && shellmode) /*EMPTY*/;
	else
# endif
	if (isptymode() && parentfd < 0 && !maxcmdline) {
		minline = filetop(win);
		maxline = minline + FILEPERROW;
		ypos -= n_line - maxline;
	}
#endif	/* DEP_PTY */

#ifdef	DEP_ORIGSHELL
	if (dumbmode || shellmode) win_y = 0;
	else
#endif
	{
		win_y = ypos;
		for (i = 0; i < WCMDLINE; i++) {
			if (ypos + i >= maxline) break;
			Xlocate(0, ypos + i);
			Xputterm(L_CLEAR);
		}
	}
	dispprompt(prompt, 1);
	Xtflush();

	inputbuf = c_realloc(NULL, 0, &inputsize);
	inputlen = 0;
	if (def) {
		quote = '\0';
		qtop = 0;
		for (i = 0; def[i]; i++, inputlen++) {
			insertbuf(3);
			pc = parsechar(&(def[i]), -1,
				'!', EA_FINDMETA, &quote, NULL);
			if (pc == PC_WCHAR) inputbuf[inputlen++] = def[i++];
#ifndef	FAKEESCAPE
			else if (prompt) /*EMPTY*/;
			else if (pc == PC_EXMETA || pc == '!') {
				if (quote) {
					if (ptr > inputlen) ptr += 2;
					inputbuf[inputlen++] = quote;
					quote = '\0';
				}
				else {
					if (ptr > inputlen) ptr++;
				}
				inputbuf[inputlen++] = PESCAPE;
				qtop = inputlen + 1;
			}
#endif	/* !FAKEESCAPE */
			else if (pc == PC_META) {
				if (ptr > inputlen) ptr++;
				memmove(&(inputbuf[qtop + 1]),
					&(inputbuf[qtop]), inputlen++ - qtop);
				inputbuf[qtop] = quote = '"';
				if (Xstrchr(DQ_METACHAR, def[i])) {
					if (ptr > inputlen) ptr++;
#ifdef	FAKEESCAPE
					inputbuf[inputlen++] = def[i];
#else
					inputbuf[inputlen++] = PESCAPE;
#endif
				}
			}
			inputbuf[inputlen] = def[i];
		}
		if (quote) {
			if (ptr > inputlen) ptr++;
			inputbuf[inputlen++] = quote;
		}
	}
	inputbuf[inputlen] = '\0';

#ifndef	_NOSPLITWIN
	if (h == HST_PATH && windows > 1) {
		if ((i = win - 1) < 0) i = windows - 1;
		VOID_C entryhist(winvar[i].v_fullpath, HST_PATH | HST_UNIQ);
	}
#endif

	comline = (h == HST_COMM) ? 1 : 0;
#ifdef	DEP_ORIGSHELL
	if (promptstr == promptstr2) comline = 0;
	lastofs2 = 0;
#endif
	ch = _inputstr(ptr, comline, h);
	win_x = dupwin_x;
	win_y = dupwin_y;
	len = strlen(inputbuf);

#ifdef	DEP_ORIGSHELL
	if (dumbmode || shellmode) prompt = NULL;
	dumbmode = 0;
#endif
	if (!prompt || !*prompt) {
		if (ch != K_ESC) {
			if (ch == cc_intr) {
				XXcputs("^C");
				ch = K_ESC;
			}
#ifdef	DEP_PTY
			if (minline > 0
			&& ypos + ptr2line(vlen(inputbuf, len)) >= maxline - 1)
				scrollup();
			else
#endif
			Xcputnl();
		}
	}
	else {
		for (i = 0; i < WCMDLINE; i++) {
			if (ypos + i >= maxline) break;
			Xlocate(0, ypos + i);
			Xputterm(L_CLEAR);
		}
		Xlocate(win_x, win_y);
		Xtflush();
		if (maxcmdline) /*EMPTY*/;
		else if ((!lcmdline && ypos < L_CMDLINE)
		|| (lcmdline > 0 && ypos < lcmdline + maxline))
			rewritefile(1);
	}
	lcmdline = maxcmdline = 0;

	if (ch == K_ESC) {
		Xfree(inputbuf);
		return(NULL);
	}

	if (delsp && len > 0 && inputbuf[len - 1] == ' ' && yesno(DELSP_K)) {
		for (len--; len > 0; len--)
			if (inputbuf[len - 1] != ' ') break;
		inputbuf[len] = '\0';
	}

	return(inputbuf);
}

static int NEAR truncstr(s)
CONST char *s;
{
	char *cp, *tmp;
	int n, len;

	n = strlen2(s);
	if ((len = n + YESNOSIZE - n_lastcolumn) <= 0
	|| !(cp = Xstrchr(s, '[')) || !(tmp = Xstrchr(cp, ']')))
		return(n);

	cp++;
	len = tmp - cp - len;
	if (len <= 0) len = 0;
	else if (onkanji1(cp, len - 1)) len--;
#ifdef	CODEEUC
	else if (isekana(cp, len - 1)) len--;
#endif
	Xstrcpy(&(cp[len]), tmp);

	return(n);
}

static int NEAR yesnomes(mes)
CONST char *mes;
{
	int len;

#ifdef	DEP_ORIGSHELL
	if (dumbmode) forwline(xpos);
	else if (shellmode) locate2(0, 0);
	else
#endif
	Xlocate(xpos, ypos);
#ifdef	DEP_ORIGSHELL
	if (dumbmode) /*EMPTY*/;
	else
#endif
	{
		Xputterm(L_CLEAR);
		Xputterm(T_STANDOUT);
	}
	len = kanjiputs2(mes, n_lastcolumn - YESNOSIZE, -1);
	win_x += len;
	XXcputs(YESNOSTR);
	win_x += YESNOSIZE;
#ifdef	DEP_ORIGSHELL
	if (dumbmode) /*EMPTY*/;
	else
#endif
	Xputterm(END_STANDOUT);
	Xtflush();

	return(len);
}

#ifdef	USESTDARGH
/*VARARGS1*/
int yesno(CONST char *fmt, ...)
#else
/*VARARGS1*/
int yesno(fmt, va_alist)
CONST char *fmt;
va_dcl
#endif
{
	va_list args;
	int len, ch, x, dupwin_x, dupwin_y, duperrno, ret;
	char *buf;

	VA_START(args, fmt);
	VOID_C vasprintf2(&buf, fmt, args);
	va_end(args);

	dupwin_x = win_x;
	dupwin_y = win_y;
	duperrno = errno;
	win_x = xpos = 0;
#ifdef	DEP_ORIGSHELL
	dumbmode = (dumbterm || dumbshell) ? !Xtermmode(-1) : 0;
	if (dumbmode || shellmode) lcmdline = -1;
#endif

	minline = 0;
	maxline = n_line;
	if (!lcmdline) ypos = L_MESLINE;
	else if (lcmdline > 0) ypos = lcmdline;
	else ypos = maxline - 1;

#ifdef	DEP_ORIGSHELL
	if (dumbmode || shellmode) win_y = 0;
	else
#endif
	win_y = ypos;

	len = truncstr(buf);
	ret = yesnomes(buf);
	if (ret < len) len = ret;
	if (win_x >= n_column) win_x = n_column - 1;

	subwindow = 1;
	Xgetkey(-1, 0, 0);

	ret = 1;
	x = len + 1;
	do {
		keyflush();
#ifdef	DEP_ORIGSHELL
		if (dumbmode) {
			if (xpos + x < win_x) backcursor(xpos + x);
			else while (xpos + x > win_x) {
				VOID_C XXputch(YESNOSTR[win_x - xpos - len]);
				win_x++;
			}
		}
		else
#endif
		locate2(x, 0);
		Xtflush();
		switch (ch = Xgetkey(1, 0, 0)) {
			case 'y':
			case 'Y':
				ret = 1;
				ch = K_CR;
				break;
			case 'n':
			case 'N':
			case ' ':
			case K_ESC:
				ret = 0;
				ch = K_CR;
				break;
			case K_RIGHT:
				ret = 0;
				break;
			case K_LEFT:
				ret = 1;
				break;
			case K_CTRL('L'):
				maxline = n_line;
				if (!lcmdline) ypos = L_MESLINE;
				else if (lcmdline > 0) ypos = lcmdline;
				else ypos = maxline - 1;
				yesnomes(buf);
				break;
			default:
				break;
		}
		x = len + 1 + (1 - ret) * 2;
	} while (ch != K_CR);
	Xfree(buf);

#ifdef	DEP_ORIGSHELL
	if (dumbmode) Xcputnl();
	else
#endif
	if (lcmdline) {
		locate2(x, 0);
		VOID_C XXputch((ret) ? 'Y' : 'N');
		Xcputnl();
	}
	else {
		locate2(0, 0);
		Xputterm(L_CLEAR);
	}
#ifdef	DEP_ORIGSHELL
	dumbmode = 0;
#endif
	Xtflush();
	lcmdline = 0;

	win_x = dupwin_x;
	win_y = dupwin_y;
	subwindow = 0;
	Xgetkey(-1, 0, 0);
	errno = duperrno;

	return(ret);
}

VOID warning(n, s)
int n;
CONST char *s;
{
	char *tmp, *err;
	int y, len, wastty, dupwin_x, dupwin_y;

	dupwin_x = win_x;
	dupwin_y = win_y;
	subwindow = 1;

	err = Xstrerror((n < 0) ? errno : n);
	tmp = NULL;
	if (!s) s = err;
	else if (n) {
		len = n_lastcolumn - strlen2(err) - 3;
		tmp = Xmalloc(n_lastcolumn * KANAWID + 1);
		VOID_C strncpy2(tmp, s, &len, -1);
		strcat(tmp, ": ");
		strcat(tmp, err);
		s = tmp;
	}
	ringbell();

	y = (lcmdline) ? lcmdline : L_MESLINE;
	Xlocate(0, y);
	Xputterm(L_CLEAR);
	win_x = attrkanjiputs2(s, n_lastcolumn, -1);
	win_y = y;
	Xtflush();

	if (win_x >= n_lastcolumn) win_x = n_lastcolumn - 1;

	if (!(wastty = isttyiomode)) Xttyiomode(1);
	keyflush();
	do {
		getkey3(sigalrm(1), getinkcode(), 0);
	} while (kbhit2(WAITAFTERWARN * 1000L));
	if (!wastty) Xstdiomode();

	win_x = dupwin_x;
	win_y = dupwin_y;
	subwindow = 0;

	if (n && !lcmdline) rewritefile(1);
	else {
		Xlocate(0, y);
		Xputterm(L_CLEAR);
		Xlocate(win_x, win_y);
		Xtflush();
	}

	Xfree(tmp);
	hideclock = lcmdline = 0;
}

static int NEAR selectcnt(max, str, multi)
int max;
char *CONST *str;
int multi;
{
	int i, len;

	for (i = len = 0; i < max; i++) if (str[i]) {
		len += strlen2(str[i]) + 1;
		if (multi) len++;
	}

	return(len);
}

static int NEAR selectadj(max, x, str, tmpstr, xx, multi)
int max, x;
CONST char *CONST *str;
char **tmpstr;
int *xx, multi;
{
	char *cp, **new;
	int i, len, maxlen;

	for (i = 0; i < max; i++) {
		Xfree(tmpstr[i]);
		tmpstr[i] = Xstrdup(str[i]);
	}
	len = selectcnt(max, tmpstr, multi);

	if (x + len < n_lastcolumn) /*EMPTY*/;
	else if ((x = n_lastcolumn - 1 - len) >= 0) /*EMPTY*/;
	else {
		x = maxlen = 0;
		new = (char **)Xmalloc(max * sizeof(char *));
		for (i = 0; i < max; i++) {
			if (!(cp = tmpstr[i])) {
				new[i] = NULL;
				continue;
			}
			if (Xisupper(*cp) && cp[1] == ':')
				for (cp += 2; *cp == ' '; cp++) /*EMPTY*/;
			len = strlen3(cp);
			if (len > maxlen) maxlen = len;
			new[i] = Xstrdup(cp);
		}

		for (; maxlen > 0; maxlen--) {
			for (i = 0; i < max; i++) if (new[i]) {
				len = maxlen;
				tmpstr[i] = Xrealloc(tmpstr[i],
					len * KANAWID + 1);
				VOID_C strncpy2(tmpstr[i], new[i], &len, -1);
			}
			if (x + selectcnt(max, tmpstr, multi) < n_lastcolumn)
				break;
		}
		for (i = 0; i < max; i++) Xfree(new[i]);
		Xfree(new);
		if (maxlen <= 0) return(-1);
	}

	xx[0] = 0;
	for (i = 0; i < max; i++) {
		if (!tmpstr[i]) {
			xx[i + 1] = xx[i];
			continue;
		}
		xx[i + 1] = xx[i] + strlen2(tmpstr[i]) + 1;
		if (multi) (xx[i + 1])++;
	}

	return(x);
}

static VOID NEAR selectmes(num, max, x, str, val, xx, multi)
int num, max, x;
char *CONST str[];
int val[], *xx, multi;
{
	int i;

	Xlocate(x, L_MESLINE);
	Xputterm(L_CLEAR);
	for (i = 0; i < max; i++) {
		if (!str[i]) continue;
		Xlocate(x + xx[i] + 1, L_MESLINE);
		if (multi) VOID_C XXputch((val[i]) ? '*' : ' ');
		VOID_C Xattrkanjiputs(str[i], i == num);
	}
}

int selectstr(num, max, x, str, val)
int *num, max, x;
CONST char *CONST str[];
int val[];
{
	char **tmpstr;
	int i, ch, old, new, multi, tmpx, dupwin_x, dupwin_y, *xx, *initial;

	dupwin_x = win_x;
	dupwin_y = win_y;
	subwindow = 1;
	Xgetkey(-1, 0, 0);

	xx = (int *)Xmalloc((max + 1) * sizeof(int));
	initial = (int *)Xmalloc(max * sizeof(int));
	tmpstr = (char **)Xmalloc(max * sizeof(char *));

	new = 0;
	multi = (num) ? 0 : 1;
	for (i = 0; i < max; i++) {
		tmpstr[i] = NULL;
		initial[i] = (str[i] && Xisupper(*(str[i]))) ? *str[i] : -1;
		if (num && val[i] == *num) new = i;
	}
	tmpx = selectadj(max, x, str, tmpstr, xx, multi);
	selectmes(new, max, tmpx, tmpstr, val, xx, multi);

	win_y = L_MESLINE;
	do {
		keyflush();
		win_x = tmpx + xx[new + 1];
		Xlocate(win_x, win_y);
		Xtflush();
		old = new;

		switch (ch = Xgetkey(1, 0, 0)) {
			case K_RIGHT:
				for (new++; new != old; new++) {
					if (new >= max) new = 0;
					if (tmpstr[new]) break;
				}
				break;
			case K_LEFT:
				for (new--; new != old; new--) {
					if (new < 0) new = max - 1;
					if (tmpstr[new]) break;
				}
				break;
			case K_CTRL('L'):
				win_y = L_MESLINE;
				tmpx = selectadj(max, x,
					str, tmpstr, xx, multi);
				selectmes(new, max, tmpx,
					tmpstr, val, xx, multi);
				break;
			case ' ':
				if (num) break;
				val[new] = (val[new]) ? 0 : 1;
				Xlocate(tmpx + xx[new] + 1, L_MESLINE);
				VOID_C XXputch((val[new]) ? '*' : ' ');
				break;
			default:
				if (!Xisalpha(ch)) break;
				ch = Xtoupper(ch);
				for (i = 0; i < max; i++)
					if (ch == initial[i]) break;
				if (i >= max) break;
				new = i;
				if (num) {
					ch = K_CR;
					break;
				}
				val[new] = (val[new]) ? 0 : 1;
				Xlocate(tmpx + xx[new] + 1, L_MESLINE);
				VOID_C XXputch((val[new]) ? '*' : ' ');
				break;
		}
		if (new != old) {
			i = tmpx + 1;
			if (!num) i++;
			Xlocate(i + xx[new], L_MESLINE);
			VOID_C Xattrkanjiputs(tmpstr[new], 1);
			Xlocate(i + xx[old], L_MESLINE);
			if (stable_standout) Xputterm(END_STANDOUT);
			else VOID_C Xkanjiputs(tmpstr[old]);
		}
	} while (ch != K_ESC && ch != K_CR && ch != cc_intr);

	win_x = dupwin_x;
	win_y = dupwin_y;
	subwindow = 0;
	Xgetkey(-1, 0, 0);

	if (stable_standout) {
		Xlocate(tmpx + 1, L_MESLINE);
		Xputterm(L_CLEAR);
	}
	if (num) {
		if (ch != K_CR) new = -1;
		else *num = val[new];
		for (i = 0; i < max; i++) {
			if (!tmpstr[i]) continue;
			Xlocate(tmpx + xx[i] + 1, L_MESLINE);
			if (i == new) VOID_C Xkanjiputs(tmpstr[i]);
			else cputspace(strlen2(tmpstr[i]));
		}
	}
	else {
		if (ch != K_CR) for (i = 0; i < max; i++) val[i] = 0;
		for (i = 0; i < max; i++) {
			if (!tmpstr[i]) continue;
			Xlocate(tmpx + xx[i] + 1, L_MESLINE);
			VOID_C XXputch(' ');
			if (val[i]) VOID_C Xkanjiputs(tmpstr[i]);
			else cputspace(strlen2(tmpstr[i]));
		}
	}
	Xlocate(win_x, win_y);
	Xtflush();
	Xfree(xx);
	Xfree(initial);
	for (i = 0; i < max; i++) Xfree(tmpstr[i]);
	Xfree(tmpstr);

	return(ch);
}

#ifdef	DEP_URLPATH
char *inputpass(VOID_A)
{
	CONST char *cp;
	char *buf, kbuf[MAXKLEN + 1];
	ALLOC_T size;
	int n, ch, x, y, len, max, wastty, dupwin_x, dupwin_y;

	dupwin_x = win_x;
	dupwin_y = win_y;
	subwindow = 1;
	Xgetkey(-1, 0, 0);

	y = L_CMDLINE;
	cp = PASWD_K;
	if (!(wastty = isttyiomode)) {
		x = strlen2(cp);
		VOID_C Xfprintf(Xstderr, "%k", cp);
		Xfflush(Xstderr);
	}
	else {
		Xlocate(0, y);
		Xputterm(L_CLEAR);
		VOID_C XXputch(' ');
		x = 1 + attrkanjiputs2(cp, n_column, -1);
		win_x = x;
		win_y = y;
		Xtflush();
	}
	max = n_column - 1 - x;

	keyflush();
	buf = c_realloc(NULL, 0, &size);
	len = 0;
	for (;;) {
		if (!wastty) Xttyiomode(1);
		ch = getkey3(sigalrm(1), getinkcode(), 0);
		n = getkanjikey(kbuf, ch);
		if (!wastty) Xstdiomode();

		if (ch == K_CR) break;
		else if (ch == K_BS) {
			if (!len) ch = -1;
			else {
				len--;
				if (!hidepasswd) {
					if (wastty) {
						VOID_C XXcprintf("%c %c",
							C_BS, C_BS);
						Xtflush();
						win_x--;
					}
					else {
						Xfputs("\b \b", Xstderr);
						Xfflush(Xstderr);
					}
				}
			}
		}
		else if (ch == K_ESC) {
			len = 0;
			break;
		}
		else if (n != 1 || ismsb(ch) || kbuf[1]) ch = -1;
		else if (!hidepasswd && len >= max) ch = -1;
		else {
			buf = c_realloc(buf, len + 1, &size);
			buf[len++] = ch;
			if (!hidepasswd) {
				ch = '*';
				if (wastty) {
					VOID_C XXputch(ch);
					Xtflush();
					win_x++;
				}
				else {
					Xfputc(ch, Xstderr);
					Xfflush(Xstderr);
				}
			}
		}

		if (ch >= 0) /*EMPTY*/;
		else if (wastty) {
			ringbell();
			Xtflush();
		}
		else {
			Xfputc('\007', Xstderr);
			Xfflush(Xstderr);
		}
	}

	win_x = dupwin_x;
	win_y = dupwin_y;
	subwindow = 0;

	if (wastty) {
		Xlocate(0, y);
		Xputterm(L_CLEAR);
		Xlocate(win_x, win_y);
		Xtflush();
	}
	else {
		VOID_C fputnl(Xstderr);
		Xfflush(Xstderr);
	}

	buf = Xrealloc(buf, len + 1);
	buf[len] = '\0';

	return(buf);
}
#endif	/* DEP_URLPATH */
