1 #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 
23 static struct list_head cli_history_head;
24 
clear_screen(void)25 void clear_screen(void)
26 {
27     //dprintf("enter");
28     fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
29 }
30 
mygetkey_timeout(clock_t * kbd_to,clock_t * tto)31 static 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 
cmd_reverse_search(int * cursor,clock_t * kbd_to,clock_t * tto)60 static 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 
edit_cmdline(const char * input,int top,int (* pDraw_Menu)(int,int,int),void (* show_fkey)(int),bool * timedout)124 const 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 
cli_init(void)476 static int __constructor cli_init(void)
477 {
478 	INIT_LIST_HEAD(&cli_history_head);
479 
480 	return 0;
481 }
482 
cli_exit(void)483 static void __destructor cli_exit(void)
484 {
485 	/* Nothing to do */
486 }
487