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