better 'exec'
[k8sterm.git] / src / tcmdline.c
blob3d4f4114b2fc70e0bca4230c50bb5e585a6941fb
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 if (term != NULL) {
39 k8t_tmDirtyMark(term, term->row-term->topline-1, 2);
40 k8t_tmWantRedraw(term, 1);
45 static void tcmdlinefixofs (K8Term *term, K8TCmdLine *cmdline) {
46 int len = k8t_UTF8strlen(cmdline->cmdline);
47 int ofs = len-(term->col-1);
48 if (ofs < 0) ofs = 0;
49 for (cmdline->cmdofs = 0; ofs > 0; --ofs) cmdline->cmdofs += k8t_UTF8Size(cmdline->cmdline+cmdline->cmdofs);
50 tcmdlinedirty(term, cmdline);
54 static void tcmdlinehide (K8Term *term, K8TCmdLine *cmdline) {
55 cmdHistoryCurrent = -1;
56 cmdline->cmdMode = K8T_CMDMODE_NONE;
57 cmdline->cmdcurtabc = NULL;
58 tcmdlinedirty(term, cmdline);
62 // utf-8
63 static void tcmdlinemsg (K8Term *term, K8TCmdLine *cmdline, const char *msg) {
64 if (msg != NULL) {
65 int ofs = 0;
67 cmdHistoryCurrent = -1;
68 cmdline->cmdMode = K8T_CMDMODE_MESSAGE;
69 cmdline->cmdofs = 0;
70 cmdline->cmdtabpos = -1;
71 cmdline->cmdcurtabc = NULL;
73 while (*msg) {
74 int len = k8t_UTF8Size(msg);
76 if (len < 1 || ofs+len >= sizeof(cmdline->cmdline)-1) break;
77 memcpy(cmdline->cmdline+ofs, msg, len);
78 ofs += len;
79 msg += len;
82 cmdline->cmdline[ofs] = 0;
83 tcmdlinedirty(term, cmdline);
88 static __attribute__((format(printf,3,4))) void tcmdlinemsgf (K8Term *term, K8TCmdLine *cmdline, const char *fmt, ...) {
89 char buf[128];
90 char *xbuf = buf;
91 int size = sizeof(buf)-1;
92 va_list ap;
94 for (;;) {
95 int n;
96 char *t;
98 va_start(ap, fmt);
99 n = vsnprintf(xbuf, size, fmt, ap);
100 va_end(ap);
101 if (n > -1 && n < size) break;
102 if (n > -1) size = n+1; else size += 4096;
103 if (xbuf == buf) xbuf = NULL;
104 if ((t = realloc(xbuf, size)) == NULL) { if (xbuf) free(xbuf); return; }
105 xbuf = t;
107 tcmdlinemsg(term, cmdline, xbuf);
108 if (xbuf != buf) free(xbuf);
112 static void tcmdlineinitex (K8Term *term, K8TCmdLine *cmdline, const char *msg) {
113 cmdline->cmdMode = K8T_CMDMODE_INPUT;
114 cmdline->cmdofs = 0;
115 cmdline->cmdline[0] = 0;
116 cmdline->cmdc[0] = 0;
117 cmdline->cmdcl = 0;
118 cmdline->cmdtabpos = -1;
119 cmdline->cmdcurtabc = NULL;
120 cmdline->cmdreslen = 0;
121 cmdline->cmdexecfn = NULL;
122 if (msg != NULL && msg[0]) {
123 strcpy(cmdline->cmdline, msg);
124 cmdline->cmdreslen = strlen(cmdline->cmdline);
126 tcmdlinefixofs(term, cmdline);
130 static void tcmdlineinit (K8Term *term, K8TCmdLine *cmdline) {
131 tcmdlineinitex(term, cmdline, NULL);
135 static void tcmdlinechoplast (K8Term *term, K8TCmdLine *cmdline) {
136 if (cmdline->cmdcl != 0) {
137 cmdline->cmdcl = 0;
138 } else {
139 if (strlen(cmdline->cmdline) > cmdline->cmdreslen) k8t_UTF8ChopLast(cmdline->cmdline);
141 tcmdlinefixofs(term, cmdline);
145 // utf-8
146 static void tcmdaddchar (K8Term *term, K8TCmdLine *cmdline, const char *s) {
147 int len = k8t_UTF8Size(s);
148 if (len > 0) {
149 int slen = strlen(cmdline->cmdline);
150 if (slen+len < sizeof(cmdline->cmdline)) {
151 memcpy(cmdline->cmdline+slen, s, len);
152 cmdline->cmdline[slen+len] = 0;
153 tcmdlinefixofs(term, cmdline);
159 // externally called (from selection processing code)
160 static void tcmdput (K8Term *term, K8TCmdLine *cmdline, const char *s, int len) {
161 cmdHistoryCurrent = -1;
162 while (len-- > 0) {
163 int ok;
164 cmdline->cmdc[cmdline->cmdcl++] = *s++;
165 cmdline->cmdc[cmdline->cmdcl] = 0;
166 if ((ok = k8t_UTF8IsFull(cmdline->cmdc, cmdline->cmdcl)) != 0 || cmdline->cmdcl == K8T_UTF_SIZ) {
167 if (ok) tcmdaddchar(term, cmdline, cmdline->cmdc);
168 cmdline->cmdcl = 0;
174 ////////////////////////////////////////////////////////////////////////////////
175 static void tcmdlHistoryMove (K8Term *term, K8TCmdLine *cmdline, int delta) {
176 if (delta) {
177 if (cmdHistoryCurrent < 0) {
178 // save current command line
179 snprintf(cmdLineCur, sizeof(cmdLineCur), "%s", cmdline->cmdline);
180 cmdHistoryCurrent = (delta < 0 ? cmdHistoryCount-1 : 0);
181 } else {
182 cmdHistoryCurrent += (delta < 0 ? -1 : 1);
184 if (cmdHistoryCurrent < 0 || cmdHistoryCurrent >= cmdHistoryCount) {
185 cmdHistoryCurrent = -1;
186 // restore command line
187 snprintf(cmdline->cmdline, sizeof(cmdline->cmdline), "%s", cmdLineCur);
188 } else {
189 snprintf(cmdline->cmdline, sizeof(cmdline->cmdline), "%s", cmdHistory[cmdHistoryCurrent]);
191 tcmdlinefixofs(term, cmdline);
196 // return !0 if event was eaten
197 static int tcmdlProcessKeys (K8Term *term, K8TCmdLine *cmdline, KeySym ksym, const char *ksbuf, int ksbuflen, XKeyEvent *e) {
198 int mode = cmdline->cmdMode;
199 switch (ksym) {
200 case XK_Insert:
201 if (cmdline->cmdline[0] && !(e->state&(Mod1Mask|ShiftMask)) && (e->state&ControlMask)) {
202 k8t_selClear(term);
203 if (lastSelStr != NULL) free(lastSelStr);
204 lastSelStr = strdup(cmdline->cmdline);
205 xfixsel();
207 break;
208 case XK_Return:
209 tcmdlinehide(term, cmdline);
210 if (cmdline->cmdexecfn != NULL) {
211 cmdline->cmdexecfn(term, cmdline, 0);
212 } else if (mode == K8T_CMDMODE_INPUT) {
213 cmdAddToHistory(cmdline->cmdline);
214 executeCommands(cmdline->cmdline);
216 break;
217 case XK_BackSpace:
218 if (mode == K8T_CMDMODE_INPUT) {
219 cmdHistoryCurrent = -1;
220 tcmdlinechoplast(term, cmdline);
221 cmdline->cmdtabpos = -1;
222 cmdline->cmdcurtabc = NULL;
223 } else {
224 tcmdlinehide(term, cmdline);
225 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
227 break;
228 case XK_Escape:
229 tcmdlinehide(term, cmdline);
230 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
231 break;
232 case XK_Tab:
233 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdline[0] && cmdline->cmdcl == 0 && cmdline->cmdexecfn == NULL) {
234 const char *cpl;
235 cmdHistoryCurrent = -1;
236 if (cmdline->cmdtabpos < 0) {
237 cmdline->cmdtabpos = 0;
238 while (cmdline->cmdline[cmdline->cmdtabpos] && isalnum(cmdline->cmdline[cmdline->cmdtabpos])) ++cmdline->cmdtabpos;
239 if (cmdline->cmdline[cmdline->cmdtabpos]) {
240 cmdline->cmdtabpos = -1;
241 break;
243 cmdline->cmdcurtabc = NULL;
245 cpl = findCommandCompletion(cmdline->cmdline, cmdline->cmdtabpos, cmdline->cmdcurtabc);
246 if (cpl == NULL && cmdline->cmdcurtabc != NULL) cpl = findCommandCompletion(cmdline->cmdline, cmdline->cmdtabpos, NULL);
247 cmdline->cmdcurtabc = cpl;
248 if (cpl != NULL) { strcpy(cmdline->cmdline, cpl); tcmdaddchar(term, cmdline, " "); }
249 tcmdlinefixofs(term, cmdline);
250 } else if (mode != K8T_CMDMODE_INPUT) {
251 tcmdlinehide(term, cmdline);
252 if (cmdline->cmdexecfn != NULL) cmdline->cmdexecfn(term, cmdline, 1);
254 break;
255 case XK_Up: case XK_KP_8:
256 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdexecfn == NULL) tcmdlHistoryMove(term, cmdline, -1);
257 break;
258 case XK_Down: case XK_KP_2:
259 if (mode == K8T_CMDMODE_INPUT && cmdline->cmdexecfn == NULL) tcmdlHistoryMove(term, cmdline, 1);
260 break;
261 case XK_space:
262 if (mode != K8T_CMDMODE_INPUT) {
263 tcmdlinehide(term, cmdline);
264 break;
266 // fallthru
267 default:
268 if (mode == K8T_CMDMODE_INPUT) {
269 if (ksbuflen > 0 && (unsigned char)ksbuf[0] >= 32) {
270 cmdHistoryCurrent = -1;
271 tcmdput(term, cmdline, ksbuf, ksbuflen);
272 cmdline->cmdtabpos = -1;
273 cmdline->cmdcurtabc = NULL;
276 break;
279 return 1; // eat any event