1 /* $Id: linein.c,v 1.35 2007/05/23 12:14:24 inu Exp $ */
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 *
inputLineHistSearch(char * prompt,char * def_str,int flag,Hist * hist,int (* incrfunc)(int ch,Str str,Lineprop * prop))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 || (space_autocomplete && 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
getcntrl(void)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
addPasswd(char * p,Lineprop * pr,int len,int offset,int limit)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
addStr(char * p,Lineprop * pr,int len,int offset,int limit)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
ins_char(Str str)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
_esc(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
insC(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
delC(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
_mvL(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
_mvLw(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
_mvRw(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
_mvR(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
_bs(void)559 _bs(void)
560 {
561     if (CPos > 0) {
562 	_mvL();
563 	delC();
564     }
565 }
566 
567 static void
_bsw(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
_enter(void)579 _enter(void)
580 {
581     i_cont = FALSE;
582 }
583 
584 static void
insertself(char c)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
_quo(void)596 _quo(void)
597 {
598     i_quote = TRUE;
599 }
600 
601 static void
_mvB(void)602 _mvB(void)
603 {
604     CPos = 0;
605 }
606 
607 static void
_mvE(void)608 _mvE(void)
609 {
610     CPos = CLen;
611 }
612 
613 static void
killn(void)614 killn(void)
615 {
616     CLen = CPos;
617     Strtruncate(strBuf, CLen);
618 }
619 
620 static void
killb(void)621 killb(void)
622 {
623     while (CPos > 0)
624 	_bs();
625 }
626 
627 static void
_inbrk(void)628 _inbrk(void)
629 {
630     i_cont = FALSE;
631     i_broken = TRUE;
632 }
633 
634 static void
_compl(void)635 _compl(void)
636 {
637     next_compl(1);
638 }
639 
640 static void
_rcompl(void)641 _rcompl(void)
642 {
643     next_compl(-1);
644 }
645 
646 static void
_tcompl(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
next_compl(int next)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
_dcompl(void)703 _dcompl(void)
704 {
705     next_dcompl(1);
706 }
707 
708 static void
_rdcompl(void)709 _rdcompl(void)
710 {
711     next_dcompl(-1);
712 }
713 
714 static void
next_dcompl(int next)715 next_dcompl(int next)
716 {
717     static int col, row;
718     static unsigned int len;
719     static Str d;
720     int i, j, n, y;
721     Str f;
722     char *p;
723     struct stat st;
724     int comment, nline;
725 
726     if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
727 	return;
728     cm_disp_clear = FALSE;
729     if (CurrentTab)
730 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
731     if (LASTLINE >= 3) {
732 	comment = TRUE;
733 	nline = LASTLINE - 2;
734     }
735     else if (LASTLINE) {
736 	comment = FALSE;
737 	nline = LASTLINE;
738     }
739     else {
740 	return;
741     }
742 
743     if (cm_disp_next >= 0) {
744 	if (next == 1) {
745 	    cm_disp_next += col * nline;
746 	    if (cm_disp_next >= NCFileBuf)
747 		cm_disp_next = 0;
748 	}
749 	else if (next == -1) {
750 	    cm_disp_next -= col * nline;
751 	    if (cm_disp_next < 0)
752 		cm_disp_next = 0;
753 	}
754 	row = (NCFileBuf - cm_disp_next + col - 1) / col;
755 	goto disp_next;
756     }
757 
758     cm_next = FALSE;
759     next_compl(0);
760     if (NCFileBuf == 0)
761 	return;
762     cm_disp_next = 0;
763 
764     d = Str_conv_to_system(Strdup(CDirBuf));
765     if (d->length > 0 && Strlastchar(d) != '/')
766 	Strcat_char(d, '/');
767     if (cm_mode & CPL_URL && d->ptr[0] == 'f') {
768 	p = d->ptr;
769 	if (strncmp(p, "file://localhost/", 17) == 0)
770 	    p = &p[16];
771 	else if (strncmp(p, "file:///", 8) == 0)
772 	    p = &p[7];
773 	else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
774 	    p = &p[5];
775 	d = Strnew_charp(p);
776     }
777 
778     len = 0;
779     for (i = 0; i < NCFileBuf; i++) {
780 	n = strlen(CFileBuf[i]) + 3;
781 	if (len < n)
782 	    len = n;
783     }
784     if (len > 0 && COLS > len)
785         col = COLS / len;
786     else
787         col = 1;
788     row = (NCFileBuf + col - 1) / col;
789 
790   disp_next:
791     if (comment) {
792 	if (row > nline) {
793 	    row = nline;
794 	    y = 0;
795 	}
796 	else
797 	    y = nline - row + 1;
798     }
799     else {
800 	if (row >= nline) {
801 	    row = nline;
802 	    y = 0;
803 	}
804 	else
805 	    y = nline - row - 1;
806     }
807     if (y) {
808 	move(y - 1, 0);
809 	clrtoeolx();
810     }
811     if (comment) {
812 	move(y, 0);
813 	clrtoeolx();
814 	bold();
815 	/* FIXME: gettextize? */
816 	addstr("----- Completion list -----");
817 	boldend();
818 	y++;
819     }
820     for (i = 0; i < row; i++) {
821 	for (j = 0; j < col; j++) {
822 	    n = cm_disp_next + j * row + i;
823 	    if (n >= NCFileBuf)
824 		break;
825 	    move(y, j * len);
826 	    clrtoeolx();
827 	    f = Strdup(d);
828 	    Strcat_charp(f, CFileBuf[n]);
829 	    addstr(conv_from_system(CFileBuf[n]));
830 	    if (stat(expandPath(f->ptr), &st) != -1 && S_ISDIR(st.st_mode))
831 		addstr("/");
832 	}
833 	y++;
834     }
835     if (comment && y == LASTLINE - 1) {
836 	move(y, 0);
837 	clrtoeolx();
838 	bold();
839 	if (emacs_like_lineedit)
840 	    /* FIXME: gettextize? */
841 	    addstr("----- Press TAB to continue -----");
842 	else
843 	    /* FIXME: gettextize? */
844 	    addstr("----- Press CTRL-D to continue -----");
845 	boldend();
846     }
847 }
848 
849 
850 Str
escape_spaces(Str s)851 escape_spaces(Str s)
852 {
853     Str tmp = NULL;
854     char *p;
855 
856     if (s == NULL)
857 	return s;
858     for (p = s->ptr; *p; p++) {
859 	if (*p == ' ' || *p == CTRL_I) {
860 	    if (tmp == NULL)
861 		tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
862 	    Strcat_char(tmp, '\\');
863 	}
864 	if (tmp)
865 	    Strcat_char(tmp, *p);
866     }
867     if (tmp)
868 	return tmp;
869     return s;
870 }
871 
872 
873 Str
unescape_spaces(Str s)874 unescape_spaces(Str s)
875 {
876     Str tmp = NULL;
877     char *p;
878 
879     if (s == NULL)
880 	return s;
881     for (p = s->ptr; *p; p++) {
882 	if (*p == '\\' && (*(p + 1) == ' ' || *(p + 1) == CTRL_I)) {
883 	    if (tmp == NULL)
884 		tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
885 	}
886 	else {
887 	    if (tmp)
888 		Strcat_char(tmp, *p);
889 	}
890     }
891     if (tmp)
892 	return tmp;
893     return s;
894 }
895 
896 static Str
doComplete(Str ifn,int * status,int next)897 doComplete(Str ifn, int *status, int next)
898 {
899     int fl, i;
900     char *fn, *p;
901     DIR *d;
902     Directory *dir;
903     struct stat st;
904 
905     if (!cm_next) {
906 	NCFileBuf = 0;
907 	ifn = Str_conv_to_system(ifn);
908 	if (cm_mode & CPL_ON)
909 	    ifn = unescape_spaces(ifn);
910 	CompleteBuf = Strdup(ifn);
911 	while (Strlastchar(CompleteBuf) != '/' && CompleteBuf->length > 0)
912 	    Strshrink(CompleteBuf, 1);
913 	CDirBuf = Strdup(CompleteBuf);
914 	if (cm_mode & CPL_URL) {
915 	    if (strncmp(CompleteBuf->ptr, "file://localhost/", 17) == 0)
916 		Strdelete(CompleteBuf, 0, 16);
917 	    else if (strncmp(CompleteBuf->ptr, "file:///", 8) == 0)
918 		Strdelete(CompleteBuf, 0, 7);
919 	    else if (strncmp(CompleteBuf->ptr, "file:/", 6) == 0 &&
920 		     CompleteBuf->ptr[6] != '/')
921 		Strdelete(CompleteBuf, 0, 5);
922 	    else {
923 		CompleteBuf = Strdup(ifn);
924 		*status = CPL_FAIL;
925 		return Str_conv_to_system(CompleteBuf);
926 	    }
927 	}
928 	if (CompleteBuf->length == 0) {
929 	    Strcat_char(CompleteBuf, '.');
930 	}
931 	if (Strlastchar(CompleteBuf) == '/' && CompleteBuf->length > 1) {
932 	    Strshrink(CompleteBuf, 1);
933 	}
934 	if ((d = opendir(expandPath(CompleteBuf->ptr))) == NULL) {
935 	    CompleteBuf = Strdup(ifn);
936 	    *status = CPL_FAIL;
937 	    if (cm_mode & CPL_ON)
938 		CompleteBuf = escape_spaces(CompleteBuf);
939 	    return CompleteBuf;
940 	}
941 	fn = lastFileName(ifn->ptr);
942 	fl = strlen(fn);
943 	CFileName = Strnew();
944 	for (;;) {
945 	    dir = readdir(d);
946 	    if (dir == NULL)
947 		break;
948 	    if (fl == 0
949 		&& (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")))
950 		continue;
951 	    if (!strncmp(dir->d_name, fn, fl)) {	/* match */
952 		NCFileBuf++;
953 		CFileBuf = New_Reuse(char *, CFileBuf, NCFileBuf);
954 		CFileBuf[NCFileBuf - 1] =
955 		    NewAtom_N(char, strlen(dir->d_name) + 1);
956 		strcpy(CFileBuf[NCFileBuf - 1], dir->d_name);
957 		if (NCFileBuf == 1) {
958 		    CFileName = Strnew_charp(dir->d_name);
959 		}
960 		else {
961 		    for (i = 0; CFileName->ptr[i] == dir->d_name[i]; i++) ;
962 		    Strtruncate(CFileName, i);
963 		}
964 	    }
965 	}
966 	closedir(d);
967 	if (NCFileBuf == 0) {
968 	    CompleteBuf = Strdup(ifn);
969 	    *status = CPL_FAIL;
970 	    if (cm_mode & CPL_ON)
971 		CompleteBuf = escape_spaces(CompleteBuf);
972 	    return CompleteBuf;
973 	}
974 	qsort(CFileBuf, NCFileBuf, sizeof(CFileBuf[0]), strCmp);
975 	NCFileOffset = 0;
976 	if (NCFileBuf >= 2) {
977 	    cm_next = TRUE;
978 	    *status = CPL_AMBIG;
979 	}
980 	else {
981 	    *status = CPL_OK;
982 	}
983     }
984     else {
985 	CFileName = Strnew_charp(CFileBuf[NCFileOffset]);
986 	NCFileOffset = (NCFileOffset + next + NCFileBuf) % NCFileBuf;
987 	*status = CPL_MENU;
988     }
989     CompleteBuf = Strdup(CDirBuf);
990     if (CompleteBuf->length && Strlastchar(CompleteBuf) != '/')
991 	Strcat_char(CompleteBuf, '/');
992     Strcat(CompleteBuf, CFileName);
993     if (*status != CPL_AMBIG) {
994 	p = CompleteBuf->ptr;
995 	if (cm_mode & CPL_URL) {
996 	    if (strncmp(p, "file://localhost/", 17) == 0)
997 		p = &p[16];
998 	    else if (strncmp(p, "file:///", 8) == 0)
999 		p = &p[7];
1000 	    else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
1001 		p = &p[5];
1002 	}
1003 	if (stat(expandPath(p), &st) != -1 && S_ISDIR(st.st_mode))
1004 	    Strcat_char(CompleteBuf, '/');
1005     }
1006     if (cm_mode & CPL_ON)
1007 	CompleteBuf = escape_spaces(CompleteBuf);
1008     return Str_conv_from_system(CompleteBuf);
1009 }
1010 
1011 static void
_prev(void)1012 _prev(void)
1013 {
1014     Hist *hist = CurrentHist;
1015     char *p;
1016 
1017     if (!use_hist)
1018 	return;
1019     if (strCurrentBuf) {
1020 	p = prevHist(hist);
1021 	if (p == NULL)
1022 	    return;
1023     }
1024     else {
1025 	p = lastHist(hist);
1026 	if (p == NULL)
1027 	    return;
1028 	strCurrentBuf = strBuf;
1029     }
1030     if (DecodeURL && (cm_mode & CPL_URL) )
1031 	p = url_decode2(p, NULL);
1032     strBuf = Strnew_charp(p);
1033     CLen = CPos = setStrType(strBuf, strProp);
1034     offset = 0;
1035 }
1036 
1037 static void
_next(void)1038 _next(void)
1039 {
1040     Hist *hist = CurrentHist;
1041     char *p;
1042 
1043     if (!use_hist)
1044 	return;
1045     if (strCurrentBuf == NULL)
1046 	return;
1047     p = nextHist(hist);
1048     if (p) {
1049 	if (DecodeURL && (cm_mode & CPL_URL) )
1050 	    p = url_decode2(p, NULL);
1051 	strBuf = Strnew_charp(p);
1052     }
1053     else {
1054 	strBuf = strCurrentBuf;
1055 	strCurrentBuf = NULL;
1056     }
1057     CLen = CPos = setStrType(strBuf, strProp);
1058     offset = 0;
1059 }
1060 
1061 static int
setStrType(Str str,Lineprop * prop)1062 setStrType(Str str, Lineprop *prop)
1063 {
1064     Lineprop ctype;
1065     char *p = str->ptr, *ep = p + str->length;
1066     int i, len = 1;
1067 
1068     for (i = 0; p < ep;) {
1069 #ifdef USE_M17N
1070 	len = get_mclen(p);
1071 #endif
1072 	if (i + len > STR_LEN)
1073 	    break;
1074 	ctype = get_mctype(p);
1075 	if (is_passwd) {
1076 	    if (ctype & PC_CTRL)
1077 		ctype = PC_ASCII;
1078 #ifdef USE_M17N
1079 	    if (ctype & PC_UNKNOWN)
1080 		ctype = PC_WCHAR1;
1081 #endif
1082 	}
1083 	prop[i++] = ctype;
1084 #ifdef USE_M17N
1085 	p += len;
1086 	if (--len) {
1087 	    ctype = (ctype & ~PC_WCHAR1) | PC_WCHAR2;
1088 	    while (len--)
1089 		prop[i++] = ctype;
1090 	}
1091 #else
1092 	p++;
1093 #endif
1094     }
1095     return i;
1096 }
1097 
1098 static int
terminated(unsigned char c)1099 terminated(unsigned char c)
1100 {
1101     int termchar[] = { '/', '&', '?', ' ', -1 };
1102     int *tp;
1103 
1104     for (tp = termchar; *tp > 0; tp++) {
1105 	if (c == *tp) {
1106 	    return 1;
1107 	}
1108     }
1109 
1110     return 0;
1111 }
1112 
1113 static void
_editor(void)1114 _editor(void)
1115 {
1116     FormItemList fi;
1117     char *p;
1118 
1119     if (is_passwd)
1120 	return;
1121 
1122     fi.readonly = FALSE;
1123     fi.value = Strdup(strBuf);
1124     Strcat_char(fi.value, '\n');
1125 
1126     input_textarea(&fi);
1127 
1128     strBuf = Strnew();
1129     for (p = fi.value->ptr; *p; p++) {
1130 	if (*p == '\r' || *p == '\n')
1131 	    continue;
1132 	Strcat_char(strBuf, *p);
1133     }
1134     CLen = CPos = setStrType(strBuf, strProp);
1135     if (CurrentTab)
1136 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
1137 }
1138