#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 (!list_is_last(&last_found->list, &cli_history_head)) { p = strstr(last_found->command, buf); if (p) 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[?7l\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'; 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 { 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 */ comm_counter = malloc(sizeof(struct cli_command)); comm_counter->command = malloc(sizeof(char) * (strlen(ret) + 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 */ }