version: 17.11-rc3
[dpdk.git] / lib / librte_cmdline / cmdline_rdline.c
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
72 static void rdline_puts(struct rdline *rdl, const char *buf);
73 static void rdline_miniprintf(struct rdline *rdl,
74                               const char *buf, unsigned int val);
75
76 static void rdline_remove_old_history_item(struct rdline *rdl);
77 static void rdline_remove_first_history_item(struct rdline *rdl);
78 static unsigned int rdline_get_history_size(struct rdline *rdl);
79
80
81 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
82  * own. */
83 static int
84 isblank2(char c)
85 {
86         if (c == ' ' ||
87             c == '\t' )
88                 return 1;
89         return 0;
90 }
91
92 int
93 rdline_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
108 void
109 rdline_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
132 void
133 rdline_stop(struct rdline *rdl)
134 {
135         if (!rdl)
136                 return;
137         rdl->status = RDLINE_INIT;
138 }
139
140 void
141 rdline_quit(struct rdline *rdl)
142 {
143         if (!rdl)
144                 return;
145         rdl->status = RDLINE_EXITED;
146 }
147
148 void
149 rdline_restart(struct rdline *rdl)
150 {
151         if (!rdl)
152                 return;
153         rdl->status = RDLINE_RUNNING;
154 }
155
156 void
157 rdline_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
170 const char *
171 rdline_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
188 static void
189 display_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
206 void
207 rdline_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
224 int
225 rdline_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
548 static void
549 rdline_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
561 static void
562 rdline_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
581 static unsigned int
582 rdline_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
594 char *
595 rdline_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
620 int
621 rdline_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
649 void
650 rdline_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
660 static void
661 rdline_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' */
670 static void
671 rdline_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 }