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