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