xref: /dpdk/lib/cmdline/cmdline_rdline.c (revision 30a1de105a5f40d77b344a891c4a68f79e815c43)
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
isblank2(char c)29 isblank2(char c)
30 {
31 	if (c == ' ' ||
32 	    c == '\t' )
33 		return 1;
34 	return 0;
35 }
36 
37 int
rdline_init(struct rdline * rdl,rdline_write_char_t * write_char,rdline_validate_t * validate,rdline_complete_t * complete,void * opaque)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 *
rdline_new(rdline_write_char_t * write_char,rdline_validate_t * validate,rdline_complete_t * complete,void * opaque)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
rdline_free(struct rdline * rdl)72 rdline_free(struct rdline *rdl)
73 {
74 	free(rdl);
75 }
76 
77 void
rdline_newline(struct rdline * rdl,const char * prompt)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
rdline_stop(struct rdline * rdl)102 rdline_stop(struct rdline *rdl)
103 {
104 	if (!rdl)
105 		return;
106 	rdl->status = RDLINE_INIT;
107 }
108 
109 void
rdline_quit(struct rdline * rdl)110 rdline_quit(struct rdline *rdl)
111 {
112 	if (!rdl)
113 		return;
114 	rdl->status = RDLINE_EXITED;
115 }
116 
117 void
rdline_restart(struct rdline * rdl)118 rdline_restart(struct rdline *rdl)
119 {
120 	if (!rdl)
121 		return;
122 	rdl->status = RDLINE_RUNNING;
123 }
124 
125 void
rdline_reset(struct rdline * rdl)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 *
rdline_get_buffer(struct rdline * rdl)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
display_right_buffer(struct rdline * rdl,int force)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
rdline_redisplay(struct rdline * rdl)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
rdline_char_in(struct rdline * rdl,char c)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
rdline_remove_old_history_item(struct rdline * rdl)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
rdline_remove_first_history_item(struct rdline * rdl)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
rdline_get_history_size(struct rdline * rdl)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 *
rdline_get_history_item(struct rdline * rdl,unsigned int idx)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
rdline_get_history_buffer_size(struct rdline * rdl)591 rdline_get_history_buffer_size(struct rdline *rdl)
592 {
593 	return sizeof(rdl->history_buf);
594 }
595 
596 void *
rdline_get_opaque(struct rdline * rdl)597 rdline_get_opaque(struct rdline *rdl)
598 {
599 	return rdl != NULL ? rdl->opaque : NULL;
600 }
601 
602 int
rdline_add_history(struct rdline * rdl,const char * buf)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
rdline_clear_history(struct rdline * rdl)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
rdline_puts(struct rdline * rdl,const char * buf)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
rdline_miniprintf(struct rdline * rdl,const char * buf,unsigned int val)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