blob: 6ff30c6471a4be5126f32ad12adea801c008b1fa [file] [log] [blame]
Greg Hartman76d05dc2016-11-23 15:51:27 -08001#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <console.h>
5#include <com32.h>
6#include <syslinux/adv.h>
7#include <syslinux/config.h>
8#include <setjmp.h>
9#include <netinet/in.h>
10#include <limits.h>
11#include <minmax.h>
12#include <linux/list.h>
13#include <sys/exec.h>
14#include <sys/module.h>
15#include <dprintf.h>
16#include <core.h>
17
18#include "getkey.h"
19#include "menu.h"
20#include "cli.h"
21#include "config.h"
22
23static struct list_head cli_history_head;
24
25void clear_screen(void)
26{
27 //dprintf("enter");
28 fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
29}
30
31static int mygetkey_timeout(clock_t *kbd_to, clock_t *tto)
32{
33 clock_t t0, t1;
34 int key;
35
36 t0 = times(NULL);
37 key = get_key(stdin, *kbd_to ? *kbd_to : *tto);
38
39 /* kbdtimeout only applies to the first character */
40 if (*kbd_to)
41 *kbd_to = 0;
42
43 t1 = times(NULL) - t0;
44 if (*tto) {
45 /* Timed out. */
46 if (*tto <= (long long)t1)
47 key = KEY_NONE;
48 else {
49 /* Did it wrap? */
50 if (*tto > totaltimeout)
51 key = KEY_NONE;
52
53 *tto -= t1;
54 }
55 }
56
57 return key;
58}
59
60static const char * cmd_reverse_search(int *cursor, clock_t *kbd_to,
61 clock_t *tto)
62{
63 int key;
64 int i = 0;
65 char buf[MAX_CMDLINE_LEN];
66 const char *p = NULL;
67 struct cli_command *last_found;
68 struct cli_command *last_good = NULL;
69
70 last_found = list_entry(cli_history_head.next, typeof(*last_found), list);
71
72 memset(buf, 0, MAX_CMDLINE_LEN);
73
74 printf("\033[1G\033[1;36m(reverse-i-search)`': \033[0m");
75 while (1) {
76 key = mygetkey_timeout(kbd_to, tto);
77
78 if (key == KEY_CTRL('C')) {
79 return NULL;
80 } else if (key == KEY_CTRL('R')) {
81 if (i == 0)
82 continue; /* User typed nothing yet */
83 /* User typed 'CTRL-R' again, so try the next */
84 last_found = list_entry(last_found->list.next, typeof(*last_found), list);
85 } else if (key >= ' ' && key <= 'z') {
86 buf[i++] = key;
87 } else {
88 /* Treat other input chars as terminal */
89 break;
90 }
91
92 while (last_found) {
93 p = strstr(last_found->command, buf);
94 if (p)
95 break;
96
97 if (list_is_last(&last_found->list, &cli_history_head))
98 break;
99
100 last_found = list_entry(last_found->list.next, typeof(*last_found), list);
101 }
102
103 if (!p && !last_good) {
104 return NULL;
105 } else if (!p) {
106 continue;
107 } else {
108 last_good = last_found;
109 *cursor = p - last_good->command;
110 }
111
112 printf("\033[?7l\033[?25l");
113 /* Didn't handle the line wrap case here */
114 printf("\033[1G\033[1;36m(reverse-i-search)\033[0m`%s': %s",
115 buf, last_good->command ? : "");
116 printf("\033[K\r");
117 }
118
119 return last_good ? last_good->command : NULL;
120}
121
122
123
124const char *edit_cmdline(const char *input, int top /*, int width */ ,
125 int (*pDraw_Menu) (int, int, int),
126 void (*show_fkey) (int), bool *timedout)
127{
128 char cmdline[MAX_CMDLINE_LEN] = { };
129 int key, len, prev_len, cursor;
130 int redraw = 0;
131 int x, y;
132 bool done = false;
133 const char *ret;
134 int width = 0;
135 struct cli_command *comm_counter = NULL;
136 clock_t kbd_to = kbdtimeout;
137 clock_t tto = totaltimeout;
138
139 if (!width) {
140 int height;
141 if (getscreensize(1, &height, &width))
142 width = 80;
143 }
144
145 len = cursor = 0;
146 prev_len = 0;
147 x = y = 0;
148
149 /*
150 * Before we start messing with the x,y coordinates print 'input'
151 * so that it follows whatever text has been written to the screen
152 * previously.
153 */
154 printf("%s ", input);
155
156 while (!done) {
157 if (redraw > 1) {
158 /* Clear and redraw whole screen */
159 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
160 to avoid confusing the Linux console */
161 clear_screen();
162 if (pDraw_Menu)
163 (*pDraw_Menu) (-1, top, 1);
164 prev_len = 0;
165 printf("\033[2J\033[H");
166 // printf("\033[0m\033[2J\033[H");
167 }
168
169 if (redraw > 0) {
170 int dy, at;
171
172 prev_len = max(len, prev_len);
173
174 /* Redraw the command line */
175 printf("\033[?25l");
176 printf("\033[1G%s ", input);
177
178 x = strlen(input);
179 y = 0;
180 at = 0;
181 while (at < prev_len) {
182 putchar(at >= len ? ' ' : cmdline[at]);
183 at++;
184 x++;
185 if (x >= width) {
186 printf("\r\n");
187 x = 0;
188 y++;
189 }
190 }
191 printf("\033[K\r");
192
193 dy = y - (cursor + strlen(input) + 1) / width;
194 x = (cursor + strlen(input) + 1) % width;
195
196 if (dy) {
197 printf("\033[%dA", dy);
198 y -= dy;
199 }
200 if (x)
201 printf("\033[%dC", x);
202 printf("\033[?25h");
203 prev_len = len;
204 redraw = 0;
205 }
206
207 key = mygetkey_timeout(&kbd_to, &tto);
208
209 switch (key) {
210 case KEY_NONE:
211 /* We timed out. */
212 *timedout = true;
213 return NULL;
214
215 case KEY_CTRL('L'):
216 redraw = 2;
217 break;
218
219 case KEY_ENTER:
220 case KEY_CTRL('J'):
221 ret = cmdline;
222 done = true;
223 break;
224
225 case KEY_BACKSPACE:
226 case KEY_DEL:
227 if (cursor) {
228 memmove(cmdline + cursor - 1, cmdline + cursor,
229 len - cursor + 1);
230 len--;
231 cursor--;
232 redraw = 1;
233 }
234 break;
235
236 case KEY_CTRL('D'):
237 case KEY_DELETE:
238 if (cursor < len) {
239 memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor);
240 len--;
241 redraw = 1;
242 }
243 break;
244
245 case KEY_CTRL('U'):
246 if (len) {
247 len = cursor = 0;
248 cmdline[len] = '\0';
249 redraw = 1;
250 }
251 break;
252
253 case KEY_CTRL('W'):
254 if (cursor) {
255 int prevcursor = cursor;
256
257 while (cursor && my_isspace(cmdline[cursor - 1]))
258 cursor--;
259
260 while (cursor && !my_isspace(cmdline[cursor - 1]))
261 cursor--;
262
263#if 0
264 memmove(cmdline + cursor, cmdline + prevcursor,
265 len - prevcursor + 1);
266#else
267 {
268 int i;
269 char *q = cmdline + cursor;
270 char *p = cmdline + prevcursor;
271 for (i = 0; i < len - prevcursor + 1; i++)
272 *q++ = *p++;
273 }
274#endif
275 len -= (prevcursor - cursor);
276 redraw = 1;
277 }
278 break;
279
280 case KEY_LEFT:
281 case KEY_CTRL('B'):
282 if (cursor) {
283 cursor--;
284 redraw = 1;
285 }
286 break;
287
288 case KEY_RIGHT:
289 case KEY_CTRL('F'):
290 if (cursor < len) {
291 putchar(cmdline[cursor]);
292 cursor++;
293 x++;
294 if (x >= width) {
295 printf("\r\n");
296 y++;
297 x = 0;
298 }
299 }
300 break;
301
302 case KEY_CTRL('K'):
303 if (cursor < len) {
304 cmdline[len = cursor] = '\0';
305 redraw = 1;
306 }
307 break;
308
309 case KEY_HOME:
310 case KEY_CTRL('A'):
311 if (cursor) {
312 cursor = 0;
313 redraw = 1;
314 }
315 break;
316
317 case KEY_END:
318 case KEY_CTRL('E'):
319 if (cursor != len) {
320 cursor = len;
321 redraw = 1;
322 }
323 break;
324
325 case KEY_F1:
326 case KEY_F2:
327 case KEY_F3:
328 case KEY_F4:
329 case KEY_F5:
330 case KEY_F6:
331 case KEY_F7:
332 case KEY_F8:
333 case KEY_F9:
334 case KEY_F10:
335 case KEY_F11:
336 case KEY_F12:
337 if (show_fkey != NULL) {
338 (*show_fkey) (key);
339 redraw = 1;
340 }
341 break;
342 case KEY_CTRL('P'):
343 case KEY_UP:
344 {
345 if (!list_empty(&cli_history_head)) {
346 struct list_head *next;
347
348 if (!comm_counter)
349 next = cli_history_head.next;
350 else
351 next = comm_counter->list.next;
352
353 comm_counter =
354 list_entry(next, typeof(*comm_counter), list);
355
356 if (&comm_counter->list != &cli_history_head)
357 strcpy(cmdline, comm_counter->command);
358
359 cursor = len = strlen(cmdline);
360 redraw = 1;
361 }
362 }
363 break;
364 case KEY_CTRL('N'):
365 case KEY_DOWN:
366 {
367 if (!list_empty(&cli_history_head)) {
368 struct list_head *prev;
369
370 if (!comm_counter)
371 prev = cli_history_head.prev;
372 else
373 prev = comm_counter->list.prev;
374
375 comm_counter =
376 list_entry(prev, typeof(*comm_counter), list);
377
378 if (&comm_counter->list != &cli_history_head)
379 strcpy(cmdline, comm_counter->command);
380
381 cursor = len = strlen(cmdline);
382 redraw = 1;
383 }
384 }
385 break;
386 case KEY_CTRL('R'):
387 {
388 /*
389 * Handle this case in another function, since it's
390 * a kind of special.
391 */
392 const char *p = cmd_reverse_search(&cursor, &kbd_to, &tto);
393 if (p) {
394 strcpy(cmdline, p);
395 len = strlen(cmdline);
396 } else {
397 cmdline[0] = '\0';
398 cursor = len = 0;
399 }
400 redraw = 1;
401 }
402 break;
403 case KEY_TAB:
404 {
405 const char *p;
406 size_t len;
407
408 /* Label completion enabled? */
409 if (nocomplete)
410 break;
411
412 p = cmdline;
413 len = 0;
414 while(*p && !my_isspace(*p)) {
415 p++;
416 len++;
417 }
418
419 print_labels(cmdline, len);
420 redraw = 1;
421 break;
422 }
423 case KEY_CTRL('V'):
424 if (BIOSName)
425 printf("%s%s%s", syslinux_banner,
426 (char *)MK_PTR(0, BIOSName), copyright_str);
427 else
428 printf("%s%s", syslinux_banner, copyright_str);
429
430 redraw = 1;
431 break;
432
433 default:
434 if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) {
435 if (cursor == len) {
436 cmdline[len++] = key;
437 cmdline[len] = '\0';
438 putchar(key);
439 cursor++;
440 x++;
441 if (x >= width) {
442 printf("\r\n\033[K");
443 y++;
444 x = 0;
445 }
446 prev_len++;
447 } else {
448 if (cursor > len)
449 return NULL;
450
451 memmove(cmdline + cursor + 1, cmdline + cursor,
452 len - cursor + 1);
453 cmdline[cursor++] = key;
454 len++;
455 redraw = 1;
456 }
457 }
458 break;
459 }
460 }
461
462 printf("\033[?7h");
463
464 /* Add the command to the history if its length is larger than 0 */
465 len = strlen(ret);
466 if (len > 0) {
467 comm_counter = malloc(sizeof(struct cli_command));
468 comm_counter->command = malloc(sizeof(char) * (len + 1));
469 strcpy(comm_counter->command, ret);
470 list_add(&(comm_counter->list), &cli_history_head);
471 }
472
473 return len ? ret : NULL;
474}
475
476static int __constructor cli_init(void)
477{
478 INIT_LIST_HEAD(&cli_history_head);
479
480 return 0;
481}
482
483static void __destructor cli_exit(void)
484{
485 /* Nothing to do */
486}