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