/* $Id: linein.c,v 1.1.1.1 2001/11/08 05:15:04 a-ito Exp $ */
#include "fm.h"
#include "local.h"
#include "myctype.h"
#ifdef MOUSE
#ifdef USE_GPM
#include <gpm.h>
#endif
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
extern int do_getch();
#define getch() do_getch()
#endif /* USE_GPM */
#endif /* MOUSE */
#ifdef __EMX__
#include <sys/kbdscan.h>
#endif
#define STR_LEN 1024
#define CLEN (COLS - 2)
static Str strBuf;
static Lineprop strProp[STR_LEN];
static Str CompleteBuf;
static Str CFileName;
static Str CBeforeBuf;
static Str CAfterBuf;
static Str CDirBuf;
static char **CFileBuf = NULL;
static int NCFileBuf;
static int NCFileOffset;
static void insertself(char c),
_mvR(void), _mvL(void), _mvRw(void), _mvLw(void), delC(void), insC(void),
_mvB(void), _mvE(void), _enter(void), _quo(void), _bs(void), _bsw(void),
killn(void), killb(void), _inbrk(void), _esc(void),
_prev(void), _next(void), _compl(void), _rcompl(void), _tcompl(void),
_dcompl(void), _rdcompl(void);
#ifdef _EMX_
static int getcntrl();
#endif
static int terminated(unsigned char c);
#define iself ((void(*)())insertself)
static void next_compl(int next);
static void next_dcompl(int next);
static Str doComplete(Str ifn, int *status, int next);
void (*InputKeymap[32]) () = {
/* C-@ C-a C-b C-c C-d C-e C-f C-g */
_compl, _mvB, _mvL, _inbrk, delC, _mvE, _mvR, _inbrk,
/* C-h C-i C-j C-k C-l C-m C-n C-o */
_bs, iself, _enter, killn, iself, _enter, _next, iself,
/* C-p C-q C-r C-s C-t C-u C-v C-w */
_prev, _quo, _bsw, iself, _mvLw, killb, _quo, iself,
/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */
_tcompl,_mvRw, iself, _esc, iself, iself, iself, iself,
};
static int setStrType(Str str, Lineprop * prop);
static void addPasswd(char *p, Lineprop *pr, int len, int pos, int limit);
static void addStr(char *p, Lineprop *pr, int len, int pos, int limit);
static int CPos, CLen, offset;
static int i_cont, i_broken, i_quote;
static int cm_mode, cm_next, cm_clear, cm_disp_next, cm_disp_clear;
static int need_redraw, is_passwd;
static int move_word;
static Hist *CurrentHist;
static Str strCurrentBuf;
static int use_hist;
#ifdef JP_CHARSET
static int in_kanji;
static void ins_kanji(Str tmp);
#endif
char *
inputLineHist(char *prompt, char *def_str, int flag, Hist * hist)
{
int opos, x, y, lpos, rpos, epos;
unsigned char c;
char *p;
Lineprop mode;
#ifdef JP_CHARSET
Str tmp = Strnew();
#endif /* JP_CHARSET */
mode = PC_ASCII;
#ifdef JP_CHARSET
in_kanji = FALSE;
#endif
is_passwd = FALSE;
move_word = FALSE;
CurrentHist = hist;
if (hist != NULL) {
use_hist = TRUE;
strCurrentBuf = NULL;
} else {
use_hist = FALSE;
}
if (flag & IN_URL) {
cm_mode = CPL_ALWAYS | CPL_URL;
move_word = TRUE;
} else if (flag & IN_FILENAME) {
cm_mode = CPL_ALWAYS;
move_word = TRUE;
} else if (flag & IN_PASSWORD) {
cm_mode = CPL_NEVER;
is_passwd = TRUE;
} else if (flag & IN_COMMAND)
cm_mode = CPL_ON;
else
cm_mode = CPL_OFF;
opos = strlen(prompt);
epos = CLEN - opos;
if (epos < 0)
epos = 0;
lpos = epos / 3;
rpos = epos * 2 / 3;
offset = 0;
if (def_str) {
strBuf = Strnew_charp(def_str);
CLen = CPos = setStrType(strBuf, strProp);
} else {
strBuf = Strnew();
CLen = CPos = 0;
}
i_cont = TRUE;
i_broken = FALSE;
i_quote = FALSE;
cm_next = FALSE;
cm_disp_next = -1;
need_redraw = FALSE;
do {
x = calcPosition(strBuf->ptr, strProp, CLen, CPos, 0, CP_FORCE);
if (x - rpos > offset) {
y = calcPosition(strBuf->ptr, strProp, CLen, CLen, 0, CP_AUTO);
if (y - epos > x - rpos)
offset = x - rpos;
else if (y - epos > 0)
offset = y - epos;
} else if (x - lpos < offset) {
if (x - lpos > 0)
offset = x - lpos;
else
offset = 0;
}
move(LASTLINE, 0);
addstr(prompt);
if (is_passwd)
addPasswd(strBuf->ptr, strProp, CLen, offset, COLS - opos);
else
addStr(strBuf->ptr, strProp, CLen, offset, COLS - opos);
clrtoeolx();
move(LASTLINE, opos + x - offset);
refresh();
next_char:
c = getch();
#ifdef __EMX__
if (c == 0) {
if (!(c = getcntrl()))
goto next_char;
}
#endif
cm_clear = TRUE;
cm_disp_clear = TRUE;
#ifdef JP_CHARSET
if (mode == PC_KANJI1) {
mode = PC_KANJI2;
if (CLen >= STR_LEN)
goto next_char;
Strcat_char(tmp, (c | (DisplayCode == CODE_SJIS ? 0 : 0x80)));
tmp = conv_str(tmp,
(DisplayCode == CODE_SJIS ? CODE_SJIS : CODE_EUC), InnerCode);
ins_kanji(tmp);
}
else
#endif
if (!i_quote &&
(((cm_mode & CPL_ALWAYS) && (c == CTRL_I || c == ' ')) ||
((cm_mode & CPL_ON) && (c == CTRL_I)))) {
#ifdef EMACS_LIKE_LINEEDIT
if (cm_next) {
_dcompl();
need_redraw = TRUE;
}
else {
_compl();
cm_disp_next = -1;
}
#else
_compl();
cm_disp_next = -1;
}
else if (!i_quote && CLen == CPos &&
(cm_mode & CPL_ALWAYS || cm_mode & CPL_ON) && c == CTRL_D) {
_dcompl();
need_redraw = TRUE;
#endif
}
else if (!i_quote && c == DEL_CODE) {
_bs();
cm_next = FALSE;
cm_disp_next = -1;
}
else if (!i_quote && c < 0x20) { /* Control code */
(*InputKeymap[(int) c]) (c);
if (cm_clear)
cm_next = FALSE;
if (cm_disp_clear)
cm_disp_next = -1;
}
#ifdef JP_CHARSET
else if (DisplayCode == CODE_SJIS && 0xa0 <= c && c <= 0xdf) {
i_quote = FALSE;
cm_next = FALSE;
if (CLen >= STR_LEN)
goto next_char;
Strclear(tmp);
Strcat_char(tmp, c);
tmp = conv_str(tmp, DisplayCode, InnerCode);
ins_kanji(tmp);
}
else if ((c & 0x80) || in_kanji) { /* Kanji 1 */
i_quote = FALSE;
cm_next = FALSE;
cm_disp_next = -1;
if (CLen >= STR_LEN - 1)
goto next_char;
Strclear(tmp);
Strcat_char(tmp, (c | 0x80));
mode = PC_KANJI1;
goto next_char;
}
#endif /* JP_CHARSET */
else {
i_quote = FALSE;
cm_next = FALSE;
cm_disp_next = -1;
if (CLen >= STR_LEN)
goto next_char;
insC();
strBuf->ptr[CPos] = c;
if (!is_passwd && IS_CNTRL(c))
strProp[CPos] = PC_CTRL;
else
strProp[CPos] = PC_ASCII;
CPos++;
mode = PC_ASCII;
}
} while (i_cont);
if (need_redraw)
displayBuffer(Currentbuf, B_FORCE_REDRAW);
if (i_broken)
return NULL;
move(LASTLINE, 0);
refresh();
p = strBuf->ptr;
if (flag & (IN_FILENAME | IN_COMMAND)) {
SKIP_BLANKS(p);
}
if (use_hist && !(flag & IN_URL) && *p != '\0') {
char *q = lastHist(hist);
if (! q || strcmp(q, p))
pushHist(hist, p);
}
if (flag & IN_FILENAME)
return expandName(p);
else
return allocStr(p, 0);
}
#ifdef __EMX__
static int
getcntrl(void)
{
switch (getch()) {
case K_DEL:
return CTRL_D;
case K_LEFT:
retrun CTRL_B;
case K_RIGHT:
return CTRL_F;
case K_UP:
return CTRL_P;
case K_DOWN:
return CTRL_N;
case K_HOME:
case K_CTRL_LEFT:
return CTRL_A;
case K_END:
case K_CTRL_RIGHT:
return CTRL_E;
case K_CTRL_HOME:
return CTRL_U;
case K_CTRL_END:
return CTRL_K;
}
return 0;
}
#endif
static void
addPasswd(char *p, Lineprop *pr, int len, int offset, int limit)
{
int rcol = 0, ncol;
ncol = calcPosition(p, pr, len, len, 0, CP_AUTO);
if (ncol> offset + limit)
ncol = offset + limit;
if (offset) {
addChar('{', 0);
rcol = offset + 1;
}
for (; rcol < ncol; rcol++)
addChar('*', 0);
}
static void
addStr(char *p, Lineprop *pr, int len, int offset, int limit)
{
int i = 0, rcol = 0, ncol, delta = 1;
if (offset) {
for (i = 0; i < len; i++) {
if (calcPosition(p, pr, len, i, 0, CP_AUTO) > offset)
break;
}
if (i >= len)
return;
#ifdef JP_CHARSET
while (pr[i] == PC_KANJI2)
i++;
#endif
addChar('{', 0);
rcol = offset + 1;
ncol = calcPosition(p, pr, len, i, 0, CP_AUTO);
for (; rcol < ncol; rcol++)
addChar(' ', 0);
}
for (; i < len; i += delta) {
#ifdef JP_CHARSET
if (CharType(pr[i]) == PC_KANJI1)
delta = 2;
else
delta = 1;
#endif
ncol = calcPosition(p, pr, len, i + delta, 0, CP_AUTO);
if (ncol - offset > limit)
break;
if (p[i] == '\t') {
for (; rcol < ncol; rcol++)
addChar(' ', 0);
continue;
#ifdef JP_CHARSET
} else if (delta == 2) {
addChar(p[i], pr[i]);
addChar(p[i+1], pr[i+1]);
#endif
} else
addChar(p[i], pr[i]);
rcol = ncol;
}
}
#ifdef JP_CHARSET
static void
ins_kanji(Str tmp)
{
if (tmp->length != 2)
return;
insC();
strBuf->ptr[CPos] = tmp->ptr[0];
strProp[CPos] = PC_KANJI1;
CPos++;
insC();
strBuf->ptr[CPos] = tmp->ptr[1];
strProp[CPos] = PC_KANJI2;
CPos++;
}
#endif
static void
_esc(void)
{
char c, c2;
switch (c = getch()) {
case '[':
case 'O':
switch (c = getch()) {
case 'A':
_prev();
break;
case 'B':
_next();
break;
case 'C':
_mvR();
break;
case 'D':
_mvL();
break;
}
break;
case CTRL_I:
case ' ':
#ifdef EMACS_LIKE_LINEEDIT
_rdcompl();
cm_clear = FALSE;
#else
_rcompl();
break;
case CTRL_D:
_rdcompl();
#endif
need_redraw = TRUE;
break;
#ifdef EMACS_LIKE_LINEEDIT
case 'f':
_mvRw();
break;
case 'b':
_mvLw();
break;
case CTRL_H:
_bsw();
break;
#endif
#ifdef JP_CHARSET
case '$':
/* ISO-2022-jp characters */
c2 = getch();
in_kanji = TRUE;
break;
case '(':
/* ISO-2022-jp characters */
c2 = getch();
in_kanji = FALSE;
break;
#endif
}
}
static void
insC(void)
{
int i;
Strinsert_char(strBuf, CPos, ' ');
CLen = strBuf->length;
for (i = CLen; i > CPos; i--) {
strProp[i] = strProp[i - 1];
}
}
static void
delC(void)
{
int i = CPos;
int delta = 1;
if (CLen == CPos)
return;
#ifdef JP_CHARSET
if (strProp[i] == PC_KANJI1)
delta = 2;
#endif /* JP_CHARSET */
for (i = CPos; i < CLen; i++) {
strProp[i] = strProp[i + delta];
}
Strdelete(strBuf, CPos, delta);
CLen -= delta;
}
static void
_mvL(void)
{
if (CPos > 0)
CPos--;
#ifdef JP_CHARSET
if (strProp[CPos] == PC_KANJI2)
CPos--;
#endif /* JP_CHARSET */
}
static void
_mvLw(void)
{
int first = 1;
while(CPos > 0
&& ( first || !terminated(strBuf->ptr[CPos-1])))
{
CPos--;
first = 0;
#ifdef JP_CHARSET
if (strProp[CPos] == PC_KANJI2)
CPos--;
#endif /* JP_CHARSET */
if (!move_word)
break;
}
}
static void
_mvRw(void)
{
int first = 1;
while(CPos < CLen
&& ( first || !terminated(strBuf->ptr[CPos-1])))
{
CPos++;
first = 0;
#ifdef JP_CHARSET
if (strProp[CPos] == PC_KANJI2)
CPos++;
#endif /* JP_CHARSET */
if(!move_word)
break;
}
}
static void
_mvR(void)
{
if (CPos < CLen)
CPos++;
#ifdef JP_CHARSET
if (strProp[CPos] == PC_KANJI2)
CPos++;
#endif /* JP_CHARSET */
}
static void
_bs(void)
{
if (CPos > 0) {
_mvL();
delC();
}
}
static void
_bsw(void)
{
int t = 0;
while(CPos > 0 && !t) {
_mvL();
t = (move_word && terminated(strBuf->ptr[CPos-1]));
delC();
}
}
static void
_enter(void)
{
i_cont = FALSE;
}
static void
insertself(char c)
{
if (CLen >= STR_LEN)
return;
insC();
strBuf->ptr[CPos] = c;
strProp[CPos] = (is_passwd) ? PC_ASCII : PC_CTRL;
CPos++;
}
static void
_quo(void)
{
i_quote = TRUE;
}
static void
_mvB(void)
{
CPos = 0;
}
static void
_mvE(void)
{
CPos = CLen;
}
static void
killn(void)
{
CLen = CPos;
Strtruncate(strBuf, CLen);
}
static void
killb(void)
{
while (CPos > 0)
_bs();
}
static void
_inbrk(void)
{
i_cont = FALSE;
i_broken = TRUE;
}
static void
_compl(void)
{
next_compl(1);
}
static void
_rcompl(void)
{
next_compl(-1);
}
static void
_tcompl(void)
{
if (cm_mode & CPL_OFF)
cm_mode = CPL_ON;
else if (cm_mode & CPL_ON)
cm_mode = CPL_OFF;
}
static void
next_compl(int next)
{
int status;
int b, a;
Str buf;
Str s;
if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
return;
cm_clear = FALSE;
if (!cm_next) {
for (b = CPos - 1; b >= 0; b--) {
if (strBuf->ptr[b] == ' ' || strBuf->ptr[b] == CTRL_I)
break;
}
b++;
a = CPos;
CBeforeBuf = Strsubstr(strBuf, 0, b);
buf = Strsubstr(strBuf, b, a - b);
CAfterBuf = Strsubstr(strBuf, a, strBuf->length - a);
s = doComplete(buf, &status, next);
}
else {
s = doComplete(strBuf, &status, next);
}
if (next == 0)
return;
if (status != CPL_OK && status != CPL_MENU)
bell();
if (status == CPL_FAIL)
return;
strBuf = Strnew_m_charp(CBeforeBuf->ptr, s->ptr, CAfterBuf->ptr, NULL);
CLen = setStrType(strBuf, strProp);
CPos = CBeforeBuf->length + s->length;
if (CPos > CLen)
CPos = CLen;
}
static void
_dcompl(void)
{
next_dcompl(1);
}
static void
_rdcompl(void)
{
next_dcompl(-1);
}
static void
next_dcompl(int next)
{
static int col, row, len;
static Str d;
int i, j, n, y;
Str f;
char *p;
struct stat st;
int comment, nline;
if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
return;
cm_disp_clear = FALSE;
displayBuffer(Currentbuf, B_FORCE_REDRAW);
if (LASTLINE >= 3) {
comment = TRUE;
nline = LASTLINE - 2;
} else if (LASTLINE) {
comment = FALSE;
nline = LASTLINE;
} else {
return;
}
if (cm_disp_next >= 0) {
if (next == 1) {
cm_disp_next += col * nline;
if (cm_disp_next >= NCFileBuf)
cm_disp_next = 0;
}
else if (next == -1) {
cm_disp_next -= col * nline;
if (cm_disp_next < 0)
cm_disp_next = 0;
}
row = (NCFileBuf - cm_disp_next + col - 1) / col;
goto disp_next;
}
cm_next = FALSE;
next_compl(0);
if (NCFileBuf == 0)
return;
cm_disp_next = 0;
d = Strdup(CDirBuf);
if (d->length > 0 && Strlastchar(d) != '/')
Strcat_char(d, '/');
if (cm_mode & CPL_URL && d->ptr[0] == 'f') {
p = d->ptr;
if (strncmp(p, "file://localhost/", 17) == 0)
p = &p[16];
else if (strncmp(p, "file:///", 8) == 0)
p = &p[7];
else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
p = &p[5];
d = Strnew_charp(p);
}
len = 0;
for (i = 0; i < NCFileBuf; i++) {
n = strlen(CFileBuf[i]) + 3;
if (len < n)
len = n;
}
col = COLS / len;
if (col == 0)
col = 1;
row = (NCFileBuf + col - 1) / col;
disp_next:
if (comment) {
if (row > nline) {
row = nline;
y = 0;
} else
y = nline - row + 1;
} else {
if (row >= nline) {
row = nline;
y = 0;
} else
y = nline - row - 1;
}
if (y) {
move(y - 1, 0);
clrtoeolx();
}
if (comment) {
move(y, 0);
clrtoeolx();
bold();
addstr("----- Completion list -----");
boldend();
y++;
}
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
n = cm_disp_next + j * row + i;
if (n >= NCFileBuf)
break;
move(y, j * len);
clrtoeolx();
f = Strdup(d);
Strcat_charp(f, CFileBuf[n]);
addstr(CFileBuf[n]);
if (stat(expandName(f->ptr), &st) != -1 && S_ISDIR(st.st_mode))
addstr("/");
}
y++;
}
if (comment && y == LASTLINE - 1) {
move(y, 0);
clrtoeolx();
bold();
#ifdef EMACS_LIKE_LINEEDIT
addstr("----- Press TAB to continue -----");
#else
addstr("----- Press CTRL-D to continue -----");
#endif
boldend();
}
}
static Str
doComplete(Str ifn, int *status, int next)
{
int fl, i;
char *fn, *p;
DIR *d;
Directory *dir;
struct stat st;
if (!cm_next) {
NCFileBuf = 0;
CompleteBuf = Strdup(ifn);
while (Strlastchar(CompleteBuf) != '/' &&
CompleteBuf->length > 0)
Strshrink(CompleteBuf, 1);
CDirBuf = Strdup(CompleteBuf);
if (cm_mode & CPL_URL) {
if (strncmp(CompleteBuf->ptr, "file://localhost/", 17) == 0)
Strdelete(CompleteBuf, 0, 16);
else if (strncmp(CompleteBuf->ptr, "file:///", 8) == 0)
Strdelete(CompleteBuf, 0, 7);
else if (strncmp(CompleteBuf->ptr, "file:/", 6) == 0 &&
CompleteBuf->ptr[6] != '/')
Strdelete(CompleteBuf, 0, 5);
else {
CompleteBuf = Strdup(ifn);
*status = CPL_FAIL;
return CompleteBuf;
}
}
if (CompleteBuf->length == 0) {
Strcat_char(CompleteBuf, '.');
}
if (Strlastchar(CompleteBuf) == '/' && CompleteBuf->length > 1) {
Strshrink(CompleteBuf, 1);
}
if ((d = opendir(expandName(CompleteBuf->ptr))) == NULL) {
CompleteBuf = Strdup(ifn);
*status = CPL_FAIL;
return CompleteBuf;
}
fn = lastFileName(ifn->ptr);
fl = strlen(fn);
CFileName = Strnew();
for (;;) {
dir = readdir(d);
if (dir == NULL)
break;
if (fl == 0 && (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")))
continue;
if (!strncmp(dir->d_name, fn, fl)) { /* match */
NCFileBuf++;
CFileBuf = New_Reuse(char *, CFileBuf, NCFileBuf);
CFileBuf[NCFileBuf - 1] = New_N(char, strlen(dir->d_name) + 1);
strcpy(CFileBuf[NCFileBuf - 1], dir->d_name);
if (NCFileBuf == 1) {
CFileName = Strnew_charp(dir->d_name);
}
else {
for (i = 0; CFileName->ptr[i] == dir->d_name[i]; i++);
Strtruncate(CFileName, i);
}
}
}
closedir(d);
if (NCFileBuf == 0) {
CompleteBuf = Strdup(ifn);
*status = CPL_FAIL;
return CompleteBuf;
}
qsort(CFileBuf, NCFileBuf, sizeof(CFileBuf[0]), strCmp);
NCFileOffset = 0;
if (NCFileBuf >= 2) {
cm_next = TRUE;
*status = CPL_AMBIG;
}
else {
*status = CPL_OK;
}
}
else {
CFileName = Strnew_charp(CFileBuf[NCFileOffset]);
NCFileOffset = (NCFileOffset + next + NCFileBuf) % NCFileBuf;
*status = CPL_MENU;
}
CompleteBuf = Strdup(CDirBuf);
if (CompleteBuf->length == 0)
Strcat(CompleteBuf, CFileName);
else if (Strlastchar(CompleteBuf) == '/')
Strcat(CompleteBuf, CFileName);
else {
Strcat_char(CompleteBuf, '/');
Strcat(CompleteBuf, CFileName);
}
if (*status != CPL_AMBIG) {
p = CompleteBuf->ptr;
if (cm_mode & CPL_URL) {
if (strncmp(p, "file://localhost/", 17) == 0)
p = &p[16];
else if (strncmp(p, "file:///", 8) == 0)
p = &p[7];
else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
p = &p[5];
}
if (stat(expandName(p), &st) != -1 && S_ISDIR(st.st_mode))
Strcat_char(CompleteBuf, '/');
}
return CompleteBuf;
}
static void
_prev(void)
{
Hist *hist = CurrentHist;
char *p;
if (! use_hist)
return;
if (strCurrentBuf) {
p = prevHist(hist);
if (p == NULL)
return;
} else {
p = lastHist(hist);
if (p == NULL)
return;
strCurrentBuf = strBuf;
}
strBuf = Strnew_charp(p);
CLen = CPos = setStrType(strBuf, strProp);
offset = 0;
}
static void
_next(void)
{
Hist *hist = CurrentHist;
char *p;
if (! use_hist)
return;
if (strCurrentBuf == NULL)
return;
p = nextHist(hist);
if (p) {
strBuf = Strnew_charp(p);
} else {
strBuf = strCurrentBuf;
strCurrentBuf = NULL;
}
CLen = CPos = setStrType(strBuf, strProp);
offset = 0;
}
static int
setStrType(Str str, Lineprop * prop)
{
Lineprop ctype;
int i = 0, delta;
char *s = str->ptr;
for (; *s != '\0' && i < STR_LEN; s += delta, i += delta) {
ctype = get_mctype(s);
if (is_passwd && ctype & PC_CTRL)
ctype = PC_ASCII;
delta = get_mclen(ctype);
#ifdef JP_CHARSET
if (ctype == PC_KANJI) {
prop[i] = PC_KANJI1;
prop[i+1] = PC_KANJI2;
}
else
#endif
prop[i] = ctype;
}
return i;
}
static int
terminated(unsigned char c){
int termchar[] = {'/', '&', '?', -1};
int *tp;
for(tp = termchar; *tp > 0; tp++){
if(c == *tp){
return 1;
}
}
return 0;
}