#include <string.h>
#include <stdarg.h>
#include <ctype.h>
+#include <unistd.h>
#include "cmdline_cirbuf.h"
#include "cmdline_rdline.h"
-
-static void rdline_puts(struct rdline *rdl, const char *buf);
-static void rdline_miniprintf(struct rdline *rdl,
- const char *buf, unsigned int val);
+#include "cmdline_parse.h"
#ifndef NO_RDLINE_HISTORY
static void rdline_remove_old_history_item(struct rdline *rdl);
static unsigned int rdline_get_history_size(struct rdline *rdl);
#endif /* !NO_RDLINE_HISTORY */
+#ifndef NO_PAGER
+static int rdline_pager_next_page(struct rdline *rdl);
+static void rdline_asyncpager_reset(struct rdline *rdl);
+#endif /* !NO_PAGER */
+
/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
* own. */
return 0;
}
-void rdline_init(struct rdline *rdl,
- rdline_write_char_t *write_char,
- rdline_validate_t *validate,
- rdline_complete_t *complete)
+void
+rdline_init(struct rdline *rdl,
+ int fd_in, int fd_out,
+ rdline_validate_t *validate,
+ rdline_complete_t *complete,
+ rdline_help_t *help)
{
memset(rdl, 0, sizeof(*rdl));
+ rdl->fd_in = fd_in;
+ rdl->fd_out = fd_out;
rdl->validate = validate;
rdl->complete = complete;
- rdl->write_char = write_char;
+ rdl->help = help;
rdl->status = RDLINE_INIT;
#ifndef NO_RDLINE_HISTORY
cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
void
rdline_newline(struct rdline *rdl, const char *prompt)
{
- unsigned int i;
-
vt100_init(&rdl->vt100);
cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
+ /* if pointer is the same, don't copy it */
if (prompt != rdl->prompt)
- memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
- rdl->prompt_size = strlen(prompt);
+ snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
- for (i=0 ; i<rdl->prompt_size ; i++)
- rdl->write_char(rdl, rdl->prompt[i]);
+ rdline_printf(rdl, "%s", rdl->prompt);
rdl->status = RDLINE_RUNNING;
#ifndef NO_RDLINE_HISTORY
rdl->status = RDLINE_INIT;
}
+void
+rdline_quit(struct rdline *rdl)
+{
+ rdl->status = RDLINE_EXITED;
+}
+
void
rdline_restart(struct rdline *rdl)
{
len_r = CIRBUF_GET_LEN(&rdl->right);
memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
- rdl->left_buf[len_l + len_r] = '\n';
- rdl->left_buf[len_l + len_r + 1] = '\0';
+ rdl->left_buf[len_l + len_r] = '\0';
return rdl->left_buf;
}
static void
-display_right_buffer(struct rdline *rdl)
+display_right_buffer(struct rdline *rdl, int force)
{
unsigned int i;
char tmp;
- rdline_puts(rdl, vt100_clear_right);
- if (!CIRBUF_IS_EMPTY(&rdl->right)) {
- CIRBUF_FOREACH(&rdl->right, i, tmp) {
- rdl->write_char(rdl, tmp);
- }
- rdline_miniprintf(rdl, vt100_multi_left,
- CIRBUF_GET_LEN(&rdl->right));
+ if (!force && CIRBUF_IS_EMPTY(&rdl->right))
+ return;
+
+ rdline_printf(rdl, vt100_clear_right);
+ CIRBUF_FOREACH(&rdl->right, i, tmp) {
+ rdline_printf(rdl, "%c", tmp);
}
+ if (!CIRBUF_IS_EMPTY(&rdl->right))
+ rdline_printf(rdl, vt100_multi_left,
+ CIRBUF_GET_LEN(&rdl->right));
}
-void rdline_redisplay(struct rdline *rdl)
+void
+rdline_redisplay(struct rdline *rdl)
{
unsigned int i;
char tmp;
- rdline_puts(rdl, vt100_home);
- for (i=0 ; i<rdl->prompt_size ; i++)
- rdl->write_char(rdl, rdl->prompt[i]);
+ rdline_printf(rdl, vt100_home);
+ rdline_printf(rdl, "%s", rdl->prompt);
CIRBUF_FOREACH(&rdl->left, i, tmp) {
- rdl->write_char(rdl, tmp);
+ rdline_printf(rdl, "%c", tmp);
}
- display_right_buffer(rdl);
+ display_right_buffer(rdl, 1);
}
-int
-rdline_char_in(struct rdline *rdl, char c)
+static int
+rdline_parse_char(struct rdline *rdl, char c)
{
unsigned int i;
int cmd;
char *buf;
#endif
- if (rdl->status != RDLINE_RUNNING)
- return RDLINE_RES_NOT_RUNNING;
-
cmd = vt100_parser(&rdl->vt100, c);
- if (cmd == -2)
+ if (cmd == VT100_NOT_COMPLETE)
+ return RDLINE_RES_SUCCESS;
+
+#ifndef NO_PAGER
+ /* display asynchrounous printf if any */
+ if (rdl->pager_buf != NULL) {
+
+ /* user ask to exit pager, or last page is displayed*/
+ if ((cmd == VT100_STD_CHAR && c == 'q') ||
+ rdline_pager_next_page(rdl) == 0) {
+ int ret;
+
+ ret = rdl->pager_ret;
+ rdline_asyncpager_reset(rdl);
+ if (rdl->pager_cb != NULL) {
+ rdl->pager_cb(rdl, rdl->pager_arg);
+ rdl->pager_cb = NULL;
+ }
+ /* maybe the pager was reloaded in the
+ * callback */
+ if (rdl->pager_buf != NULL)
+ return RDLINE_RES_SUCCESS;
+
+ /* else, redisplay prompt and return the saved status */
+ rdline_redisplay(rdl);
+ return ret;
+ }
+
+ /* Some pages remain, lines were displayed in
+ * rdline_pager_next_page() */
return RDLINE_RES_SUCCESS;
+ }
+#endif
- if (cmd >= 0) {
+ /* process control chars */
+ if (cmd != VT100_STD_CHAR) {
switch (cmd) {
case CMDLINE_KEY_CTRL_B:
case CMDLINE_KEY_LEFT_ARR:
tmp = cirbuf_get_tail(&rdl->left);
cirbuf_del_tail(&rdl->left);
cirbuf_add_head(&rdl->right, tmp);
- rdline_puts(rdl, vt100_left_arr);
+ rdline_printf(rdl, vt100_left_arr);
break;
case CMDLINE_KEY_CTRL_F:
tmp = cirbuf_get_head(&rdl->right);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
break;
case CMDLINE_KEY_WLEFT:
while (! CIRBUF_IS_EMPTY(&rdl->left) &&
(tmp = cirbuf_get_tail(&rdl->left)) &&
isblank2(tmp)) {
- rdline_puts(rdl, vt100_left_arr);
+ rdline_printf(rdl, vt100_left_arr);
cirbuf_del_tail(&rdl->left);
cirbuf_add_head(&rdl->right, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->left) &&
(tmp = cirbuf_get_tail(&rdl->left)) &&
!isblank2(tmp)) {
- rdline_puts(rdl, vt100_left_arr);
+ rdline_printf(rdl, vt100_left_arr);
cirbuf_del_tail(&rdl->left);
cirbuf_add_head(&rdl->right, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->right) &&
(tmp = cirbuf_get_head(&rdl->right)) &&
isblank2(tmp)) {
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->right) &&
(tmp = cirbuf_get_head(&rdl->right)) &&
!isblank2(tmp)) {
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
}
case CMDLINE_KEY_BKSPACE:
if(!cirbuf_del_tail_safe(&rdl->left)) {
- rdline_puts(rdl, vt100_bs);
- display_right_buffer(rdl);
+ rdline_printf(rdl, vt100_bs);
+ display_right_buffer(rdl, 1);
}
break;
case CMDLINE_KEY_META_BKSPACE:
- while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
- rdline_puts(rdl, vt100_bs);
+ case CMDLINE_KEY_CTRL_W:
+ while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+ isblank2(cirbuf_get_tail(&rdl->left))) {
+ rdline_printf(rdl, vt100_bs);
cirbuf_del_tail(&rdl->left);
}
- while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
- rdline_puts(rdl, vt100_bs);
+ while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+ !isblank2(cirbuf_get_tail(&rdl->left))) {
+ rdline_printf(rdl, vt100_bs);
cirbuf_del_tail(&rdl->left);
}
- display_right_buffer(rdl);
+ display_right_buffer(rdl, 1);
+ break;
+
+ case CMDLINE_KEY_META_D:
+ while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+ isblank2(cirbuf_get_head(&rdl->right)))
+ cirbuf_del_head(&rdl->right);
+ while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+ !isblank2(cirbuf_get_head(&rdl->right)))
+ cirbuf_del_head(&rdl->right);
+ display_right_buffer(rdl, 1);
break;
case CMDLINE_KEY_SUPPR:
return RDLINE_RES_EOF;
}
if (!cirbuf_del_head_safe(&rdl->right)) {
- display_right_buffer(rdl);
+ display_right_buffer(rdl, 1);
}
break;
case CMDLINE_KEY_CTRL_A:
if (CIRBUF_IS_EMPTY(&rdl->left))
break;
- rdline_miniprintf(rdl, vt100_multi_left,
- CIRBUF_GET_LEN(&rdl->left));
+ rdline_printf(rdl, vt100_multi_left,
+ CIRBUF_GET_LEN(&rdl->left));
while (! CIRBUF_IS_EMPTY(&rdl->left)) {
tmp = cirbuf_get_tail(&rdl->left);
cirbuf_del_tail(&rdl->left);
case CMDLINE_KEY_CTRL_E:
if (CIRBUF_IS_EMPTY(&rdl->right))
break;
- rdline_miniprintf(rdl, vt100_multi_right,
- CIRBUF_GET_LEN(&rdl->right));
+ rdline_printf(rdl, vt100_multi_right,
+ CIRBUF_GET_LEN(&rdl->right));
while (! CIRBUF_IS_EMPTY(&rdl->right)) {
tmp = cirbuf_get_head(&rdl->right);
cirbuf_del_head(&rdl->right);
cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
- rdline_puts(rdl, vt100_clear_right);
+ rdline_printf(rdl, vt100_clear_right);
break;
case CMDLINE_KEY_CTRL_Y:
RDLINE_BUF_SIZE &&
i < rdl->kill_size) {
cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
- rdl->write_char(rdl, rdl->kill_buf[i]);
+ rdline_printf(rdl, "%c", rdl->kill_buf[i]);
i++;
}
- display_right_buffer(rdl);
+ display_right_buffer(rdl, 0);
break;
#endif /* !NO_RDLINE_KILL_BUF */
case CMDLINE_KEY_CTRL_C:
- rdline_puts(rdl, "\r\n");
+ rdline_printf(rdl, "\r\n");
rdline_newline(rdl, rdl->prompt);
break;
rdline_redisplay(rdl);
break;
- case CMDLINE_KEY_TAB:
- case CMDLINE_KEY_HELP:
+ case CMDLINE_KEY_HELP: {
+ if (rdl->help == NULL)
+ break;
+
cirbuf_align_left(&rdl->left);
rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
- if (rdl->complete) {
- char tmp_buf[BUFSIZ];
- int complete_state;
- int ret;
- unsigned int tmp_size;
-
- if (cmd == CMDLINE_KEY_TAB)
- complete_state = 0;
- else
- complete_state = -1;
-
- /* see in parse.h for help on complete() */
- ret = rdl->complete(rdl, rdl->left_buf,
- tmp_buf, sizeof(tmp_buf),
- &complete_state);
- /* no completion or error */
- if (ret <= 0) {
- return RDLINE_RES_COMPLETE;
- }
-
- tmp_size = strlen(tmp_buf);
- /* add chars */
- if (ret == RDLINE_RES_COMPLETE) {
- i=0;
- while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
- RDLINE_BUF_SIZE &&
- i < tmp_size) {
- cirbuf_add_tail(&rdl->left, tmp_buf[i]);
- rdl->write_char(rdl, tmp_buf[i]);
- i++;
- }
- display_right_buffer(rdl);
- return RDLINE_RES_COMPLETE; /* ?? */
- }
-
- /* choice */
- rdline_puts(rdl, "\r\n");
- while (ret) {
- rdl->write_char(rdl, ' ');
- for (i=0 ; tmp_buf[i] ; i++)
- rdl->write_char(rdl, tmp_buf[i]);
- rdline_puts(rdl, "\r\n");
- ret = rdl->complete(rdl, rdl->left_buf,
- tmp_buf, sizeof(tmp_buf),
- &complete_state);
- }
+ rdline_printf(rdl, "\r\n");
+#ifndef NO_PAGER
+ rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write, rdl);
+ if (rdl->pager_buf != NULL)
+ return RDLINE_RES_SUCCESS;
+ else
+ rdline_asyncpager_reset(rdl);
+#else
+ rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
+#endif
+ rdline_redisplay(rdl);
+ break;
+ }
+
+ case CMDLINE_KEY_TAB: {
+ char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
+ int ret;
+ unsigned int tmp_size;
+ if (rdl->complete == NULL)
+ break;
+
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+
+ /* see in parse.h for help on complete() */
+ ret = rdl->complete(rdl, rdl->left_buf,
+ tmp_buf, sizeof(tmp_buf));
+ /* no completion or error */
+ if (ret == CMDLINE_COMPLETE_NONE ||
+ ret == CMDLINE_COMPLETE_MANY) {
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+ rdline_printf(rdl, "\r\n");
+#ifndef NO_PAGER
+ rdl->help(rdl, rdl->left_buf, rdline_asyncpager_write,
+ rdl);
+ if (rdl->pager_buf != NULL)
+ return RDLINE_RES_SUCCESS;
+ else
+ rdline_asyncpager_reset(rdl);
+#else
+ rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
+#endif
rdline_redisplay(rdl);
+ break;
}
- return RDLINE_RES_COMPLETE;
+
+ tmp_size = strlen(tmp_buf);
+ /* add chars */
+ i = 0;
+ while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
+ RDLINE_BUF_SIZE &&
+ i < tmp_size) {
+ cirbuf_add_tail(&rdl->left, tmp_buf[i]);
+ rdline_printf(rdl, "%c", tmp_buf[i]);
+ i++;
+ }
+ display_right_buffer(rdl, 1);
+ break;
+ }
case CMDLINE_KEY_RETURN:
- case CMDLINE_KEY_RETURN2:
- rdline_get_buffer(rdl);
- rdl->status = RDLINE_INIT;
- rdline_puts(rdl, "\r\n");
+ case CMDLINE_KEY_RETURN2: {
+ char tmp;
+ while (!CIRBUF_IS_EMPTY(&rdl->right) &&
+ (tmp = cirbuf_get_head(&rdl->right))) {
+ cirbuf_del_head(&rdl->right);
+ cirbuf_add_tail(&rdl->left, tmp);
+ }
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+ rdline_printf(rdl, "\r\n");
#ifndef NO_RDLINE_HISTORY
if (rdl->history_cur_line != -1)
rdline_remove_first_history_item(rdl);
#endif
if (rdl->validate)
- rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
- return RDLINE_RES_VALIDATED;
+ rdl->validate(rdl, rdl->left_buf,
+ CIRBUF_GET_LEN(&rdl->left)+2);
+#ifndef NO_PAGER
+ /* user may have stopped rdline */
+ if (rdl->status == RDLINE_EXITED) {
+ rdline_asyncpager_reset(rdl);
+ return RDLINE_RES_EXITED;
+ }
+ /* there is something in pager buffer, save
+ * return value that will be return once
+ * paging is finished */
+ if (rdl->pager_buf != NULL) {
+ rdl->pager_ret = RDLINE_RES_VALIDATED;
+ return RDLINE_RES_SUCCESS;
+ }
+ rdline_asyncpager_reset(rdl);
+ rdl->status = RDLINE_INIT;
+#else
+ if (rdl->status == RDLINE_EXITED)
+ return RDLINE_RES_EXITED;
+#endif
+ return RDLINE_RES_VALIDATED;
+ }
#ifndef NO_RDLINE_HISTORY
case CMDLINE_KEY_UP_ARR:
+ case CMDLINE_KEY_CTRL_P:
if (rdl->history_cur_line == 0) {
rdline_remove_first_history_item(rdl);
}
break;
case CMDLINE_KEY_DOWN_ARR:
+ case CMDLINE_KEY_CTRL_N:
if (rdl->history_cur_line - 1 < 0)
break;
break;
#endif /* !NO_RDLINE_HISTORY */
-
default:
break;
}
if (cirbuf_add_tail_safe(&rdl->left, c))
return RDLINE_RES_SUCCESS;
- rdl->write_char(rdl, c);
- display_right_buffer(rdl);
+ rdline_printf(rdl, "%c", c);
+ display_right_buffer(rdl, 0);
return RDLINE_RES_SUCCESS;
}
+int
+rdline_char_in(struct rdline *rdl, char c)
+{
+ int ret, same = 0;
+ const char *history, *buffer;
+
+ if (rdl->status == RDLINE_EXITED)
+ return RDLINE_RES_EXITED;
+ if (rdl->status != RDLINE_RUNNING)
+ return RDLINE_RES_NOT_RUNNING;
+
+ ret = rdline_parse_char(rdl, c);
+
+ /* add line to history */
+ if (ret == RDLINE_RES_VALIDATED) {
+ buffer = rdline_get_buffer(rdl);
+ history = rdline_get_history_item(rdl, 0);
+ if (history)
+ same = !strcmp(buffer, history);
+
+ if (strlen(buffer) >= 1 && same == 0)
+ rdline_add_history(rdl, buffer);
+ }
+
+ return ret;
+}
+
+int
+rdline(struct rdline *rdl, const char *prompt)
+{
+ char c;
+ int ret = RDLINE_RES_NOT_RUNNING;
+
+ rdline_newline(rdl, prompt);
+ while (1) {
+ if (read(rdl->fd_in, &c, 1) < 0)
+ break;
+ ret = rdline_char_in(rdl, c);
+ if (ret != RDLINE_RES_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
/* HISTORY */
int
rdline_add_history(struct rdline * rdl, const char * buf)
{
- unsigned int len, i;
+ unsigned int len;
len = strlen(buf);
- for (i=0; i<len ; i++) {
- if (buf[i] == '\n') {
- len = i;
- break;
- }
- }
-
- if ( len >= RDLINE_HISTORY_BUF_SIZE )
+ if (len >= RDLINE_HISTORY_BUF_SIZE)
return -1;
- while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
+ while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
rdline_remove_old_history_item(rdl);
}
#else /* !NO_RDLINE_HISTORY */
-int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
-void rdline_clear_history(struct rdline * rdl) {}
-char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
+int rdline_add_history(struct rdline * rdl, const char * buf)
+{
+ return -1;
+}
+
+void rdline_clear_history(struct rdline * rdl)
+{
+ return;
+}
+
+char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
+{
+ return NULL;
+}
#endif /* !NO_RDLINE_HISTORY */
-/* STATIC USEFUL FUNCS */
+ssize_t
+rdline_write(const struct rdline *rdl, void *buf, size_t count)
+{
+ return write(rdl->fd_out, buf, count);
+}
-static void
-rdline_puts(struct rdline * rdl, const char * buf)
+int
+rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
{
- char c;
- while ( (c = *(buf++)) != '\0' ) {
- rdl->write_char(rdl, c);
+ int ret;
+#ifndef _GNU_SOURCE
+ char *buf;
+#endif
+
+ if (rdl->fd_out < 0)
+ return -1;
+
+#ifdef _GNU_SOURCE
+ ret = vdprintf(rdl->fd_out, fmt, ap);
+#else
+ buf = malloc(BUFSIZ);
+ if (buf == NULL)
+ return -1;
+
+ ret = vsnprintf(buf, BUFSIZ, fmt, ap);
+
+ if (ret > 0)
+ write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
+ free(buf);
+#endif
+
+ return ret;
+}
+
+int
+rdline_printf(const struct rdline *rdl, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = rdline_vprintf(rdl, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+#ifndef NO_PAGER
+/* reset pager state */
+void
+rdline_asyncpager_reset(struct rdline *rdl)
+{
+ if (rdl->pager_buf) {
+ free(rdl->pager_buf);
+ rdl->pager_buf = NULL;
}
+ rdl->pager_lines = 0;
+ rdl->pager_len = 0;
+ rdl->pager_off = 0;
+ rdl->pager_cb = NULL;
+ rdl->pager_arg = NULL;
+ rdl->pager_ret = RDLINE_RES_SUCCESS;
}
-/* a very very basic printf with one arg and one format 'u' */
-static void
-rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
+/* Return the offset of the i-th occurence of char c in string s. If
+ * there is less than i occurences, return -1 and fill i with the
+ * count. */
+static int
+strnchr(const char *s, char c, int *i)
{
- char c, started=0, div=100;
+ int n = 0;
+ const char *orig = s;
+
+ while (*s) {
+ if (*s == c)
+ n++;
+ if (*i == n)
+ return s - orig;
+ s++;
+ }
+ *i = n;
+ return -1;
+}
- while ( (c=*(buf++)) ) {
- if (c != '%') {
- rdl->write_char(rdl, c);
- continue;
- }
- c = *(buf++);
- if (c != 'u') {
- rdl->write_char(rdl, '%');
- rdl->write_char(rdl, c);
- continue;
- }
- /* val is never more than 255 */
- while (div) {
- c = (char)(val / div);
- if (c || started) {
- rdl->write_char(rdl, (char)(c+'0'));
- started = 1;
- }
- val %= div;
- div /= 10;
+/* display a page of data from pager, return 0 if all is displayed */
+static int
+rdline_pager_next_page(struct rdline *rdl)
+{
+ int lines = RDLINE_MAX_LINES;
+ int displen;
+ char *s;
+
+ s = rdl->pager_buf;
+ if (s == NULL)
+ return 0;
+
+ rdline_printf(rdl, vt100_home);
+ rdline_printf(rdl, vt100_clear_right);
+
+ s += rdl->pager_off;
+
+ /* we know that s is 0-terminated */
+ displen = strnchr(s, '\n', &lines);
+ rdl->pager_lines = lines;
+
+ /* we can display all the data */
+ if (displen == -1) {
+ write(rdl->fd_out, s, rdl->pager_len);
+ free(rdl->pager_buf);
+ rdl->pager_buf = NULL;
+ return 0;
+ }
+
+ displen = displen + 1; /* include \n */
+ write(rdl->fd_out, s, displen);
+ rdl->pager_off += displen;
+ rdl->pager_len -= displen;
+
+ rdline_printf(rdl, "--- press a key to continue ---");
+ return -1;
+}
+
+/* push data in pager */
+ssize_t
+rdline_asyncpager_write(struct rdline *rdl, void *buf, size_t len)
+{
+ char *s = buf;
+
+ /* display as many lines as we can */
+ if (rdl->pager_lines < RDLINE_MAX_LINES) {
+ int lines = RDLINE_MAX_LINES - rdl->pager_lines;
+ int displen;
+
+ /* we know that s is 0-terminated */
+ displen = strnchr(s, '\n', &lines);
+ rdl->pager_lines += lines;
+
+ /* we can display all the data */
+ if (displen == -1) {
+ write(rdl->fd_out, s, len);
+ return 0;
}
+ displen = displen + 1; /* include \n */
+ write(rdl->fd_out, s, displen);
+ s += displen;
+ len -= displen;
+ }
+
+ if (rdl->pager_buf == NULL) {
+ rdline_printf(rdl, "--- press a key to continue ---");
}
+ rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len);
+ if (rdl->pager_buf == NULL) {
+ rdline_asyncpager_reset(rdl);
+ return -1;
+ }
+
+ memcpy(rdl->pager_buf + rdl->pager_len, s, len);
+ rdl->pager_len += len;
+ return 0;
}
+/* Print data asynchronously (using pager if needed) */
+int
+rdline_asyncpager_printf(struct rdline *rdl, const char *fmt, ...)
+{
+ int n;
+ char *buf;
+ va_list ap;
+
+ if (rdl->fd_out < 0)
+ return -1;
+
+ buf = malloc(BUFSIZ);
+ if (buf == NULL)
+ return -1;
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, BUFSIZ, fmt, ap);
+ va_end(ap);
+
+ if (n >= BUFSIZ)
+ n = BUFSIZ-1;
+ if (n > 0)
+ rdline_asyncpager_write(rdl, buf, n);
+ free(buf);
+ return n;
+}
+
+int rdline_asyncpager_set_cb(struct rdline *rdl, rdline_asyncpager_cb_t *cb,
+ void *arg)
+{
+ if (rdl->pager_buf == NULL)
+ return -1;
+
+ rdl->pager_cb = cb;
+ rdl->pager_arg = arg;
+ return 0;
+}
+#endif /* !NO_PAGER */