X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=src%2Flib%2Fcmdline_rdline.c;h=785a23cd024b64f4f7a16613c4040eaf6e8cf0a6;hb=568d6d51fc9e6e2d8842fc1abe63c23de503f8bc;hp=6c2d9dd7f051898e1da36bc6c4a03e9920cd51c5;hpb=36dfbc791ada2e788180a474299d25641f56903f;p=libcmdline.git diff --git a/src/lib/cmdline_rdline.c b/src/lib/cmdline_rdline.c index 6c2d9dd..785a23c 100644 --- a/src/lib/cmdline_rdline.c +++ b/src/lib/cmdline_rdline.c @@ -65,13 +65,11 @@ #include #include #include +#include #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); @@ -79,6 +77,11 @@ static void rdline_remove_first_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. */ @@ -91,16 +94,20 @@ isblank2(char c) 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->status = RDLINE_INIT; + rdl->help = help; + rdl->status = RDLINE_STOPPED; #ifndef NO_RDLINE_HISTORY cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); #endif /* !NO_RDLINE_HISTORY */ @@ -109,18 +116,15 @@ void rdline_init(struct rdline *rdl, 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 ; iprompt_size ; i++) - rdl->write_char(rdl, rdl->prompt[i]); + rdline_printf(rdl, "%s", rdl->prompt); rdl->status = RDLINE_RUNNING; #ifndef NO_RDLINE_HISTORY @@ -131,7 +135,13 @@ rdline_newline(struct rdline *rdl, const char *prompt) void rdline_stop(struct rdline *rdl) { - rdl->status = RDLINE_INIT; + rdl->status = RDLINE_STOPPED; +} + +void +rdline_quit(struct rdline *rdl) +{ + rdl->status = RDLINE_EXITED; } void @@ -151,43 +161,44 @@ rdline_get_buffer(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 ; iprompt_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; @@ -196,14 +207,43 @@ rdline_char_in(struct rdline *rdl, char c) char *buf; #endif - if (rdl->status != RDLINE_RUNNING) - return -1; - cmd = vt100_parser(&rdl->vt100, c); - if (cmd == -2) - return 0; + 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: @@ -212,7 +252,7 @@ rdline_char_in(struct rdline *rdl, char c) 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: @@ -222,21 +262,21 @@ rdline_char_in(struct rdline *rdl, char c) 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); } @@ -246,14 +286,14 @@ rdline_char_in(struct rdline *rdl, char c) 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); } @@ -261,21 +301,34 @@ rdline_char_in(struct rdline *rdl, char c) 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: @@ -283,18 +336,18 @@ rdline_char_in(struct rdline *rdl, char c) if (cmd == CMDLINE_KEY_CTRL_D && CIRBUF_IS_EMPTY(&rdl->left) && CIRBUF_IS_EMPTY(&rdl->right)) { - return -2; + 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); @@ -305,8 +358,8 @@ rdline_char_in(struct rdline *rdl, char c) 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); @@ -319,7 +372,7 @@ rdline_char_in(struct rdline *rdl, char c) 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: @@ -328,15 +381,15 @@ rdline_char_in(struct rdline *rdl, char c) 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; @@ -344,77 +397,117 @@ rdline_char_in(struct rdline *rdl, char c) 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'; + 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'; - 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 2; - } - - tmp_size = strlen(tmp_buf); - /* add chars */ - if (ret == 2) { - 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 2; /* ?? */ - } - - /* 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); - } + /* 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; + } + + 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++; } - return 2; + 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 1; + 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); +#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); } @@ -436,6 +529,7 @@ rdline_char_in(struct rdline *rdl, char c) break; case CMDLINE_KEY_DOWN_ARR: + case CMDLINE_KEY_CTRL_N: if (rdl->history_cur_line - 1 < 0) break; @@ -452,30 +546,73 @@ rdline_char_in(struct rdline *rdl, char c) break; #endif /* !NO_RDLINE_HISTORY */ - default: break; } - return 0; + return RDLINE_RES_SUCCESS; } if (!isprint((int)c)) - return 0; + return RDLINE_RES_SUCCESS; /* standard chars */ if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE) - return 0; + return RDLINE_RES_SUCCESS; if (cirbuf_add_tail_safe(&rdl->left, c)) - return 0; + return RDLINE_RES_SUCCESS; - rdl->write_char(rdl, c); - display_right_buffer(rdl); + rdline_printf(rdl, "%c", c); + display_right_buffer(rdl, 0); - return 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 */ @@ -552,20 +689,13 @@ rdline_get_history_item(struct rdline * rdl, unsigned int idx) int rdline_add_history(struct rdline * rdl, const char * buf) { - unsigned int len, i; + unsigned int len; len = strlen(buf); - for (i=0; i= 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); } @@ -583,52 +713,222 @@ rdline_clear_history(struct rdline * 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 */