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