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