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