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