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