remove version in all files
[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 <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 #ifndef NO_RDLINE_HISTORY
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 #endif /* !NO_RDLINE_HISTORY */
81
82
83 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
84  * own. */
85 static int
86 isblank2(char c)
87 {
88         if (c == ' ' ||
89             c == '\t' )
90                 return 1;
91         return 0;
92 }
93
94 void
95 rdline_init(struct rdline *rdl,
96                  rdline_write_char_t *write_char,
97                  rdline_validate_t *validate,
98                  rdline_complete_t *complete)
99 {
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 #ifndef NO_RDLINE_HISTORY
106         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
107 #endif /* !NO_RDLINE_HISTORY */
108 }
109
110 void
111 rdline_newline(struct rdline *rdl, const char *prompt)
112 {
113         unsigned int i;
114
115         vt100_init(&rdl->vt100);
116         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
117         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
118
119         if (prompt != rdl->prompt)
120                 memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
121         rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE);
122
123         for (i=0 ; i<rdl->prompt_size ; i++)
124                 rdl->write_char(rdl, rdl->prompt[i]);
125         rdl->status = RDLINE_RUNNING;
126
127 #ifndef NO_RDLINE_HISTORY
128         rdl->history_cur_line = -1;
129 #endif /* !NO_RDLINE_HISTORY */
130 }
131
132 void
133 rdline_stop(struct rdline *rdl)
134 {
135         rdl->status = RDLINE_INIT;
136 }
137
138 void
139 rdline_quit(struct rdline *rdl)
140 {
141         rdl->status = RDLINE_EXITED;
142 }
143
144 void
145 rdline_restart(struct rdline *rdl)
146 {
147         rdl->status = RDLINE_RUNNING;
148 }
149
150 void
151 rdline_reset(struct rdline *rdl)
152 {
153         vt100_init(&rdl->vt100);
154         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
155         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
156
157         rdl->status = RDLINE_RUNNING;
158
159 #ifndef NO_RDLINE_HISTORY
160         rdl->history_cur_line = -1;
161 #endif  /* !NO_RDLINE_HISTORY */
162 }
163
164 const char *
165 rdline_get_buffer(struct rdline *rdl)
166 {
167         unsigned int len_l, len_r;
168         cirbuf_align_left(&rdl->left);
169         cirbuf_align_left(&rdl->right);
170
171         len_l = CIRBUF_GET_LEN(&rdl->left);
172         len_r = CIRBUF_GET_LEN(&rdl->right);
173         memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
174
175         rdl->left_buf[len_l + len_r] = '\n';
176         rdl->left_buf[len_l + len_r + 1] = '\0';
177         return rdl->left_buf;
178 }
179
180 static void
181 display_right_buffer(struct rdline *rdl, int force)
182 {
183         unsigned int i;
184         char tmp;
185
186         if (!force && CIRBUF_IS_EMPTY(&rdl->right))
187                 return;
188
189         rdline_puts(rdl, vt100_clear_right);
190         CIRBUF_FOREACH(&rdl->right, i, tmp) {
191                 rdl->write_char(rdl, tmp);
192         }
193         if (!CIRBUF_IS_EMPTY(&rdl->right))
194                 rdline_miniprintf(rdl, vt100_multi_left,
195                                   CIRBUF_GET_LEN(&rdl->right));
196 }
197
198 void
199 rdline_redisplay(struct rdline *rdl)
200 {
201         unsigned int i;
202         char tmp;
203
204         rdline_puts(rdl, vt100_home);
205         for (i=0 ; i<rdl->prompt_size ; i++)
206                 rdl->write_char(rdl, rdl->prompt[i]);
207         CIRBUF_FOREACH(&rdl->left, i, tmp) {
208                 rdl->write_char(rdl, tmp);
209         }
210         display_right_buffer(rdl, 1);
211 }
212
213 int
214 rdline_char_in(struct rdline *rdl, char c)
215 {
216         unsigned int i;
217         int cmd;
218         char tmp;
219 #ifndef NO_RDLINE_HISTORY
220         char *buf;
221 #endif
222
223         if (rdl->status == RDLINE_EXITED)
224                 return RDLINE_RES_EXITED;
225         if (rdl->status != RDLINE_RUNNING)
226                 return RDLINE_RES_NOT_RUNNING;
227
228         cmd = vt100_parser(&rdl->vt100, c);
229         if (cmd == -2)
230                 return RDLINE_RES_SUCCESS;
231
232         if (cmd >= 0) {
233                 switch (cmd) {
234                 case CMDLINE_KEY_CTRL_B:
235                 case CMDLINE_KEY_LEFT_ARR:
236                         if (CIRBUF_IS_EMPTY(&rdl->left))
237                                 break;
238                         tmp = cirbuf_get_tail(&rdl->left);
239                         cirbuf_del_tail(&rdl->left);
240                         cirbuf_add_head(&rdl->right, tmp);
241                         rdline_puts(rdl, vt100_left_arr);
242                         break;
243
244                 case CMDLINE_KEY_CTRL_F:
245                 case CMDLINE_KEY_RIGHT_ARR:
246                         if (CIRBUF_IS_EMPTY(&rdl->right))
247                                 break;
248                         tmp = cirbuf_get_head(&rdl->right);
249                         cirbuf_del_head(&rdl->right);
250                         cirbuf_add_tail(&rdl->left, tmp);
251                         rdline_puts(rdl, vt100_right_arr);
252                         break;
253
254                 case CMDLINE_KEY_WLEFT:
255                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
256                                (tmp = cirbuf_get_tail(&rdl->left)) &&
257                                isblank2(tmp)) {
258                                 rdline_puts(rdl, vt100_left_arr);
259                                 cirbuf_del_tail(&rdl->left);
260                                 cirbuf_add_head(&rdl->right, tmp);
261                         }
262                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
263                                (tmp = cirbuf_get_tail(&rdl->left)) &&
264                                !isblank2(tmp)) {
265                                 rdline_puts(rdl, vt100_left_arr);
266                                 cirbuf_del_tail(&rdl->left);
267                                 cirbuf_add_head(&rdl->right, tmp);
268                         }
269                         break;
270
271                 case CMDLINE_KEY_WRIGHT:
272                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
273                                (tmp = cirbuf_get_head(&rdl->right)) &&
274                                isblank2(tmp)) {
275                                 rdline_puts(rdl, vt100_right_arr);
276                                 cirbuf_del_head(&rdl->right);
277                                 cirbuf_add_tail(&rdl->left, tmp);
278                         }
279                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
280                                (tmp = cirbuf_get_head(&rdl->right)) &&
281                                !isblank2(tmp)) {
282                                 rdline_puts(rdl, vt100_right_arr);
283                                 cirbuf_del_head(&rdl->right);
284                                 cirbuf_add_tail(&rdl->left, tmp);
285                         }
286                         break;
287
288                 case CMDLINE_KEY_BKSPACE:
289                         if(!cirbuf_del_tail_safe(&rdl->left)) {
290                                 rdline_puts(rdl, vt100_bs);
291                                 display_right_buffer(rdl, 1);
292                         }
293                         break;
294
295                 case CMDLINE_KEY_META_BKSPACE:
296                 case CMDLINE_KEY_CTRL_W:
297                         while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
298                                 rdline_puts(rdl, vt100_bs);
299                                 cirbuf_del_tail(&rdl->left);
300                         }
301                         while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
302                                 rdline_puts(rdl, vt100_bs);
303                                 cirbuf_del_tail(&rdl->left);
304                         }
305                         display_right_buffer(rdl, 1);
306                         break;
307
308                 case CMDLINE_KEY_META_D:
309                         while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
310                                 cirbuf_del_head(&rdl->right);
311                         while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
312                                 cirbuf_del_head(&rdl->right);
313                         display_right_buffer(rdl, 1);
314                         break;
315
316                 case CMDLINE_KEY_SUPPR:
317                 case CMDLINE_KEY_CTRL_D:
318                         if (cmd == CMDLINE_KEY_CTRL_D &&
319                             CIRBUF_IS_EMPTY(&rdl->left) &&
320                             CIRBUF_IS_EMPTY(&rdl->right)) {
321                                 return RDLINE_RES_EOF;
322                         }
323                         if (!cirbuf_del_head_safe(&rdl->right)) {
324                                 display_right_buffer(rdl, 1);
325                         }
326                         break;
327
328                 case CMDLINE_KEY_CTRL_A:
329                         if (CIRBUF_IS_EMPTY(&rdl->left))
330                                 break;
331                         rdline_miniprintf(rdl, vt100_multi_left,
332                                             CIRBUF_GET_LEN(&rdl->left));
333                         while (! CIRBUF_IS_EMPTY(&rdl->left)) {
334                                 tmp = cirbuf_get_tail(&rdl->left);
335                                 cirbuf_del_tail(&rdl->left);
336                                 cirbuf_add_head(&rdl->right, tmp);
337                         }
338                         break;
339
340                 case CMDLINE_KEY_CTRL_E:
341                         if (CIRBUF_IS_EMPTY(&rdl->right))
342                                 break;
343                         rdline_miniprintf(rdl, vt100_multi_right,
344                                             CIRBUF_GET_LEN(&rdl->right));
345                         while (! CIRBUF_IS_EMPTY(&rdl->right)) {
346                                 tmp = cirbuf_get_head(&rdl->right);
347                                 cirbuf_del_head(&rdl->right);
348                                 cirbuf_add_tail(&rdl->left, tmp);
349                         }
350                         break;
351
352 #ifndef NO_RDLINE_KILL_BUF
353                 case CMDLINE_KEY_CTRL_K:
354                         cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
355                         rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
356                         cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
357                         rdline_puts(rdl, vt100_clear_right);
358                         break;
359
360                 case CMDLINE_KEY_CTRL_Y:
361                         i=0;
362                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
363                               RDLINE_BUF_SIZE &&
364                               i < rdl->kill_size) {
365                                 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
366                                 rdl->write_char(rdl, rdl->kill_buf[i]);
367                                 i++;
368                         }
369                         display_right_buffer(rdl, 0);
370                         break;
371 #endif /* !NO_RDLINE_KILL_BUF */
372
373                 case CMDLINE_KEY_CTRL_C:
374                         rdline_puts(rdl, "\r\n");
375                         rdline_newline(rdl, rdl->prompt);
376                         break;
377
378                 case CMDLINE_KEY_CTRL_L:
379                         rdline_redisplay(rdl);
380                         break;
381
382                 case CMDLINE_KEY_TAB:
383                 case CMDLINE_KEY_HELP:
384                         cirbuf_align_left(&rdl->left);
385                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
386                         if (rdl->complete) {
387                                 char tmp_buf[BUFSIZ];
388                                 int complete_state;
389                                 int ret;
390                                 unsigned int tmp_size;
391
392                                 if (cmd == CMDLINE_KEY_TAB)
393                                         complete_state = 0;
394                                 else
395                                         complete_state = -1;
396
397                                 /* see in parse.h for help on complete() */
398                                 ret = rdl->complete(rdl, rdl->left_buf,
399                                                     tmp_buf, sizeof(tmp_buf),
400                                                     &complete_state);
401                                 /* no completion or error */
402                                 if (ret <= 0) {
403                                         return RDLINE_RES_COMPLETE;
404                                 }
405
406                                 tmp_size = strnlen(tmp_buf, sizeof(tmp_buf));
407                                 /* add chars */
408                                 if (ret == RDLINE_RES_COMPLETE) {
409                                         i=0;
410                                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
411                                               RDLINE_BUF_SIZE &&
412                                               i < tmp_size) {
413                                                 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
414                                                 rdl->write_char(rdl, tmp_buf[i]);
415                                                 i++;
416                                         }
417                                         display_right_buffer(rdl, 1);
418                                         return RDLINE_RES_COMPLETE; /* ?? */
419                                 }
420
421                                 /* choice */
422                                 rdline_puts(rdl, "\r\n");
423                                 while (ret) {
424                                         rdl->write_char(rdl, ' ');
425                                         for (i=0 ; tmp_buf[i] ; i++)
426                                                 rdl->write_char(rdl, tmp_buf[i]);
427                                         rdline_puts(rdl, "\r\n");
428                                         ret = rdl->complete(rdl, rdl->left_buf,
429                                                             tmp_buf, sizeof(tmp_buf),
430                                                             &complete_state);
431                                 }
432
433                                 rdline_redisplay(rdl);
434                         }
435                         return RDLINE_RES_COMPLETE;
436
437                 case CMDLINE_KEY_RETURN:
438                 case CMDLINE_KEY_RETURN2:
439                         rdline_get_buffer(rdl);
440                         rdl->status = RDLINE_INIT;
441                         rdline_puts(rdl, "\r\n");
442 #ifndef NO_RDLINE_HISTORY
443                         if (rdl->history_cur_line != -1)
444                                 rdline_remove_first_history_item(rdl);
445 #endif
446
447                         if (rdl->validate)
448                                 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
449                         /* user may have stopped rdline */
450                         if (rdl->status == RDLINE_EXITED)
451                                 return RDLINE_RES_EXITED;
452                         return RDLINE_RES_VALIDATED;
453
454 #ifndef NO_RDLINE_HISTORY
455                 case CMDLINE_KEY_UP_ARR:
456                 case CMDLINE_KEY_CTRL_P:
457                         if (rdl->history_cur_line == 0) {
458                                 rdline_remove_first_history_item(rdl);
459                         }
460                         if (rdl->history_cur_line <= 0) {
461                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
462                                 rdl->history_cur_line = 0;
463                         }
464
465                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
466                         if (!buf)
467                                 break;
468
469                         rdl->history_cur_line ++;
470                         vt100_init(&rdl->vt100);
471                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
472                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
473                         cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
474                         rdline_redisplay(rdl);
475                         break;
476
477                 case CMDLINE_KEY_DOWN_ARR:
478                 case CMDLINE_KEY_CTRL_N:
479                         if (rdl->history_cur_line - 1 < 0)
480                                 break;
481
482                         rdl->history_cur_line --;
483                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
484                         if (!buf)
485                                 break;
486                         vt100_init(&rdl->vt100);
487                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
488                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
489                         cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
490                         rdline_redisplay(rdl);
491
492                         break;
493 #endif /* !NO_RDLINE_HISTORY */
494
495
496                 default:
497                         break;
498                 }
499
500                 return RDLINE_RES_SUCCESS;
501         }
502
503         if (!isprint((int)c))
504                 return RDLINE_RES_SUCCESS;
505
506         /* standard chars */
507         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
508                 return RDLINE_RES_SUCCESS;
509
510         if (cirbuf_add_tail_safe(&rdl->left, c))
511                 return RDLINE_RES_SUCCESS;
512
513         rdl->write_char(rdl, c);
514         display_right_buffer(rdl, 0);
515
516         return RDLINE_RES_SUCCESS;
517 }
518
519
520 /* HISTORY */
521
522 #ifndef NO_RDLINE_HISTORY
523 static void
524 rdline_remove_old_history_item(struct rdline * rdl)
525 {
526         char tmp;
527
528         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
529                 tmp = cirbuf_get_head(&rdl->history);
530                 cirbuf_del_head(&rdl->history);
531                 if (!tmp)
532                         break;
533         }
534 }
535
536 static void
537 rdline_remove_first_history_item(struct rdline * rdl)
538 {
539         char tmp;
540
541         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
542                 return;
543         }
544         else {
545                 cirbuf_del_tail(&rdl->history);
546         }
547
548         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
549                 tmp = cirbuf_get_tail(&rdl->history);
550                 if (!tmp)
551                         break;
552                 cirbuf_del_tail(&rdl->history);
553         }
554 }
555
556 static unsigned int
557 rdline_get_history_size(struct rdline * rdl)
558 {
559         unsigned int i, tmp, ret=0;
560
561         CIRBUF_FOREACH(&rdl->history, i, tmp) {
562                 if (tmp == 0)
563                         ret ++;
564         }
565
566         return ret;
567 }
568
569 char *
570 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
571 {
572         unsigned int len, i, tmp;
573
574         len = rdline_get_history_size(rdl);
575         if ( idx >= len ) {
576                 return NULL;
577         }
578
579         cirbuf_align_left(&rdl->history);
580
581         CIRBUF_FOREACH(&rdl->history, i, tmp) {
582                 if ( idx == len - 1) {
583                         return rdl->history_buf + i;
584                 }
585                 if (tmp == 0)
586                         len --;
587         }
588
589         return NULL;
590 }
591
592 int
593 rdline_add_history(struct rdline * rdl, const char * buf)
594 {
595         unsigned int len, i;
596
597         len = strnlen(buf, RDLINE_BUF_SIZE);
598         for (i=0; i<len ; i++) {
599                 if (buf[i] == '\n') {
600                         len = i;
601                         break;
602                 }
603         }
604
605         if ( len >= RDLINE_HISTORY_BUF_SIZE )
606                 return -1;
607
608         while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
609                 rdline_remove_old_history_item(rdl);
610         }
611
612         cirbuf_add_buf_tail(&rdl->history, buf, len);
613         cirbuf_add_tail(&rdl->history, 0);
614
615         return 0;
616 }
617
618 void
619 rdline_clear_history(struct rdline * rdl)
620 {
621         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
622 }
623
624 #else /* !NO_RDLINE_HISTORY */
625
626 int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
627 void rdline_clear_history(struct rdline * rdl) {}
628 char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
629
630
631 #endif /* !NO_RDLINE_HISTORY */
632
633
634 /* STATIC USEFUL FUNCS */
635
636 static void
637 rdline_puts(struct rdline * rdl, const char * buf)
638 {
639         char c;
640         while ( (c = *(buf++)) != '\0' ) {
641                 rdl->write_char(rdl, c);
642         }
643 }
644
645 /* a very very basic printf with one arg and one format 'u' */
646 static void
647 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
648 {
649         char c, started=0, div=100;
650
651         while ( (c=*(buf++)) ) {
652                 if (c != '%') {
653                         rdl->write_char(rdl, c);
654                         continue;
655                 }
656                 c = *(buf++);
657                 if (c != 'u') {
658                         rdl->write_char(rdl, '%');
659                         rdl->write_char(rdl, c);
660                         continue;
661                 }
662                 /* val is never more than 255 */
663                 while (div) {
664                         c = (char)(val / div);
665                         if (c || started) {
666                                 rdl->write_char(rdl, (char)(c+'0'));
667                                 started = 1;
668                         }
669                         val %= div;
670                         div /= 10;
671                 }
672         }
673 }
674