w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

linein.c (21804B)


      1 /* $Id$ */
      2 #include "fm.h"
      3 #include "local.h"
      4 #include "myctype.h"
      5 
      6 #ifdef USE_MOUSE
      7 #ifdef USE_GPM
      8 #include <gpm.h>
      9 #endif
     10 #if defined(USE_GPM) || defined(USE_SYSMOUSE)
     11 extern int do_getch();
     12 #define getch()	do_getch()
     13 #endif				/* USE_GPM */
     14 #endif				/* USE_MOUSE */
     15 
     16 #ifdef __EMX__
     17 #include <sys/kbdscan.h>
     18 #endif
     19 
     20 #define STR_LEN	1024
     21 #define CLEN (COLS - 2)
     22 
     23 static Str strBuf;
     24 static Lineprop strProp[STR_LEN];
     25 
     26 static Str CompleteBuf;
     27 static Str CFileName;
     28 static Str CBeforeBuf;
     29 static Str CAfterBuf;
     30 static Str CDirBuf;
     31 static char **CFileBuf = NULL;
     32 static int NCFileBuf;
     33 static int NCFileOffset;
     34 
     35 static void insertself(char c),
     36 _mvR(void), _mvL(void), _mvRw(void), _mvLw(void), delC(void), insC(void),
     37 _mvB(void), _mvE(void), _enter(void), _quo(void), _bs(void), _bsw(void),
     38 killn(void), killb(void), _inbrk(void), _esc(void), _editor(void),
     39 _prev(void), _next(void), _compl(void), _tcompl(void),
     40 _dcompl(void), _rdcompl(void), _rcompl(void);
     41 #ifdef __EMX__
     42 static int getcntrl(void);
     43 #endif
     44 
     45 static int terminated(unsigned char c);
     46 #define iself ((void(*)())insertself)
     47 
     48 static void next_compl(int next);
     49 static void next_dcompl(int next);
     50 static Str doComplete(Str ifn, int *status, int next);
     51 
     52 /* *INDENT-OFF* */
     53 void (*InputKeymap[32]) () = {
     54 /*  C-@     C-a     C-b     C-c     C-d     C-e     C-f     C-g     */
     55     _compl, _mvB,   _mvL,   _inbrk, delC,   _mvE,   _mvR,   _inbrk,
     56 /*  C-h     C-i     C-j     C-k     C-l     C-m     C-n     C-o     */
     57     _bs,    iself,  _enter, killn,  iself,  _enter, _next,  _editor,
     58 /*  C-p     C-q     C-r     C-s     C-t     C-u     C-v     C-w     */
     59     _prev,  _quo,   _bsw,   iself,  _mvLw,  killb,  _quo,   _bsw,
     60 /*  C-x     C-y     C-z     C-[     C-\     C-]     C-^     C-_     */
     61     _tcompl,_mvRw,  iself,  _esc,   iself,  iself,  iself,  iself,
     62 };
     63 /* *INDENT-ON* */
     64 
     65 static int setStrType(Str str, Lineprop *prop);
     66 static void addPasswd(char *p, Lineprop *pr, int len, int pos, int limit);
     67 static void addStr(char *p, Lineprop *pr, int len, int pos, int limit);
     68 
     69 static int CPos, CLen, offset;
     70 static int i_cont, i_broken, i_quote;
     71 static int cm_mode, cm_next, cm_clear, cm_disp_next, cm_disp_clear;
     72 static int need_redraw, is_passwd;
     73 static int move_word;
     74 
     75 static Hist *CurrentHist;
     76 static Str strCurrentBuf;
     77 static int use_hist;
     78 #ifdef USE_M17N
     79 static void ins_char(Str str);
     80 #else
     81 static void ins_char(char c);
     82 #endif
     83 
     84 char *
     85 inputLineHistSearch(char *prompt, char *def_str, int flag, Hist *hist,
     86 		    int (*incrfunc) (int ch, Str str, Lineprop *prop))
     87 {
     88     int opos, x, y, lpos, rpos, epos;
     89     unsigned char c;
     90     char *p;
     91 #ifdef USE_M17N
     92     Str tmp;
     93 #endif
     94 
     95     is_passwd = FALSE;
     96     move_word = TRUE;
     97 
     98     CurrentHist = hist;
     99     if (hist != NULL) {
    100 	use_hist = TRUE;
    101 	strCurrentBuf = NULL;
    102     }
    103     else {
    104 	use_hist = FALSE;
    105     }
    106     if (flag & IN_URL) {
    107 	cm_mode = CPL_ALWAYS | CPL_URL;
    108     }
    109     else if (flag & IN_FILENAME) {
    110 	cm_mode = CPL_ALWAYS;
    111     }
    112     else if (flag & IN_PASSWORD) {
    113 	cm_mode = CPL_NEVER;
    114 	is_passwd = TRUE;
    115 	move_word = FALSE;
    116     }
    117     else if (flag & IN_COMMAND)
    118 	cm_mode = CPL_ON;
    119     else
    120 	cm_mode = CPL_OFF;
    121     opos = get_strwidth(prompt);
    122     epos = CLEN - opos;
    123     if (epos < 0)
    124 	epos = 0;
    125     lpos = epos / 3;
    126     rpos = epos * 2 / 3;
    127     offset = 0;
    128 
    129     if (def_str) {
    130 	strBuf = Strnew_charp(def_str);
    131 	CLen = CPos = setStrType(strBuf, strProp);
    132     }
    133     else {
    134 	strBuf = Strnew();
    135 	CLen = CPos = 0;
    136     }
    137 
    138 #ifdef SUPPORT_WIN9X_CONSOLE_MBCS
    139     enable_win9x_console_input();
    140 #endif
    141     i_cont = TRUE;
    142     i_broken = FALSE;
    143     i_quote = FALSE;
    144     cm_next = FALSE;
    145     cm_disp_next = -1;
    146     need_redraw = FALSE;
    147 
    148 #ifdef USE_M17N
    149     wc_char_conv_init(wc_guess_8bit_charset(DisplayCharset), InnerCharset);
    150 #endif
    151     do {
    152 	x = calcPosition(strBuf->ptr, strProp, CLen, CPos, 0, CP_FORCE);
    153 	if (x - rpos > offset) {
    154 	    y = calcPosition(strBuf->ptr, strProp, CLen, CLen, 0, CP_AUTO);
    155 	    if (y - epos > x - rpos)
    156 		offset = x - rpos;
    157 	    else if (y - epos > 0)
    158 		offset = y - epos;
    159 	}
    160 	else if (x - lpos < offset) {
    161 	    if (x - lpos > 0)
    162 		offset = x - lpos;
    163 	    else
    164 		offset = 0;
    165 	}
    166 	move(LASTLINE, 0);
    167 	addstr(prompt);
    168 	if (is_passwd)
    169 	    addPasswd(strBuf->ptr, strProp, CLen, offset, COLS - opos);
    170 	else
    171 	    addStr(strBuf->ptr, strProp, CLen, offset, COLS - opos);
    172 	clrtoeolx();
    173 	move(LASTLINE, opos + x - offset);
    174 	refresh();
    175 
    176       next_char:
    177 	c = getch();
    178 #ifdef __EMX__
    179 	if (c == 0) {
    180 	    if (!(c = getcntrl()))
    181 		goto next_char;
    182 	}
    183 #endif
    184 	cm_clear = TRUE;
    185 	cm_disp_clear = TRUE;
    186 	if (!i_quote &&
    187 	    (((cm_mode & CPL_ALWAYS) && (c == CTRL_I || c == ' ')) ||
    188 	     ((cm_mode & CPL_ON) && (c == CTRL_I)))) {
    189 	    if (emacs_like_lineedit && cm_next) {
    190 		_dcompl();
    191 		need_redraw = TRUE;
    192 	    }
    193 	    else {
    194 		_compl();
    195 		cm_disp_next = -1;
    196 	    }
    197 	}
    198 	else if (!i_quote && CLen == CPos &&
    199 		 (cm_mode & CPL_ALWAYS || cm_mode & CPL_ON) && c == CTRL_D) {
    200 	    if (!emacs_like_lineedit) {
    201 		_dcompl();
    202 		need_redraw = TRUE;
    203 	    }
    204 	}
    205 	else if (!i_quote && c == DEL_CODE) {
    206 	    _bs();
    207 	    cm_next = FALSE;
    208 	    cm_disp_next = -1;
    209 	}
    210 	else if (!i_quote && c < 0x20) {	/* Control code */
    211 	    if (incrfunc == NULL
    212 		|| (c = incrfunc((int)c, strBuf, strProp)) < 0x20)
    213 		(*InputKeymap[(int)c]) (c);
    214 	    if (incrfunc && c != (unsigned char)-1 && c != CTRL_J)
    215 		incrfunc(-1, strBuf, strProp);
    216 	    if (cm_clear)
    217 		cm_next = FALSE;
    218 	    if (cm_disp_clear)
    219 		cm_disp_next = -1;
    220 	}
    221 #ifdef USE_M17N
    222 	else {
    223 	    tmp = wc_char_conv(c);
    224 	    if (tmp == NULL) {
    225 		i_quote = TRUE;
    226 		goto next_char;
    227 	    }
    228 	    i_quote = FALSE;
    229 	    cm_next = FALSE;
    230 	    cm_disp_next = -1;
    231 	    if (CLen + tmp->length > STR_LEN || !tmp->length)
    232 		goto next_char;
    233 	    ins_char(tmp);
    234 	    if (incrfunc)
    235 		incrfunc(-1, strBuf, strProp);
    236 	}
    237 #else
    238 	else {
    239 	    i_quote = FALSE;
    240 	    cm_next = FALSE;
    241 	    cm_disp_next = -1;
    242 	    if (CLen >= STR_LEN)
    243 		goto next_char;
    244 	    insC();
    245 	    strBuf->ptr[CPos] = c;
    246 	    if (!is_passwd && get_mctype(&c) == PC_CTRL)
    247 		strProp[CPos] = PC_CTRL;
    248 	    else
    249 		strProp[CPos] = PC_ASCII;
    250 	    CPos++;
    251 	    if (incrfunc)
    252 		incrfunc(-1, strBuf, strProp);
    253 	}
    254 #endif
    255 	if (CLen && (flag & IN_CHAR))
    256 	    break;
    257     } while (i_cont);
    258 
    259     if (CurrentTab) {
    260 	if (need_redraw)
    261 	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    262     }
    263 
    264 #ifdef SUPPORT_WIN9X_CONSOLE_MBCS
    265     disable_win9x_console_input();
    266 #endif
    267 
    268     if (i_broken)
    269 	return NULL;
    270 
    271     move(LASTLINE, 0);
    272     refresh();
    273     p = strBuf->ptr;
    274     if (flag & (IN_FILENAME | IN_COMMAND)) {
    275 	SKIP_BLANKS(p);
    276     }
    277     if (use_hist && !(flag & IN_URL) && *p != '\0') {
    278 	char *q = lastHist(hist);
    279 	if (!q || strcmp(q, p))
    280 	    pushHist(hist, p);
    281     }
    282     if (flag & IN_FILENAME)
    283 	return expandPath(p);
    284     else
    285 	return allocStr(p, -1);
    286 }
    287 
    288 #ifdef __EMX__
    289 static int
    290 getcntrl(void)
    291 {
    292     switch (getch()) {
    293     case K_DEL:
    294 	return CTRL_D;
    295     case K_LEFT:
    296 	return CTRL_B;
    297     case K_RIGHT:
    298 	return CTRL_F;
    299     case K_UP:
    300 	return CTRL_P;
    301     case K_DOWN:
    302 	return CTRL_N;
    303     case K_HOME:
    304     case K_CTRL_LEFT:
    305 	return CTRL_A;
    306     case K_END:
    307     case K_CTRL_RIGHT:
    308 	return CTRL_E;
    309     case K_CTRL_HOME:
    310 	return CTRL_U;
    311     case K_CTRL_END:
    312 	return CTRL_K;
    313     }
    314     return 0;
    315 }
    316 #endif
    317 
    318 static void
    319 addPasswd(char *p, Lineprop *pr, int len, int offset, int limit)
    320 {
    321     int rcol = 0, ncol;
    322 
    323     ncol = calcPosition(p, pr, len, len, 0, CP_AUTO);
    324     if (ncol > offset + limit)
    325 	ncol = offset + limit;
    326     if (offset) {
    327 	addChar('{', 0);
    328 	rcol = offset + 1;
    329     }
    330     for (; rcol < ncol; rcol++)
    331 	addChar('*', 0);
    332 }
    333 
    334 static void
    335 addStr(char *p, Lineprop *pr, int len, int offset, int limit)
    336 {
    337     int i = 0, rcol = 0, ncol, delta = 1;
    338 
    339     if (offset) {
    340 	for (i = 0; i < len; i++) {
    341 	    if (calcPosition(p, pr, len, i, 0, CP_AUTO) > offset)
    342 		break;
    343 	}
    344 	if (i >= len)
    345 	    return;
    346 #ifdef USE_M17N
    347 	while (pr[i] & PC_WCHAR2)
    348 	    i++;
    349 #endif
    350 	addChar('{', 0);
    351 	rcol = offset + 1;
    352 	ncol = calcPosition(p, pr, len, i, 0, CP_AUTO);
    353 	for (; rcol < ncol; rcol++)
    354 	    addChar(' ', 0);
    355     }
    356     for (; i < len; i += delta) {
    357 #ifdef USE_M17N
    358 	delta = wtf_len((wc_uchar *) & p[i]);
    359 #endif
    360 	ncol = calcPosition(p, pr, len, i + delta, 0, CP_AUTO);
    361 	if (ncol - offset > limit)
    362 	    break;
    363 	if (p[i] == '\t') {
    364 	    for (; rcol < ncol; rcol++)
    365 		addChar(' ', 0);
    366 	    continue;
    367 	}
    368 	else {
    369 #ifdef USE_M17N
    370 	    addMChar(&p[i], pr[i], delta);
    371 #else
    372 	    addChar(p[i], pr[i]);
    373 #endif
    374 	}
    375 	rcol = ncol;
    376     }
    377 }
    378 
    379 #ifdef USE_M17N
    380 static void
    381 ins_char(Str str)
    382 {
    383     char *p = str->ptr, *ep = p + str->length;
    384     Lineprop ctype;
    385     int len;
    386 
    387     if (CLen + str->length >= STR_LEN)
    388 	return;
    389     while (p < ep) {
    390 	len = get_mclen(p);
    391 	ctype = get_mctype(p);
    392 	if (is_passwd) {
    393 	    if (ctype & PC_CTRL)
    394 		ctype = PC_ASCII;
    395 	    if (ctype & PC_UNKNOWN)
    396 		ctype = PC_WCHAR1;
    397 	}
    398 	insC();
    399 	strBuf->ptr[CPos] = *(p++);
    400 	strProp[CPos] = ctype;
    401 	CPos++;
    402 	if (--len) {
    403 	    ctype = (ctype & ~PC_WCHAR1) | PC_WCHAR2;
    404 	    while (len--) {
    405 		insC();
    406 		strBuf->ptr[CPos] = *(p++);
    407 		strProp[CPos] = ctype;
    408 		CPos++;
    409 	    }
    410 	}
    411     }
    412 }
    413 #endif
    414 
    415 static void
    416 _esc(void)
    417 {
    418     char c;
    419 
    420     switch (c = getch()) {
    421     case '[':
    422     case 'O':
    423 	switch (c = getch()) {
    424 	case 'A':
    425 	    _prev();
    426 	    break;
    427 	case 'B':
    428 	    _next();
    429 	    break;
    430 	case 'C':
    431 	    _mvR();
    432 	    break;
    433 	case 'D':
    434 	    _mvL();
    435 	    break;
    436 	}
    437 	break;
    438     case CTRL_I:
    439     case ' ':
    440 	if (emacs_like_lineedit) {
    441 	    _rdcompl();
    442 	    cm_clear = FALSE;
    443 	    need_redraw = TRUE;
    444 	}
    445 	else
    446 	    _rcompl();
    447 	break;
    448     case CTRL_D:
    449 	if (!emacs_like_lineedit)
    450 	    _rdcompl();
    451 	need_redraw = TRUE;
    452 	break;
    453     case 'f':
    454 	if (emacs_like_lineedit)
    455 	    _mvRw();
    456 	break;
    457     case 'b':
    458 	if (emacs_like_lineedit)
    459 	    _mvLw();
    460 	break;
    461     case CTRL_H:
    462 	if (emacs_like_lineedit)
    463 	    _bsw();
    464 	break;
    465 #ifdef USE_M17N
    466     default:
    467 	if (wc_char_conv(ESC_CODE) == NULL && wc_char_conv(c) == NULL)
    468 	    i_quote = TRUE;
    469 #endif
    470     }
    471 }
    472 
    473 static void
    474 insC(void)
    475 {
    476     int i;
    477 
    478     Strinsert_char(strBuf, CPos, ' ');
    479     CLen = strBuf->length;
    480     for (i = CLen; i > CPos; i--) {
    481 	strProp[i] = strProp[i - 1];
    482     }
    483 }
    484 
    485 static void
    486 delC(void)
    487 {
    488     int i = CPos;
    489     int delta = 1;
    490 
    491     if (CLen == CPos)
    492 	return;
    493 #ifdef USE_M17N
    494     while (i + delta < CLen && strProp[i + delta] & PC_WCHAR2)
    495 	delta++;
    496 #endif
    497     for (i = CPos; i < CLen; i++) {
    498 	strProp[i] = strProp[i + delta];
    499     }
    500     Strdelete(strBuf, CPos, delta);
    501     CLen -= delta;
    502 }
    503 
    504 static void
    505 _mvL(void)
    506 {
    507     if (CPos > 0)
    508 	CPos--;
    509 #ifdef USE_M17N
    510     while (CPos > 0 && strProp[CPos] & PC_WCHAR2)
    511 	CPos--;
    512 #endif
    513 }
    514 
    515 static void
    516 _mvLw(void)
    517 {
    518     int first = 1;
    519     while (CPos > 0 && (first || !terminated(strBuf->ptr[CPos - 1]))) {
    520 	CPos--;
    521 	first = 0;
    522 #ifdef USE_M17N
    523 	if (CPos > 0 && strProp[CPos] & PC_WCHAR2)
    524 	    CPos--;
    525 #endif
    526 	if (!move_word)
    527 	    break;
    528     }
    529 }
    530 
    531 static void
    532 _mvRw(void)
    533 {
    534     int first = 1;
    535     while (CPos < CLen && (first || !terminated(strBuf->ptr[CPos - 1]))) {
    536 	CPos++;
    537 	first = 0;
    538 #ifdef USE_M17N
    539 	if (CPos < CLen && strProp[CPos] & PC_WCHAR2)
    540 	    CPos++;
    541 #endif
    542 	if (!move_word)
    543 	    break;
    544     }
    545 }
    546 
    547 static void
    548 _mvR(void)
    549 {
    550     if (CPos < CLen)
    551 	CPos++;
    552 #ifdef USE_M17N
    553     while (CPos < CLen && strProp[CPos] & PC_WCHAR2)
    554 	CPos++;
    555 #endif
    556 }
    557 
    558 static void
    559 _bs(void)
    560 {
    561     if (CPos > 0) {
    562 	_mvL();
    563 	delC();
    564     }
    565 }
    566 
    567 static void
    568 _bsw(void)
    569 {
    570     int t = 0;
    571     while (CPos > 0 && !t) {
    572 	_mvL();
    573 	t = (move_word && terminated(strBuf->ptr[CPos - 1]));
    574 	delC();
    575     }
    576 }
    577 
    578 static void
    579 _enter(void)
    580 {
    581     i_cont = FALSE;
    582 }
    583 
    584 static void
    585 insertself(char c)
    586 {
    587     if (CLen >= STR_LEN)
    588 	return;
    589     insC();
    590     strBuf->ptr[CPos] = c;
    591     strProp[CPos] = (is_passwd) ? PC_ASCII : PC_CTRL;
    592     CPos++;
    593 }
    594 
    595 static void
    596 _quo(void)
    597 {
    598     i_quote = TRUE;
    599 }
    600 
    601 static void
    602 _mvB(void)
    603 {
    604     CPos = 0;
    605 }
    606 
    607 static void
    608 _mvE(void)
    609 {
    610     CPos = CLen;
    611 }
    612 
    613 static void
    614 killn(void)
    615 {
    616     CLen = CPos;
    617     Strtruncate(strBuf, CLen);
    618 }
    619 
    620 static void
    621 killb(void)
    622 {
    623     while (CPos > 0)
    624 	_bs();
    625 }
    626 
    627 static void
    628 _inbrk(void)
    629 {
    630     i_cont = FALSE;
    631     i_broken = TRUE;
    632 }
    633 
    634 static void
    635 _compl(void)
    636 {
    637     next_compl(1);
    638 }
    639 
    640 static void
    641 _rcompl(void)
    642 {
    643     next_compl(-1);
    644 }
    645 
    646 static void
    647 _tcompl(void)
    648 {
    649     if (cm_mode & CPL_OFF)
    650 	cm_mode = CPL_ON;
    651     else if (cm_mode & CPL_ON)
    652 	cm_mode = CPL_OFF;
    653 }
    654 
    655 static void
    656 next_compl(int next)
    657 {
    658     int status;
    659     int b, a;
    660     Str buf;
    661     Str s;
    662 
    663     if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
    664 	return;
    665     cm_clear = FALSE;
    666     if (!cm_next) {
    667 	if (cm_mode & CPL_ALWAYS) {
    668 	    b = 0;
    669 	}
    670 	else {
    671 	    for (b = CPos - 1; b >= 0; b--) {
    672 		if ((strBuf->ptr[b] == ' ' || strBuf->ptr[b] == CTRL_I) &&
    673 		    !((b > 0) && strBuf->ptr[b - 1] == '\\'))
    674 		    break;
    675 	    }
    676 	    b++;
    677 	}
    678 	a = CPos;
    679 	CBeforeBuf = Strsubstr(strBuf, 0, b);
    680 	buf = Strsubstr(strBuf, b, a - b);
    681 	CAfterBuf = Strsubstr(strBuf, a, strBuf->length - a);
    682 	s = doComplete(buf, &status, next);
    683     }
    684     else {
    685 	s = doComplete(strBuf, &status, next);
    686     }
    687     if (next == 0)
    688 	return;
    689 
    690     if (status != CPL_OK && status != CPL_MENU)
    691 	bell();
    692     if (status == CPL_FAIL)
    693 	return;
    694 
    695     strBuf = Strnew_m_charp(CBeforeBuf->ptr, s->ptr, CAfterBuf->ptr, NULL);
    696     CLen = setStrType(strBuf, strProp);
    697     CPos = CBeforeBuf->length + s->length;
    698     if (CPos > CLen)
    699 	CPos = CLen;
    700 }
    701 
    702 static void
    703 _dcompl(void)
    704 {
    705     next_dcompl(1);
    706 }
    707 
    708 static void
    709 _rdcompl(void)
    710 {
    711     next_dcompl(-1);
    712 }
    713 
    714 static void
    715 next_dcompl(int next)
    716 {
    717     static int col, row, len;
    718     static Str d;
    719     int i, j, n, y;
    720     Str f;
    721     char *p;
    722     struct stat st;
    723     int comment, nline;
    724 
    725     if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
    726 	return;
    727     cm_disp_clear = FALSE;
    728     if (CurrentTab)
    729 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
    730     if (LASTLINE >= 3) {
    731 	comment = TRUE;
    732 	nline = LASTLINE - 2;
    733     }
    734     else if (LASTLINE) {
    735 	comment = FALSE;
    736 	nline = LASTLINE;
    737     }
    738     else {
    739 	return;
    740     }
    741 
    742     if (cm_disp_next >= 0) {
    743 	if (next == 1) {
    744 	    cm_disp_next += col * nline;
    745 	    if (cm_disp_next >= NCFileBuf)
    746 		cm_disp_next = 0;
    747 	}
    748 	else if (next == -1) {
    749 	    cm_disp_next -= col * nline;
    750 	    if (cm_disp_next < 0)
    751 		cm_disp_next = 0;
    752 	}
    753 	row = (NCFileBuf - cm_disp_next + col - 1) / col;
    754 	goto disp_next;
    755     }
    756 
    757     cm_next = FALSE;
    758     next_compl(0);
    759     if (NCFileBuf == 0)
    760 	return;
    761     cm_disp_next = 0;
    762 
    763     d = Str_conv_to_system(Strdup(CDirBuf));
    764     if (d->length > 0 && Strlastchar(d) != '/')
    765 	Strcat_char(d, '/');
    766     if (cm_mode & CPL_URL && d->ptr[0] == 'f') {
    767 	p = d->ptr;
    768 	if (strncmp(p, "file://localhost/", 17) == 0)
    769 	    p = &p[16];
    770 	else if (strncmp(p, "file:///", 8) == 0)
    771 	    p = &p[7];
    772 	else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
    773 	    p = &p[5];
    774 	d = Strnew_charp(p);
    775     }
    776 
    777     len = 0;
    778     for (i = 0; i < NCFileBuf; i++) {
    779 	n = strlen(CFileBuf[i]) + 3;
    780 	if (len < n)
    781 	    len = n;
    782     }
    783     col = COLS / len;
    784     if (col == 0)
    785 	col = 1;
    786     row = (NCFileBuf + col - 1) / col;
    787 
    788   disp_next:
    789     if (comment) {
    790 	if (row > nline) {
    791 	    row = nline;
    792 	    y = 0;
    793 	}
    794 	else
    795 	    y = nline - row + 1;
    796     }
    797     else {
    798 	if (row >= nline) {
    799 	    row = nline;
    800 	    y = 0;
    801 	}
    802 	else
    803 	    y = nline - row - 1;
    804     }
    805     if (y) {
    806 	move(y - 1, 0);
    807 	clrtoeolx();
    808     }
    809     if (comment) {
    810 	move(y, 0);
    811 	clrtoeolx();
    812 	bold();
    813 	/* FIXME: gettextize? */
    814 	addstr("----- Completion list -----");
    815 	boldend();
    816 	y++;
    817     }
    818     for (i = 0; i < row; i++) {
    819 	for (j = 0; j < col; j++) {
    820 	    n = cm_disp_next + j * row + i;
    821 	    if (n >= NCFileBuf)
    822 		break;
    823 	    move(y, j * len);
    824 	    clrtoeolx();
    825 	    f = Strdup(d);
    826 	    Strcat_charp(f, CFileBuf[n]);
    827 	    addstr(conv_from_system(CFileBuf[n]));
    828 	    if (stat(expandPath(f->ptr), &st) != -1 && S_ISDIR(st.st_mode))
    829 		addstr("/");
    830 	}
    831 	y++;
    832     }
    833     if (comment && y == LASTLINE - 1) {
    834 	move(y, 0);
    835 	clrtoeolx();
    836 	bold();
    837 	if (emacs_like_lineedit)
    838 	    /* FIXME: gettextize? */
    839 	    addstr("----- Press TAB to continue -----");
    840 	else
    841 	    /* FIXME: gettextize? */
    842 	    addstr("----- Press CTRL-D to continue -----");
    843 	boldend();
    844     }
    845 }
    846 
    847 
    848 Str
    849 escape_spaces(Str s)
    850 {
    851     Str tmp = NULL;
    852     char *p;
    853 
    854     if (s == NULL)
    855 	return s;
    856     for (p = s->ptr; *p; p++) {
    857 	if (*p == ' ' || *p == CTRL_I) {
    858 	    if (tmp == NULL)
    859 		tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
    860 	    Strcat_char(tmp, '\\');
    861 	}
    862 	if (tmp)
    863 	    Strcat_char(tmp, *p);
    864     }
    865     if (tmp)
    866 	return tmp;
    867     return s;
    868 }
    869 
    870 
    871 Str
    872 unescape_spaces(Str s)
    873 {
    874     Str tmp = NULL;
    875     char *p;
    876 
    877     if (s == NULL)
    878 	return s;
    879     for (p = s->ptr; *p; p++) {
    880 	if (*p == '\\' && (*(p + 1) == ' ' || *(p + 1) == CTRL_I)) {
    881 	    if (tmp == NULL)
    882 		tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
    883 	}
    884 	else {
    885 	    if (tmp)
    886 		Strcat_char(tmp, *p);
    887 	}
    888     }
    889     if (tmp)
    890 	return tmp;
    891     return s;
    892 }
    893 
    894 static Str
    895 doComplete(Str ifn, int *status, int next)
    896 {
    897     int fl, i;
    898     char *fn, *p;
    899     DIR *d;
    900     Directory *dir;
    901     struct stat st;
    902 
    903     if (!cm_next) {
    904 	NCFileBuf = 0;
    905 	ifn = Str_conv_to_system(ifn);
    906 	if (cm_mode & CPL_ON)
    907 	    ifn = unescape_spaces(ifn);
    908 	CompleteBuf = Strdup(ifn);
    909 	while (Strlastchar(CompleteBuf) != '/' && CompleteBuf->length > 0)
    910 	    Strshrink(CompleteBuf, 1);
    911 	CDirBuf = Strdup(CompleteBuf);
    912 	if (cm_mode & CPL_URL) {
    913 	    if (strncmp(CompleteBuf->ptr, "file://localhost/", 17) == 0)
    914 		Strdelete(CompleteBuf, 0, 16);
    915 	    else if (strncmp(CompleteBuf->ptr, "file:///", 8) == 0)
    916 		Strdelete(CompleteBuf, 0, 7);
    917 	    else if (strncmp(CompleteBuf->ptr, "file:/", 6) == 0 &&
    918 		     CompleteBuf->ptr[6] != '/')
    919 		Strdelete(CompleteBuf, 0, 5);
    920 	    else {
    921 		CompleteBuf = Strdup(ifn);
    922 		*status = CPL_FAIL;
    923 		return Str_conv_to_system(CompleteBuf);
    924 	    }
    925 	}
    926 	if (CompleteBuf->length == 0) {
    927 	    Strcat_char(CompleteBuf, '.');
    928 	}
    929 	if (Strlastchar(CompleteBuf) == '/' && CompleteBuf->length > 1) {
    930 	    Strshrink(CompleteBuf, 1);
    931 	}
    932 	if ((d = opendir(expandPath(CompleteBuf->ptr))) == NULL) {
    933 	    CompleteBuf = Strdup(ifn);
    934 	    *status = CPL_FAIL;
    935 	    if (cm_mode & CPL_ON)
    936 		CompleteBuf = escape_spaces(CompleteBuf);
    937 	    return CompleteBuf;
    938 	}
    939 	fn = lastFileName(ifn->ptr);
    940 	fl = strlen(fn);
    941 	CFileName = Strnew();
    942 	for (;;) {
    943 	    dir = readdir(d);
    944 	    if (dir == NULL)
    945 		break;
    946 	    if (fl == 0
    947 		&& (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")))
    948 		continue;
    949 	    if (!strncmp(dir->d_name, fn, fl)) {	/* match */
    950 		NCFileBuf++;
    951 		CFileBuf = New_Reuse(char *, CFileBuf, NCFileBuf);
    952 		CFileBuf[NCFileBuf - 1] =
    953 		    NewAtom_N(char, strlen(dir->d_name) + 1);
    954 		strcpy(CFileBuf[NCFileBuf - 1], dir->d_name);
    955 		if (NCFileBuf == 1) {
    956 		    CFileName = Strnew_charp(dir->d_name);
    957 		}
    958 		else {
    959 		    for (i = 0; CFileName->ptr[i] == dir->d_name[i]; i++) ;
    960 		    Strtruncate(CFileName, i);
    961 		}
    962 	    }
    963 	}
    964 	closedir(d);
    965 	if (NCFileBuf == 0) {
    966 	    CompleteBuf = Strdup(ifn);
    967 	    *status = CPL_FAIL;
    968 	    if (cm_mode & CPL_ON)
    969 		CompleteBuf = escape_spaces(CompleteBuf);
    970 	    return CompleteBuf;
    971 	}
    972 	qsort(CFileBuf, NCFileBuf, sizeof(CFileBuf[0]), strCmp);
    973 	NCFileOffset = 0;
    974 	if (NCFileBuf >= 2) {
    975 	    cm_next = TRUE;
    976 	    *status = CPL_AMBIG;
    977 	}
    978 	else {
    979 	    *status = CPL_OK;
    980 	}
    981     }
    982     else {
    983 	CFileName = Strnew_charp(CFileBuf[NCFileOffset]);
    984 	NCFileOffset = (NCFileOffset + next + NCFileBuf) % NCFileBuf;
    985 	*status = CPL_MENU;
    986     }
    987     CompleteBuf = Strdup(CDirBuf);
    988     if (CompleteBuf->length && Strlastchar(CompleteBuf) != '/')
    989 	Strcat_char(CompleteBuf, '/');
    990     Strcat(CompleteBuf, CFileName);
    991     if (*status != CPL_AMBIG) {
    992 	p = CompleteBuf->ptr;
    993 	if (cm_mode & CPL_URL) {
    994 	    if (strncmp(p, "file://localhost/", 17) == 0)
    995 		p = &p[16];
    996 	    else if (strncmp(p, "file:///", 8) == 0)
    997 		p = &p[7];
    998 	    else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
    999 		p = &p[5];
   1000 	}
   1001 	if (stat(expandPath(p), &st) != -1 && S_ISDIR(st.st_mode))
   1002 	    Strcat_char(CompleteBuf, '/');
   1003     }
   1004     if (cm_mode & CPL_ON)
   1005 	CompleteBuf = escape_spaces(CompleteBuf);
   1006     return Str_conv_from_system(CompleteBuf);
   1007 }
   1008 
   1009 static void
   1010 _prev(void)
   1011 {
   1012     Hist *hist = CurrentHist;
   1013     char *p;
   1014 
   1015     if (!use_hist)
   1016 	return;
   1017     if (strCurrentBuf) {
   1018 	p = prevHist(hist);
   1019 	if (p == NULL)
   1020 	    return;
   1021     }
   1022     else {
   1023 	p = lastHist(hist);
   1024 	if (p == NULL)
   1025 	    return;
   1026 	strCurrentBuf = strBuf;
   1027     }
   1028     if (DecodeURL && (cm_mode & CPL_URL) )
   1029 	p = url_unquote_conv(p, 0);
   1030     strBuf = Strnew_charp(p);
   1031     CLen = CPos = setStrType(strBuf, strProp);
   1032     offset = 0;
   1033 }
   1034 
   1035 static void
   1036 _next(void)
   1037 {
   1038     Hist *hist = CurrentHist;
   1039     char *p;
   1040 
   1041     if (!use_hist)
   1042 	return;
   1043     if (strCurrentBuf == NULL)
   1044 	return;
   1045     p = nextHist(hist);
   1046     if (p) {
   1047 	if (DecodeURL && (cm_mode & CPL_URL) )
   1048 	    p = url_unquote_conv(p, 0);
   1049 	strBuf = Strnew_charp(p);
   1050     }
   1051     else {
   1052 	strBuf = strCurrentBuf;
   1053 	strCurrentBuf = NULL;
   1054     }
   1055     CLen = CPos = setStrType(strBuf, strProp);
   1056     offset = 0;
   1057 }
   1058 
   1059 static int
   1060 setStrType(Str str, Lineprop *prop)
   1061 {
   1062     Lineprop ctype;
   1063     char *p = str->ptr, *ep = p + str->length;
   1064     int i, len = 1;
   1065 
   1066     for (i = 0; p < ep;) {
   1067 #ifdef USE_M17N
   1068 	len = get_mclen(p);
   1069 #endif
   1070 	if (i + len > STR_LEN)
   1071 	    break;
   1072 	ctype = get_mctype(p);
   1073 	if (is_passwd) {
   1074 	    if (ctype & PC_CTRL)
   1075 		ctype = PC_ASCII;
   1076 #ifdef USE_M17N
   1077 	    if (ctype & PC_UNKNOWN)
   1078 		ctype = PC_WCHAR1;
   1079 #endif
   1080 	}
   1081 	prop[i++] = ctype;
   1082 #ifdef USE_M17N
   1083 	p += len;
   1084 	if (--len) {
   1085 	    ctype = (ctype & ~PC_WCHAR1) | PC_WCHAR2;
   1086 	    while (len--)
   1087 		prop[i++] = ctype;
   1088 	}
   1089 #else
   1090 	p++;
   1091 #endif
   1092     }
   1093     return i;
   1094 }
   1095 
   1096 static int
   1097 terminated(unsigned char c)
   1098 {
   1099     int termchar[] = { '/', '&', '?', ' ', -1 };
   1100     int *tp;
   1101 
   1102     for (tp = termchar; *tp > 0; tp++) {
   1103 	if (c == *tp) {
   1104 	    return 1;
   1105 	}
   1106     }
   1107 
   1108     return 0;
   1109 }
   1110 
   1111 static void
   1112 _editor(void)
   1113 {
   1114     FormItemList fi;
   1115     char *p;
   1116 
   1117     if (is_passwd)
   1118 	return;
   1119 
   1120     fi.readonly = FALSE;
   1121     fi.value = Strdup(strBuf);
   1122     Strcat_char(fi.value, '\n');
   1123 
   1124     input_textarea(&fi);
   1125 
   1126     strBuf = Strnew();
   1127     for (p = fi.value->ptr; *p; p++) {
   1128 	if (*p == '\r' || *p == '\n')
   1129 	    continue;
   1130 	Strcat_char(strBuf, *p);
   1131     }
   1132     CLen = CPos = setStrType(strBuf, strProp);
   1133     if (CurrentTab)
   1134 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1135 }