5632a872a4d3052221888687d99ae123b248f690
[libcmdline.git] / src / lib / cmdline_rdline.c
1 /*-
2  * Copyright (c) <2010>, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in
14  *   the documentation and/or other materials provided with the
15  *   distribution.
16  *
17  * - Neither the name of Intel Corporation nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
32  * OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 /*
36  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
37  * All rights reserved.
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions are met:
40  *
41  *     * Redistributions of source code must retain the above copyright
42  *       notice, this list of conditions and the following disclaimer.
43  *     * Redistributions in binary form must reproduce the above copyright
44  *       notice, this list of conditions and the following disclaimer in the
45  *       documentation and/or other materials provided with the distribution.
46  *     * Neither the name of the University of California, Berkeley nor the
47  *       names of its contributors may be used to endorse or promote products
48  *       derived from this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
51  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
54  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
55  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
57  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60  */
61
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <stdint.h>
65 #include <string.h>
66 #include <stdarg.h>
67 #include <ctype.h>
68 #include <unistd.h>
69
70 #include "cmdline_cirbuf.h"
71 #include "cmdline_rdline.h"
72 #include "cmdline_parse.h"
73
74 #ifndef NO_RDLINE_HISTORY
75 static void rdline_remove_old_history_item(struct rdline *rdl);
76 static void rdline_remove_first_history_item(struct rdline *rdl);
77 static unsigned int rdline_get_history_size(struct rdline *rdl);
78 #endif /* !NO_RDLINE_HISTORY */
79
80 #ifndef NO_PAGER
81 static int rdline_pager_next_page(struct rdline *rdl);
82 static void rdline_asyncpager_reset(struct rdline *rdl);
83 #endif /* !NO_PAGER */
84
85
86 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
87  * own. */
88 static int
89 isblank2(char c)
90 {
91         if (c == ' ' ||
92             c == '\t' )
93                 return 1;
94         return 0;
95 }
96
97 void
98 rdline_init(struct rdline *rdl,
99             int fd_in, int fd_out,
100             rdline_validate_t *validate,
101             rdline_complete_t *complete,
102             rdline_help_t *help)
103 {
104         memset(rdl, 0, sizeof(*rdl));
105         rdl->fd_in = fd_in;
106         rdl->fd_out = fd_out;
107         rdl->validate = validate;
108         rdl->complete = complete;
109         rdl->help = help;
110         rdl->status = RDLINE_INIT;
111 #ifndef NO_RDLINE_HISTORY
112         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
113 #endif /* !NO_RDLINE_HISTORY */
114 }
115
116 void
117 rdline_newline(struct rdline *rdl, const char *prompt)
118 {
119         vt100_init(&rdl->vt100);
120         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
121         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
122
123         /* if pointer is the same, don't copy it */
124         if (prompt != rdl->prompt)
125                 snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
126
127         rdline_printf(rdl, "%s", rdl->prompt);
128         rdl->status = RDLINE_RUNNING;
129
130 #ifndef NO_RDLINE_HISTORY
131         rdl->history_cur_line = -1;
132 #endif /* !NO_RDLINE_HISTORY */
133 }
134
135 void
136 rdline_stop(struct rdline *rdl)
137 {
138         rdl->status = RDLINE_INIT;
139 }
140
141 void
142 rdline_quit(struct rdline *rdl)
143 {
144         rdl->status = RDLINE_EXITED;
145 }
146
147 void
148 rdline_restart(struct rdline *rdl)
149 {
150         rdl->status = RDLINE_RUNNING;
151 }
152
153 const char *
154 rdline_get_buffer(struct rdline *rdl)
155 {
156         unsigned int len_l, len_r;
157         cirbuf_align_left(&rdl->left);
158         cirbuf_align_left(&rdl->right);
159
160         len_l = CIRBUF_GET_LEN(&rdl->left);
161         len_r = CIRBUF_GET_LEN(&rdl->right);
162         memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
163
164         rdl->left_buf[len_l + len_r] = '\0';
165         return rdl->left_buf;
166 }
167
168 static void
169 display_right_buffer(struct rdline *rdl, int force)
170 {
171         unsigned int i;
172         char tmp;
173
174         if (!force && CIRBUF_IS_EMPTY(&rdl->right))
175                 return;
176
177         rdline_printf(rdl, vt100_clear_right);
178         CIRBUF_FOREACH(&rdl->right, i, tmp) {
179                 rdline_printf(rdl, "%c", tmp);
180         }
181         if (!CIRBUF_IS_EMPTY(&rdl->right))
182                 rdline_printf(rdl, vt100_multi_left,
183                               CIRBUF_GET_LEN(&rdl->right));
184 }
185
186 void
187 rdline_redisplay(struct rdline *rdl)
188 {
189         unsigned int i;
190         char tmp;
191
192         rdline_printf(rdl, vt100_home);
193         rdline_printf(rdl, "%s", rdl->prompt);
194         CIRBUF_FOREACH(&rdl->left, i, tmp) {
195                 rdline_printf(rdl, "%c", tmp);
196         }
197         display_right_buffer(rdl, 1);
198 }
199
200 static int
201 rdline_parse_char(struct rdline *rdl, char c)
202 {
203         unsigned int i;
204         int cmd;
205         char tmp;
206 #ifndef NO_RDLINE_HISTORY
207         char *buf;
208 #endif
209
210         cmd = vt100_parser(&rdl->vt100, c);
211         if (cmd == VT100_NOT_COMPLETE)
212                 return RDLINE_RES_SUCCESS;
213
214 #ifndef NO_PAGER
215         /* display asynchrounous printf if any */
216         if (rdl->pager_buf != NULL) {
217
218                 /* user ask to exit pager, or last page is displayed*/
219                 if ((cmd == VT100_STD_CHAR && c == 'q') ||
220                     rdline_pager_next_page(rdl) == 0) {
221                         int ret;
222
223                         ret = rdl->pager_ret;
224                         rdline_asyncpager_reset(rdl);
225                         if (rdl->pager_cb != NULL) {
226                                 rdl->pager_cb(rdl, rdl->pager_arg);
227                                 rdl->pager_cb = NULL;
228                         }
229                         /* maybe the pager was reloaded in the
230                          * callback */
231                         if (rdl->pager_buf != NULL)
232                                 return RDLINE_RES_SUCCESS;
233
234                         /* else, redisplay prompt and return the saved status */
235                         rdline_redisplay(rdl);
236                         return ret;
237                 }
238
239                 /* Some pages remain, lines were displayed in
240                  * rdline_pager_next_page() */
241                 return RDLINE_RES_SUCCESS;
242         }
243 #endif
244
245         /* process control chars */
246         if (cmd != VT100_STD_CHAR) {
247                 switch (cmd) {
248                 case CMDLINE_KEY_CTRL_B:
249                 case CMDLINE_KEY_LEFT_ARR:
250                         if (CIRBUF_IS_EMPTY(&rdl->left))
251                                 break;
252                         tmp = cirbuf_get_tail(&rdl->left);
253                         cirbuf_del_tail(&rdl->left);
254                         cirbuf_add_head(&rdl->right, tmp);
255                         rdline_printf(rdl, vt100_left_arr);
256                         break;
257
258                 case CMDLINE_KEY_CTRL_F:
259                 case CMDLINE_KEY_RIGHT_ARR:
260                         if (CIRBUF_IS_EMPTY(&rdl->right))
261                                 break;
262                         tmp = cirbuf_get_head(&rdl->right);
263                         cirbuf_del_head(&rdl->right);
264                         cirbuf_add_tail(&rdl->left, tmp);
265                         rdline_printf(rdl, vt100_right_arr);
266                         break;
267
268                 case CMDLINE_KEY_WLEFT:
269                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
270                                (tmp = cirbuf_get_tail(&rdl->left)) &&
271                                isblank2(tmp)) {
272                                 rdline_printf(rdl, vt100_left_arr);
273                                 cirbuf_del_tail(&rdl->left);
274                                 cirbuf_add_head(&rdl->right, tmp);
275                         }
276                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
277                                (tmp = cirbuf_get_tail(&rdl->left)) &&
278                                !isblank2(tmp)) {
279                                 rdline_printf(rdl, vt100_left_arr);
280                                 cirbuf_del_tail(&rdl->left);
281                                 cirbuf_add_head(&rdl->right, tmp);
282                         }
283                         break;
284
285                 case CMDLINE_KEY_WRIGHT:
286                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
287                                (tmp = cirbuf_get_head(&rdl->right)) &&
288                                isblank2(tmp)) {
289                                 rdline_printf(rdl, vt100_right_arr);
290                                 cirbuf_del_head(&rdl->right);
291                                 cirbuf_add_tail(&rdl->left, tmp);
292                         }
293                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
294                                (tmp = cirbuf_get_head(&rdl->right)) &&
295                                !isblank2(tmp)) {
296                                 rdline_printf(rdl, vt100_right_arr);
297                                 cirbuf_del_head(&rdl->right);
298                                 cirbuf_add_tail(&rdl->left, tmp);
299                         }
300                         break;
301
302                 case CMDLINE_KEY_BKSPACE:
303                         if(!cirbuf_del_tail_safe(&rdl->left)) {
304                                 rdline_printf(rdl, vt100_bs);
305                                 display_right_buffer(rdl, 1);
306                         }
307                         break;
308
309                 case CMDLINE_KEY_META_BKSPACE:
310                 case CMDLINE_KEY_CTRL_W:
311                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
312                                isblank2(cirbuf_get_tail(&rdl->left))) {
313                                 rdline_printf(rdl, vt100_bs);
314                                 cirbuf_del_tail(&rdl->left);
315                         }
316                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
317                                !isblank2(cirbuf_get_tail(&rdl->left))) {
318                                 rdline_printf(rdl, vt100_bs);
319                                 cirbuf_del_tail(&rdl->left);
320                         }
321                         display_right_buffer(rdl, 1);
322                         break;
323
324                 case CMDLINE_KEY_META_D:
325                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
326                                isblank2(cirbuf_get_head(&rdl->right)))
327                                 cirbuf_del_head(&rdl->right);
328                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
329                                !isblank2(cirbuf_get_head(&rdl->right)))
330                                 cirbuf_del_head(&rdl->right);
331                         display_right_buffer(rdl, 1);
332                         break;
333
334                 case CMDLINE_KEY_SUPPR:
335                 case CMDLINE_KEY_CTRL_D:
336                         if (cmd == CMDLINE_KEY_CTRL_D &&
337                             CIRBUF_IS_EMPTY(&rdl->left) &&
338                             CIRBUF_IS_EMPTY(&rdl->right)) {
339                                 return RDLINE_RES_EOF;
340                         }
341                         if (!cirbuf_del_head_safe(&rdl->right)) {
342                                 display_right_buffer(rdl, 1);
343                         }
344                         break;
345
346                 case CMDLINE_KEY_CTRL_A:
347                         if (CIRBUF_IS_EMPTY(&rdl->left))
348                                 break;
349                         rdline_printf(rdl, vt100_multi_left,
350                                       CIRBUF_GET_LEN(&rdl->left));
351                         while (! CIRBUF_IS_EMPTY(&rdl->left)) {
352                                 tmp = cirbuf_get_tail(&rdl->left);
353                                 cirbuf_del_tail(&rdl->left);
354                                 cirbuf_add_head(&rdl->right, tmp);
355                         }
356                         break;
357
358                 case CMDLINE_KEY_CTRL_E:
359                         if (CIRBUF_IS_EMPTY(&rdl->right))
360                                 break;
361                         rdline_printf(rdl, vt100_multi_right,
362                                       CIRBUF_GET_LEN(&rdl->right));
363                         while (! CIRBUF_IS_EMPTY(&rdl->right)) {
364                                 tmp = cirbuf_get_head(&rdl->right);
365                                 cirbuf_del_head(&rdl->right);
366                                 cirbuf_add_tail(&rdl->left, tmp);
367                         }
368                         break;
369
370 #ifndef NO_RDLINE_KILL_BUF
371                 case CMDLINE_KEY_CTRL_K:
372                         cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
373                         rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
374                         cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
375                         rdline_printf(rdl, vt100_clear_right);
376                         break;
377
378                 case CMDLINE_KEY_CTRL_Y:
379                         i=0;
380                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
381                               RDLINE_BUF_SIZE &&
382                               i < rdl->kill_size) {
383                                 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
384                                 rdline_printf(rdl, "%c", rdl->kill_buf[i]);
385                                 i++;
386                         }
387                         display_right_buffer(rdl, 0);
388                         break;
389 #endif /* !NO_RDLINE_KILL_BUF */
390
391                 case CMDLINE_KEY_CTRL_C:
392                         rdline_printf(rdl, "\r\n");
393                         rdline_newline(rdl, rdl->prompt);
394                         break;
395
396                 case CMDLINE_KEY_CTRL_L:
397                         rdline_redisplay(rdl);
398                         break;
399
400                 case CMDLINE_KEY_HELP: {
401                         if (rdl->help == NULL)
402                                 break;
403
404                         cirbuf_align_left(&rdl->left);
405                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
406                         rdline_printf(rdl, "\r\n");
407 #ifndef NO_PAGER
408                         rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write, rdl);
409                         if (rdl->pager_buf != NULL)
410                                 return RDLINE_RES_SUCCESS;
411                         else
412                                 rdline_asyncpager_reset(rdl);
413 #else
414                         rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
415 #endif
416                         rdline_redisplay(rdl);
417                         break;
418                 }
419
420                 case CMDLINE_KEY_TAB: {
421                         char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
422                         int ret;
423                         unsigned int tmp_size;
424
425                         if (rdl->complete == NULL)
426                                 break;
427
428                         cirbuf_align_left(&rdl->left);
429                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
430
431                         /* see in parse.h for help on complete() */
432                         ret = rdl->complete(rdl, rdl->left_buf,
433                                             tmp_buf, sizeof(tmp_buf));
434                         /* no completion or error */
435                         if (ret == CMDLINE_COMPLETE_NONE ||
436                             ret == CMDLINE_COMPLETE_MANY) {
437                                 cirbuf_align_left(&rdl->left);
438                                 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
439                                 rdline_printf(rdl, "\r\n");
440 #ifndef NO_PAGER
441                                 rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write,
442                                           rdl);
443                                 if (rdl->pager_buf != NULL)
444                                         return RDLINE_RES_SUCCESS;
445                                 else
446                                         rdline_asyncpager_reset(rdl);
447 #else
448                                 rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
449 #endif
450                                 rdline_redisplay(rdl);
451                                 break;
452                         }
453
454                         tmp_size = strlen(tmp_buf);
455                         /* add chars */
456                         i = 0;
457                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
458                               RDLINE_BUF_SIZE &&
459                               i < tmp_size) {
460                                 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
461                                 rdline_printf(rdl, "%c", tmp_buf[i]);
462                                 i++;
463                         }
464                         display_right_buffer(rdl, 1);
465                         break;
466                 }
467
468                 case CMDLINE_KEY_RETURN:
469                 case CMDLINE_KEY_RETURN2: {
470                         char tmp;
471                         while (!CIRBUF_IS_EMPTY(&rdl->right) &&
472                                (tmp = cirbuf_get_head(&rdl->right))) {
473                                 cirbuf_del_head(&rdl->right);
474                                 cirbuf_add_tail(&rdl->left, tmp);
475                         }
476                         cirbuf_align_left(&rdl->left);
477                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\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                         rdl->status = RDLINE_INIT;
503 #else
504                         if (rdl->status == RDLINE_EXITED)
505                                 return RDLINE_RES_EXITED;
506 #endif
507                         return RDLINE_RES_VALIDATED;
508                 }
509 #ifndef NO_RDLINE_HISTORY
510                 case CMDLINE_KEY_UP_ARR:
511                 case CMDLINE_KEY_CTRL_P:
512                         if (rdl->history_cur_line == 0) {
513                                 rdline_remove_first_history_item(rdl);
514                         }
515                         if (rdl->history_cur_line <= 0) {
516                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
517                                 rdl->history_cur_line = 0;
518                         }
519
520                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
521                         if (!buf)
522                                 break;
523
524                         rdl->history_cur_line ++;
525                         vt100_init(&rdl->vt100);
526                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
527                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
528                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
529                         rdline_redisplay(rdl);
530                         break;
531
532                 case CMDLINE_KEY_DOWN_ARR:
533                 case CMDLINE_KEY_CTRL_N:
534                         if (rdl->history_cur_line - 1 < 0)
535                                 break;
536
537                         rdl->history_cur_line --;
538                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
539                         if (!buf)
540                                 break;
541                         vt100_init(&rdl->vt100);
542                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
543                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
544                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
545                         rdline_redisplay(rdl);
546
547                         break;
548 #endif /* !NO_RDLINE_HISTORY */
549
550                 default:
551                         break;
552                 }
553
554                 return RDLINE_RES_SUCCESS;
555         }
556
557         if (!isprint((int)c))
558                 return RDLINE_RES_SUCCESS;
559
560         /* standard chars */
561         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
562                 return RDLINE_RES_SUCCESS;
563
564         if (cirbuf_add_tail_safe(&rdl->left, c))
565                 return RDLINE_RES_SUCCESS;
566
567         rdline_printf(rdl, "%c", c);
568         display_right_buffer(rdl, 0);
569
570         return RDLINE_RES_SUCCESS;
571 }
572
573 int
574 rdline_char_in(struct rdline *rdl, char c)
575 {
576         int ret, same = 0;
577         const char *history, *buffer;
578
579         if (rdl->status == RDLINE_EXITED)
580                 return RDLINE_RES_EXITED;
581         if (rdl->status != RDLINE_RUNNING)
582                 return RDLINE_RES_NOT_RUNNING;
583
584         ret = rdline_parse_char(rdl, c);
585
586         /* add line to history */
587         if (ret == RDLINE_RES_VALIDATED) {
588                 buffer = rdline_get_buffer(rdl);
589                 history = rdline_get_history_item(rdl, 0);
590                 if (history)
591                         same = !strcmp(buffer, history);
592
593                 if (strlen(buffer) >= 1 && same == 0)
594                         rdline_add_history(rdl, buffer);
595         }
596
597         return ret;
598 }
599
600 int
601 rdline(struct rdline *rdl, const char *prompt)
602 {
603         char c;
604         int ret = RDLINE_RES_NOT_RUNNING;
605
606         rdline_newline(rdl, prompt);
607         while (1) {
608                 if (read(rdl->fd_in, &c, 1) < 0)
609                         break;
610                 ret = rdline_char_in(rdl, c);
611                 if (ret != RDLINE_RES_SUCCESS)
612                         break;
613         }
614
615         return ret;
616 }
617
618 /* HISTORY */
619
620 #ifndef NO_RDLINE_HISTORY
621 static void
622 rdline_remove_old_history_item(struct rdline * rdl)
623 {
624         char tmp;
625
626         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
627                 tmp = cirbuf_get_head(&rdl->history);
628                 cirbuf_del_head(&rdl->history);
629                 if (!tmp)
630                         break;
631         }
632 }
633
634 static void
635 rdline_remove_first_history_item(struct rdline * rdl)
636 {
637         char tmp;
638
639         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
640                 return;
641         }
642         else {
643                 cirbuf_del_tail(&rdl->history);
644         }
645
646         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
647                 tmp = cirbuf_get_tail(&rdl->history);
648                 if (!tmp)
649                         break;
650                 cirbuf_del_tail(&rdl->history);
651         }
652 }
653
654 static unsigned int
655 rdline_get_history_size(struct rdline * rdl)
656 {
657         unsigned int i, tmp, ret=0;
658
659         CIRBUF_FOREACH(&rdl->history, i, tmp) {
660                 if (tmp == 0)
661                         ret ++;
662         }
663
664         return ret;
665 }
666
667 char *
668 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
669 {
670         unsigned int len, i, tmp;
671
672         len = rdline_get_history_size(rdl);
673         if ( idx >= len ) {
674                 return NULL;
675         }
676
677         cirbuf_align_left(&rdl->history);
678
679         CIRBUF_FOREACH(&rdl->history, i, tmp) {
680                 if ( idx == len - 1) {
681                         return rdl->history_buf + i;
682                 }
683                 if (tmp == 0)
684                         len --;
685         }
686
687         return NULL;
688 }
689
690 int
691 rdline_add_history(struct rdline * rdl, const char * buf)
692 {
693         unsigned int len;
694
695         len = strlen(buf);
696         if (len >= RDLINE_HISTORY_BUF_SIZE)
697                 return -1;
698
699         while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
700                 rdline_remove_old_history_item(rdl);
701         }
702
703         cirbuf_add_buf_tail(&rdl->history, buf, len);
704         cirbuf_add_tail(&rdl->history, 0);
705
706         return 0;
707 }
708
709 void
710 rdline_clear_history(struct rdline * rdl)
711 {
712         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
713 }
714
715 #else /* !NO_RDLINE_HISTORY */
716
717 int rdline_add_history(struct rdline * rdl, const char * buf)
718 {
719         return -1;
720 }
721
722 void rdline_clear_history(struct rdline * rdl)
723 {
724         return;
725 }
726
727 char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
728 {
729         return NULL;
730 }
731
732
733 #endif /* !NO_RDLINE_HISTORY */
734
735
736 ssize_t
737 rdline_write(const struct rdline *rdl, void *buf, size_t count)
738 {
739         return write(rdl->fd_out, buf, count);
740 }
741
742 int
743 rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
744 {
745         int ret;
746 #ifndef _GNU_SOURCE
747         char *buf;
748 #endif
749
750         if (rdl->fd_out < 0)
751                 return -1;
752
753 #ifdef _GNU_SOURCE
754         ret = vdprintf(rdl->fd_out, fmt, ap);
755 #else
756         buf = malloc(BUFSIZ);
757         if (buf == NULL)
758                 return -1;
759
760         ret = vsnprintf(buf, BUFSIZ, fmt, ap);
761
762         if (ret > 0)
763                 write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
764         free(buf);
765 #endif
766
767         return ret;
768 }
769
770 int
771 rdline_printf(const struct rdline *rdl, const char *fmt, ...)
772 {
773         va_list ap;
774         int ret;
775
776         va_start(ap, fmt);
777         ret = rdline_vprintf(rdl, fmt, ap);
778         va_end(ap);
779
780         return ret;
781 }
782
783 #ifndef NO_PAGER
784 /* reset pager state */
785 void
786 rdline_asyncpager_reset(struct rdline *rdl)
787 {
788         if (rdl->pager_buf) {
789                 free(rdl->pager_buf);
790                 rdl->pager_buf = NULL;
791         }
792         rdl->pager_lines = 0;
793         rdl->pager_len = 0;
794         rdl->pager_off = 0;
795         rdl->pager_cb = NULL;
796         rdl->pager_arg = NULL;
797         rdl->pager_ret = RDLINE_RES_SUCCESS;
798 }
799
800 /* Return the offset of the i-th occurence of char c in string s. If
801  * there is less than i occurences, return -1 and fill i with the
802  * count. */
803 static int
804 strnchr(const char *s, char c, int *i)
805 {
806         int n = 0;
807         const char *orig = s;
808
809         while (*s) {
810                 if (*s == c)
811                         n++;
812                 if (*i == n)
813                         return s - orig;
814                 s++;
815         }
816         *i = n;
817         return -1;
818 }
819
820 /* display a page of data from pager, return 0 if all is displayed */
821 static int
822 rdline_pager_next_page(struct rdline *rdl)
823 {
824         int lines = RDLINE_MAX_LINES;
825         int displen;
826         char *s;
827
828         s = rdl->pager_buf;
829         if (s == NULL)
830                 return 0;
831
832         rdline_printf(rdl, vt100_home);
833         rdline_printf(rdl, vt100_clear_right);
834
835         s += rdl->pager_off;
836
837         /* we know that s is 0-terminated */
838         displen = strnchr(s, '\n', &lines);
839         rdl->pager_lines = lines;
840
841         /* we can display all the data */
842         if (displen == -1) {
843                 write(rdl->fd_out, s, rdl->pager_len);
844                 free(rdl->pager_buf);
845                 rdl->pager_buf = NULL;
846                 return 0;
847         }
848
849         displen = displen + 1; /* include \n */
850         write(rdl->fd_out, s, displen);
851         rdl->pager_off += displen;
852         rdl->pager_len -= displen;
853
854         rdline_printf(rdl, "--- press a key to continue ---");
855         return -1;
856 }
857
858 /* push data in pager */
859 ssize_t
860 rdline_asyncpager_write(struct rdline *rdl, void *buf, size_t len)
861 {
862         char *s = buf;
863
864         /* display as many lines as we can */
865         if (rdl->pager_lines < RDLINE_MAX_LINES) {
866                 int lines = RDLINE_MAX_LINES - rdl->pager_lines;
867                 int displen;
868
869                 /* we know that s is 0-terminated */
870                 displen = strnchr(s, '\n', &lines);
871                 rdl->pager_lines += lines;
872
873                 /* we can display all the data */
874                 if (displen == -1) {
875                         write(rdl->fd_out, s, len);
876                         return 0;
877                 }
878                 displen = displen + 1; /* include \n */
879                 write(rdl->fd_out, s, displen);
880                 s += displen;
881                 len -= displen;
882         }
883
884         if (rdl->pager_buf == NULL) {
885                 rdline_printf(rdl, "--- press a key to continue ---");
886         }
887         rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len);
888         if (rdl->pager_buf == NULL) {
889                 rdline_asyncpager_reset(rdl);
890                 return -1;
891         }
892
893         memcpy(rdl->pager_buf + rdl->pager_len, s, len);
894         rdl->pager_len += len;
895         return 0;
896 }
897
898 /* Print data asynchronously (using pager if needed) */
899 int
900 rdline_asyncpager_printf(struct rdline *rdl, const char *fmt, ...)
901 {
902         int n;
903         char *buf;
904         va_list ap;
905
906         if (rdl->fd_out < 0)
907                 return -1;
908
909         buf = malloc(BUFSIZ);
910         if (buf == NULL)
911                 return -1;
912
913         va_start(ap, fmt);
914         n = vsnprintf(buf, BUFSIZ, fmt, ap);
915         va_end(ap);
916
917         if (n >= BUFSIZ)
918                 n = BUFSIZ-1;
919         if (n > 0)
920                 rdline_asyncpager_write(rdl, buf, n);
921         free(buf);
922         return n;
923 }
924
925 int rdline_asyncpager_set_cb(struct rdline *rdl, rdline_asyncpager_cb_t *cb,
926                              void *arg)
927 {
928         if (rdl->pager_buf == NULL)
929                 return -1;
930
931         rdl->pager_cb = cb;
932         rdl->pager_arg = arg;
933         return 0;
934 }
935 #endif /* !NO_PAGER */