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