Intial commit
[profile/ivi/w3m.git] / linein.c
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 *
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 }