xref: /dpdk/lib/cmdline/cmdline_rdline.c (revision b7fe612ac1de393f869c9818d5503633c8e96b36)
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