1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation. 3 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org> 4 * All rights reserved. 5 */ 6 7 #include <stdlib.h> 8 #include <stdio.h> 9 #include <stdint.h> 10 #include <string.h> 11 #include <stdarg.h> 12 #include <errno.h> 13 #include <ctype.h> 14 15 #include "cmdline_cirbuf.h" 16 #include "cmdline_private.h" 17 #include "cmdline_rdline.h" 18 19 static void rdline_puts(struct rdline *rdl, const char *buf); 20 static void rdline_miniprintf(struct rdline *rdl, 21 const char *buf, unsigned int val); 22 23 static void rdline_remove_old_history_item(struct rdline *rdl); 24 static void rdline_remove_first_history_item(struct rdline *rdl); 25 static unsigned int rdline_get_history_size(struct rdline *rdl); 26 27 28 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our 29 * own. */ 30 static int 31 isblank2(char c) 32 { 33 if (c == ' ' || 34 c == '\t' ) 35 return 1; 36 return 0; 37 } 38 39 int 40 rdline_init(struct rdline *rdl, 41 rdline_write_char_t *write_char, 42 rdline_validate_t *validate, 43 rdline_complete_t *complete, 44 void *opaque) 45 { 46 if (!rdl || !write_char || !validate || !complete) 47 return -EINVAL; 48 memset(rdl, 0, sizeof(*rdl)); 49 rdl->validate = validate; 50 rdl->complete = complete; 51 rdl->write_char = write_char; 52 rdl->opaque = opaque; 53 rdl->status = RDLINE_INIT; 54 return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); 55 } 56 57 struct rdline * 58 rdline_new(rdline_write_char_t *write_char, 59 rdline_validate_t *validate, 60 rdline_complete_t *complete, 61 void *opaque) 62 { 63 struct rdline *rdl; 64 65 rdl = malloc(sizeof(*rdl)); 66 if (rdline_init(rdl, write_char, validate, complete, opaque) < 0) { 67 free(rdl); 68 rdl = NULL; 69 } 70 return rdl; 71 } 72 73 void 74 rdline_free(struct rdline *rdl) 75 { 76 free(rdl); 77 } 78 79 void 80 rdline_newline(struct rdline *rdl, const char *prompt) 81 { 82 unsigned int i; 83 84 if (!rdl || !prompt) 85 return; 86 87 vt100_init(&rdl->vt100); 88 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); 89 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); 90 91 rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1); 92 if (prompt != rdl->prompt) 93 memcpy(rdl->prompt, prompt, rdl->prompt_size); 94 rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0'; 95 96 for (i=0 ; i<rdl->prompt_size ; i++) 97 rdl->write_char(rdl, rdl->prompt[i]); 98 rdl->status = RDLINE_RUNNING; 99 100 rdl->history_cur_line = -1; 101 } 102 103 void 104 rdline_stop(struct rdline *rdl) 105 { 106 if (!rdl) 107 return; 108 rdl->status = RDLINE_INIT; 109 } 110 111 void 112 rdline_quit(struct rdline *rdl) 113 { 114 if (!rdl) 115 return; 116 rdl->status = RDLINE_EXITED; 117 } 118 119 void 120 rdline_restart(struct rdline *rdl) 121 { 122 if (!rdl) 123 return; 124 rdl->status = RDLINE_RUNNING; 125 } 126 127 void 128 rdline_reset(struct rdline *rdl) 129 { 130 if (!rdl) 131 return; 132 vt100_init(&rdl->vt100); 133 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); 134 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); 135 136 rdl->status = RDLINE_RUNNING; 137 138 rdl->history_cur_line = -1; 139 } 140 141 const char * 142 rdline_get_buffer(struct rdline *rdl) 143 { 144 if (!rdl) 145 return NULL; 146 unsigned int len_l, len_r; 147 cirbuf_align_left(&rdl->left); 148 cirbuf_align_left(&rdl->right); 149 150 len_l = CIRBUF_GET_LEN(&rdl->left); 151 len_r = CIRBUF_GET_LEN(&rdl->right); 152 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r); 153 154 rdl->left_buf[len_l + len_r] = '\n'; 155 rdl->left_buf[len_l + len_r + 1] = '\0'; 156 return rdl->left_buf; 157 } 158 159 static void 160 display_right_buffer(struct rdline *rdl, int force) 161 { 162 unsigned int i; 163 char tmp; 164 165 if (!force && CIRBUF_IS_EMPTY(&rdl->right)) 166 return; 167 168 rdline_puts(rdl, vt100_clear_right); 169 CIRBUF_FOREACH(&rdl->right, i, tmp) { 170 rdl->write_char(rdl, tmp); 171 } 172 if (!CIRBUF_IS_EMPTY(&rdl->right)) 173 rdline_miniprintf(rdl, vt100_multi_left, 174 CIRBUF_GET_LEN(&rdl->right)); 175 } 176 177 void 178 rdline_redisplay(struct rdline *rdl) 179 { 180 unsigned int i; 181 char tmp; 182 183 if (!rdl) 184 return; 185 186 rdline_puts(rdl, vt100_home); 187 for (i=0 ; i<rdl->prompt_size ; i++) 188 rdl->write_char(rdl, rdl->prompt[i]); 189 CIRBUF_FOREACH(&rdl->left, i, tmp) { 190 rdl->write_char(rdl, tmp); 191 } 192 display_right_buffer(rdl, 1); 193 } 194 195 int 196 rdline_char_in(struct rdline *rdl, char c) 197 { 198 unsigned int i; 199 int cmd; 200 char tmp; 201 char *buf; 202 203 if (!rdl) 204 return -EINVAL; 205 206 if (rdl->status == RDLINE_EXITED) 207 return RDLINE_RES_EXITED; 208 if (rdl->status != RDLINE_RUNNING) 209 return RDLINE_RES_NOT_RUNNING; 210 211 cmd = vt100_parser(&rdl->vt100, c); 212 if (cmd == -2) 213 return RDLINE_RES_SUCCESS; 214 215 if (cmd >= 0) { 216 switch (cmd) { 217 /* move caret 1 char to the left */ 218 case CMDLINE_KEY_CTRL_B: 219 case CMDLINE_KEY_LEFT_ARR: 220 if (CIRBUF_IS_EMPTY(&rdl->left)) 221 break; 222 tmp = cirbuf_get_tail(&rdl->left); 223 cirbuf_del_tail(&rdl->left); 224 cirbuf_add_head(&rdl->right, tmp); 225 rdline_puts(rdl, vt100_left_arr); 226 break; 227 228 /* move caret 1 char to the right */ 229 case CMDLINE_KEY_CTRL_F: 230 case CMDLINE_KEY_RIGHT_ARR: 231 if (CIRBUF_IS_EMPTY(&rdl->right)) 232 break; 233 tmp = cirbuf_get_head(&rdl->right); 234 cirbuf_del_head(&rdl->right); 235 cirbuf_add_tail(&rdl->left, tmp); 236 rdline_puts(rdl, vt100_right_arr); 237 break; 238 239 /* move caret 1 word to the left */ 240 /* keyboard equivalent: Alt+B */ 241 case CMDLINE_KEY_WLEFT: 242 while (! CIRBUF_IS_EMPTY(&rdl->left) && 243 (tmp = cirbuf_get_tail(&rdl->left)) && 244 isblank2(tmp)) { 245 rdline_puts(rdl, vt100_left_arr); 246 cirbuf_del_tail(&rdl->left); 247 cirbuf_add_head(&rdl->right, tmp); 248 } 249 while (! CIRBUF_IS_EMPTY(&rdl->left) && 250 (tmp = cirbuf_get_tail(&rdl->left)) && 251 !isblank2(tmp)) { 252 rdline_puts(rdl, vt100_left_arr); 253 cirbuf_del_tail(&rdl->left); 254 cirbuf_add_head(&rdl->right, tmp); 255 } 256 break; 257 258 /* move caret 1 word to the right */ 259 /* keyboard equivalent: Alt+F */ 260 case CMDLINE_KEY_WRIGHT: 261 while (! CIRBUF_IS_EMPTY(&rdl->right) && 262 (tmp = cirbuf_get_head(&rdl->right)) && 263 isblank2(tmp)) { 264 rdline_puts(rdl, vt100_right_arr); 265 cirbuf_del_head(&rdl->right); 266 cirbuf_add_tail(&rdl->left, tmp); 267 } 268 while (! CIRBUF_IS_EMPTY(&rdl->right) && 269 (tmp = cirbuf_get_head(&rdl->right)) && 270 !isblank2(tmp)) { 271 rdline_puts(rdl, vt100_right_arr); 272 cirbuf_del_head(&rdl->right); 273 cirbuf_add_tail(&rdl->left, tmp); 274 } 275 break; 276 277 /* move caret to the left */ 278 case CMDLINE_KEY_CTRL_A: 279 if (CIRBUF_IS_EMPTY(&rdl->left)) 280 break; 281 rdline_miniprintf(rdl, vt100_multi_left, 282 CIRBUF_GET_LEN(&rdl->left)); 283 while (! CIRBUF_IS_EMPTY(&rdl->left)) { 284 tmp = cirbuf_get_tail(&rdl->left); 285 cirbuf_del_tail(&rdl->left); 286 cirbuf_add_head(&rdl->right, tmp); 287 } 288 break; 289 290 /* move caret to the right */ 291 case CMDLINE_KEY_CTRL_E: 292 if (CIRBUF_IS_EMPTY(&rdl->right)) 293 break; 294 rdline_miniprintf(rdl, vt100_multi_right, 295 CIRBUF_GET_LEN(&rdl->right)); 296 while (! CIRBUF_IS_EMPTY(&rdl->right)) { 297 tmp = cirbuf_get_head(&rdl->right); 298 cirbuf_del_head(&rdl->right); 299 cirbuf_add_tail(&rdl->left, tmp); 300 } 301 break; 302 303 /* delete 1 char from the left */ 304 case CMDLINE_KEY_BKSPACE: 305 case CMDLINE_KEY_BKSPACE2: 306 if(!cirbuf_del_tail_safe(&rdl->left)) { 307 rdline_puts(rdl, vt100_bs); 308 display_right_buffer(rdl, 1); 309 } 310 break; 311 312 /* delete 1 char from the right */ 313 case CMDLINE_KEY_SUPPR: 314 case CMDLINE_KEY_CTRL_D: 315 if (cmd == CMDLINE_KEY_CTRL_D && 316 CIRBUF_IS_EMPTY(&rdl->left) && 317 CIRBUF_IS_EMPTY(&rdl->right)) { 318 return RDLINE_RES_EOF; 319 } 320 if (!cirbuf_del_head_safe(&rdl->right)) { 321 display_right_buffer(rdl, 1); 322 } 323 break; 324 325 /* delete 1 word from the left */ 326 case CMDLINE_KEY_META_BKSPACE: 327 case CMDLINE_KEY_CTRL_W: 328 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) { 329 rdline_puts(rdl, vt100_bs); 330 cirbuf_del_tail(&rdl->left); 331 } 332 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) { 333 rdline_puts(rdl, vt100_bs); 334 cirbuf_del_tail(&rdl->left); 335 } 336 display_right_buffer(rdl, 1); 337 break; 338 339 /* delete 1 word from the right */ 340 case CMDLINE_KEY_META_D: 341 while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right))) 342 cirbuf_del_head(&rdl->right); 343 while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right))) 344 cirbuf_del_head(&rdl->right); 345 display_right_buffer(rdl, 1); 346 break; 347 348 /* set kill buffer to contents on the right side of caret */ 349 case CMDLINE_KEY_CTRL_K: 350 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE); 351 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right); 352 cirbuf_del_buf_head(&rdl->right, rdl->kill_size); 353 rdline_puts(rdl, vt100_clear_right); 354 break; 355 356 /* paste contents of kill buffer to the left side of caret */ 357 case CMDLINE_KEY_CTRL_Y: 358 i=0; 359 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < 360 RDLINE_BUF_SIZE && 361 i < rdl->kill_size) { 362 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]); 363 rdl->write_char(rdl, rdl->kill_buf[i]); 364 i++; 365 } 366 display_right_buffer(rdl, 0); 367 break; 368 369 /* clear and newline */ 370 case CMDLINE_KEY_CTRL_C: 371 rdline_puts(rdl, "\r\n"); 372 rdline_newline(rdl, rdl->prompt); 373 break; 374 375 /* redisplay (helps when prompt is lost in other output) */ 376 case CMDLINE_KEY_CTRL_L: 377 rdline_redisplay(rdl); 378 break; 379 380 /* autocomplete */ 381 case CMDLINE_KEY_TAB: 382 case CMDLINE_KEY_HELP: 383 cirbuf_align_left(&rdl->left); 384 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; 385 if (rdl->complete) { 386 char tmp_buf[BUFSIZ]; 387 int complete_state; 388 int ret; 389 unsigned int tmp_size; 390 391 if (cmd == CMDLINE_KEY_TAB) 392 complete_state = 0; 393 else 394 complete_state = -1; 395 396 /* see in parse.h for help on complete() */ 397 ret = rdl->complete(rdl, rdl->left_buf, 398 tmp_buf, sizeof(tmp_buf), 399 &complete_state); 400 /* no completion or error */ 401 if (ret <= 0) { 402 return RDLINE_RES_COMPLETE; 403 } 404 405 tmp_size = strnlen(tmp_buf, sizeof(tmp_buf)); 406 /* add chars */ 407 if (ret == RDLINE_RES_COMPLETE) { 408 i=0; 409 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < 410 RDLINE_BUF_SIZE && 411 i < tmp_size) { 412 cirbuf_add_tail(&rdl->left, tmp_buf[i]); 413 rdl->write_char(rdl, tmp_buf[i]); 414 i++; 415 } 416 display_right_buffer(rdl, 1); 417 return RDLINE_RES_COMPLETE; /* ?? */ 418 } 419 420 /* choice */ 421 rdline_puts(rdl, "\r\n"); 422 while (ret) { 423 rdl->write_char(rdl, ' '); 424 for (i=0 ; tmp_buf[i] ; i++) 425 rdl->write_char(rdl, tmp_buf[i]); 426 rdline_puts(rdl, "\r\n"); 427 ret = rdl->complete(rdl, rdl->left_buf, 428 tmp_buf, sizeof(tmp_buf), 429 &complete_state); 430 } 431 432 rdline_redisplay(rdl); 433 } 434 return RDLINE_RES_COMPLETE; 435 436 /* complete buffer */ 437 case CMDLINE_KEY_RETURN: 438 case CMDLINE_KEY_RETURN2: 439 rdline_get_buffer(rdl); 440 rdl->status = RDLINE_INIT; 441 rdline_puts(rdl, "\r\n"); 442 if (rdl->history_cur_line != -1) 443 rdline_remove_first_history_item(rdl); 444 445 if (rdl->validate) 446 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2); 447 /* user may have stopped rdline */ 448 if (rdl->status == RDLINE_EXITED) 449 return RDLINE_RES_EXITED; 450 return RDLINE_RES_VALIDATED; 451 452 /* previous element in history */ 453 case CMDLINE_KEY_UP_ARR: 454 case CMDLINE_KEY_CTRL_P: 455 if (rdl->history_cur_line == 0) { 456 rdline_remove_first_history_item(rdl); 457 } 458 if (rdl->history_cur_line <= 0) { 459 rdline_add_history(rdl, rdline_get_buffer(rdl)); 460 rdl->history_cur_line = 0; 461 } 462 463 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1); 464 if (!buf) 465 break; 466 467 rdl->history_cur_line ++; 468 vt100_init(&rdl->vt100); 469 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); 470 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); 471 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE)); 472 rdline_redisplay(rdl); 473 break; 474 475 /* next element in history */ 476 case CMDLINE_KEY_DOWN_ARR: 477 case CMDLINE_KEY_CTRL_N: 478 if (rdl->history_cur_line - 1 < 0) 479 break; 480 481 rdl->history_cur_line --; 482 buf = rdline_get_history_item(rdl, rdl->history_cur_line); 483 if (!buf) 484 break; 485 vt100_init(&rdl->vt100); 486 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); 487 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); 488 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE)); 489 rdline_redisplay(rdl); 490 491 break; 492 493 494 default: 495 break; 496 } 497 498 return RDLINE_RES_SUCCESS; 499 } 500 501 if (!isprint((int)c)) 502 return RDLINE_RES_SUCCESS; 503 504 /* standard chars */ 505 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE) 506 return RDLINE_RES_SUCCESS; 507 508 if (cirbuf_add_tail_safe(&rdl->left, c)) 509 return RDLINE_RES_SUCCESS; 510 511 rdl->write_char(rdl, c); 512 display_right_buffer(rdl, 0); 513 514 return RDLINE_RES_SUCCESS; 515 } 516 517 518 /* HISTORY */ 519 520 static void 521 rdline_remove_old_history_item(struct rdline * rdl) 522 { 523 char tmp; 524 525 while (! CIRBUF_IS_EMPTY(&rdl->history) ) { 526 tmp = cirbuf_get_head(&rdl->history); 527 cirbuf_del_head(&rdl->history); 528 if (!tmp) 529 break; 530 } 531 } 532 533 static void 534 rdline_remove_first_history_item(struct rdline * rdl) 535 { 536 char tmp; 537 538 if ( CIRBUF_IS_EMPTY(&rdl->history) ) { 539 return; 540 } 541 else { 542 cirbuf_del_tail(&rdl->history); 543 } 544 545 while (! CIRBUF_IS_EMPTY(&rdl->history) ) { 546 tmp = cirbuf_get_tail(&rdl->history); 547 if (!tmp) 548 break; 549 cirbuf_del_tail(&rdl->history); 550 } 551 } 552 553 static unsigned int 554 rdline_get_history_size(struct rdline * rdl) 555 { 556 unsigned int i, tmp, ret=0; 557 558 CIRBUF_FOREACH(&rdl->history, i, tmp) { 559 if (tmp == 0) 560 ret ++; 561 } 562 563 return ret; 564 } 565 566 char * 567 rdline_get_history_item(struct rdline * rdl, unsigned int idx) 568 { 569 unsigned int len, i, tmp; 570 571 if (!rdl) 572 return NULL; 573 574 len = rdline_get_history_size(rdl); 575 if ( idx >= len ) { 576 return NULL; 577 } 578 579 cirbuf_align_left(&rdl->history); 580 581 CIRBUF_FOREACH(&rdl->history, i, tmp) { 582 if ( idx == len - 1) { 583 return rdl->history_buf + i; 584 } 585 if (tmp == 0) 586 len --; 587 } 588 589 return NULL; 590 } 591 592 size_t 593 rdline_get_history_buffer_size(struct rdline *rdl) 594 { 595 return sizeof(rdl->history_buf); 596 } 597 598 void * 599 rdline_get_opaque(struct rdline *rdl) 600 { 601 return rdl != NULL ? rdl->opaque : NULL; 602 } 603 604 int 605 rdline_add_history(struct rdline * rdl, const char * buf) 606 { 607 unsigned int len, i; 608 609 if (!rdl || !buf) 610 return -EINVAL; 611 612 len = strnlen(buf, RDLINE_BUF_SIZE); 613 for (i=0; i<len ; i++) { 614 if (buf[i] == '\n') { 615 len = i; 616 break; 617 } 618 } 619 620 if ( len >= RDLINE_HISTORY_BUF_SIZE ) 621 return -1; 622 623 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) { 624 rdline_remove_old_history_item(rdl); 625 } 626 627 cirbuf_add_buf_tail(&rdl->history, buf, len); 628 cirbuf_add_tail(&rdl->history, 0); 629 630 return 0; 631 } 632 633 void 634 rdline_clear_history(struct rdline * rdl) 635 { 636 if (!rdl) 637 return; 638 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); 639 } 640 641 642 /* STATIC USEFUL FUNCS */ 643 644 static void 645 rdline_puts(struct rdline * rdl, const char * buf) 646 { 647 char c; 648 while ( (c = *(buf++)) != '\0' ) { 649 rdl->write_char(rdl, c); 650 } 651 } 652 653 /* a very very basic printf with one arg and one format 'u' */ 654 static void 655 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val) 656 { 657 char c, started=0, div=100; 658 659 while ( (c=*(buf++)) ) { 660 if (c != '%') { 661 rdl->write_char(rdl, c); 662 continue; 663 } 664 c = *(buf++); 665 if (c != 'u') { 666 rdl->write_char(rdl, '%'); 667 rdl->write_char(rdl, c); 668 continue; 669 } 670 /* val is never more than 255 */ 671 while (div) { 672 c = (char)(val / div); 673 if (c || started) { 674 rdl->write_char(rdl, (char)(c+'0')); 675 started = 1; 676 } 677 val %= div; 678 div /= 10; 679 } 680 } 681 } 682