1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation.
3 * Copyright (c) 2009, Olivier MATZ <[email protected]>
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
isblank2(char c)30 isblank2(char c)
31 {
32 if (c == ' ' ||
33 c == '\t' )
34 return 1;
35 return 0;
36 }
37
38 int
rdline_init(struct rdline * rdl,rdline_write_char_t * write_char,rdline_validate_t * validate,rdline_complete_t * complete)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
rdline_newline(struct rdline * rdl,const char * prompt)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
rdline_stop(struct rdline * rdl)79 rdline_stop(struct rdline *rdl)
80 {
81 if (!rdl)
82 return;
83 rdl->status = RDLINE_INIT;
84 }
85
86 void
rdline_quit(struct rdline * rdl)87 rdline_quit(struct rdline *rdl)
88 {
89 if (!rdl)
90 return;
91 rdl->status = RDLINE_EXITED;
92 }
93
94 void
rdline_restart(struct rdline * rdl)95 rdline_restart(struct rdline *rdl)
96 {
97 if (!rdl)
98 return;
99 rdl->status = RDLINE_RUNNING;
100 }
101
102 void
rdline_reset(struct rdline * rdl)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 *
rdline_get_buffer(struct rdline * rdl)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
display_right_buffer(struct rdline * rdl,int force)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
rdline_redisplay(struct rdline * rdl)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
rdline_char_in(struct rdline * rdl,char c)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
rdline_remove_old_history_item(struct rdline * rdl)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
rdline_remove_first_history_item(struct rdline * rdl)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
rdline_get_history_size(struct rdline * rdl)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 *
rdline_get_history_item(struct rdline * rdl,unsigned int idx)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
rdline_add_history(struct rdline * rdl,const char * buf)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
rdline_clear_history(struct rdline * rdl)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
rdline_puts(struct rdline * rdl,const char * buf)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
rdline_miniprintf(struct rdline * rdl,const char * buf,unsigned int val)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