1252190Srpaulo/* 2252190Srpaulo * Command line editing and history 3252190Srpaulo * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo#include <termios.h> 11252190Srpaulo 12252190Srpaulo#include "common.h" 13252190Srpaulo#include "eloop.h" 14252190Srpaulo#include "list.h" 15252190Srpaulo#include "edit.h" 16252190Srpaulo 17252190Srpaulo#define CMD_BUF_LEN 256 18252190Srpaulostatic char cmdbuf[CMD_BUF_LEN]; 19252190Srpaulostatic int cmdbuf_pos = 0; 20252190Srpaulostatic int cmdbuf_len = 0; 21252190Srpaulostatic char currbuf[CMD_BUF_LEN]; 22252190Srpaulostatic int currbuf_valid = 0; 23252190Srpaulostatic const char *ps2 = NULL; 24252190Srpaulo 25252190Srpaulo#define HISTORY_MAX 100 26252190Srpaulo 27252190Srpaulostruct edit_history { 28252190Srpaulo struct dl_list list; 29252190Srpaulo char str[1]; 30252190Srpaulo}; 31252190Srpaulo 32252190Srpaulostatic struct dl_list history_list; 33252190Srpaulostatic struct edit_history *history_curr; 34252190Srpaulo 35252190Srpaulostatic void *edit_cb_ctx; 36252190Srpaulostatic void (*edit_cmd_cb)(void *ctx, char *cmd); 37252190Srpaulostatic void (*edit_eof_cb)(void *ctx); 38252190Srpaulostatic char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = 39252190Srpaulo NULL; 40252190Srpaulo 41252190Srpaulostatic struct termios prevt, newt; 42252190Srpaulo 43252190Srpaulo 44252190Srpaulo#define CLEAR_END_LINE "\e[K" 45252190Srpaulo 46252190Srpaulo 47252190Srpaulovoid edit_clear_line(void) 48252190Srpaulo{ 49252190Srpaulo int i; 50252190Srpaulo putchar('\r'); 51252190Srpaulo for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) 52252190Srpaulo putchar(' '); 53252190Srpaulo} 54252190Srpaulo 55252190Srpaulo 56252190Srpaulostatic void move_start(void) 57252190Srpaulo{ 58252190Srpaulo cmdbuf_pos = 0; 59252190Srpaulo edit_redraw(); 60252190Srpaulo} 61252190Srpaulo 62252190Srpaulo 63252190Srpaulostatic void move_end(void) 64252190Srpaulo{ 65252190Srpaulo cmdbuf_pos = cmdbuf_len; 66252190Srpaulo edit_redraw(); 67252190Srpaulo} 68252190Srpaulo 69252190Srpaulo 70252190Srpaulostatic void move_left(void) 71252190Srpaulo{ 72252190Srpaulo if (cmdbuf_pos > 0) { 73252190Srpaulo cmdbuf_pos--; 74252190Srpaulo edit_redraw(); 75252190Srpaulo } 76252190Srpaulo} 77252190Srpaulo 78252190Srpaulo 79252190Srpaulostatic void move_right(void) 80252190Srpaulo{ 81252190Srpaulo if (cmdbuf_pos < cmdbuf_len) { 82252190Srpaulo cmdbuf_pos++; 83252190Srpaulo edit_redraw(); 84252190Srpaulo } 85252190Srpaulo} 86252190Srpaulo 87252190Srpaulo 88252190Srpaulostatic void move_word_left(void) 89252190Srpaulo{ 90252190Srpaulo while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') 91252190Srpaulo cmdbuf_pos--; 92252190Srpaulo while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') 93252190Srpaulo cmdbuf_pos--; 94252190Srpaulo edit_redraw(); 95252190Srpaulo} 96252190Srpaulo 97252190Srpaulo 98252190Srpaulostatic void move_word_right(void) 99252190Srpaulo{ 100252190Srpaulo while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') 101252190Srpaulo cmdbuf_pos++; 102252190Srpaulo while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') 103252190Srpaulo cmdbuf_pos++; 104252190Srpaulo edit_redraw(); 105252190Srpaulo} 106252190Srpaulo 107252190Srpaulo 108252190Srpaulostatic void delete_left(void) 109252190Srpaulo{ 110252190Srpaulo if (cmdbuf_pos == 0) 111252190Srpaulo return; 112252190Srpaulo 113252190Srpaulo edit_clear_line(); 114252190Srpaulo os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, 115252190Srpaulo cmdbuf_len - cmdbuf_pos); 116252190Srpaulo cmdbuf_pos--; 117252190Srpaulo cmdbuf_len--; 118252190Srpaulo edit_redraw(); 119252190Srpaulo} 120252190Srpaulo 121252190Srpaulo 122252190Srpaulostatic void delete_current(void) 123252190Srpaulo{ 124252190Srpaulo if (cmdbuf_pos == cmdbuf_len) 125252190Srpaulo return; 126252190Srpaulo 127252190Srpaulo edit_clear_line(); 128252190Srpaulo os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, 129252190Srpaulo cmdbuf_len - cmdbuf_pos); 130252190Srpaulo cmdbuf_len--; 131252190Srpaulo edit_redraw(); 132252190Srpaulo} 133252190Srpaulo 134252190Srpaulo 135252190Srpaulostatic void delete_word(void) 136252190Srpaulo{ 137252190Srpaulo int pos; 138252190Srpaulo 139252190Srpaulo edit_clear_line(); 140252190Srpaulo pos = cmdbuf_pos; 141252190Srpaulo while (pos > 0 && cmdbuf[pos - 1] == ' ') 142252190Srpaulo pos--; 143252190Srpaulo while (pos > 0 && cmdbuf[pos - 1] != ' ') 144252190Srpaulo pos--; 145252190Srpaulo os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 146252190Srpaulo cmdbuf_len -= cmdbuf_pos - pos; 147252190Srpaulo cmdbuf_pos = pos; 148252190Srpaulo edit_redraw(); 149252190Srpaulo} 150252190Srpaulo 151252190Srpaulo 152252190Srpaulostatic void clear_left(void) 153252190Srpaulo{ 154252190Srpaulo if (cmdbuf_pos == 0) 155252190Srpaulo return; 156252190Srpaulo 157252190Srpaulo edit_clear_line(); 158252190Srpaulo os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 159252190Srpaulo cmdbuf_len -= cmdbuf_pos; 160252190Srpaulo cmdbuf_pos = 0; 161252190Srpaulo edit_redraw(); 162252190Srpaulo} 163252190Srpaulo 164252190Srpaulo 165252190Srpaulostatic void clear_right(void) 166252190Srpaulo{ 167252190Srpaulo if (cmdbuf_pos == cmdbuf_len) 168252190Srpaulo return; 169252190Srpaulo 170252190Srpaulo edit_clear_line(); 171252190Srpaulo cmdbuf_len = cmdbuf_pos; 172252190Srpaulo edit_redraw(); 173252190Srpaulo} 174252190Srpaulo 175252190Srpaulo 176252190Srpaulostatic void history_add(const char *str) 177252190Srpaulo{ 178252190Srpaulo struct edit_history *h, *match = NULL, *last = NULL; 179252190Srpaulo size_t len, count = 0; 180252190Srpaulo 181252190Srpaulo if (str[0] == '\0') 182252190Srpaulo return; 183252190Srpaulo 184252190Srpaulo dl_list_for_each(h, &history_list, struct edit_history, list) { 185252190Srpaulo if (os_strcmp(str, h->str) == 0) { 186252190Srpaulo match = h; 187252190Srpaulo break; 188252190Srpaulo } 189252190Srpaulo last = h; 190252190Srpaulo count++; 191252190Srpaulo } 192252190Srpaulo 193252190Srpaulo if (match) { 194252190Srpaulo dl_list_del(&h->list); 195252190Srpaulo dl_list_add(&history_list, &h->list); 196252190Srpaulo history_curr = h; 197252190Srpaulo return; 198252190Srpaulo } 199252190Srpaulo 200252190Srpaulo if (count >= HISTORY_MAX && last) { 201252190Srpaulo dl_list_del(&last->list); 202252190Srpaulo os_free(last); 203252190Srpaulo } 204252190Srpaulo 205252190Srpaulo len = os_strlen(str); 206252190Srpaulo h = os_zalloc(sizeof(*h) + len); 207252190Srpaulo if (h == NULL) 208252190Srpaulo return; 209252190Srpaulo dl_list_add(&history_list, &h->list); 210252190Srpaulo os_strlcpy(h->str, str, len + 1); 211252190Srpaulo history_curr = h; 212252190Srpaulo} 213252190Srpaulo 214252190Srpaulo 215252190Srpaulostatic void history_use(void) 216252190Srpaulo{ 217252190Srpaulo edit_clear_line(); 218252190Srpaulo cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); 219252190Srpaulo os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); 220252190Srpaulo edit_redraw(); 221252190Srpaulo} 222252190Srpaulo 223252190Srpaulo 224252190Srpaulostatic void history_prev(void) 225252190Srpaulo{ 226252190Srpaulo if (history_curr == NULL) 227252190Srpaulo return; 228252190Srpaulo 229252190Srpaulo if (history_curr == 230252190Srpaulo dl_list_first(&history_list, struct edit_history, list)) { 231252190Srpaulo if (!currbuf_valid) { 232252190Srpaulo cmdbuf[cmdbuf_len] = '\0'; 233252190Srpaulo os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); 234252190Srpaulo currbuf_valid = 1; 235252190Srpaulo history_use(); 236252190Srpaulo return; 237252190Srpaulo } 238252190Srpaulo } 239252190Srpaulo 240252190Srpaulo if (history_curr == 241252190Srpaulo dl_list_last(&history_list, struct edit_history, list)) 242252190Srpaulo return; 243252190Srpaulo 244252190Srpaulo history_curr = dl_list_entry(history_curr->list.next, 245252190Srpaulo struct edit_history, list); 246252190Srpaulo history_use(); 247252190Srpaulo} 248252190Srpaulo 249252190Srpaulo 250252190Srpaulostatic void history_next(void) 251252190Srpaulo{ 252252190Srpaulo if (history_curr == NULL || 253252190Srpaulo history_curr == 254252190Srpaulo dl_list_first(&history_list, struct edit_history, list)) { 255252190Srpaulo if (currbuf_valid) { 256252190Srpaulo currbuf_valid = 0; 257252190Srpaulo edit_clear_line(); 258252190Srpaulo cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); 259252190Srpaulo os_memcpy(cmdbuf, currbuf, cmdbuf_len); 260252190Srpaulo edit_redraw(); 261252190Srpaulo } 262252190Srpaulo return; 263252190Srpaulo } 264252190Srpaulo 265252190Srpaulo history_curr = dl_list_entry(history_curr->list.prev, 266252190Srpaulo struct edit_history, list); 267252190Srpaulo history_use(); 268252190Srpaulo} 269252190Srpaulo 270252190Srpaulo 271252190Srpaulostatic void history_read(const char *fname) 272252190Srpaulo{ 273252190Srpaulo FILE *f; 274252190Srpaulo char buf[CMD_BUF_LEN], *pos; 275252190Srpaulo 276252190Srpaulo f = fopen(fname, "r"); 277252190Srpaulo if (f == NULL) 278252190Srpaulo return; 279252190Srpaulo 280252190Srpaulo while (fgets(buf, CMD_BUF_LEN, f)) { 281252190Srpaulo for (pos = buf; *pos; pos++) { 282252190Srpaulo if (*pos == '\r' || *pos == '\n') { 283252190Srpaulo *pos = '\0'; 284252190Srpaulo break; 285252190Srpaulo } 286252190Srpaulo } 287252190Srpaulo history_add(buf); 288252190Srpaulo } 289252190Srpaulo 290252190Srpaulo fclose(f); 291252190Srpaulo} 292252190Srpaulo 293252190Srpaulo 294252190Srpaulostatic void history_write(const char *fname, 295252190Srpaulo int (*filter_cb)(void *ctx, const char *cmd)) 296252190Srpaulo{ 297252190Srpaulo FILE *f; 298252190Srpaulo struct edit_history *h; 299252190Srpaulo 300252190Srpaulo f = fopen(fname, "w"); 301252190Srpaulo if (f == NULL) 302252190Srpaulo return; 303252190Srpaulo 304252190Srpaulo dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { 305252190Srpaulo if (filter_cb && filter_cb(edit_cb_ctx, h->str)) 306252190Srpaulo continue; 307252190Srpaulo fprintf(f, "%s\n", h->str); 308252190Srpaulo } 309252190Srpaulo 310252190Srpaulo fclose(f); 311252190Srpaulo} 312252190Srpaulo 313252190Srpaulo 314252190Srpaulostatic void history_debug_dump(void) 315252190Srpaulo{ 316252190Srpaulo struct edit_history *h; 317252190Srpaulo edit_clear_line(); 318252190Srpaulo printf("\r"); 319252190Srpaulo dl_list_for_each_reverse(h, &history_list, struct edit_history, list) 320252190Srpaulo printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); 321252190Srpaulo if (currbuf_valid) 322252190Srpaulo printf("{%s}\n", currbuf); 323252190Srpaulo edit_redraw(); 324252190Srpaulo} 325252190Srpaulo 326252190Srpaulo 327252190Srpaulostatic void insert_char(int c) 328252190Srpaulo{ 329252190Srpaulo if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) 330252190Srpaulo return; 331252190Srpaulo if (cmdbuf_len == cmdbuf_pos) { 332252190Srpaulo cmdbuf[cmdbuf_pos++] = c; 333252190Srpaulo cmdbuf_len++; 334252190Srpaulo putchar(c); 335252190Srpaulo fflush(stdout); 336252190Srpaulo } else { 337252190Srpaulo os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, 338252190Srpaulo cmdbuf_len - cmdbuf_pos); 339252190Srpaulo cmdbuf[cmdbuf_pos++] = c; 340252190Srpaulo cmdbuf_len++; 341252190Srpaulo edit_redraw(); 342252190Srpaulo } 343252190Srpaulo} 344252190Srpaulo 345252190Srpaulo 346252190Srpaulostatic void process_cmd(void) 347252190Srpaulo{ 348252190Srpaulo 349252190Srpaulo if (cmdbuf_len == 0) { 350252190Srpaulo printf("\n%s> ", ps2 ? ps2 : ""); 351252190Srpaulo fflush(stdout); 352252190Srpaulo return; 353252190Srpaulo } 354252190Srpaulo printf("\n"); 355252190Srpaulo cmdbuf[cmdbuf_len] = '\0'; 356252190Srpaulo history_add(cmdbuf); 357252190Srpaulo cmdbuf_pos = 0; 358252190Srpaulo cmdbuf_len = 0; 359252190Srpaulo edit_cmd_cb(edit_cb_ctx, cmdbuf); 360252190Srpaulo printf("%s> ", ps2 ? ps2 : ""); 361252190Srpaulo fflush(stdout); 362252190Srpaulo} 363252190Srpaulo 364252190Srpaulo 365252190Srpaulostatic void free_completions(char **c) 366252190Srpaulo{ 367252190Srpaulo int i; 368252190Srpaulo if (c == NULL) 369252190Srpaulo return; 370252190Srpaulo for (i = 0; c[i]; i++) 371252190Srpaulo os_free(c[i]); 372252190Srpaulo os_free(c); 373252190Srpaulo} 374252190Srpaulo 375252190Srpaulo 376252190Srpaulostatic int filter_strings(char **c, char *str, size_t len) 377252190Srpaulo{ 378252190Srpaulo int i, j; 379252190Srpaulo 380252190Srpaulo for (i = 0, j = 0; c[j]; j++) { 381252190Srpaulo if (os_strncasecmp(c[j], str, len) == 0) { 382252190Srpaulo if (i != j) { 383252190Srpaulo c[i] = c[j]; 384252190Srpaulo c[j] = NULL; 385252190Srpaulo } 386252190Srpaulo i++; 387252190Srpaulo } else { 388252190Srpaulo os_free(c[j]); 389252190Srpaulo c[j] = NULL; 390252190Srpaulo } 391252190Srpaulo } 392252190Srpaulo c[i] = NULL; 393252190Srpaulo return i; 394252190Srpaulo} 395252190Srpaulo 396252190Srpaulo 397252190Srpaulostatic int common_len(const char *a, const char *b) 398252190Srpaulo{ 399252190Srpaulo int len = 0; 400252190Srpaulo while (a[len] && a[len] == b[len]) 401252190Srpaulo len++; 402252190Srpaulo return len; 403252190Srpaulo} 404252190Srpaulo 405252190Srpaulo 406252190Srpaulostatic int max_common_length(char **c) 407252190Srpaulo{ 408252190Srpaulo int len, i; 409252190Srpaulo 410252190Srpaulo len = os_strlen(c[0]); 411252190Srpaulo for (i = 1; c[i]; i++) { 412252190Srpaulo int same = common_len(c[0], c[i]); 413252190Srpaulo if (same < len) 414252190Srpaulo len = same; 415252190Srpaulo } 416252190Srpaulo 417252190Srpaulo return len; 418252190Srpaulo} 419252190Srpaulo 420252190Srpaulo 421252190Srpaulostatic int cmp_str(const void *a, const void *b) 422252190Srpaulo{ 423252190Srpaulo return os_strcmp(* (const char **) a, * (const char **) b); 424252190Srpaulo} 425252190Srpaulo 426252190Srpaulostatic void complete(int list) 427252190Srpaulo{ 428252190Srpaulo char **c; 429252190Srpaulo int i, len, count; 430252190Srpaulo int start, end; 431252190Srpaulo int room, plen, add_space; 432252190Srpaulo 433252190Srpaulo if (edit_completion_cb == NULL) 434252190Srpaulo return; 435252190Srpaulo 436252190Srpaulo cmdbuf[cmdbuf_len] = '\0'; 437252190Srpaulo c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); 438252190Srpaulo if (c == NULL) 439252190Srpaulo return; 440252190Srpaulo 441252190Srpaulo end = cmdbuf_pos; 442252190Srpaulo start = end; 443252190Srpaulo while (start > 0 && cmdbuf[start - 1] != ' ') 444252190Srpaulo start--; 445252190Srpaulo plen = end - start; 446252190Srpaulo 447252190Srpaulo count = filter_strings(c, &cmdbuf[start], plen); 448252190Srpaulo if (count == 0) { 449252190Srpaulo free_completions(c); 450252190Srpaulo return; 451252190Srpaulo } 452252190Srpaulo 453252190Srpaulo len = max_common_length(c); 454252190Srpaulo if (len <= plen && count > 1) { 455252190Srpaulo if (list) { 456252190Srpaulo qsort(c, count, sizeof(char *), cmp_str); 457252190Srpaulo edit_clear_line(); 458252190Srpaulo printf("\r"); 459252190Srpaulo for (i = 0; c[i]; i++) 460252190Srpaulo printf("%s%s", i > 0 ? " " : "", c[i]); 461252190Srpaulo printf("\n"); 462252190Srpaulo edit_redraw(); 463252190Srpaulo } 464252190Srpaulo free_completions(c); 465252190Srpaulo return; 466252190Srpaulo } 467252190Srpaulo len -= plen; 468252190Srpaulo 469252190Srpaulo room = sizeof(cmdbuf) - 1 - cmdbuf_len; 470252190Srpaulo if (room < len) 471252190Srpaulo len = room; 472252190Srpaulo add_space = count == 1 && len < room; 473252190Srpaulo 474252190Srpaulo os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, 475252190Srpaulo cmdbuf_len - cmdbuf_pos); 476252190Srpaulo os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); 477252190Srpaulo if (add_space) 478252190Srpaulo cmdbuf[cmdbuf_pos + len] = ' '; 479252190Srpaulo 480252190Srpaulo cmdbuf_pos += len + add_space; 481252190Srpaulo cmdbuf_len += len + add_space; 482252190Srpaulo 483252190Srpaulo edit_redraw(); 484252190Srpaulo 485252190Srpaulo free_completions(c); 486252190Srpaulo} 487252190Srpaulo 488252190Srpaulo 489252190Srpauloenum edit_key_code { 490252190Srpaulo EDIT_KEY_NONE = 256, 491252190Srpaulo EDIT_KEY_TAB, 492252190Srpaulo EDIT_KEY_UP, 493252190Srpaulo EDIT_KEY_DOWN, 494252190Srpaulo EDIT_KEY_RIGHT, 495252190Srpaulo EDIT_KEY_LEFT, 496252190Srpaulo EDIT_KEY_ENTER, 497252190Srpaulo EDIT_KEY_BACKSPACE, 498252190Srpaulo EDIT_KEY_INSERT, 499252190Srpaulo EDIT_KEY_DELETE, 500252190Srpaulo EDIT_KEY_HOME, 501252190Srpaulo EDIT_KEY_END, 502252190Srpaulo EDIT_KEY_PAGE_UP, 503252190Srpaulo EDIT_KEY_PAGE_DOWN, 504252190Srpaulo EDIT_KEY_F1, 505252190Srpaulo EDIT_KEY_F2, 506252190Srpaulo EDIT_KEY_F3, 507252190Srpaulo EDIT_KEY_F4, 508252190Srpaulo EDIT_KEY_F5, 509252190Srpaulo EDIT_KEY_F6, 510252190Srpaulo EDIT_KEY_F7, 511252190Srpaulo EDIT_KEY_F8, 512252190Srpaulo EDIT_KEY_F9, 513252190Srpaulo EDIT_KEY_F10, 514252190Srpaulo EDIT_KEY_F11, 515252190Srpaulo EDIT_KEY_F12, 516252190Srpaulo EDIT_KEY_CTRL_UP, 517252190Srpaulo EDIT_KEY_CTRL_DOWN, 518252190Srpaulo EDIT_KEY_CTRL_RIGHT, 519252190Srpaulo EDIT_KEY_CTRL_LEFT, 520252190Srpaulo EDIT_KEY_CTRL_A, 521252190Srpaulo EDIT_KEY_CTRL_B, 522252190Srpaulo EDIT_KEY_CTRL_D, 523252190Srpaulo EDIT_KEY_CTRL_E, 524252190Srpaulo EDIT_KEY_CTRL_F, 525252190Srpaulo EDIT_KEY_CTRL_G, 526252190Srpaulo EDIT_KEY_CTRL_H, 527252190Srpaulo EDIT_KEY_CTRL_J, 528252190Srpaulo EDIT_KEY_CTRL_K, 529252190Srpaulo EDIT_KEY_CTRL_L, 530252190Srpaulo EDIT_KEY_CTRL_N, 531252190Srpaulo EDIT_KEY_CTRL_O, 532252190Srpaulo EDIT_KEY_CTRL_P, 533252190Srpaulo EDIT_KEY_CTRL_R, 534252190Srpaulo EDIT_KEY_CTRL_T, 535252190Srpaulo EDIT_KEY_CTRL_U, 536252190Srpaulo EDIT_KEY_CTRL_V, 537252190Srpaulo EDIT_KEY_CTRL_W, 538252190Srpaulo EDIT_KEY_ALT_UP, 539252190Srpaulo EDIT_KEY_ALT_DOWN, 540252190Srpaulo EDIT_KEY_ALT_RIGHT, 541252190Srpaulo EDIT_KEY_ALT_LEFT, 542252190Srpaulo EDIT_KEY_SHIFT_UP, 543252190Srpaulo EDIT_KEY_SHIFT_DOWN, 544252190Srpaulo EDIT_KEY_SHIFT_RIGHT, 545252190Srpaulo EDIT_KEY_SHIFT_LEFT, 546252190Srpaulo EDIT_KEY_ALT_SHIFT_UP, 547252190Srpaulo EDIT_KEY_ALT_SHIFT_DOWN, 548252190Srpaulo EDIT_KEY_ALT_SHIFT_RIGHT, 549252190Srpaulo EDIT_KEY_ALT_SHIFT_LEFT, 550252190Srpaulo EDIT_KEY_EOF 551252190Srpaulo}; 552252190Srpaulo 553252190Srpaulostatic void show_esc_buf(const char *esc_buf, char c, int i) 554252190Srpaulo{ 555252190Srpaulo edit_clear_line(); 556252190Srpaulo printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); 557252190Srpaulo edit_redraw(); 558252190Srpaulo} 559252190Srpaulo 560252190Srpaulo 561252190Srpaulostatic enum edit_key_code esc_seq_to_key1_no(char last) 562252190Srpaulo{ 563252190Srpaulo switch (last) { 564252190Srpaulo case 'A': 565252190Srpaulo return EDIT_KEY_UP; 566252190Srpaulo case 'B': 567252190Srpaulo return EDIT_KEY_DOWN; 568252190Srpaulo case 'C': 569252190Srpaulo return EDIT_KEY_RIGHT; 570252190Srpaulo case 'D': 571252190Srpaulo return EDIT_KEY_LEFT; 572252190Srpaulo default: 573252190Srpaulo return EDIT_KEY_NONE; 574252190Srpaulo } 575252190Srpaulo} 576252190Srpaulo 577252190Srpaulo 578252190Srpaulostatic enum edit_key_code esc_seq_to_key1_shift(char last) 579252190Srpaulo{ 580252190Srpaulo switch (last) { 581252190Srpaulo case 'A': 582252190Srpaulo return EDIT_KEY_SHIFT_UP; 583252190Srpaulo case 'B': 584252190Srpaulo return EDIT_KEY_SHIFT_DOWN; 585252190Srpaulo case 'C': 586252190Srpaulo return EDIT_KEY_SHIFT_RIGHT; 587252190Srpaulo case 'D': 588252190Srpaulo return EDIT_KEY_SHIFT_LEFT; 589252190Srpaulo default: 590252190Srpaulo return EDIT_KEY_NONE; 591252190Srpaulo } 592252190Srpaulo} 593252190Srpaulo 594252190Srpaulo 595252190Srpaulostatic enum edit_key_code esc_seq_to_key1_alt(char last) 596252190Srpaulo{ 597252190Srpaulo switch (last) { 598252190Srpaulo case 'A': 599252190Srpaulo return EDIT_KEY_ALT_UP; 600252190Srpaulo case 'B': 601252190Srpaulo return EDIT_KEY_ALT_DOWN; 602252190Srpaulo case 'C': 603252190Srpaulo return EDIT_KEY_ALT_RIGHT; 604252190Srpaulo case 'D': 605252190Srpaulo return EDIT_KEY_ALT_LEFT; 606252190Srpaulo default: 607252190Srpaulo return EDIT_KEY_NONE; 608252190Srpaulo } 609252190Srpaulo} 610252190Srpaulo 611252190Srpaulo 612252190Srpaulostatic enum edit_key_code esc_seq_to_key1_alt_shift(char last) 613252190Srpaulo{ 614252190Srpaulo switch (last) { 615252190Srpaulo case 'A': 616252190Srpaulo return EDIT_KEY_ALT_SHIFT_UP; 617252190Srpaulo case 'B': 618252190Srpaulo return EDIT_KEY_ALT_SHIFT_DOWN; 619252190Srpaulo case 'C': 620252190Srpaulo return EDIT_KEY_ALT_SHIFT_RIGHT; 621252190Srpaulo case 'D': 622252190Srpaulo return EDIT_KEY_ALT_SHIFT_LEFT; 623252190Srpaulo default: 624252190Srpaulo return EDIT_KEY_NONE; 625252190Srpaulo } 626252190Srpaulo} 627252190Srpaulo 628252190Srpaulo 629252190Srpaulostatic enum edit_key_code esc_seq_to_key1_ctrl(char last) 630252190Srpaulo{ 631252190Srpaulo switch (last) { 632252190Srpaulo case 'A': 633252190Srpaulo return EDIT_KEY_CTRL_UP; 634252190Srpaulo case 'B': 635252190Srpaulo return EDIT_KEY_CTRL_DOWN; 636252190Srpaulo case 'C': 637252190Srpaulo return EDIT_KEY_CTRL_RIGHT; 638252190Srpaulo case 'D': 639252190Srpaulo return EDIT_KEY_CTRL_LEFT; 640252190Srpaulo default: 641252190Srpaulo return EDIT_KEY_NONE; 642252190Srpaulo } 643252190Srpaulo} 644252190Srpaulo 645252190Srpaulo 646252190Srpaulostatic enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) 647252190Srpaulo{ 648252190Srpaulo /* ESC-[<param1>;<param2><last> */ 649252190Srpaulo 650252190Srpaulo if (param1 < 0 && param2 < 0) 651252190Srpaulo return esc_seq_to_key1_no(last); 652252190Srpaulo 653252190Srpaulo if (param1 == 1 && param2 == 2) 654252190Srpaulo return esc_seq_to_key1_shift(last); 655252190Srpaulo 656252190Srpaulo if (param1 == 1 && param2 == 3) 657252190Srpaulo return esc_seq_to_key1_alt(last); 658252190Srpaulo 659252190Srpaulo if (param1 == 1 && param2 == 4) 660252190Srpaulo return esc_seq_to_key1_alt_shift(last); 661252190Srpaulo 662252190Srpaulo if (param1 == 1 && param2 == 5) 663252190Srpaulo return esc_seq_to_key1_ctrl(last); 664252190Srpaulo 665252190Srpaulo if (param2 < 0) { 666252190Srpaulo if (last != '~') 667252190Srpaulo return EDIT_KEY_NONE; 668252190Srpaulo switch (param1) { 669252190Srpaulo case 2: 670252190Srpaulo return EDIT_KEY_INSERT; 671252190Srpaulo case 3: 672252190Srpaulo return EDIT_KEY_DELETE; 673252190Srpaulo case 5: 674252190Srpaulo return EDIT_KEY_PAGE_UP; 675252190Srpaulo case 6: 676252190Srpaulo return EDIT_KEY_PAGE_DOWN; 677252190Srpaulo case 15: 678252190Srpaulo return EDIT_KEY_F5; 679252190Srpaulo case 17: 680252190Srpaulo return EDIT_KEY_F6; 681252190Srpaulo case 18: 682252190Srpaulo return EDIT_KEY_F7; 683252190Srpaulo case 19: 684252190Srpaulo return EDIT_KEY_F8; 685252190Srpaulo case 20: 686252190Srpaulo return EDIT_KEY_F9; 687252190Srpaulo case 21: 688252190Srpaulo return EDIT_KEY_F10; 689252190Srpaulo case 23: 690252190Srpaulo return EDIT_KEY_F11; 691252190Srpaulo case 24: 692252190Srpaulo return EDIT_KEY_F12; 693252190Srpaulo } 694252190Srpaulo } 695252190Srpaulo 696252190Srpaulo return EDIT_KEY_NONE; 697252190Srpaulo} 698252190Srpaulo 699252190Srpaulo 700252190Srpaulostatic enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) 701252190Srpaulo{ 702252190Srpaulo /* ESC-O<param1>;<param2><last> */ 703252190Srpaulo 704252190Srpaulo if (param1 >= 0 || param2 >= 0) 705252190Srpaulo return EDIT_KEY_NONE; 706252190Srpaulo 707252190Srpaulo switch (last) { 708252190Srpaulo case 'F': 709252190Srpaulo return EDIT_KEY_END; 710252190Srpaulo case 'H': 711252190Srpaulo return EDIT_KEY_HOME; 712252190Srpaulo case 'P': 713252190Srpaulo return EDIT_KEY_F1; 714252190Srpaulo case 'Q': 715252190Srpaulo return EDIT_KEY_F2; 716252190Srpaulo case 'R': 717252190Srpaulo return EDIT_KEY_F3; 718252190Srpaulo case 'S': 719252190Srpaulo return EDIT_KEY_F4; 720252190Srpaulo default: 721252190Srpaulo return EDIT_KEY_NONE; 722252190Srpaulo } 723252190Srpaulo} 724252190Srpaulo 725252190Srpaulo 726252190Srpaulostatic enum edit_key_code esc_seq_to_key(char *seq) 727252190Srpaulo{ 728252190Srpaulo char last, *pos; 729252190Srpaulo int param1 = -1, param2 = -1; 730252190Srpaulo enum edit_key_code ret = EDIT_KEY_NONE; 731252190Srpaulo 732252190Srpaulo last = '\0'; 733252190Srpaulo for (pos = seq; *pos; pos++) 734252190Srpaulo last = *pos; 735252190Srpaulo 736252190Srpaulo if (seq[1] >= '0' && seq[1] <= '9') { 737252190Srpaulo param1 = atoi(&seq[1]); 738252190Srpaulo pos = os_strchr(seq, ';'); 739252190Srpaulo if (pos) 740252190Srpaulo param2 = atoi(pos + 1); 741252190Srpaulo } 742252190Srpaulo 743252190Srpaulo if (seq[0] == '[') 744252190Srpaulo ret = esc_seq_to_key1(param1, param2, last); 745252190Srpaulo else if (seq[0] == 'O') 746252190Srpaulo ret = esc_seq_to_key2(param1, param2, last); 747252190Srpaulo 748252190Srpaulo if (ret != EDIT_KEY_NONE) 749252190Srpaulo return ret; 750252190Srpaulo 751252190Srpaulo edit_clear_line(); 752252190Srpaulo printf("\rUnknown escape sequence '%s'\n", seq); 753252190Srpaulo edit_redraw(); 754252190Srpaulo return EDIT_KEY_NONE; 755252190Srpaulo} 756252190Srpaulo 757252190Srpaulo 758252190Srpaulostatic enum edit_key_code edit_read_key(int sock) 759252190Srpaulo{ 760252190Srpaulo int c; 761252190Srpaulo unsigned char buf[1]; 762252190Srpaulo int res; 763252190Srpaulo static int esc = -1; 764252190Srpaulo static char esc_buf[7]; 765252190Srpaulo 766252190Srpaulo res = read(sock, buf, 1); 767252190Srpaulo if (res < 0) 768252190Srpaulo perror("read"); 769252190Srpaulo if (res <= 0) 770252190Srpaulo return EDIT_KEY_EOF; 771252190Srpaulo 772252190Srpaulo c = buf[0]; 773252190Srpaulo 774252190Srpaulo if (esc >= 0) { 775252190Srpaulo if (c == 27 /* ESC */) { 776252190Srpaulo esc = 0; 777252190Srpaulo return EDIT_KEY_NONE; 778252190Srpaulo } 779252190Srpaulo 780252190Srpaulo if (esc == 6) { 781252190Srpaulo show_esc_buf(esc_buf, c, 0); 782252190Srpaulo esc = -1; 783252190Srpaulo } else { 784252190Srpaulo esc_buf[esc++] = c; 785252190Srpaulo esc_buf[esc] = '\0'; 786252190Srpaulo } 787252190Srpaulo } 788252190Srpaulo 789252190Srpaulo if (esc == 1) { 790252190Srpaulo if (esc_buf[0] != '[' && esc_buf[0] != 'O') { 791252190Srpaulo show_esc_buf(esc_buf, c, 1); 792252190Srpaulo esc = -1; 793252190Srpaulo return EDIT_KEY_NONE; 794252190Srpaulo } else 795252190Srpaulo return EDIT_KEY_NONE; /* Escape sequence continues */ 796252190Srpaulo } 797252190Srpaulo 798252190Srpaulo if (esc > 1) { 799252190Srpaulo if ((c >= '0' && c <= '9') || c == ';') 800252190Srpaulo return EDIT_KEY_NONE; /* Escape sequence continues */ 801252190Srpaulo 802252190Srpaulo if (c == '~' || (c >= 'A' && c <= 'Z')) { 803252190Srpaulo esc = -1; 804252190Srpaulo return esc_seq_to_key(esc_buf); 805252190Srpaulo } 806252190Srpaulo 807252190Srpaulo show_esc_buf(esc_buf, c, 2); 808252190Srpaulo esc = -1; 809252190Srpaulo return EDIT_KEY_NONE; 810252190Srpaulo } 811252190Srpaulo 812252190Srpaulo switch (c) { 813252190Srpaulo case 1: 814252190Srpaulo return EDIT_KEY_CTRL_A; 815252190Srpaulo case 2: 816252190Srpaulo return EDIT_KEY_CTRL_B; 817252190Srpaulo case 4: 818252190Srpaulo return EDIT_KEY_CTRL_D; 819252190Srpaulo case 5: 820252190Srpaulo return EDIT_KEY_CTRL_E; 821252190Srpaulo case 6: 822252190Srpaulo return EDIT_KEY_CTRL_F; 823252190Srpaulo case 7: 824252190Srpaulo return EDIT_KEY_CTRL_G; 825252190Srpaulo case 8: 826252190Srpaulo return EDIT_KEY_CTRL_H; 827252190Srpaulo case 9: 828252190Srpaulo return EDIT_KEY_TAB; 829252190Srpaulo case 10: 830252190Srpaulo return EDIT_KEY_CTRL_J; 831252190Srpaulo case 13: /* CR */ 832252190Srpaulo return EDIT_KEY_ENTER; 833252190Srpaulo case 11: 834252190Srpaulo return EDIT_KEY_CTRL_K; 835252190Srpaulo case 12: 836252190Srpaulo return EDIT_KEY_CTRL_L; 837252190Srpaulo case 14: 838252190Srpaulo return EDIT_KEY_CTRL_N; 839252190Srpaulo case 15: 840252190Srpaulo return EDIT_KEY_CTRL_O; 841252190Srpaulo case 16: 842252190Srpaulo return EDIT_KEY_CTRL_P; 843252190Srpaulo case 18: 844252190Srpaulo return EDIT_KEY_CTRL_R; 845252190Srpaulo case 20: 846252190Srpaulo return EDIT_KEY_CTRL_T; 847252190Srpaulo case 21: 848252190Srpaulo return EDIT_KEY_CTRL_U; 849252190Srpaulo case 22: 850252190Srpaulo return EDIT_KEY_CTRL_V; 851252190Srpaulo case 23: 852252190Srpaulo return EDIT_KEY_CTRL_W; 853252190Srpaulo case 27: /* ESC */ 854252190Srpaulo esc = 0; 855252190Srpaulo return EDIT_KEY_NONE; 856252190Srpaulo case 127: 857252190Srpaulo return EDIT_KEY_BACKSPACE; 858252190Srpaulo default: 859252190Srpaulo return c; 860252190Srpaulo } 861252190Srpaulo} 862252190Srpaulo 863252190Srpaulo 864252190Srpaulostatic char search_buf[21]; 865252190Srpaulostatic int search_skip; 866252190Srpaulo 867252190Srpaulostatic char * search_find(void) 868252190Srpaulo{ 869252190Srpaulo struct edit_history *h; 870252190Srpaulo size_t len = os_strlen(search_buf); 871252190Srpaulo int skip = search_skip; 872252190Srpaulo 873252190Srpaulo if (len == 0) 874252190Srpaulo return NULL; 875252190Srpaulo 876252190Srpaulo dl_list_for_each(h, &history_list, struct edit_history, list) { 877252190Srpaulo if (os_strstr(h->str, search_buf)) { 878252190Srpaulo if (skip == 0) 879252190Srpaulo return h->str; 880252190Srpaulo skip--; 881252190Srpaulo } 882252190Srpaulo } 883252190Srpaulo 884252190Srpaulo search_skip = 0; 885252190Srpaulo return NULL; 886252190Srpaulo} 887252190Srpaulo 888252190Srpaulo 889252190Srpaulostatic void search_redraw(void) 890252190Srpaulo{ 891252190Srpaulo char *match = search_find(); 892252190Srpaulo printf("\rsearch '%s': %s" CLEAR_END_LINE, 893252190Srpaulo search_buf, match ? match : ""); 894252190Srpaulo printf("\rsearch '%s", search_buf); 895252190Srpaulo fflush(stdout); 896252190Srpaulo} 897252190Srpaulo 898252190Srpaulo 899252190Srpaulostatic void search_start(void) 900252190Srpaulo{ 901252190Srpaulo edit_clear_line(); 902252190Srpaulo search_buf[0] = '\0'; 903252190Srpaulo search_skip = 0; 904252190Srpaulo search_redraw(); 905252190Srpaulo} 906252190Srpaulo 907252190Srpaulo 908252190Srpaulostatic void search_clear(void) 909252190Srpaulo{ 910252190Srpaulo search_redraw(); 911252190Srpaulo printf("\r" CLEAR_END_LINE); 912252190Srpaulo} 913252190Srpaulo 914252190Srpaulo 915252190Srpaulostatic void search_stop(void) 916252190Srpaulo{ 917252190Srpaulo char *match = search_find(); 918252190Srpaulo search_buf[0] = '\0'; 919252190Srpaulo search_clear(); 920252190Srpaulo if (match) { 921252190Srpaulo os_strlcpy(cmdbuf, match, CMD_BUF_LEN); 922252190Srpaulo cmdbuf_len = os_strlen(cmdbuf); 923252190Srpaulo cmdbuf_pos = cmdbuf_len; 924252190Srpaulo } 925252190Srpaulo edit_redraw(); 926252190Srpaulo} 927252190Srpaulo 928252190Srpaulo 929252190Srpaulostatic void search_cancel(void) 930252190Srpaulo{ 931252190Srpaulo search_buf[0] = '\0'; 932252190Srpaulo search_clear(); 933252190Srpaulo edit_redraw(); 934252190Srpaulo} 935252190Srpaulo 936252190Srpaulo 937252190Srpaulostatic void search_backspace(void) 938252190Srpaulo{ 939252190Srpaulo size_t len; 940252190Srpaulo len = os_strlen(search_buf); 941252190Srpaulo if (len == 0) 942252190Srpaulo return; 943252190Srpaulo search_buf[len - 1] = '\0'; 944252190Srpaulo search_skip = 0; 945252190Srpaulo search_redraw(); 946252190Srpaulo} 947252190Srpaulo 948252190Srpaulo 949252190Srpaulostatic void search_next(void) 950252190Srpaulo{ 951252190Srpaulo search_skip++; 952252190Srpaulo search_find(); 953252190Srpaulo search_redraw(); 954252190Srpaulo} 955252190Srpaulo 956252190Srpaulo 957252190Srpaulostatic void search_char(char c) 958252190Srpaulo{ 959252190Srpaulo size_t len; 960252190Srpaulo len = os_strlen(search_buf); 961252190Srpaulo if (len == sizeof(search_buf) - 1) 962252190Srpaulo return; 963252190Srpaulo search_buf[len] = c; 964252190Srpaulo search_buf[len + 1] = '\0'; 965252190Srpaulo search_skip = 0; 966252190Srpaulo search_redraw(); 967252190Srpaulo} 968252190Srpaulo 969252190Srpaulo 970252190Srpaulostatic enum edit_key_code search_key(enum edit_key_code c) 971252190Srpaulo{ 972252190Srpaulo switch (c) { 973252190Srpaulo case EDIT_KEY_ENTER: 974252190Srpaulo case EDIT_KEY_CTRL_J: 975252190Srpaulo case EDIT_KEY_LEFT: 976252190Srpaulo case EDIT_KEY_RIGHT: 977252190Srpaulo case EDIT_KEY_HOME: 978252190Srpaulo case EDIT_KEY_END: 979252190Srpaulo case EDIT_KEY_CTRL_A: 980252190Srpaulo case EDIT_KEY_CTRL_E: 981252190Srpaulo search_stop(); 982252190Srpaulo return c; 983252190Srpaulo case EDIT_KEY_DOWN: 984252190Srpaulo case EDIT_KEY_UP: 985252190Srpaulo search_cancel(); 986252190Srpaulo return EDIT_KEY_EOF; 987252190Srpaulo case EDIT_KEY_CTRL_H: 988252190Srpaulo case EDIT_KEY_BACKSPACE: 989252190Srpaulo search_backspace(); 990252190Srpaulo break; 991252190Srpaulo case EDIT_KEY_CTRL_R: 992252190Srpaulo search_next(); 993252190Srpaulo break; 994252190Srpaulo default: 995252190Srpaulo if (c >= 32 && c <= 255) 996252190Srpaulo search_char(c); 997252190Srpaulo break; 998252190Srpaulo } 999252190Srpaulo 1000252190Srpaulo return EDIT_KEY_NONE; 1001252190Srpaulo} 1002252190Srpaulo 1003252190Srpaulo 1004252190Srpaulostatic void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) 1005252190Srpaulo{ 1006252190Srpaulo static int last_tab = 0; 1007252190Srpaulo static int search = 0; 1008252190Srpaulo enum edit_key_code c; 1009252190Srpaulo 1010252190Srpaulo c = edit_read_key(sock); 1011252190Srpaulo 1012252190Srpaulo if (search) { 1013252190Srpaulo c = search_key(c); 1014252190Srpaulo if (c == EDIT_KEY_NONE) 1015252190Srpaulo return; 1016252190Srpaulo search = 0; 1017252190Srpaulo if (c == EDIT_KEY_EOF) 1018252190Srpaulo return; 1019252190Srpaulo } 1020252190Srpaulo 1021252190Srpaulo if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) 1022252190Srpaulo last_tab = 0; 1023252190Srpaulo 1024252190Srpaulo switch (c) { 1025252190Srpaulo case EDIT_KEY_NONE: 1026252190Srpaulo break; 1027252190Srpaulo case EDIT_KEY_EOF: 1028252190Srpaulo edit_eof_cb(edit_cb_ctx); 1029252190Srpaulo break; 1030252190Srpaulo case EDIT_KEY_TAB: 1031252190Srpaulo complete(last_tab); 1032252190Srpaulo last_tab = 1; 1033252190Srpaulo break; 1034252190Srpaulo case EDIT_KEY_UP: 1035252190Srpaulo case EDIT_KEY_CTRL_P: 1036252190Srpaulo history_prev(); 1037252190Srpaulo break; 1038252190Srpaulo case EDIT_KEY_DOWN: 1039252190Srpaulo case EDIT_KEY_CTRL_N: 1040252190Srpaulo history_next(); 1041252190Srpaulo break; 1042252190Srpaulo case EDIT_KEY_RIGHT: 1043252190Srpaulo case EDIT_KEY_CTRL_F: 1044252190Srpaulo move_right(); 1045252190Srpaulo break; 1046252190Srpaulo case EDIT_KEY_LEFT: 1047252190Srpaulo case EDIT_KEY_CTRL_B: 1048252190Srpaulo move_left(); 1049252190Srpaulo break; 1050252190Srpaulo case EDIT_KEY_CTRL_RIGHT: 1051252190Srpaulo move_word_right(); 1052252190Srpaulo break; 1053252190Srpaulo case EDIT_KEY_CTRL_LEFT: 1054252190Srpaulo move_word_left(); 1055252190Srpaulo break; 1056252190Srpaulo case EDIT_KEY_DELETE: 1057252190Srpaulo delete_current(); 1058252190Srpaulo break; 1059252190Srpaulo case EDIT_KEY_END: 1060252190Srpaulo move_end(); 1061252190Srpaulo break; 1062252190Srpaulo case EDIT_KEY_HOME: 1063252190Srpaulo case EDIT_KEY_CTRL_A: 1064252190Srpaulo move_start(); 1065252190Srpaulo break; 1066252190Srpaulo case EDIT_KEY_F2: 1067252190Srpaulo history_debug_dump(); 1068252190Srpaulo break; 1069252190Srpaulo case EDIT_KEY_CTRL_D: 1070252190Srpaulo if (cmdbuf_len > 0) { 1071252190Srpaulo delete_current(); 1072252190Srpaulo return; 1073252190Srpaulo } 1074252190Srpaulo printf("\n"); 1075252190Srpaulo edit_eof_cb(edit_cb_ctx); 1076252190Srpaulo break; 1077252190Srpaulo case EDIT_KEY_CTRL_E: 1078252190Srpaulo move_end(); 1079252190Srpaulo break; 1080252190Srpaulo case EDIT_KEY_CTRL_H: 1081252190Srpaulo case EDIT_KEY_BACKSPACE: 1082252190Srpaulo delete_left(); 1083252190Srpaulo break; 1084252190Srpaulo case EDIT_KEY_ENTER: 1085252190Srpaulo case EDIT_KEY_CTRL_J: 1086252190Srpaulo process_cmd(); 1087252190Srpaulo break; 1088252190Srpaulo case EDIT_KEY_CTRL_K: 1089252190Srpaulo clear_right(); 1090252190Srpaulo break; 1091252190Srpaulo case EDIT_KEY_CTRL_L: 1092252190Srpaulo edit_clear_line(); 1093252190Srpaulo edit_redraw(); 1094252190Srpaulo break; 1095252190Srpaulo case EDIT_KEY_CTRL_R: 1096252190Srpaulo search = 1; 1097252190Srpaulo search_start(); 1098252190Srpaulo break; 1099252190Srpaulo case EDIT_KEY_CTRL_U: 1100252190Srpaulo clear_left(); 1101252190Srpaulo break; 1102252190Srpaulo case EDIT_KEY_CTRL_W: 1103252190Srpaulo delete_word(); 1104252190Srpaulo break; 1105252190Srpaulo default: 1106252190Srpaulo if (c >= 32 && c <= 255) 1107252190Srpaulo insert_char(c); 1108252190Srpaulo break; 1109252190Srpaulo } 1110252190Srpaulo} 1111252190Srpaulo 1112252190Srpaulo 1113252190Srpauloint edit_init(void (*cmd_cb)(void *ctx, char *cmd), 1114252190Srpaulo void (*eof_cb)(void *ctx), 1115252190Srpaulo char ** (*completion_cb)(void *ctx, const char *cmd, int pos), 1116252190Srpaulo void *ctx, const char *history_file, const char *ps) 1117252190Srpaulo{ 1118252190Srpaulo currbuf[0] = '\0'; 1119252190Srpaulo dl_list_init(&history_list); 1120252190Srpaulo history_curr = NULL; 1121252190Srpaulo if (history_file) 1122252190Srpaulo history_read(history_file); 1123252190Srpaulo 1124252190Srpaulo edit_cb_ctx = ctx; 1125252190Srpaulo edit_cmd_cb = cmd_cb; 1126252190Srpaulo edit_eof_cb = eof_cb; 1127252190Srpaulo edit_completion_cb = completion_cb; 1128252190Srpaulo 1129252190Srpaulo tcgetattr(STDIN_FILENO, &prevt); 1130252190Srpaulo newt = prevt; 1131252190Srpaulo newt.c_lflag &= ~(ICANON | ECHO); 1132252190Srpaulo tcsetattr(STDIN_FILENO, TCSANOW, &newt); 1133252190Srpaulo 1134252190Srpaulo eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); 1135252190Srpaulo 1136252190Srpaulo ps2 = ps; 1137252190Srpaulo printf("%s> ", ps2 ? ps2 : ""); 1138252190Srpaulo fflush(stdout); 1139252190Srpaulo 1140252190Srpaulo return 0; 1141252190Srpaulo} 1142252190Srpaulo 1143252190Srpaulo 1144252190Srpaulovoid edit_deinit(const char *history_file, 1145252190Srpaulo int (*filter_cb)(void *ctx, const char *cmd)) 1146252190Srpaulo{ 1147252190Srpaulo struct edit_history *h; 1148252190Srpaulo if (history_file) 1149252190Srpaulo history_write(history_file, filter_cb); 1150252190Srpaulo while ((h = dl_list_first(&history_list, struct edit_history, list))) { 1151252190Srpaulo dl_list_del(&h->list); 1152252190Srpaulo os_free(h); 1153252190Srpaulo } 1154252190Srpaulo edit_clear_line(); 1155252190Srpaulo putchar('\r'); 1156252190Srpaulo fflush(stdout); 1157252190Srpaulo eloop_unregister_read_sock(STDIN_FILENO); 1158252190Srpaulo tcsetattr(STDIN_FILENO, TCSANOW, &prevt); 1159252190Srpaulo} 1160252190Srpaulo 1161252190Srpaulo 1162252190Srpaulovoid edit_redraw(void) 1163252190Srpaulo{ 1164252190Srpaulo char tmp; 1165252190Srpaulo cmdbuf[cmdbuf_len] = '\0'; 1166252190Srpaulo printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); 1167252190Srpaulo if (cmdbuf_pos != cmdbuf_len) { 1168252190Srpaulo tmp = cmdbuf[cmdbuf_pos]; 1169252190Srpaulo cmdbuf[cmdbuf_pos] = '\0'; 1170252190Srpaulo printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); 1171252190Srpaulo cmdbuf[cmdbuf_pos] = tmp; 1172252190Srpaulo } 1173252190Srpaulo fflush(stdout); 1174252190Srpaulo} 1175