Home | History | Annotate | Download | only in ldlinux
      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 
     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 
     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 
     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 
    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 
    476 static int __constructor cli_init(void)
    477 {
    478 	INIT_LIST_HEAD(&cli_history_head);
    479 
    480 	return 0;
    481 }
    482 
    483 static void __destructor cli_exit(void)
    484 {
    485 	/* Nothing to do */
    486 }
    487