edit.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
13839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o/* 23839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o * Command line editing and history 3efc6f628e15de95bcd13e4f0ee223cb42115d520Theodore Ts'o * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> 419c78dc07fce2d6f39b5e541562afc3ca1ea38ffTheodore Ts'o * 519c78dc07fce2d6f39b5e541562afc3ca1ea38ffTheodore Ts'o * This software may be distributed under the terms of the BSD license. 619c78dc07fce2d6f39b5e541562afc3ca1ea38ffTheodore Ts'o * See README for more details. 7543547a52a20cb7e69d74921b2f691078fd55d83Theodore Ts'o */ 8543547a52a20cb7e69d74921b2f691078fd55d83Theodore Ts'o 919c78dc07fce2d6f39b5e541562afc3ca1ea38ffTheodore Ts'o#include "includes.h" 103839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#include <termios.h> 113839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 123839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#include "common.h" 133839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#include "eloop.h" 144cbe8af4b0d0c72fb28bb500c1bd8a46b00fdde3Theodore Ts'o#include "list.h" 153839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#include "edit.h" 164cbe8af4b0d0c72fb28bb500c1bd8a46b00fdde3Theodore Ts'o 173839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#define CMD_BUF_LEN 256 183839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic char cmdbuf[CMD_BUF_LEN]; 191d2ff46ae7533ffd038534b189f272d2a4122e4eTheodore Ts'ostatic int cmdbuf_pos = 0; 203839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic int cmdbuf_len = 0; 211d2ff46ae7533ffd038534b189f272d2a4122e4eTheodore Ts'ostatic char currbuf[CMD_BUF_LEN]; 221d2ff46ae7533ffd038534b189f272d2a4122e4eTheodore Ts'ostatic int currbuf_valid = 0; 233839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 241d2ff46ae7533ffd038534b189f272d2a4122e4eTheodore Ts'o#define HISTORY_MAX 100 253839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 26b5abe6fac9c9e7caf4710501d1657d30e4857ef6Theodore Ts'ostruct edit_history { 273839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o struct dl_list list; 283839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o char str[1]; 29e6198e5a0191b2cbbf9765c4e4df11c7929279e6Theodore Ts'o}; 30e6198e5a0191b2cbbf9765c4e4df11c7929279e6Theodore Ts'o 31e6198e5a0191b2cbbf9765c4e4df11c7929279e6Theodore Ts'ostatic struct dl_list history_list; 32e6198e5a0191b2cbbf9765c4e4df11c7929279e6Theodore Ts'ostatic struct edit_history *history_curr; 3331dbecd482405e0d3a67eb58e1a1c8cb9f2ad83eTheodore Ts'o 343839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void *edit_cb_ctx; 353839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void (*edit_cmd_cb)(void *ctx, char *cmd); 36e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrallstatic void (*edit_eof_cb)(void *ctx); 373839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = 382e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o NULL; 3931dbecd482405e0d3a67eb58e1a1c8cb9f2ad83eTheodore Ts'o 4031dbecd482405e0d3a67eb58e1a1c8cb9f2ad83eTheodore Ts'ostatic struct termios prevt, newt; 41e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall 423839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 433839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o#define CLEAR_END_LINE "\e[K" 44f3db3566b5e1342e49dffc5ec3f418a838584194Theodore Ts'o 45f3db3566b5e1342e49dffc5ec3f418a838584194Theodore Ts'o 463839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ovoid edit_clear_line(void) 473839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 483839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o int i; 493839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o putchar('\r'); 5050e1e10fa0ac12a3e2a9d20a75ee9041873cda96Theodore Ts'o for (i = 0; i < cmdbuf_len + 2; i++) 5150e1e10fa0ac12a3e2a9d20a75ee9041873cda96Theodore Ts'o putchar(' '); 523839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 533839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 543839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 553839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void move_start(void) 563839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 573839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos = 0; 583839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 59e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall} 603839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 613839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 623839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void move_end(void) 633839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 643839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos = cmdbuf_len; 653839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 663839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 673839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 683839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 693839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void move_left(void) 703839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 712e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o if (cmdbuf_pos > 0) { 722e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o cmdbuf_pos--; 732e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o edit_redraw(); 742e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o } 752e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o} 762e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o 772e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o 782e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'ostatic void move_right(void) 792e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o{ 802e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o if (cmdbuf_pos < cmdbuf_len) { 813839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos++; 823839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 833839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o } 846a525069a99787ef3ae1156f12230044b3568f4bTheodore Ts'o} 853839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 861ca1059fd0126fd2c065f272a566c18f14bab16dTheodore Ts'o 87e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrallstatic void move_word_left(void) 88e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall{ 89e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') 90e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall cmdbuf_pos--; 913839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') 923839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos--; 933839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 943839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 953839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 963839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 9750e1e10fa0ac12a3e2a9d20a75ee9041873cda96Theodore Ts'ostatic void move_word_right(void) 983839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 993839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') 100efc6f628e15de95bcd13e4f0ee223cb42115d520Theodore Ts'o cmdbuf_pos++; 1013839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') 1023839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos++; 1033839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 104e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall} 105e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall 106e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall 107e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrallstatic void delete_left(void) 108e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall{ 109e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall if (cmdbuf_pos == 0) 110e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall return; 111e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall 112e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall edit_clear_line(); 113e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, 1143839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_len - cmdbuf_pos); 1153839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_pos--; 1163839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_len--; 1173839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 1183839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 1193839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 1203839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 1211f0b6c1f895d189fea6999d0c07a7fee936a4baaTheodore Ts'ostatic void delete_current(void) 1223839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 1233839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o if (cmdbuf_pos == cmdbuf_len) 1243839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o return; 1251f0b6c1f895d189fea6999d0c07a7fee936a4baaTheodore Ts'o 1263839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_clear_line(); 127e6198e5a0191b2cbbf9765c4e4df11c7929279e6Theodore Ts'o os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, 1283839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_len - cmdbuf_pos); 1293839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o cmdbuf_len--; 1303839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o edit_redraw(); 1313839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 1323839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 133dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o 134dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'ostatic void delete_word(void) 135dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o{ 1362e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o int pos; 1372e8d40d562ec93d68505800a46c5b9dcc229264eTheodore Ts'o 138dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o edit_clear_line(); 139dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o pos = cmdbuf_pos; 140dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o while (pos > 0 && cmdbuf[pos - 1] == ' ') 141efc6f628e15de95bcd13e4f0ee223cb42115d520Theodore Ts'o pos--; 142dab278af8180bc85783da0b908e2a1af1388522dTheodore Ts'o while (pos > 0 && cmdbuf[pos - 1] != ' ') 1433839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o pos--; 1443839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 145e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall cmdbuf_len -= cmdbuf_pos - pos; 1467f961d424b1ba527e835d01ad24e0e4c3f4088c5Theodore Ts'o cmdbuf_pos = pos; 1478bd0c95908baa3af706b9e731daff9472bec74c9Theodore Ts'o edit_redraw(); 1483839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o} 1493839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 150c4e3d3f374b409500e3dd05c0b0eca6ac98a6b4eTheodore Ts'o 1513839e65723771b85975f4263102dd3ceec4523cTheodore Ts'ostatic void clear_left(void) 1523839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o{ 1533839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o if (cmdbuf_pos == 0) 1543839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o return; 1553839e65723771b85975f4263102dd3ceec4523cTheodore Ts'o 156 edit_clear_line(); 157 os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 158 cmdbuf_len -= cmdbuf_pos; 159 cmdbuf_pos = 0; 160 edit_redraw(); 161} 162 163 164static void clear_right(void) 165{ 166 if (cmdbuf_pos == cmdbuf_len) 167 return; 168 169 edit_clear_line(); 170 cmdbuf_len = cmdbuf_pos; 171 edit_redraw(); 172} 173 174 175static void history_add(const char *str) 176{ 177 struct edit_history *h, *match = NULL, *last = NULL; 178 size_t len, count = 0; 179 180 if (str[0] == '\0') 181 return; 182 183 dl_list_for_each(h, &history_list, struct edit_history, list) { 184 if (os_strcmp(str, h->str) == 0) { 185 match = h; 186 break; 187 } 188 last = h; 189 count++; 190 } 191 192 if (match) { 193 dl_list_del(&h->list); 194 dl_list_add(&history_list, &h->list); 195 history_curr = h; 196 return; 197 } 198 199 if (count >= HISTORY_MAX && last) { 200 dl_list_del(&last->list); 201 os_free(last); 202 } 203 204 len = os_strlen(str); 205 h = os_zalloc(sizeof(*h) + len); 206 if (h == NULL) 207 return; 208 dl_list_add(&history_list, &h->list); 209 os_strlcpy(h->str, str, len + 1); 210 history_curr = h; 211} 212 213 214static void history_use(void) 215{ 216 edit_clear_line(); 217 cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); 218 os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); 219 edit_redraw(); 220} 221 222 223static void history_prev(void) 224{ 225 if (history_curr == NULL) 226 return; 227 228 if (history_curr == 229 dl_list_first(&history_list, struct edit_history, list)) { 230 if (!currbuf_valid) { 231 cmdbuf[cmdbuf_len] = '\0'; 232 os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); 233 currbuf_valid = 1; 234 history_use(); 235 return; 236 } 237 } 238 239 if (history_curr == 240 dl_list_last(&history_list, struct edit_history, list)) 241 return; 242 243 history_curr = dl_list_entry(history_curr->list.next, 244 struct edit_history, list); 245 history_use(); 246} 247 248 249static void history_next(void) 250{ 251 if (history_curr == NULL || 252 history_curr == 253 dl_list_first(&history_list, struct edit_history, list)) { 254 if (currbuf_valid) { 255 currbuf_valid = 0; 256 edit_clear_line(); 257 cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); 258 os_memcpy(cmdbuf, currbuf, cmdbuf_len); 259 edit_redraw(); 260 } 261 return; 262 } 263 264 history_curr = dl_list_entry(history_curr->list.prev, 265 struct edit_history, list); 266 history_use(); 267} 268 269 270static void history_read(const char *fname) 271{ 272 FILE *f; 273 char buf[CMD_BUF_LEN], *pos; 274 275 f = fopen(fname, "r"); 276 if (f == NULL) 277 return; 278 279 while (fgets(buf, CMD_BUF_LEN, f)) { 280 for (pos = buf; *pos; pos++) { 281 if (*pos == '\r' || *pos == '\n') { 282 *pos = '\0'; 283 break; 284 } 285 } 286 history_add(buf); 287 } 288 289 fclose(f); 290} 291 292 293static void history_write(const char *fname, 294 int (*filter_cb)(void *ctx, const char *cmd)) 295{ 296 FILE *f; 297 struct edit_history *h; 298 299 f = fopen(fname, "w"); 300 if (f == NULL) 301 return; 302 303 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { 304 if (filter_cb && filter_cb(edit_cb_ctx, h->str)) 305 continue; 306 fprintf(f, "%s\n", h->str); 307 } 308 309 fclose(f); 310} 311 312 313static void history_debug_dump(void) 314{ 315 struct edit_history *h; 316 edit_clear_line(); 317 printf("\r"); 318 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) 319 printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); 320 if (currbuf_valid) 321 printf("{%s}\n", currbuf); 322 edit_redraw(); 323} 324 325 326static void insert_char(int c) 327{ 328 if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) 329 return; 330 if (cmdbuf_len == cmdbuf_pos) { 331 cmdbuf[cmdbuf_pos++] = c; 332 cmdbuf_len++; 333 putchar(c); 334 fflush(stdout); 335 } else { 336 os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, 337 cmdbuf_len - cmdbuf_pos); 338 cmdbuf[cmdbuf_pos++] = c; 339 cmdbuf_len++; 340 edit_redraw(); 341 } 342} 343 344 345static void process_cmd(void) 346{ 347 348 if (cmdbuf_len == 0) { 349 printf("\n> "); 350 fflush(stdout); 351 return; 352 } 353 printf("\n"); 354 cmdbuf[cmdbuf_len] = '\0'; 355 history_add(cmdbuf); 356 cmdbuf_pos = 0; 357 cmdbuf_len = 0; 358 edit_cmd_cb(edit_cb_ctx, cmdbuf); 359 printf("> "); 360 fflush(stdout); 361} 362 363 364static void free_completions(char **c) 365{ 366 int i; 367 if (c == NULL) 368 return; 369 for (i = 0; c[i]; i++) 370 os_free(c[i]); 371 os_free(c); 372} 373 374 375static int filter_strings(char **c, char *str, size_t len) 376{ 377 int i, j; 378 379 for (i = 0, j = 0; c[j]; j++) { 380 if (os_strncasecmp(c[j], str, len) == 0) { 381 if (i != j) { 382 c[i] = c[j]; 383 c[j] = NULL; 384 } 385 i++; 386 } else { 387 os_free(c[j]); 388 c[j] = NULL; 389 } 390 } 391 c[i] = NULL; 392 return i; 393} 394 395 396static int common_len(const char *a, const char *b) 397{ 398 int len = 0; 399 while (a[len] && a[len] == b[len]) 400 len++; 401 return len; 402} 403 404 405static int max_common_length(char **c) 406{ 407 int len, i; 408 409 len = os_strlen(c[0]); 410 for (i = 1; c[i]; i++) { 411 int same = common_len(c[0], c[i]); 412 if (same < len) 413 len = same; 414 } 415 416 return len; 417} 418 419 420static int cmp_str(const void *a, const void *b) 421{ 422 return os_strcmp(* (const char **) a, * (const char **) b); 423} 424 425static void complete(int list) 426{ 427 char **c; 428 int i, len, count; 429 int start, end; 430 int room, plen, add_space; 431 432 if (edit_completion_cb == NULL) 433 return; 434 435 cmdbuf[cmdbuf_len] = '\0'; 436 c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); 437 if (c == NULL) 438 return; 439 440 end = cmdbuf_pos; 441 start = end; 442 while (start > 0 && cmdbuf[start - 1] != ' ') 443 start--; 444 plen = end - start; 445 446 count = filter_strings(c, &cmdbuf[start], plen); 447 if (count == 0) { 448 free_completions(c); 449 return; 450 } 451 452 len = max_common_length(c); 453 if (len <= plen && count > 1) { 454 if (list) { 455 qsort(c, count, sizeof(char *), cmp_str); 456 edit_clear_line(); 457 printf("\r"); 458 for (i = 0; c[i]; i++) 459 printf("%s%s", i > 0 ? " " : "", c[i]); 460 printf("\n"); 461 edit_redraw(); 462 } 463 free_completions(c); 464 return; 465 } 466 len -= plen; 467 468 room = sizeof(cmdbuf) - 1 - cmdbuf_len; 469 if (room < len) 470 len = room; 471 add_space = count == 1 && len < room; 472 473 os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, 474 cmdbuf_len - cmdbuf_pos); 475 os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); 476 if (add_space) 477 cmdbuf[cmdbuf_pos + len] = ' '; 478 479 cmdbuf_pos += len + add_space; 480 cmdbuf_len += len + add_space; 481 482 edit_redraw(); 483 484 free_completions(c); 485} 486 487 488enum edit_key_code { 489 EDIT_KEY_NONE = 256, 490 EDIT_KEY_TAB, 491 EDIT_KEY_UP, 492 EDIT_KEY_DOWN, 493 EDIT_KEY_RIGHT, 494 EDIT_KEY_LEFT, 495 EDIT_KEY_ENTER, 496 EDIT_KEY_BACKSPACE, 497 EDIT_KEY_INSERT, 498 EDIT_KEY_DELETE, 499 EDIT_KEY_HOME, 500 EDIT_KEY_END, 501 EDIT_KEY_PAGE_UP, 502 EDIT_KEY_PAGE_DOWN, 503 EDIT_KEY_F1, 504 EDIT_KEY_F2, 505 EDIT_KEY_F3, 506 EDIT_KEY_F4, 507 EDIT_KEY_F5, 508 EDIT_KEY_F6, 509 EDIT_KEY_F7, 510 EDIT_KEY_F8, 511 EDIT_KEY_F9, 512 EDIT_KEY_F10, 513 EDIT_KEY_F11, 514 EDIT_KEY_F12, 515 EDIT_KEY_CTRL_UP, 516 EDIT_KEY_CTRL_DOWN, 517 EDIT_KEY_CTRL_RIGHT, 518 EDIT_KEY_CTRL_LEFT, 519 EDIT_KEY_CTRL_A, 520 EDIT_KEY_CTRL_B, 521 EDIT_KEY_CTRL_D, 522 EDIT_KEY_CTRL_E, 523 EDIT_KEY_CTRL_F, 524 EDIT_KEY_CTRL_G, 525 EDIT_KEY_CTRL_H, 526 EDIT_KEY_CTRL_J, 527 EDIT_KEY_CTRL_K, 528 EDIT_KEY_CTRL_L, 529 EDIT_KEY_CTRL_N, 530 EDIT_KEY_CTRL_O, 531 EDIT_KEY_CTRL_P, 532 EDIT_KEY_CTRL_R, 533 EDIT_KEY_CTRL_T, 534 EDIT_KEY_CTRL_U, 535 EDIT_KEY_CTRL_V, 536 EDIT_KEY_CTRL_W, 537 EDIT_KEY_ALT_UP, 538 EDIT_KEY_ALT_DOWN, 539 EDIT_KEY_ALT_RIGHT, 540 EDIT_KEY_ALT_LEFT, 541 EDIT_KEY_SHIFT_UP, 542 EDIT_KEY_SHIFT_DOWN, 543 EDIT_KEY_SHIFT_RIGHT, 544 EDIT_KEY_SHIFT_LEFT, 545 EDIT_KEY_ALT_SHIFT_UP, 546 EDIT_KEY_ALT_SHIFT_DOWN, 547 EDIT_KEY_ALT_SHIFT_RIGHT, 548 EDIT_KEY_ALT_SHIFT_LEFT, 549 EDIT_KEY_EOF 550}; 551 552static void show_esc_buf(const char *esc_buf, char c, int i) 553{ 554 edit_clear_line(); 555 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); 556 edit_redraw(); 557} 558 559 560static enum edit_key_code esc_seq_to_key1_no(char last) 561{ 562 switch (last) { 563 case 'A': 564 return EDIT_KEY_UP; 565 case 'B': 566 return EDIT_KEY_DOWN; 567 case 'C': 568 return EDIT_KEY_RIGHT; 569 case 'D': 570 return EDIT_KEY_LEFT; 571 default: 572 return EDIT_KEY_NONE; 573 } 574} 575 576 577static enum edit_key_code esc_seq_to_key1_shift(char last) 578{ 579 switch (last) { 580 case 'A': 581 return EDIT_KEY_SHIFT_UP; 582 case 'B': 583 return EDIT_KEY_SHIFT_DOWN; 584 case 'C': 585 return EDIT_KEY_SHIFT_RIGHT; 586 case 'D': 587 return EDIT_KEY_SHIFT_LEFT; 588 default: 589 return EDIT_KEY_NONE; 590 } 591} 592 593 594static enum edit_key_code esc_seq_to_key1_alt(char last) 595{ 596 switch (last) { 597 case 'A': 598 return EDIT_KEY_ALT_UP; 599 case 'B': 600 return EDIT_KEY_ALT_DOWN; 601 case 'C': 602 return EDIT_KEY_ALT_RIGHT; 603 case 'D': 604 return EDIT_KEY_ALT_LEFT; 605 default: 606 return EDIT_KEY_NONE; 607 } 608} 609 610 611static enum edit_key_code esc_seq_to_key1_alt_shift(char last) 612{ 613 switch (last) { 614 case 'A': 615 return EDIT_KEY_ALT_SHIFT_UP; 616 case 'B': 617 return EDIT_KEY_ALT_SHIFT_DOWN; 618 case 'C': 619 return EDIT_KEY_ALT_SHIFT_RIGHT; 620 case 'D': 621 return EDIT_KEY_ALT_SHIFT_LEFT; 622 default: 623 return EDIT_KEY_NONE; 624 } 625} 626 627 628static enum edit_key_code esc_seq_to_key1_ctrl(char last) 629{ 630 switch (last) { 631 case 'A': 632 return EDIT_KEY_CTRL_UP; 633 case 'B': 634 return EDIT_KEY_CTRL_DOWN; 635 case 'C': 636 return EDIT_KEY_CTRL_RIGHT; 637 case 'D': 638 return EDIT_KEY_CTRL_LEFT; 639 default: 640 return EDIT_KEY_NONE; 641 } 642} 643 644 645static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) 646{ 647 /* ESC-[<param1>;<param2><last> */ 648 649 if (param1 < 0 && param2 < 0) 650 return esc_seq_to_key1_no(last); 651 652 if (param1 == 1 && param2 == 2) 653 return esc_seq_to_key1_shift(last); 654 655 if (param1 == 1 && param2 == 3) 656 return esc_seq_to_key1_alt(last); 657 658 if (param1 == 1 && param2 == 4) 659 return esc_seq_to_key1_alt_shift(last); 660 661 if (param1 == 1 && param2 == 5) 662 return esc_seq_to_key1_ctrl(last); 663 664 if (param2 < 0) { 665 if (last != '~') 666 return EDIT_KEY_NONE; 667 switch (param1) { 668 case 2: 669 return EDIT_KEY_INSERT; 670 case 3: 671 return EDIT_KEY_DELETE; 672 case 5: 673 return EDIT_KEY_PAGE_UP; 674 case 6: 675 return EDIT_KEY_PAGE_DOWN; 676 case 15: 677 return EDIT_KEY_F5; 678 case 17: 679 return EDIT_KEY_F6; 680 case 18: 681 return EDIT_KEY_F7; 682 case 19: 683 return EDIT_KEY_F8; 684 case 20: 685 return EDIT_KEY_F9; 686 case 21: 687 return EDIT_KEY_F10; 688 case 23: 689 return EDIT_KEY_F11; 690 case 24: 691 return EDIT_KEY_F12; 692 } 693 } 694 695 return EDIT_KEY_NONE; 696} 697 698 699static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) 700{ 701 /* ESC-O<param1>;<param2><last> */ 702 703 if (param1 >= 0 || param2 >= 0) 704 return EDIT_KEY_NONE; 705 706 switch (last) { 707 case 'F': 708 return EDIT_KEY_END; 709 case 'H': 710 return EDIT_KEY_HOME; 711 case 'P': 712 return EDIT_KEY_F1; 713 case 'Q': 714 return EDIT_KEY_F2; 715 case 'R': 716 return EDIT_KEY_F3; 717 case 'S': 718 return EDIT_KEY_F4; 719 default: 720 return EDIT_KEY_NONE; 721 } 722} 723 724 725static enum edit_key_code esc_seq_to_key(char *seq) 726{ 727 char last, *pos; 728 int param1 = -1, param2 = -1; 729 enum edit_key_code ret = EDIT_KEY_NONE; 730 731 last = '\0'; 732 for (pos = seq; *pos; pos++) 733 last = *pos; 734 735 if (seq[1] >= '0' && seq[1] <= '9') { 736 param1 = atoi(&seq[1]); 737 pos = os_strchr(seq, ';'); 738 if (pos) 739 param2 = atoi(pos + 1); 740 } 741 742 if (seq[0] == '[') 743 ret = esc_seq_to_key1(param1, param2, last); 744 else if (seq[0] == 'O') 745 ret = esc_seq_to_key2(param1, param2, last); 746 747 if (ret != EDIT_KEY_NONE) 748 return ret; 749 750 edit_clear_line(); 751 printf("\rUnknown escape sequence '%s'\n", seq); 752 edit_redraw(); 753 return EDIT_KEY_NONE; 754} 755 756 757static enum edit_key_code edit_read_key(int sock) 758{ 759 int c; 760 unsigned char buf[1]; 761 int res; 762 static int esc = -1; 763 static char esc_buf[7]; 764 765 res = read(sock, buf, 1); 766 if (res < 0) 767 perror("read"); 768 if (res <= 0) 769 return EDIT_KEY_EOF; 770 771 c = buf[0]; 772 773 if (esc >= 0) { 774 if (c == 27 /* ESC */) { 775 esc = 0; 776 return EDIT_KEY_NONE; 777 } 778 779 if (esc == 6) { 780 show_esc_buf(esc_buf, c, 0); 781 esc = -1; 782 } else { 783 esc_buf[esc++] = c; 784 esc_buf[esc] = '\0'; 785 } 786 } 787 788 if (esc == 1) { 789 if (esc_buf[0] != '[' && esc_buf[0] != 'O') { 790 show_esc_buf(esc_buf, c, 1); 791 esc = -1; 792 return EDIT_KEY_NONE; 793 } else 794 return EDIT_KEY_NONE; /* Escape sequence continues */ 795 } 796 797 if (esc > 1) { 798 if ((c >= '0' && c <= '9') || c == ';') 799 return EDIT_KEY_NONE; /* Escape sequence continues */ 800 801 if (c == '~' || (c >= 'A' && c <= 'Z')) { 802 esc = -1; 803 return esc_seq_to_key(esc_buf); 804 } 805 806 show_esc_buf(esc_buf, c, 2); 807 esc = -1; 808 return EDIT_KEY_NONE; 809 } 810 811 switch (c) { 812 case 1: 813 return EDIT_KEY_CTRL_A; 814 case 2: 815 return EDIT_KEY_CTRL_B; 816 case 4: 817 return EDIT_KEY_CTRL_D; 818 case 5: 819 return EDIT_KEY_CTRL_E; 820 case 6: 821 return EDIT_KEY_CTRL_F; 822 case 7: 823 return EDIT_KEY_CTRL_G; 824 case 8: 825 return EDIT_KEY_CTRL_H; 826 case 9: 827 return EDIT_KEY_TAB; 828 case 10: 829 return EDIT_KEY_CTRL_J; 830 case 13: /* CR */ 831 return EDIT_KEY_ENTER; 832 case 11: 833 return EDIT_KEY_CTRL_K; 834 case 12: 835 return EDIT_KEY_CTRL_L; 836 case 14: 837 return EDIT_KEY_CTRL_N; 838 case 15: 839 return EDIT_KEY_CTRL_O; 840 case 16: 841 return EDIT_KEY_CTRL_P; 842 case 18: 843 return EDIT_KEY_CTRL_R; 844 case 20: 845 return EDIT_KEY_CTRL_T; 846 case 21: 847 return EDIT_KEY_CTRL_U; 848 case 22: 849 return EDIT_KEY_CTRL_V; 850 case 23: 851 return EDIT_KEY_CTRL_W; 852 case 27: /* ESC */ 853 esc = 0; 854 return EDIT_KEY_NONE; 855 case 127: 856 return EDIT_KEY_BACKSPACE; 857 default: 858 return c; 859 } 860} 861 862 863static char search_buf[21]; 864static int search_skip; 865 866static char * search_find(void) 867{ 868 struct edit_history *h; 869 size_t len = os_strlen(search_buf); 870 int skip = search_skip; 871 872 if (len == 0) 873 return NULL; 874 875 dl_list_for_each(h, &history_list, struct edit_history, list) { 876 if (os_strstr(h->str, search_buf)) { 877 if (skip == 0) 878 return h->str; 879 skip--; 880 } 881 } 882 883 search_skip = 0; 884 return NULL; 885} 886 887 888static void search_redraw(void) 889{ 890 char *match = search_find(); 891 printf("\rsearch '%s': %s" CLEAR_END_LINE, 892 search_buf, match ? match : ""); 893 printf("\rsearch '%s", search_buf); 894 fflush(stdout); 895} 896 897 898static void search_start(void) 899{ 900 edit_clear_line(); 901 search_buf[0] = '\0'; 902 search_skip = 0; 903 search_redraw(); 904} 905 906 907static void search_clear(void) 908{ 909 search_redraw(); 910 printf("\r" CLEAR_END_LINE); 911} 912 913 914static void search_stop(void) 915{ 916 char *match = search_find(); 917 search_buf[0] = '\0'; 918 search_clear(); 919 if (match) { 920 os_strlcpy(cmdbuf, match, CMD_BUF_LEN); 921 cmdbuf_len = os_strlen(cmdbuf); 922 cmdbuf_pos = cmdbuf_len; 923 } 924 edit_redraw(); 925} 926 927 928static void search_cancel(void) 929{ 930 search_buf[0] = '\0'; 931 search_clear(); 932 edit_redraw(); 933} 934 935 936static void search_backspace(void) 937{ 938 size_t len; 939 len = os_strlen(search_buf); 940 if (len == 0) 941 return; 942 search_buf[len - 1] = '\0'; 943 search_skip = 0; 944 search_redraw(); 945} 946 947 948static void search_next(void) 949{ 950 search_skip++; 951 search_find(); 952 search_redraw(); 953} 954 955 956static void search_char(char c) 957{ 958 size_t len; 959 len = os_strlen(search_buf); 960 if (len == sizeof(search_buf) - 1) 961 return; 962 search_buf[len] = c; 963 search_buf[len + 1] = '\0'; 964 search_skip = 0; 965 search_redraw(); 966} 967 968 969static enum edit_key_code search_key(enum edit_key_code c) 970{ 971 switch (c) { 972 case EDIT_KEY_ENTER: 973 case EDIT_KEY_CTRL_J: 974 case EDIT_KEY_LEFT: 975 case EDIT_KEY_RIGHT: 976 case EDIT_KEY_HOME: 977 case EDIT_KEY_END: 978 case EDIT_KEY_CTRL_A: 979 case EDIT_KEY_CTRL_E: 980 search_stop(); 981 return c; 982 case EDIT_KEY_DOWN: 983 case EDIT_KEY_UP: 984 search_cancel(); 985 return EDIT_KEY_EOF; 986 case EDIT_KEY_CTRL_H: 987 case EDIT_KEY_BACKSPACE: 988 search_backspace(); 989 break; 990 case EDIT_KEY_CTRL_R: 991 search_next(); 992 break; 993 default: 994 if (c >= 32 && c <= 255) 995 search_char(c); 996 break; 997 } 998 999 return EDIT_KEY_NONE; 1000} 1001 1002 1003static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) 1004{ 1005 static int last_tab = 0; 1006 static int search = 0; 1007 enum edit_key_code c; 1008 1009 c = edit_read_key(sock); 1010 1011 if (search) { 1012 c = search_key(c); 1013 if (c == EDIT_KEY_NONE) 1014 return; 1015 search = 0; 1016 if (c == EDIT_KEY_EOF) 1017 return; 1018 } 1019 1020 if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) 1021 last_tab = 0; 1022 1023 switch (c) { 1024 case EDIT_KEY_NONE: 1025 break; 1026 case EDIT_KEY_EOF: 1027 edit_eof_cb(edit_cb_ctx); 1028 break; 1029 case EDIT_KEY_TAB: 1030 complete(last_tab); 1031 last_tab = 1; 1032 break; 1033 case EDIT_KEY_UP: 1034 case EDIT_KEY_CTRL_P: 1035 history_prev(); 1036 break; 1037 case EDIT_KEY_DOWN: 1038 case EDIT_KEY_CTRL_N: 1039 history_next(); 1040 break; 1041 case EDIT_KEY_RIGHT: 1042 case EDIT_KEY_CTRL_F: 1043 move_right(); 1044 break; 1045 case EDIT_KEY_LEFT: 1046 case EDIT_KEY_CTRL_B: 1047 move_left(); 1048 break; 1049 case EDIT_KEY_CTRL_RIGHT: 1050 move_word_right(); 1051 break; 1052 case EDIT_KEY_CTRL_LEFT: 1053 move_word_left(); 1054 break; 1055 case EDIT_KEY_DELETE: 1056 delete_current(); 1057 break; 1058 case EDIT_KEY_END: 1059 move_end(); 1060 break; 1061 case EDIT_KEY_HOME: 1062 case EDIT_KEY_CTRL_A: 1063 move_start(); 1064 break; 1065 case EDIT_KEY_F2: 1066 history_debug_dump(); 1067 break; 1068 case EDIT_KEY_CTRL_D: 1069 if (cmdbuf_len > 0) { 1070 delete_current(); 1071 return; 1072 } 1073 printf("\n"); 1074 edit_eof_cb(edit_cb_ctx); 1075 break; 1076 case EDIT_KEY_CTRL_E: 1077 move_end(); 1078 break; 1079 case EDIT_KEY_CTRL_H: 1080 case EDIT_KEY_BACKSPACE: 1081 delete_left(); 1082 break; 1083 case EDIT_KEY_ENTER: 1084 case EDIT_KEY_CTRL_J: 1085 process_cmd(); 1086 break; 1087 case EDIT_KEY_CTRL_K: 1088 clear_right(); 1089 break; 1090 case EDIT_KEY_CTRL_L: 1091 edit_clear_line(); 1092 edit_redraw(); 1093 break; 1094 case EDIT_KEY_CTRL_R: 1095 search = 1; 1096 search_start(); 1097 break; 1098 case EDIT_KEY_CTRL_U: 1099 clear_left(); 1100 break; 1101 case EDIT_KEY_CTRL_W: 1102 delete_word(); 1103 break; 1104 default: 1105 if (c >= 32 && c <= 255) 1106 insert_char(c); 1107 break; 1108 } 1109} 1110 1111 1112int edit_init(void (*cmd_cb)(void *ctx, char *cmd), 1113 void (*eof_cb)(void *ctx), 1114 char ** (*completion_cb)(void *ctx, const char *cmd, int pos), 1115 void *ctx, const char *history_file) 1116{ 1117 currbuf[0] = '\0'; 1118 dl_list_init(&history_list); 1119 history_curr = NULL; 1120 if (history_file) 1121 history_read(history_file); 1122 1123 edit_cb_ctx = ctx; 1124 edit_cmd_cb = cmd_cb; 1125 edit_eof_cb = eof_cb; 1126 edit_completion_cb = completion_cb; 1127 1128 tcgetattr(STDIN_FILENO, &prevt); 1129 newt = prevt; 1130 newt.c_lflag &= ~(ICANON | ECHO); 1131 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 1132 1133 eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); 1134 1135 printf("> "); 1136 fflush(stdout); 1137 1138 return 0; 1139} 1140 1141 1142void edit_deinit(const char *history_file, 1143 int (*filter_cb)(void *ctx, const char *cmd)) 1144{ 1145 struct edit_history *h; 1146 if (history_file) 1147 history_write(history_file, filter_cb); 1148 while ((h = dl_list_first(&history_list, struct edit_history, list))) { 1149 dl_list_del(&h->list); 1150 os_free(h); 1151 } 1152 edit_clear_line(); 1153 putchar('\r'); 1154 fflush(stdout); 1155 eloop_unregister_read_sock(STDIN_FILENO); 1156 tcsetattr(STDIN_FILENO, TCSANOW, &prevt); 1157} 1158 1159 1160void edit_redraw(void) 1161{ 1162 char tmp; 1163 cmdbuf[cmdbuf_len] = '\0'; 1164 printf("\r> %s", cmdbuf); 1165 if (cmdbuf_pos != cmdbuf_len) { 1166 tmp = cmdbuf[cmdbuf_pos]; 1167 cmdbuf[cmdbuf_pos] = '\0'; 1168 printf("\r> %s", cmdbuf); 1169 cmdbuf[cmdbuf_pos] = tmp; 1170 } 1171 fflush(stdout); 1172} 1173