rdline: don't display prompt if stopped by user
[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_STOPPED;
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_STOPPED;
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)] = '\0';
478                         rdline_printf(rdl, "\r\n");
479 #ifndef NO_RDLINE_HISTORY
480                         if (rdl->history_cur_line != -1)
481                                 rdline_remove_first_history_item(rdl);
482 #endif
483
484                         if (rdl->validate)
485                                 rdl->validate(rdl, rdl->left_buf,
486                                               CIRBUF_GET_LEN(&rdl->left)+2);
487 #ifndef NO_PAGER
488                         /* user may have stopped rdline */
489                         if (rdl->status == RDLINE_EXITED) {
490                                 rdline_asyncpager_reset(rdl);
491                                 return RDLINE_RES_EXITED;
492                         }
493                         /* there is something in pager buffer, save
494                          * return value that will be return once
495                          * paging is finished */
496                         if (rdl->pager_buf != NULL) {
497                                 rdl->pager_ret = RDLINE_RES_VALIDATED;
498                                 return RDLINE_RES_SUCCESS;
499                         }
500
501                         rdline_asyncpager_reset(rdl);
502 #else
503                         if (rdl->status == RDLINE_EXITED)
504                                 return RDLINE_RES_EXITED;
505 #endif
506                         return RDLINE_RES_VALIDATED;
507                 }
508 #ifndef NO_RDLINE_HISTORY
509                 case CMDLINE_KEY_UP_ARR:
510                 case CMDLINE_KEY_CTRL_P:
511                         if (rdl->history_cur_line == 0) {
512                                 rdline_remove_first_history_item(rdl);
513                         }
514                         if (rdl->history_cur_line <= 0) {
515                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
516                                 rdl->history_cur_line = 0;
517                         }
518
519                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
520                         if (!buf)
521                                 break;
522
523                         rdl->history_cur_line ++;
524                         vt100_init(&rdl->vt100);
525                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
526                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
527                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
528                         rdline_redisplay(rdl);
529                         break;
530
531                 case CMDLINE_KEY_DOWN_ARR:
532                 case CMDLINE_KEY_CTRL_N:
533                         if (rdl->history_cur_line - 1 < 0)
534                                 break;
535
536                         rdl->history_cur_line --;
537                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
538                         if (!buf)
539                                 break;
540                         vt100_init(&rdl->vt100);
541                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
542                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
543                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
544                         rdline_redisplay(rdl);
545
546                         break;
547 #endif /* !NO_RDLINE_HISTORY */
548
549                 default:
550                         break;
551                 }
552
553                 return RDLINE_RES_SUCCESS;
554         }
555
556         if (!isprint((int)c))
557                 return RDLINE_RES_SUCCESS;
558
559         /* standard chars */
560         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
561                 return RDLINE_RES_SUCCESS;
562
563         if (cirbuf_add_tail_safe(&rdl->left, c))
564                 return RDLINE_RES_SUCCESS;
565
566         rdline_printf(rdl, "%c", c);
567         display_right_buffer(rdl, 0);
568
569         return RDLINE_RES_SUCCESS;
570 }
571
572 int
573 rdline_char_in(struct rdline *rdl, char c)
574 {
575         int ret, same = 0;
576         const char *history, *buffer;
577
578         if (rdl->status == RDLINE_EXITED)
579                 return RDLINE_RES_EXITED;
580         if (rdl->status != RDLINE_RUNNING)
581                 return RDLINE_RES_NOT_RUNNING;
582
583         ret = rdline_parse_char(rdl, c);
584
585         /* add line to history */
586         if (ret == RDLINE_RES_VALIDATED) {
587                 buffer = rdline_get_buffer(rdl);
588                 history = rdline_get_history_item(rdl, 0);
589                 if (history)
590                         same = !strcmp(buffer, history);
591
592                 if (strlen(buffer) >= 1 && same == 0)
593                         rdline_add_history(rdl, buffer);
594         }
595
596         return ret;
597 }
598
599 int
600 rdline(struct rdline *rdl, const char *prompt)
601 {
602         char c;
603         int ret = RDLINE_RES_NOT_RUNNING;
604
605         rdline_newline(rdl, prompt);
606         while (1) {
607                 if (read(rdl->fd_in, &c, 1) < 0)
608                         break;
609                 ret = rdline_char_in(rdl, c);
610                 if (ret != RDLINE_RES_SUCCESS)
611                         break;
612         }
613
614         return ret;
615 }
616
617 /* HISTORY */
618
619 #ifndef NO_RDLINE_HISTORY
620 static void
621 rdline_remove_old_history_item(struct rdline * rdl)
622 {
623         char tmp;
624
625         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
626                 tmp = cirbuf_get_head(&rdl->history);
627                 cirbuf_del_head(&rdl->history);
628                 if (!tmp)
629                         break;
630         }
631 }
632
633 static void
634 rdline_remove_first_history_item(struct rdline * rdl)
635 {
636         char tmp;
637
638         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
639                 return;
640         }
641         else {
642                 cirbuf_del_tail(&rdl->history);
643         }
644
645         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
646                 tmp = cirbuf_get_tail(&rdl->history);
647                 if (!tmp)
648                         break;
649                 cirbuf_del_tail(&rdl->history);
650         }
651 }
652
653 static unsigned int
654 rdline_get_history_size(struct rdline * rdl)
655 {
656         unsigned int i, tmp, ret=0;
657
658         CIRBUF_FOREACH(&rdl->history, i, tmp) {
659                 if (tmp == 0)
660                         ret ++;
661         }
662
663         return ret;
664 }
665
666 char *
667 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
668 {
669         unsigned int len, i, tmp;
670
671         len = rdline_get_history_size(rdl);
672         if ( idx >= len ) {
673                 return NULL;
674         }
675
676         cirbuf_align_left(&rdl->history);
677
678         CIRBUF_FOREACH(&rdl->history, i, tmp) {
679                 if ( idx == len - 1) {
680                         return rdl->history_buf + i;
681                 }
682                 if (tmp == 0)
683                         len --;
684         }
685
686         return NULL;
687 }
688
689 int
690 rdline_add_history(struct rdline * rdl, const char * buf)
691 {
692         unsigned int len;
693
694         len = strlen(buf);
695         if (len >= RDLINE_HISTORY_BUF_SIZE)
696                 return -1;
697
698         while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
699                 rdline_remove_old_history_item(rdl);
700         }
701
702         cirbuf_add_buf_tail(&rdl->history, buf, len);
703         cirbuf_add_tail(&rdl->history, 0);
704
705         return 0;
706 }
707
708 void
709 rdline_clear_history(struct rdline * rdl)
710 {
711         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
712 }
713
714 #else /* !NO_RDLINE_HISTORY */
715
716 int rdline_add_history(struct rdline * rdl, const char * buf)
717 {
718         return -1;
719 }
720
721 void rdline_clear_history(struct rdline * rdl)
722 {
723         return;
724 }
725
726 char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
727 {
728         return NULL;
729 }
730
731
732 #endif /* !NO_RDLINE_HISTORY */
733
734
735 ssize_t
736 rdline_write(const struct rdline *rdl, void *buf, size_t count)
737 {
738         return write(rdl->fd_out, buf, count);
739 }
740
741 int
742 rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
743 {
744         int ret;
745 #ifndef _GNU_SOURCE
746         char *buf;
747 #endif
748
749         if (rdl->fd_out < 0)
750                 return -1;
751
752 #ifdef _GNU_SOURCE
753         ret = vdprintf(rdl->fd_out, fmt, ap);
754 #else
755         buf = malloc(BUFSIZ);
756         if (buf == NULL)
757                 return -1;
758
759         ret = vsnprintf(buf, BUFSIZ, fmt, ap);
760
761         if (ret > 0)
762                 write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
763         free(buf);
764 #endif
765
766         return ret;
767 }
768
769 int
770 rdline_printf(const struct rdline *rdl, const char *fmt, ...)
771 {
772         va_list ap;
773         int ret;
774
775         va_start(ap, fmt);
776         ret = rdline_vprintf(rdl, fmt, ap);
777         va_end(ap);
778
779         return ret;
780 }
781
782 #ifndef NO_PAGER
783 /* reset pager state */
784 void
785 rdline_asyncpager_reset(struct rdline *rdl)
786 {
787         if (rdl->pager_buf) {
788                 free(rdl->pager_buf);
789                 rdl->pager_buf = NULL;
790         }
791         rdl->pager_lines = 0;
792         rdl->pager_len = 0;
793         rdl->pager_off = 0;
794         rdl->pager_cb = NULL;
795         rdl->pager_arg = NULL;
796         rdl->pager_ret = RDLINE_RES_SUCCESS;
797 }
798
799 /* Return the offset of the i-th occurence of char c in string s. If
800  * there is less than i occurences, return -1 and fill i with the
801  * count. */
802 static int
803 strnchr(const char *s, char c, int *i)
804 {
805         int n = 0;
806         const char *orig = s;
807
808         while (*s) {
809                 if (*s == c)
810                         n++;
811                 if (*i == n)
812                         return s - orig;
813                 s++;
814         }
815         *i = n;
816         return -1;
817 }
818
819 /* display a page of data from pager, return 0 if all is displayed */
820 static int
821 rdline_pager_next_page(struct rdline *rdl)
822 {
823         int lines = RDLINE_MAX_LINES;
824         int displen;
825         char *s;
826
827         s = rdl->pager_buf;
828         if (s == NULL)
829                 return 0;
830
831         rdline_printf(rdl, vt100_home);
832         rdline_printf(rdl, vt100_clear_right);
833
834         s += rdl->pager_off;
835
836         /* we know that s is 0-terminated */
837         displen = strnchr(s, '\n', &lines);
838         rdl->pager_lines = lines;
839
840         /* we can display all the data */
841         if (displen == -1) {
842                 write(rdl->fd_out, s, rdl->pager_len);
843                 free(rdl->pager_buf);
844                 rdl->pager_buf = NULL;
845                 return 0;
846         }
847
848         displen = displen + 1; /* include \n */
849         write(rdl->fd_out, s, displen);
850         rdl->pager_off += displen;
851         rdl->pager_len -= displen;
852
853         rdline_printf(rdl, "--- press a key to continue ---");
854         return -1;
855 }
856
857 /* push data in pager */
858 ssize_t
859 rdline_asyncpager_write(struct rdline *rdl, void *buf, size_t len)
860 {
861         char *s = buf;
862
863         /* display as many lines as we can */
864         if (rdl->pager_lines < RDLINE_MAX_LINES) {
865                 int lines = RDLINE_MAX_LINES - rdl->pager_lines;
866                 int displen;
867
868                 /* we know that s is 0-terminated */
869                 displen = strnchr(s, '\n', &lines);
870                 rdl->pager_lines += lines;
871
872                 /* we can display all the data */
873                 if (displen == -1) {
874                         write(rdl->fd_out, s, len);
875                         return 0;
876                 }
877                 displen = displen + 1; /* include \n */
878                 write(rdl->fd_out, s, displen);
879                 s += displen;
880                 len -= displen;
881         }
882
883         if (rdl->pager_buf == NULL) {
884                 rdline_printf(rdl, "--- press a key to continue ---");
885         }
886         rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len);
887         if (rdl->pager_buf == NULL) {
888                 rdline_asyncpager_reset(rdl);
889                 return -1;
890         }
891
892         memcpy(rdl->pager_buf + rdl->pager_len, s, len);
893         rdl->pager_len += len;
894         return 0;
895 }
896
897 /* Print data asynchronously (using pager if needed) */
898 int
899 rdline_asyncpager_printf(struct rdline *rdl, const char *fmt, ...)
900 {
901         int n;
902         char *buf;
903         va_list ap;
904
905         if (rdl->fd_out < 0)
906                 return -1;
907
908         buf = malloc(BUFSIZ);
909         if (buf == NULL)
910                 return -1;
911
912         va_start(ap, fmt);
913         n = vsnprintf(buf, BUFSIZ, fmt, ap);
914         va_end(ap);
915
916         if (n >= BUFSIZ)
917                 n = BUFSIZ-1;
918         if (n > 0)
919                 rdline_asyncpager_write(rdl, buf, n);
920         free(buf);
921         return n;
922 }
923
924 int rdline_asyncpager_set_cb(struct rdline *rdl, rdline_asyncpager_cb_t *cb,
925                              void *arg)
926 {
927         if (rdl->pager_buf == NULL)
928                 return -1;
929
930         rdl->pager_cb = cb;
931         rdl->pager_arg = arg;
932         return 0;
933 }
934 #endif /* !NO_PAGER */