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