1 /* $Id: linein.c,v 1.35 2007/05/23 12:14:24 inu Exp $ */
10 #if defined(USE_GPM) || defined(USE_SYSMOUSE)
11 extern int do_getch();
12 #define getch() do_getch()
14 #endif /* USE_MOUSE */
17 #include <sys/kbdscan.h>
21 #define CLEN (COLS - 2)
24 static Lineprop strProp[STR_LEN];
26 static Str CompleteBuf;
28 static Str CBeforeBuf;
31 static char **CFileBuf = NULL;
33 static int NCFileOffset;
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);
42 static int getcntrl(void);
45 static int terminated(unsigned char c);
46 #define iself ((void(*)())insertself)
48 static void next_compl(int next);
49 static void next_dcompl(int next);
50 static Str doComplete(Str ifn, int *status, int next);
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,
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);
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;
75 static Hist *CurrentHist;
76 static Str strCurrentBuf;
79 static void ins_char(Str str);
81 static void ins_char(char c);
85 inputLineHistSearch(char *prompt, char *def_str, int flag, Hist *hist,
86 int (*incrfunc) (int ch, Str str, Lineprop *prop))
88 int opos, x, y, lpos, rpos, epos;
101 strCurrentBuf = NULL;
107 cm_mode = CPL_ALWAYS | CPL_URL;
109 else if (flag & IN_FILENAME) {
110 cm_mode = CPL_ALWAYS;
112 else if (flag & IN_PASSWORD) {
117 else if (flag & IN_COMMAND)
121 opos = get_strwidth(prompt);
130 strBuf = Strnew_charp(def_str);
131 CLen = CPos = setStrType(strBuf, strProp);
138 #ifdef SUPPORT_WIN9X_CONSOLE_MBCS
139 enable_win9x_console_input();
149 wc_char_conv_init(wc_guess_8bit_charset(DisplayCharset), InnerCharset);
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)
157 else if (y - epos > 0)
160 else if (x - lpos < offset) {
169 addPasswd(strBuf->ptr, strProp, CLen, offset, COLS - opos);
171 addStr(strBuf->ptr, strProp, CLen, offset, COLS - opos);
173 move(LASTLINE, opos + x - offset);
180 if (!(c = getcntrl()))
185 cm_disp_clear = TRUE;
187 (((cm_mode & CPL_ALWAYS) && (c == CTRL_I || c == ' ')) ||
188 ((cm_mode & CPL_ON) && (c == CTRL_I)))) {
189 if (emacs_like_lineedit && cm_next) {
198 else if (!i_quote && CLen == CPos &&
199 (cm_mode & CPL_ALWAYS || cm_mode & CPL_ON) && c == CTRL_D) {
200 if (!emacs_like_lineedit) {
205 else if (!i_quote && c == DEL_CODE) {
210 else if (!i_quote && c < 0x20) { /* Control code */
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);
223 tmp = wc_char_conv(c);
231 if (CLen + tmp->length > STR_LEN || !tmp->length)
235 incrfunc(-1, strBuf, strProp);
245 strBuf->ptr[CPos] = c;
246 if (!is_passwd && get_mctype(&c) == PC_CTRL)
247 strProp[CPos] = PC_CTRL;
249 strProp[CPos] = PC_ASCII;
252 incrfunc(-1, strBuf, strProp);
255 if (CLen && (flag & IN_CHAR))
261 displayBuffer(Currentbuf, B_FORCE_REDRAW);
264 #ifdef SUPPORT_WIN9X_CONSOLE_MBCS
265 disable_win9x_console_input();
274 if (flag & (IN_FILENAME | IN_COMMAND)) {
277 if (use_hist && !(flag & IN_URL) && *p != '\0') {
278 char *q = lastHist(hist);
279 if (!q || strcmp(q, p))
282 if (flag & IN_FILENAME)
283 return expandPath(p);
285 return allocStr(p, -1);
319 addPasswd(char *p, Lineprop *pr, int len, int offset, int limit)
323 ncol = calcPosition(p, pr, len, len, 0, CP_AUTO);
324 if (ncol > offset + limit)
325 ncol = offset + limit;
330 for (; rcol < ncol; rcol++)
335 addStr(char *p, Lineprop *pr, int len, int offset, int limit)
337 int i = 0, rcol = 0, ncol, delta = 1;
340 for (i = 0; i < len; i++) {
341 if (calcPosition(p, pr, len, i, 0, CP_AUTO) > offset)
347 while (pr[i] & PC_WCHAR2)
352 ncol = calcPosition(p, pr, len, i, 0, CP_AUTO);
353 for (; rcol < ncol; rcol++)
356 for (; i < len; i += delta) {
358 delta = wtf_len((wc_uchar *) & p[i]);
360 ncol = calcPosition(p, pr, len, i + delta, 0, CP_AUTO);
361 if (ncol - offset > limit)
364 for (; rcol < ncol; rcol++)
370 addMChar(&p[i], pr[i], delta);
372 addChar(p[i], pr[i]);
383 char *p = str->ptr, *ep = p + str->length;
387 if (CLen + str->length >= STR_LEN)
391 ctype = get_mctype(p);
395 if (ctype & PC_UNKNOWN)
399 strBuf->ptr[CPos] = *(p++);
400 strProp[CPos] = ctype;
403 ctype = (ctype & ~PC_WCHAR1) | PC_WCHAR2;
406 strBuf->ptr[CPos] = *(p++);
407 strProp[CPos] = ctype;
420 switch (c = getch()) {
423 switch (c = getch()) {
440 if (emacs_like_lineedit) {
449 if (!emacs_like_lineedit)
454 if (emacs_like_lineedit)
458 if (emacs_like_lineedit)
462 if (emacs_like_lineedit)
467 if (wc_char_conv(ESC_CODE) == NULL && wc_char_conv(c) == NULL)
478 Strinsert_char(strBuf, CPos, ' ');
479 CLen = strBuf->length;
480 for (i = CLen; i > CPos; i--) {
481 strProp[i] = strProp[i - 1];
494 while (i + delta < CLen && strProp[i + delta] & PC_WCHAR2)
497 for (i = CPos; i < CLen; i++) {
498 strProp[i] = strProp[i + delta];
500 Strdelete(strBuf, CPos, delta);
510 while (CPos > 0 && strProp[CPos] & PC_WCHAR2)
519 while (CPos > 0 && (first || !terminated(strBuf->ptr[CPos - 1]))) {
523 if (CPos > 0 && strProp[CPos] & PC_WCHAR2)
535 while (CPos < CLen && (first || !terminated(strBuf->ptr[CPos - 1]))) {
539 if (CPos < CLen && strProp[CPos] & PC_WCHAR2)
553 while (CPos < CLen && strProp[CPos] & PC_WCHAR2)
571 while (CPos > 0 && !t) {
573 t = (move_word && terminated(strBuf->ptr[CPos - 1]));
590 strBuf->ptr[CPos] = c;
591 strProp[CPos] = (is_passwd) ? PC_ASCII : PC_CTRL;
617 Strtruncate(strBuf, CLen);
649 if (cm_mode & CPL_OFF)
651 else if (cm_mode & CPL_ON)
663 if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
667 if (cm_mode & CPL_ALWAYS) {
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] == '\\'))
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);
685 s = doComplete(strBuf, &status, next);
690 if (status != CPL_OK && status != CPL_MENU)
692 if (status == CPL_FAIL)
695 strBuf = Strnew_m_charp(CBeforeBuf->ptr, s->ptr, CAfterBuf->ptr, NULL);
696 CLen = setStrType(strBuf, strProp);
697 CPos = CBeforeBuf->length + s->length;
715 next_dcompl(int next)
717 static int col, row, len;
725 if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF)
727 cm_disp_clear = FALSE;
729 displayBuffer(Currentbuf, B_FORCE_REDRAW);
732 nline = LASTLINE - 2;
742 if (cm_disp_next >= 0) {
744 cm_disp_next += col * nline;
745 if (cm_disp_next >= NCFileBuf)
748 else if (next == -1) {
749 cm_disp_next -= col * nline;
750 if (cm_disp_next < 0)
753 row = (NCFileBuf - cm_disp_next + col - 1) / col;
763 d = Str_conv_to_system(Strdup(CDirBuf));
764 if (d->length > 0 && Strlastchar(d) != '/')
766 if (cm_mode & CPL_URL && d->ptr[0] == 'f') {
768 if (strncmp(p, "file://localhost/", 17) == 0)
770 else if (strncmp(p, "file:///", 8) == 0)
772 else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
778 for (i = 0; i < NCFileBuf; i++) {
779 n = strlen(CFileBuf[i]) + 3;
786 row = (NCFileBuf + col - 1) / col;
813 /* FIXME: gettextize? */
814 addstr("----- Completion list -----");
818 for (i = 0; i < row; i++) {
819 for (j = 0; j < col; j++) {
820 n = cm_disp_next + j * row + i;
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))
833 if (comment && y == LASTLINE - 1) {
837 if (emacs_like_lineedit)
838 /* FIXME: gettextize? */
839 addstr("----- Press TAB to continue -----");
841 /* FIXME: gettextize? */
842 addstr("----- Press CTRL-D to continue -----");
856 for (p = s->ptr; *p; p++) {
857 if (*p == ' ' || *p == CTRL_I) {
859 tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
860 Strcat_char(tmp, '\\');
863 Strcat_char(tmp, *p);
872 unescape_spaces(Str s)
879 for (p = s->ptr; *p; p++) {
880 if (*p == '\\' && (*(p + 1) == ' ' || *(p + 1) == CTRL_I)) {
882 tmp = Strnew_charp_n(s->ptr, (int)(p - s->ptr));
886 Strcat_char(tmp, *p);
895 doComplete(Str ifn, int *status, int next)
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);
921 CompleteBuf = Strdup(ifn);
923 return Str_conv_to_system(CompleteBuf);
926 if (CompleteBuf->length == 0) {
927 Strcat_char(CompleteBuf, '.');
929 if (Strlastchar(CompleteBuf) == '/' && CompleteBuf->length > 1) {
930 Strshrink(CompleteBuf, 1);
932 if ((d = opendir(expandPath(CompleteBuf->ptr))) == NULL) {
933 CompleteBuf = Strdup(ifn);
935 if (cm_mode & CPL_ON)
936 CompleteBuf = escape_spaces(CompleteBuf);
939 fn = lastFileName(ifn->ptr);
941 CFileName = Strnew();
947 && (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")))
949 if (!strncmp(dir->d_name, fn, fl)) { /* match */
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);
959 for (i = 0; CFileName->ptr[i] == dir->d_name[i]; i++) ;
960 Strtruncate(CFileName, i);
965 if (NCFileBuf == 0) {
966 CompleteBuf = Strdup(ifn);
968 if (cm_mode & CPL_ON)
969 CompleteBuf = escape_spaces(CompleteBuf);
972 qsort(CFileBuf, NCFileBuf, sizeof(CFileBuf[0]), strCmp);
974 if (NCFileBuf >= 2) {
983 CFileName = Strnew_charp(CFileBuf[NCFileOffset]);
984 NCFileOffset = (NCFileOffset + next + NCFileBuf) % NCFileBuf;
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)
996 else if (strncmp(p, "file:///", 8) == 0)
998 else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/')
1001 if (stat(expandPath(p), &st) != -1 && S_ISDIR(st.st_mode))
1002 Strcat_char(CompleteBuf, '/');
1004 if (cm_mode & CPL_ON)
1005 CompleteBuf = escape_spaces(CompleteBuf);
1006 return Str_conv_from_system(CompleteBuf);
1012 Hist *hist = CurrentHist;
1017 if (strCurrentBuf) {
1026 strCurrentBuf = strBuf;
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);
1038 Hist *hist = CurrentHist;
1043 if (strCurrentBuf == NULL)
1047 if (DecodeURL && (cm_mode & CPL_URL) )
1048 p = url_unquote_conv(p, 0);
1049 strBuf = Strnew_charp(p);
1052 strBuf = strCurrentBuf;
1053 strCurrentBuf = NULL;
1055 CLen = CPos = setStrType(strBuf, strProp);
1060 setStrType(Str str, Lineprop *prop)
1063 char *p = str->ptr, *ep = p + str->length;
1066 for (i = 0; p < ep;) {
1070 if (i + len > STR_LEN)
1072 ctype = get_mctype(p);
1074 if (ctype & PC_CTRL)
1077 if (ctype & PC_UNKNOWN)
1085 ctype = (ctype & ~PC_WCHAR1) | PC_WCHAR2;
1097 terminated(unsigned char c)
1099 int termchar[] = { '/', '&', '?', ' ', -1 };
1102 for (tp = termchar; *tp > 0; tp++) {
1120 fi.readonly = FALSE;
1121 fi.value = Strdup(strBuf);
1122 Strcat_char(fi.value, '\n');
1124 input_textarea(&fi);
1127 for (p = fi.value->ptr; *p; p++) {
1128 if (*p == '\r' || *p == '\n')
1130 Strcat_char(strBuf, *p);
1132 CLen = CPos = setStrType(strBuf, strProp);
1134 displayBuffer(Currentbuf, B_FORCE_REDRAW);