updated README
[k8sterm.git] / src / tcmdline.c
blob4fa78cae0b82d8ff9fdaccb0f1604870056d02d8
1 ////////////////////////////////////////////////////////////////////////////////
2 static char **cmdHistory = NULL;
3 static int cmdHistoryCount = 0;
4 static int cmdHistoryCurrent = -1; // <0: command line
5 static char cmdLineCur[K8T_UTF_SIZ*CMDLINE_SIZE];
8 static void cmdAddToHistory (const char *cmd) {
9 if (cmd == NULL) return;
10 while (*cmd && isspace(*cmd)) ++cmd;
11 if (!cmd[0]) return;
12 // check if we already have such command
13 for (int f = 0; f < cmdHistoryCount; ++f) {
14 if (strcmp(cmdHistory[f], cmd) == 0) {
15 // bingo! move it down
16 char *c = cmdHistory[f];
17 for (int c = f; c < cmdHistoryCount-1; ++c) cmdHistory[c] = cmdHistory[c+1];
18 cmdHistory[cmdHistoryCount-1] = c;
19 return;
22 // not found, add new
23 if (cmdHistoryCount < 128) {
24 // allocate memory for new command
25 ++cmdHistoryCount;
26 cmdHistory = realloc(cmdHistory, sizeof(char *)*cmdHistoryCount);
27 } else {
28 // remove top command, add this one
29 free(cmdHistory[0]);
30 for (int f = 1; f < cmdHistoryCount; ++f) cmdHistory[f-1] = cmdHistory[f];
32 cmdHistory[cmdHistoryCount-1] = strdup(cmd);
36 ////////////////////////////////////////////////////////////////////////////////
37 static void tcmdlinedirty (K8Term *term, K8TCmdLine *cmdline) {
38 (void)cmdline;
39 if (term != NULL) {
40 k8t_tmDirtyMark(term, term->row-term->topline-1, 2);
41 k8t_tmWantRedraw(term, 1);
46 static void tcmdlinefixofs (K8Term *term, K8TCmdLine *cmdline) {
47 int len = k8t_UTF8strlen(cmdline->cmdline);
48 int ofs = len-(term->col-1);
49 if (ofs < 0) ofs = 0;
50 for (cmdline->cmdofs = 0; ofs > 0; --ofs) cmdline->cmdofs += k8t_UTF8Size(cmdline->cmdline+cmdline->cmdofs);
51 tcmdlinedirty(term, cmdline);
55 static void tcmdlinehide (K8Term *term, K8TCmdLine *cmdline) {
56 if (cmdline->cmdMode != K8T_CMDMODE_NONE && term == xw.paste_term) k8stx_finish_paste();
57 cmdHistoryCurrent = -1;
58 cmdline->cmdMode = K8T_CMDMODE_NONE;
59 cmdline->cmdcurtabc = NULL;
60 tcmdlinedirty(term, cmdline);
64 // utf-8
65 static void tcmdlinemsg (K8Term *term, K8TCmdLine *cmdline, const char *msg) {
66 if (msg != NULL) {
67 int ofs = 0;
69 cmdHistoryCurrent = -1;
70 cmdline->cmdMode = K8T_CMDMODE_MESSAGE;
71 cmdline->cmdofs = 0;
72 cmdline->cmdtabpos = -1;
73 cmdline->cmdcurtabc = NULL;
75 while (*msg) {
76 int len = k8t_UTF8Size(msg);
78 if (len < 1 || ofs+len >= (int)sizeof(cmdline->cmdline)-1) break;
79 memcpy(cmdline->cmdline+ofs, msg, len);
80 ofs += len;
81 msg += len;
84 cmdline->cmdline[ofs] = 0;
85 tcmdlinedirty(term, cmdline);
90 static __attribute__((format(printf,3,4))) void tcmdlinemsgf (K8Term *term, K8TCmdLine *cmdline, const char *fmt, ...) {
91 char buf[128];
92 char *xbuf = buf;
93 int size = sizeof(buf)-1;
94 va_list ap;
96 for (;;) {
97 int n;
98 char *t;
100 va_start(ap, fmt);
101 n = vsnprintf(xbuf, size, fmt, ap);
102 va_end(ap);
103 if (n > -1 && n < size) break;
104 if (n > -1) size = n+1; else size += 4096;
105 if (xbuf == buf) xbuf = NULL;
106 if ((t = realloc(xbuf, size)) == NULL) { if (xbuf) free(xbuf); return; }
107 xbuf = t;
109 tcmdlinemsg(term, cmdline, xbuf);
110 if (xbuf != buf) free(xbuf);
114 static void tcmdlineinitex (K8Term *term, K8TCmdLine *cmdline, const char *msg) {
115 if (term == xw.paste_term) k8stx_finish_paste();
116 cmdline->cmdMode = K8T_CMDMODE_INPUT;
117 cmdline->cmdofs = 0;
118 cmdline->cmdline[0] = 0;
119 cmdline->cmdc[0] = 0;
120 cmdline->cmdcl = 0;
121 cmdline->cmdtabpos = -1;
122 cmdline->cmdcurtabc = NULL;
123 cmdline->cmdreslen = 0;
124 cmdline->cmdexecfn = NULL;
125 if (msg != NULL && msg[0]) {
126 strcpy(cmdline->cmdline, msg);
127 cmdline->cmdreslen = strlen(cmdline->cmdline);
129 tcmdlinefixofs(term, cmdline);
133 static void tcmdlineinit (K8Term *term, K8TCmdLine *cmdline) {
134 tcmdlineinitex(term, cmdline, NULL);
138 static void tcmdlinechoplast (K8Term *term, K8TCmdLine *cmdline) {
139 if (cmdline->cmdcl != 0) {
140 cmdline->cmdcl = 0;
141 } else {
142 if ((int)strlen(cmdline->cmdline) > cmdline->cmdreslen) {
143 k8t_UTF8ChopLast(cmdline->cmdline);
146 tcmdlinefixofs(term, cmdline);
150 // utf-8
151 static void tcmdaddchar (K8Term *term, K8TCmdLine *cmdline, const char *s) {
152 int len = k8t_UTF8Size(s);
153 if (len > 0) {
154 int slen = strlen(cmdline->cmdline);
155 if (slen+len < (int)sizeof(cmdline->cmdline)) {
156 memcpy(cmdline->cmdline+slen, s, len);
157 cmdline->cmdline[slen+len] = 0;
158 tcmdlinefixofs(term, cmdline);
164 // externally called (from selection processing code)
165 static void tcmdput (K8Term *term, K8TCmdLine *cmdline, const char *s, int len) {
166 cmdHistoryCurrent = -1;
167 while (len-- > 0) {
168 int ok;
169 cmdline->cmdc[cmdline->cmdcl++] = *s++;
170 cmdline->cmdc[cmdline->cmdcl] = 0;
171 if ((ok = k8t_UTF8IsFull(cmdline->cmdc, cmdline->cmdcl)) != 0 || cmdline->cmdcl == K8T_UTF_SIZ) {
172 if (ok) tcmdaddchar(term, cmdline, cmdline->cmdc);
173 cmdline->cmdcl = 0;
179 ////////////////////////////////////////////////////////////////////////////////
180 static void tcmdlHistoryMove (K8Term *term, K8TCmdLine *cmdline, int delta) {
181 if (delta) {
182 if (cmdHistoryCurrent < 0) {
183 // save current command line
184 snprintf(cmdLineCur, sizeof(cmdLineCur), "%s", cmdline->cmdline);
185 cmdHistoryCurrent = (delta < 0 ? cmdHistoryCount-1 : 0);
186 } else {
187 cmdHistoryCurrent += (delta < 0 ? -1 : 1);
189 if (cmdHistoryCurrent < 0 || cmdHistoryCurrent >= cmdHistoryCount) {
190 cmdHistoryCurrent = -1;
191 // restore command line
192 snprintf(cmdline->cmdline, sizeof(cmdline->cmdline), "%s", cmdLineCur);
193 } else {
194 snprintf(cmdline->cmdline, sizeof(cmdline->cmdline), "%s", cmdHistory[cmdHistoryCurrent]);
196 tcmdlinefixofs(term, cmdline);
201 // return !0 if event was eaten
202 static int tcmdlProcessKeys (K8Term *term, K8TCmdLine *cmdline, KeySym ksym, const char *ksbuf, int ksbuflen, XKeyEvent *e) {
203 int mode = cmdline->cmdMode;
204 switch (ksym) {
205 case XK_Insert:
206 if (cmdline->cmdline[0] && !(e->state&(Mod1Mask|ShiftMask)) && (e->state&ControlMask)) {
207 k8t_selClear(term);
208 if (lastSelStr != NULL) free(lastSelStr);
209 lastSelStr = strdup(cmdline->cmdline);
210 xfixsel();
212 break;
213 case XK_Return:
214 tcmdlinehide(term, cmdline);
215 if (cmdline->cmdexecfn != NULL) {
216 cmdline->cmdexecfn(term, cmdline, 0);
217 } else if (mode == K8T_CMDMODE_INPUT) {
218 cmdAddToHistory(cmdline->cmdline);
219 executeCommands(cmdline->cmdline);
221 break;
222 case XK_BackSpace:
223 if (mode == K8T_CMDMODE_INPUT) {
224 cmdHistoryCurrent = -1;
225 tcmdlinechoplast(term, cmdline);
226 cmdline->cmdtabpos = -1;
227 cmdline->cmdcurtabc = NULL;
228 } else {
229 tcmdlinehide(term, cmdline);
230 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
232 break;
233 case XK_Escape:
234 tcmdlinehide(term, cmdline);
235 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
236 break;
237 case XK_Tab:
238 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdline[0] && cmdline->cmdcl == 0 && cmdline->cmdexecfn == NULL) {
239 const char *cpl;
240 cmdHistoryCurrent = -1;
241 if (cmdline->cmdtabpos < 0) {
242 cmdline->cmdtabpos = 0;
243 while (cmdline->cmdline[cmdline->cmdtabpos] && isalnum(cmdline->cmdline[cmdline->cmdtabpos])) ++cmdline->cmdtabpos;
244 if (cmdline->cmdline[cmdline->cmdtabpos]) {
245 cmdline->cmdtabpos = -1;
246 break;
248 cmdline->cmdcurtabc = NULL;
250 cpl = findCommandCompletion(cmdline->cmdline, cmdline->cmdtabpos, cmdline->cmdcurtabc);
251 if (cpl == NULL && cmdline->cmdcurtabc != NULL) cpl = findCommandCompletion(cmdline->cmdline, cmdline->cmdtabpos, NULL);
252 cmdline->cmdcurtabc = cpl;
253 if (cpl != NULL) { strcpy(cmdline->cmdline, cpl); tcmdaddchar(term, cmdline, " "); }
254 tcmdlinefixofs(term, cmdline);
255 } else if (mode != K8T_CMDMODE_INPUT) {
256 tcmdlinehide(term, cmdline);
257 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
259 break;
260 case XK_Up: case XK_KP_8:
261 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdexecfn == NULL) tcmdlHistoryMove(term, cmdline, -1);
262 break;
263 case XK_Down: case XK_KP_2:
264 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdexecfn == NULL) tcmdlHistoryMove(term, cmdline, 1);
265 break;
266 case XK_space:
267 if (mode != K8T_CMDMODE_INPUT) {
268 tcmdlinehide(term, cmdline);
269 break;
271 // fallthru
272 default:
273 if (mode == K8T_CMDMODE_INPUT) {
274 if (ksbuflen > 0 && (unsigned char)ksbuf[0] >= 32) {
275 cmdHistoryCurrent = -1;
276 tcmdput(term, cmdline, ksbuf, ksbuflen);
277 cmdline->cmdtabpos = -1;
278 cmdline->cmdcurtabc = NULL;
281 break;
284 return 1; // eat any event