5ff851bedc04e5481d642c29b50138aa23f4cf23
[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 #ifndef NO_PAGER
81 static int rdline_pager_next_page(struct rdline *rdl);
82 static void rdline_asyncpager_reset(struct rdline *rdl);
83 #endif /* !NO_PAGER */
84
85
86 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
87  * own. */
88 static int
89 isblank2(char c)
90 {
91         if (c == ' ' ||
92             c == '\t' )
93                 return 1;
94         return 0;
95 }
96
97 void
98 rdline_init(struct rdline *rdl,
99             int fd_in, int fd_out,
100             rdline_validate_t *validate,
101             rdline_complete_t *complete,
102             rdline_help_t *help)
103 {
104         memset(rdl, 0, sizeof(*rdl));
105         rdl->fd_in = fd_in;
106         rdl->fd_out = fd_out;
107         rdl->validate = validate;
108         rdl->complete = complete;
109         rdl->help = help;
110         rdl->status = RDLINE_INIT;
111 #ifndef NO_RDLINE_HISTORY
112         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
113 #endif /* !NO_RDLINE_HISTORY */
114 }
115
116 void
117 rdline_newline(struct rdline *rdl, const char *prompt)
118 {
119         vt100_init(&rdl->vt100);
120         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
121         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
122
123         /* if pointer is the same, don't copy it */
124         if (prompt != rdl->prompt)
125                 snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
126
127         rdline_printf(rdl, "%s", rdl->prompt);
128         rdl->status = RDLINE_RUNNING;
129
130 #ifndef NO_RDLINE_HISTORY
131         rdl->history_cur_line = -1;
132 #endif /* !NO_RDLINE_HISTORY */
133 }
134
135 void
136 rdline_stop(struct rdline *rdl)
137 {
138         rdl->status = RDLINE_INIT;
139 }
140
141 void
142 rdline_quit(struct rdline *rdl)
143 {
144         rdl->status = RDLINE_EXITED;
145 }
146
147 void
148 rdline_restart(struct rdline *rdl)
149 {
150         rdl->status = RDLINE_RUNNING;
151 }
152
153 const char *
154 rdline_get_buffer(struct rdline *rdl)
155 {
156         unsigned int len_l, len_r;
157         cirbuf_align_left(&rdl->left);
158         cirbuf_align_left(&rdl->right);
159
160         len_l = CIRBUF_GET_LEN(&rdl->left);
161         len_r = CIRBUF_GET_LEN(&rdl->right);
162         memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
163
164         rdl->left_buf[len_l + len_r] = '\0';
165         return rdl->left_buf;
166 }
167
168 static void
169 display_right_buffer(struct rdline *rdl, int force)
170 {
171         unsigned int i;
172         char tmp;
173
174         if (!force && CIRBUF_IS_EMPTY(&rdl->right))
175                 return;
176
177         rdline_printf(rdl, vt100_clear_right);
178         CIRBUF_FOREACH(&rdl->right, i, tmp) {
179                 rdline_printf(rdl, "%c", tmp);
180         }
181         if (!CIRBUF_IS_EMPTY(&rdl->right))
182                 rdline_printf(rdl, vt100_multi_left,
183                               CIRBUF_GET_LEN(&rdl->right));
184 }
185
186 void
187 rdline_redisplay(struct rdline *rdl)
188 {
189         unsigned int i;
190         char tmp;
191
192         rdline_printf(rdl, vt100_home);
193         rdline_printf(rdl, "%s", rdl->prompt);
194         CIRBUF_FOREACH(&rdl->left, i, tmp) {
195                 rdline_printf(rdl, "%c", tmp);
196         }
197         display_right_buffer(rdl, 1);
198 }
199
200 static int
201 rdline_parse_char(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         cmd = vt100_parser(&rdl->vt100, c);
211         if (cmd == VT100_NOT_COMPLETE)
212                 return RDLINE_RES_SUCCESS;
213
214 #ifndef NO_PAGER
215         /* display asynchrounous printf if any */
216         if (rdl->pager_buf != NULL) {
217                 /* user ask to exit pager */
218                 if (cmd == VT100_STD_CHAR && c == 'q') {
219                         rdline_asyncpager_reset(rdl);
220                         if (rdl->pager_cb != NULL) {
221                                 rdl->pager_cb(rdl, rdl->pager_arg);
222                                 rdl->pager_cb = NULL;
223                         }
224                         rdline_redisplay(rdl);
225                         return RDLINE_RES_SUCCESS;
226                 }
227
228                 /* last page is displayed */
229                 else if (rdline_pager_next_page(rdl) == 0) {
230                         rdline_asyncpager_reset(rdl);
231                         if (rdl->pager_cb != NULL) {
232                                 rdl->pager_cb(rdl, rdl->pager_arg);
233                                 rdl->pager_cb = NULL;
234                         }
235                         rdline_redisplay(rdl);
236                 }
237
238                 /* pages remain, async printf was called in cb() */
239                 return RDLINE_RES_SUCCESS;
240         }
241 #endif
242
243         /* process control chars */
244         if (cmd != VT100_STD_CHAR) {
245                 switch (cmd) {
246                 case CMDLINE_KEY_CTRL_B:
247                 case CMDLINE_KEY_LEFT_ARR:
248                         if (CIRBUF_IS_EMPTY(&rdl->left))
249                                 break;
250                         tmp = cirbuf_get_tail(&rdl->left);
251                         cirbuf_del_tail(&rdl->left);
252                         cirbuf_add_head(&rdl->right, tmp);
253                         rdline_printf(rdl, vt100_left_arr);
254                         break;
255
256                 case CMDLINE_KEY_CTRL_F:
257                 case CMDLINE_KEY_RIGHT_ARR:
258                         if (CIRBUF_IS_EMPTY(&rdl->right))
259                                 break;
260                         tmp = cirbuf_get_head(&rdl->right);
261                         cirbuf_del_head(&rdl->right);
262                         cirbuf_add_tail(&rdl->left, tmp);
263                         rdline_printf(rdl, vt100_right_arr);
264                         break;
265
266                 case CMDLINE_KEY_WLEFT:
267                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
268                                (tmp = cirbuf_get_tail(&rdl->left)) &&
269                                isblank2(tmp)) {
270                                 rdline_printf(rdl, vt100_left_arr);
271                                 cirbuf_del_tail(&rdl->left);
272                                 cirbuf_add_head(&rdl->right, tmp);
273                         }
274                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
275                                (tmp = cirbuf_get_tail(&rdl->left)) &&
276                                !isblank2(tmp)) {
277                                 rdline_printf(rdl, vt100_left_arr);
278                                 cirbuf_del_tail(&rdl->left);
279                                 cirbuf_add_head(&rdl->right, tmp);
280                         }
281                         break;
282
283                 case CMDLINE_KEY_WRIGHT:
284                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
285                                (tmp = cirbuf_get_head(&rdl->right)) &&
286                                isblank2(tmp)) {
287                                 rdline_printf(rdl, vt100_right_arr);
288                                 cirbuf_del_head(&rdl->right);
289                                 cirbuf_add_tail(&rdl->left, tmp);
290                         }
291                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
292                                (tmp = cirbuf_get_head(&rdl->right)) &&
293                                !isblank2(tmp)) {
294                                 rdline_printf(rdl, vt100_right_arr);
295                                 cirbuf_del_head(&rdl->right);
296                                 cirbuf_add_tail(&rdl->left, tmp);
297                         }
298                         break;
299
300                 case CMDLINE_KEY_BKSPACE:
301                         if(!cirbuf_del_tail_safe(&rdl->left)) {
302                                 rdline_printf(rdl, vt100_bs);
303                                 display_right_buffer(rdl, 1);
304                         }
305                         break;
306
307                 case CMDLINE_KEY_META_BKSPACE:
308                 case CMDLINE_KEY_CTRL_W:
309                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
310                                isblank2(cirbuf_get_tail(&rdl->left))) {
311                                 rdline_printf(rdl, vt100_bs);
312                                 cirbuf_del_tail(&rdl->left);
313                         }
314                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
315                                !isblank2(cirbuf_get_tail(&rdl->left))) {
316                                 rdline_printf(rdl, vt100_bs);
317                                 cirbuf_del_tail(&rdl->left);
318                         }
319                         display_right_buffer(rdl, 1);
320                         break;
321
322                 case CMDLINE_KEY_META_D:
323                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
324                                isblank2(cirbuf_get_head(&rdl->right)))
325                                 cirbuf_del_head(&rdl->right);
326                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
327                                !isblank2(cirbuf_get_head(&rdl->right)))
328                                 cirbuf_del_head(&rdl->right);
329                         display_right_buffer(rdl, 1);
330                         break;
331
332                 case CMDLINE_KEY_SUPPR:
333                 case CMDLINE_KEY_CTRL_D:
334                         if (cmd == CMDLINE_KEY_CTRL_D &&
335                             CIRBUF_IS_EMPTY(&rdl->left) &&
336                             CIRBUF_IS_EMPTY(&rdl->right)) {
337                                 return RDLINE_RES_EOF;
338                         }
339                         if (!cirbuf_del_head_safe(&rdl->right)) {
340                                 display_right_buffer(rdl, 1);
341                         }
342                         break;
343
344                 case CMDLINE_KEY_CTRL_A:
345                         if (CIRBUF_IS_EMPTY(&rdl->left))
346                                 break;
347                         rdline_printf(rdl, vt100_multi_left,
348                                       CIRBUF_GET_LEN(&rdl->left));
349                         while (! CIRBUF_IS_EMPTY(&rdl->left)) {
350                                 tmp = cirbuf_get_tail(&rdl->left);
351                                 cirbuf_del_tail(&rdl->left);
352                                 cirbuf_add_head(&rdl->right, tmp);
353                         }
354                         break;
355
356                 case CMDLINE_KEY_CTRL_E:
357                         if (CIRBUF_IS_EMPTY(&rdl->right))
358                                 break;
359                         rdline_printf(rdl, vt100_multi_right,
360                                       CIRBUF_GET_LEN(&rdl->right));
361                         while (! CIRBUF_IS_EMPTY(&rdl->right)) {
362                                 tmp = cirbuf_get_head(&rdl->right);
363                                 cirbuf_del_head(&rdl->right);
364                                 cirbuf_add_tail(&rdl->left, tmp);
365                         }
366                         break;
367
368 #ifndef NO_RDLINE_KILL_BUF
369                 case CMDLINE_KEY_CTRL_K:
370                         cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
371                         rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
372                         cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
373                         rdline_printf(rdl, vt100_clear_right);
374                         break;
375
376                 case CMDLINE_KEY_CTRL_Y:
377                         i=0;
378                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
379                               RDLINE_BUF_SIZE &&
380                               i < rdl->kill_size) {
381                                 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
382                                 rdline_printf(rdl, "%c", rdl->kill_buf[i]);
383                                 i++;
384                         }
385                         display_right_buffer(rdl, 0);
386                         break;
387 #endif /* !NO_RDLINE_KILL_BUF */
388
389                 case CMDLINE_KEY_CTRL_C:
390                         rdline_printf(rdl, "\r\n");
391                         rdline_newline(rdl, rdl->prompt);
392                         break;
393
394                 case CMDLINE_KEY_CTRL_L:
395                         rdline_redisplay(rdl);
396                         break;
397
398                 case CMDLINE_KEY_HELP: {
399                         if (rdl->help == NULL)
400                                 break;
401
402                         cirbuf_align_left(&rdl->left);
403                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
404                         rdline_printf(rdl, "\r\n");
405 #ifndef NO_PAGER
406                         rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write, rdl);
407                         if (rdl->pager_buf != NULL)
408                                 return RDLINE_RES_SUCCESS;
409                         else
410                                 rdline_asyncpager_reset(rdl);
411 #else
412                         rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
413 #endif
414                         rdline_redisplay(rdl);
415                         break;
416                 }
417
418                 case CMDLINE_KEY_TAB: {
419                         char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
420                         int ret;
421                         unsigned int tmp_size;
422
423                         if (rdl->complete == NULL)
424                                 break;
425
426                         cirbuf_align_left(&rdl->left);
427                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
428
429                         /* see in parse.h for help on complete() */
430                         ret = rdl->complete(rdl, rdl->left_buf,
431                                             tmp_buf, sizeof(tmp_buf));
432                         /* no completion or error */
433                         if (ret == CMDLINE_COMPLETE_NONE ||
434                             ret == CMDLINE_COMPLETE_MANY) {
435                                 cirbuf_align_left(&rdl->left);
436                                 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
437                                 rdline_printf(rdl, "\r\n");
438 #ifndef NO_PAGER
439                                 rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write,
440                                           rdl);
441                                 if (rdl->pager_buf != NULL)
442                                         return RDLINE_RES_SUCCESS;
443                                 else
444                                         rdline_asyncpager_reset(rdl);
445 #else
446                                 rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
447 #endif
448                                 rdline_redisplay(rdl);
449                                 break;
450                         }
451
452                         tmp_size = strlen(tmp_buf);
453                         /* add chars */
454                         i = 0;
455                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
456                               RDLINE_BUF_SIZE &&
457                               i < tmp_size) {
458                                 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
459                                 rdline_printf(rdl, "%c", tmp_buf[i]);
460                                 i++;
461                         }
462                         display_right_buffer(rdl, 1);
463                         break;
464                 }
465
466                 case CMDLINE_KEY_RETURN:
467                 case CMDLINE_KEY_RETURN2: {
468                         char tmp;
469                         while (!CIRBUF_IS_EMPTY(&rdl->right) &&
470                                (tmp = cirbuf_get_head(&rdl->right))) {
471                                 cirbuf_del_head(&rdl->right);
472                                 cirbuf_add_tail(&rdl->left, tmp);
473                         }
474                         cirbuf_align_left(&rdl->left);
475                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\n';
476                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left) + 1] = '\0';
477                         rdline_printf(rdl, "\r\n");
478 #ifndef NO_RDLINE_HISTORY
479                         if (rdl->history_cur_line != -1)
480                                 rdline_remove_first_history_item(rdl);
481 #endif
482
483                         if (rdl->validate)
484                                 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
485 #ifndef NO_PAGER
486                         /* user may have stopped rdline */
487                         if (rdl->status == RDLINE_EXITED) {
488                                 rdline_asyncpager_reset(rdl);
489                                 return RDLINE_RES_EXITED;
490                         }
491                         if (rdl->pager_buf != NULL)
492                                 return RDLINE_RES_SUCCESS;
493                         else
494                                 rdline_asyncpager_reset(rdl);
495
496                         rdl->status = RDLINE_INIT;
497 #else
498                         if (rdl->status == RDLINE_EXITED)
499                                 return RDLINE_RES_EXITED;
500 #endif
501                         return RDLINE_RES_VALIDATED;
502                 }
503 #ifndef NO_RDLINE_HISTORY
504                 case CMDLINE_KEY_UP_ARR:
505                 case CMDLINE_KEY_CTRL_P:
506                         if (rdl->history_cur_line == 0) {
507                                 rdline_remove_first_history_item(rdl);
508                         }
509                         if (rdl->history_cur_line <= 0) {
510                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
511                                 rdl->history_cur_line = 0;
512                         }
513
514                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
515                         if (!buf)
516                                 break;
517
518                         rdl->history_cur_line ++;
519                         vt100_init(&rdl->vt100);
520                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
521                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
522                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
523                         rdline_redisplay(rdl);
524                         break;
525
526                 case CMDLINE_KEY_DOWN_ARR:
527                 case CMDLINE_KEY_CTRL_N:
528                         if (rdl->history_cur_line - 1 < 0)
529                                 break;
530
531                         rdl->history_cur_line --;
532                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
533                         if (!buf)
534                                 break;
535                         vt100_init(&rdl->vt100);
536                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
537                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
538                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
539                         rdline_redisplay(rdl);
540
541                         break;
542 #endif /* !NO_RDLINE_HISTORY */
543
544                 default:
545                         break;
546                 }
547
548                 return RDLINE_RES_SUCCESS;
549         }
550
551         if (!isprint((int)c))
552                 return RDLINE_RES_SUCCESS;
553
554         /* standard chars */
555         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
556                 return RDLINE_RES_SUCCESS;
557
558         if (cirbuf_add_tail_safe(&rdl->left, c))
559                 return RDLINE_RES_SUCCESS;
560
561         rdline_printf(rdl, "%c", c);
562         display_right_buffer(rdl, 0);
563
564         return RDLINE_RES_SUCCESS;
565 }
566
567 int
568 rdline_char_in(struct rdline *rdl, char c)
569 {
570         int ret, same = 0;
571         const char *history, *buffer;
572
573         if (rdl->status == RDLINE_EXITED)
574                 return RDLINE_RES_EXITED;
575         if (rdl->status != RDLINE_RUNNING)
576                 return RDLINE_RES_NOT_RUNNING;
577
578         ret = rdline_parse_char(rdl, c);
579
580         /* add line to history */
581         if (ret == RDLINE_RES_VALIDATED) {
582                 buffer = rdline_get_buffer(rdl);
583                 history = rdline_get_history_item(rdl, 0);
584                 if (history)
585                         same = !strcmp(buffer, history);
586
587                 if (strlen(buffer) >= 1 && same == 0)
588                         rdline_add_history(rdl, buffer);
589         }
590
591         return ret;
592 }
593
594 int
595 rdline(struct rdline *rdl, const char *prompt)
596 {
597         char c;
598         int ret = RDLINE_RES_NOT_RUNNING;
599
600         rdline_newline(rdl, prompt);
601         while (1) {
602                 if (read(rdl->fd_in, &c, 1) < 0)
603                         break;
604                 ret = rdline_char_in(rdl, c);
605                 if (ret != RDLINE_RES_SUCCESS)
606                         break;
607         }
608
609         return ret;
610 }
611
612 /* HISTORY */
613
614 #ifndef NO_RDLINE_HISTORY
615 static void
616 rdline_remove_old_history_item(struct rdline * rdl)
617 {
618         char tmp;
619
620         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
621                 tmp = cirbuf_get_head(&rdl->history);
622                 cirbuf_del_head(&rdl->history);
623                 if (!tmp)
624                         break;
625         }
626 }
627
628 static void
629 rdline_remove_first_history_item(struct rdline * rdl)
630 {
631         char tmp;
632
633         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
634                 return;
635         }
636         else {
637                 cirbuf_del_tail(&rdl->history);
638         }
639
640         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
641                 tmp = cirbuf_get_tail(&rdl->history);
642                 if (!tmp)
643                         break;
644                 cirbuf_del_tail(&rdl->history);
645         }
646 }
647
648 static unsigned int
649 rdline_get_history_size(struct rdline * rdl)
650 {
651         unsigned int i, tmp, ret=0;
652
653         CIRBUF_FOREACH(&rdl->history, i, tmp) {
654                 if (tmp == 0)
655                         ret ++;
656         }
657
658         return ret;
659 }
660
661 char *
662 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
663 {
664         unsigned int len, i, tmp;
665
666         len = rdline_get_history_size(rdl);
667         if ( idx >= len ) {
668                 return NULL;
669         }
670
671         cirbuf_align_left(&rdl->history);
672
673         CIRBUF_FOREACH(&rdl->history, i, tmp) {
674                 if ( idx == len - 1) {
675                         return rdl->history_buf + i;
676                 }
677                 if (tmp == 0)
678                         len --;
679         }
680
681         return NULL;
682 }
683
684 int
685 rdline_add_history(struct rdline * rdl, const char * buf)
686 {
687         unsigned int len;
688
689         len = strlen(buf);
690         if (len >= RDLINE_HISTORY_BUF_SIZE)
691                 return -1;
692
693         while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
694                 rdline_remove_old_history_item(rdl);
695         }
696
697         cirbuf_add_buf_tail(&rdl->history, buf, len);
698         cirbuf_add_tail(&rdl->history, 0);
699
700         return 0;
701 }
702
703 void
704 rdline_clear_history(struct rdline * rdl)
705 {
706         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
707 }
708
709 #else /* !NO_RDLINE_HISTORY */
710
711 int rdline_add_history(struct rdline * rdl, const char * buf)
712 {
713         return -1;
714 }
715
716 void rdline_clear_history(struct rdline * rdl)
717 {
718         return;
719 }
720
721 char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
722 {
723         return NULL;
724 }
725
726
727 #endif /* !NO_RDLINE_HISTORY */
728
729
730 ssize_t
731 rdline_write(const struct rdline *rdl, void *buf, size_t count)
732 {
733         return write(rdl->fd_out, buf, count);
734 }
735
736 int
737 rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
738 {
739         int ret;
740 #ifndef _GNU_SOURCE
741         char *buf;
742 #endif
743
744         if (rdl->fd_out < 0)
745                 return -1;
746
747 #ifdef _GNU_SOURCE
748         ret = vdprintf(rdl->fd_out, fmt, ap);
749 #else
750         buf = malloc(BUFSIZ);
751         if (buf == NULL)
752                 return -1;
753
754         ret = vsnprintf(buf, BUFSIZ, fmt, ap);
755
756         if (ret > 0)
757                 write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
758         free(buf);
759 #endif
760
761         return ret;
762 }
763
764 int
765 rdline_printf(const struct rdline *rdl, const char *fmt, ...)
766 {
767         va_list ap;
768         int ret;
769
770         va_start(ap, fmt);
771         ret = rdline_vprintf(rdl, fmt, ap);
772         va_end(ap);
773
774         return ret;
775 }
776
777 #ifndef NO_PAGER
778 /* reset pager state */
779 void
780 rdline_asyncpager_reset(struct rdline *rdl)
781 {
782         if (rdl->pager_buf) {
783                 free(rdl->pager_buf);
784                 rdl->pager_buf = NULL;
785         }
786         rdl->pager_lines = 0;
787         rdl->pager_len = 0;
788         rdl->pager_off = 0;
789 }
790
791 /* Return the offset of the i-th occurence of char c in string s. If
792  * there is less than i occurences, return -1 and fill i with the
793  * count. */
794 static int
795 strnchr(const char *s, char c, int *i)
796 {
797         int n = 0;
798         const char *orig = s;
799
800         while (*s) {
801                 if (*s == c)
802                         n++;
803                 if (*i == n)
804                         return s - orig;
805                 s++;
806         }
807         *i = n;
808         return -1;
809 }
810
811 /* display a page of data from pager, return 0 if all is displayed */
812 static int
813 rdline_pager_next_page(struct rdline *rdl)
814 {
815         int lines = RDLINE_MAX_LINES;
816         int displen;
817         char *s;
818
819         s = rdl->pager_buf;
820         if (s == NULL)
821                 return 0;
822
823         rdline_printf(rdl, vt100_home);
824         rdline_printf(rdl, vt100_clear_right);
825
826         s += rdl->pager_off;
827
828         /* we know that s is 0-terminated */
829         displen = strnchr(s, '\n', &lines);
830         rdl->pager_lines = lines;
831
832         /* we can display all the data */
833         if (displen == -1) {
834                 write(rdl->fd_out, s, rdl->pager_len);
835                 free(rdl->pager_buf);
836                 rdl->pager_buf = NULL;
837                 return 0;
838         }
839
840         displen = displen + 1; /* include \n */
841         write(rdl->fd_out, s, displen);
842         rdl->pager_off += displen;
843         rdl->pager_len -= displen;
844
845         rdline_printf(rdl, "--- press a key to continue ---");
846         return -1;
847 }
848
849 /* push data in pager */
850 ssize_t
851 rdline_asyncpager_write(struct rdline *rdl, void *buf, size_t len)
852 {
853         char *s = buf;
854
855         /* display as many lines as we can */
856         if (rdl->pager_lines < RDLINE_MAX_LINES) {
857                 int lines = RDLINE_MAX_LINES - rdl->pager_lines;
858                 int displen;
859
860                 /* we know that s is 0-terminated */
861                 displen = strnchr(s, '\n', &lines);
862                 rdl->pager_lines += lines;
863
864                 /* we can display all the data */
865                 if (displen == -1) {
866                         write(rdl->fd_out, s, len);
867                         return 0;
868                 }
869                 displen = displen + 1; /* include \n */
870                 write(rdl->fd_out, s, displen);
871                 s += displen;
872                 len -= displen;
873         }
874
875         if (rdl->pager_buf == NULL) {
876                 rdline_printf(rdl, "--- press a key to continue ---");
877         }
878         rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len);
879         if (rdl->pager_buf == NULL) {
880                 rdline_asyncpager_reset(rdl);
881                 return -1;
882         }
883
884         memcpy(rdl->pager_buf + rdl->pager_len, s, len);
885         rdl->pager_len += len;
886         return 0;
887 }
888
889 /* Print data asynchronously (using pager if needed) */
890 int
891 rdline_asyncpager_printf(struct rdline *rdl, const char *fmt, ...)
892 {
893         int n;
894         char *buf;
895         va_list ap;
896
897         if (rdl->fd_out < 0)
898                 return -1;
899
900         buf = malloc(BUFSIZ);
901         if (buf == NULL)
902                 return -1;
903
904         va_start(ap, fmt);
905         n = vsnprintf(buf, BUFSIZ, fmt, ap);
906         va_end(ap);
907
908         if (n >= BUFSIZ)
909                 n = BUFSIZ-1;
910         if (n > 0)
911                 rdline_asyncpager_write(rdl, buf, n);
912         free(buf);
913         return n;
914 }
915
916 int rdline_asyncpager_set_cb(struct rdline *rdl, rdline_asyncpager_cb_t *cb,
917                              void *arg)
918 {
919         if (rdl->pager_buf == NULL)
920                 return -1;
921
922         rdl->pager_cb = cb;
923         rdl->pager_arg = arg;
924         return 0;
925 }
926 #endif /* !NO_PAGER */