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