| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <console.h> |
| #include <com32.h> |
| #include <syslinux/adv.h> |
| #include <syslinux/config.h> |
| #include <setjmp.h> |
| #include <netinet/in.h> |
| #include <limits.h> |
| #include <minmax.h> |
| #include <linux/list.h> |
| #include <sys/exec.h> |
| #include <sys/module.h> |
| #include <dprintf.h> |
| #include <core.h> |
| |
| #include "getkey.h" |
| #include "menu.h" |
| #include "cli.h" |
| #include "config.h" |
| |
| static struct list_head cli_history_head; |
| |
| void clear_screen(void) |
| { |
| //dprintf("enter"); |
| fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); |
| } |
| |
| static int mygetkey_timeout(clock_t *kbd_to, clock_t *tto) |
| { |
| clock_t t0, t1; |
| int key; |
| |
| t0 = times(NULL); |
| key = get_key(stdin, *kbd_to ? *kbd_to : *tto); |
| |
| /* kbdtimeout only applies to the first character */ |
| if (*kbd_to) |
| *kbd_to = 0; |
| |
| t1 = times(NULL) - t0; |
| if (*tto) { |
| /* Timed out. */ |
| if (*tto <= (long long)t1) |
| key = KEY_NONE; |
| else { |
| /* Did it wrap? */ |
| if (*tto > totaltimeout) |
| key = KEY_NONE; |
| |
| *tto -= t1; |
| } |
| } |
| |
| return key; |
| } |
| |
| static const char * cmd_reverse_search(int *cursor, clock_t *kbd_to, |
| clock_t *tto) |
| { |
| int key; |
| int i = 0; |
| char buf[MAX_CMDLINE_LEN]; |
| const char *p = NULL; |
| struct cli_command *last_found; |
| struct cli_command *last_good = NULL; |
| |
| last_found = list_entry(cli_history_head.next, typeof(*last_found), list); |
| |
| memset(buf, 0, MAX_CMDLINE_LEN); |
| |
| printf("\033[1G\033[1;36m(reverse-i-search)`': \033[0m"); |
| while (1) { |
| key = mygetkey_timeout(kbd_to, tto); |
| |
| if (key == KEY_CTRL('C')) { |
| return NULL; |
| } else if (key == KEY_CTRL('R')) { |
| if (i == 0) |
| continue; /* User typed nothing yet */ |
| /* User typed 'CTRL-R' again, so try the next */ |
| last_found = list_entry(last_found->list.next, typeof(*last_found), list); |
| } else if (key >= ' ' && key <= 'z') { |
| buf[i++] = key; |
| } else { |
| /* Treat other input chars as terminal */ |
| break; |
| } |
| |
| while (last_found) { |
| p = strstr(last_found->command, buf); |
| if (p) |
| break; |
| |
| if (list_is_last(&last_found->list, &cli_history_head)) |
| break; |
| |
| last_found = list_entry(last_found->list.next, typeof(*last_found), list); |
| } |
| |
| if (!p && !last_good) { |
| return NULL; |
| } else if (!p) { |
| continue; |
| } else { |
| last_good = last_found; |
| *cursor = p - last_good->command; |
| } |
| |
| printf("\033[?7l\033[?25l"); |
| /* Didn't handle the line wrap case here */ |
| printf("\033[1G\033[1;36m(reverse-i-search)\033[0m`%s': %s", |
| buf, last_good->command ? : ""); |
| printf("\033[K\r"); |
| } |
| |
| return last_good ? last_good->command : NULL; |
| } |
| |
| |
| |
| const char *edit_cmdline(const char *input, int top /*, int width */ , |
| int (*pDraw_Menu) (int, int, int), |
| void (*show_fkey) (int), bool *timedout) |
| { |
| char cmdline[MAX_CMDLINE_LEN] = { }; |
| int key, len, prev_len, cursor; |
| int redraw = 0; |
| int x, y; |
| bool done = false; |
| const char *ret; |
| int width = 0; |
| struct cli_command *comm_counter = NULL; |
| clock_t kbd_to = kbdtimeout; |
| clock_t tto = totaltimeout; |
| |
| if (!width) { |
| int height; |
| if (getscreensize(1, &height, &width)) |
| width = 80; |
| } |
| |
| len = cursor = 0; |
| prev_len = 0; |
| x = y = 0; |
| |
| /* |
| * Before we start messing with the x,y coordinates print 'input' |
| * so that it follows whatever text has been written to the screen |
| * previously. |
| */ |
| printf("%s ", input); |
| |
| while (!done) { |
| if (redraw > 1) { |
| /* Clear and redraw whole screen */ |
| /* Enable ASCII on G0 and DEC VT on G1; do it in this order |
| to avoid confusing the Linux console */ |
| clear_screen(); |
| if (pDraw_Menu) |
| (*pDraw_Menu) (-1, top, 1); |
| prev_len = 0; |
| printf("\033[2J\033[H"); |
| // printf("\033[0m\033[2J\033[H"); |
| } |
| |
| if (redraw > 0) { |
| int dy, at; |
| |
| prev_len = max(len, prev_len); |
| |
| /* Redraw the command line */ |
| printf("\033[?25l"); |
| printf("\033[1G%s ", input); |
| |
| x = strlen(input); |
| y = 0; |
| at = 0; |
| while (at < prev_len) { |
| putchar(at >= len ? ' ' : cmdline[at]); |
| at++; |
| x++; |
| if (x >= width) { |
| printf("\r\n"); |
| x = 0; |
| y++; |
| } |
| } |
| printf("\033[K\r"); |
| |
| dy = y - (cursor + strlen(input) + 1) / width; |
| x = (cursor + strlen(input) + 1) % width; |
| |
| if (dy) { |
| printf("\033[%dA", dy); |
| y -= dy; |
| } |
| if (x) |
| printf("\033[%dC", x); |
| printf("\033[?25h"); |
| prev_len = len; |
| redraw = 0; |
| } |
| |
| key = mygetkey_timeout(&kbd_to, &tto); |
| |
| switch (key) { |
| case KEY_NONE: |
| /* We timed out. */ |
| *timedout = true; |
| return NULL; |
| |
| case KEY_CTRL('L'): |
| redraw = 2; |
| break; |
| |
| case KEY_ENTER: |
| case KEY_CTRL('J'): |
| ret = cmdline; |
| done = true; |
| break; |
| |
| case KEY_BACKSPACE: |
| case KEY_DEL: |
| if (cursor) { |
| memmove(cmdline + cursor - 1, cmdline + cursor, |
| len - cursor + 1); |
| len--; |
| cursor--; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_CTRL('D'): |
| case KEY_DELETE: |
| if (cursor < len) { |
| memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor); |
| len--; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_CTRL('U'): |
| if (len) { |
| len = cursor = 0; |
| cmdline[len] = '\0'; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_CTRL('W'): |
| if (cursor) { |
| int prevcursor = cursor; |
| |
| while (cursor && my_isspace(cmdline[cursor - 1])) |
| cursor--; |
| |
| while (cursor && !my_isspace(cmdline[cursor - 1])) |
| cursor--; |
| |
| #if 0 |
| memmove(cmdline + cursor, cmdline + prevcursor, |
| len - prevcursor + 1); |
| #else |
| { |
| int i; |
| char *q = cmdline + cursor; |
| char *p = cmdline + prevcursor; |
| for (i = 0; i < len - prevcursor + 1; i++) |
| *q++ = *p++; |
| } |
| #endif |
| len -= (prevcursor - cursor); |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_LEFT: |
| case KEY_CTRL('B'): |
| if (cursor) { |
| cursor--; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_RIGHT: |
| case KEY_CTRL('F'): |
| if (cursor < len) { |
| putchar(cmdline[cursor]); |
| cursor++; |
| x++; |
| if (x >= width) { |
| printf("\r\n"); |
| y++; |
| x = 0; |
| } |
| } |
| break; |
| |
| case KEY_CTRL('K'): |
| if (cursor < len) { |
| cmdline[len = cursor] = '\0'; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_HOME: |
| case KEY_CTRL('A'): |
| if (cursor) { |
| cursor = 0; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_END: |
| case KEY_CTRL('E'): |
| if (cursor != len) { |
| cursor = len; |
| redraw = 1; |
| } |
| break; |
| |
| case KEY_F1: |
| case KEY_F2: |
| case KEY_F3: |
| case KEY_F4: |
| case KEY_F5: |
| case KEY_F6: |
| case KEY_F7: |
| case KEY_F8: |
| case KEY_F9: |
| case KEY_F10: |
| case KEY_F11: |
| case KEY_F12: |
| if (show_fkey != NULL) { |
| (*show_fkey) (key); |
| redraw = 1; |
| } |
| break; |
| case KEY_CTRL('P'): |
| case KEY_UP: |
| { |
| if (!list_empty(&cli_history_head)) { |
| struct list_head *next; |
| |
| if (!comm_counter) |
| next = cli_history_head.next; |
| else |
| next = comm_counter->list.next; |
| |
| comm_counter = |
| list_entry(next, typeof(*comm_counter), list); |
| |
| if (&comm_counter->list != &cli_history_head) |
| strcpy(cmdline, comm_counter->command); |
| |
| cursor = len = strlen(cmdline); |
| redraw = 1; |
| } |
| } |
| break; |
| case KEY_CTRL('N'): |
| case KEY_DOWN: |
| { |
| if (!list_empty(&cli_history_head)) { |
| struct list_head *prev; |
| |
| if (!comm_counter) |
| prev = cli_history_head.prev; |
| else |
| prev = comm_counter->list.prev; |
| |
| comm_counter = |
| list_entry(prev, typeof(*comm_counter), list); |
| |
| if (&comm_counter->list != &cli_history_head) |
| strcpy(cmdline, comm_counter->command); |
| |
| cursor = len = strlen(cmdline); |
| redraw = 1; |
| } |
| } |
| break; |
| case KEY_CTRL('R'): |
| { |
| /* |
| * Handle this case in another function, since it's |
| * a kind of special. |
| */ |
| const char *p = cmd_reverse_search(&cursor, &kbd_to, &tto); |
| if (p) { |
| strcpy(cmdline, p); |
| len = strlen(cmdline); |
| } else { |
| cmdline[0] = '\0'; |
| cursor = len = 0; |
| } |
| redraw = 1; |
| } |
| break; |
| case KEY_TAB: |
| { |
| const char *p; |
| size_t len; |
| |
| /* Label completion enabled? */ |
| if (nocomplete) |
| break; |
| |
| p = cmdline; |
| len = 0; |
| while(*p && !my_isspace(*p)) { |
| p++; |
| len++; |
| } |
| |
| print_labels(cmdline, len); |
| redraw = 1; |
| break; |
| } |
| case KEY_CTRL('V'): |
| if (BIOSName) |
| printf("%s%s%s", syslinux_banner, |
| (char *)MK_PTR(0, BIOSName), copyright_str); |
| else |
| printf("%s%s", syslinux_banner, copyright_str); |
| |
| redraw = 1; |
| break; |
| |
| default: |
| if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) { |
| if (cursor == len) { |
| cmdline[len++] = key; |
| cmdline[len] = '\0'; |
| putchar(key); |
| cursor++; |
| x++; |
| if (x >= width) { |
| printf("\r\n\033[K"); |
| y++; |
| x = 0; |
| } |
| prev_len++; |
| } else { |
| if (cursor > len) |
| return NULL; |
| |
| memmove(cmdline + cursor + 1, cmdline + cursor, |
| len - cursor + 1); |
| cmdline[cursor++] = key; |
| len++; |
| redraw = 1; |
| } |
| } |
| break; |
| } |
| } |
| |
| printf("\033[?7h"); |
| |
| /* Add the command to the history if its length is larger than 0 */ |
| len = strlen(ret); |
| if (len > 0) { |
| comm_counter = malloc(sizeof(struct cli_command)); |
| comm_counter->command = malloc(sizeof(char) * (len + 1)); |
| strcpy(comm_counter->command, ret); |
| list_add(&(comm_counter->list), &cli_history_head); |
| } |
| |
| return len ? ret : NULL; |
| } |
| |
| static int __constructor cli_init(void) |
| { |
| INIT_LIST_HEAD(&cli_history_head); |
| |
| return 0; |
| } |
| |
| static void __destructor cli_exit(void) |
| { |
| /* Nothing to do */ |
| } |