30a1de105a
These header includes have been flagged by the iwyu_tool and removed. Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
680 lines
15 KiB
C
680 lines
15 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2014 Intel Corporation.
|
|
* Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
#include "cmdline_cirbuf.h"
|
|
#include "cmdline_private.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);
|
|
|
|
static void rdline_remove_old_history_item(struct rdline *rdl);
|
|
static void rdline_remove_first_history_item(struct rdline *rdl);
|
|
static unsigned int rdline_get_history_size(struct rdline *rdl);
|
|
|
|
|
|
/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
|
|
* own. */
|
|
static int
|
|
isblank2(char c)
|
|
{
|
|
if (c == ' ' ||
|
|
c == '\t' )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rdline_init(struct rdline *rdl,
|
|
rdline_write_char_t *write_char,
|
|
rdline_validate_t *validate,
|
|
rdline_complete_t *complete,
|
|
void *opaque)
|
|
{
|
|
if (!rdl || !write_char || !validate || !complete)
|
|
return -EINVAL;
|
|
memset(rdl, 0, sizeof(*rdl));
|
|
rdl->validate = validate;
|
|
rdl->complete = complete;
|
|
rdl->write_char = write_char;
|
|
rdl->opaque = opaque;
|
|
rdl->status = RDLINE_INIT;
|
|
return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
|
|
}
|
|
|
|
struct rdline *
|
|
rdline_new(rdline_write_char_t *write_char,
|
|
rdline_validate_t *validate,
|
|
rdline_complete_t *complete,
|
|
void *opaque)
|
|
{
|
|
struct rdline *rdl;
|
|
|
|
rdl = malloc(sizeof(*rdl));
|
|
if (rdline_init(rdl, write_char, validate, complete, opaque) < 0) {
|
|
free(rdl);
|
|
rdl = NULL;
|
|
}
|
|
return rdl;
|
|
}
|
|
|
|
void
|
|
rdline_free(struct rdline *rdl)
|
|
{
|
|
free(rdl);
|
|
}
|
|
|
|
void
|
|
rdline_newline(struct rdline *rdl, const char *prompt)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!rdl || !prompt)
|
|
return;
|
|
|
|
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);
|
|
|
|
rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1);
|
|
if (prompt != rdl->prompt)
|
|
memcpy(rdl->prompt, prompt, rdl->prompt_size);
|
|
rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0';
|
|
|
|
for (i=0 ; i<rdl->prompt_size ; i++)
|
|
rdl->write_char(rdl, rdl->prompt[i]);
|
|
rdl->status = RDLINE_RUNNING;
|
|
|
|
rdl->history_cur_line = -1;
|
|
}
|
|
|
|
void
|
|
rdline_stop(struct rdline *rdl)
|
|
{
|
|
if (!rdl)
|
|
return;
|
|
rdl->status = RDLINE_INIT;
|
|
}
|
|
|
|
void
|
|
rdline_quit(struct rdline *rdl)
|
|
{
|
|
if (!rdl)
|
|
return;
|
|
rdl->status = RDLINE_EXITED;
|
|
}
|
|
|
|
void
|
|
rdline_restart(struct rdline *rdl)
|
|
{
|
|
if (!rdl)
|
|
return;
|
|
rdl->status = RDLINE_RUNNING;
|
|
}
|
|
|
|
void
|
|
rdline_reset(struct rdline *rdl)
|
|
{
|
|
if (!rdl)
|
|
return;
|
|
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);
|
|
|
|
rdl->status = RDLINE_RUNNING;
|
|
|
|
rdl->history_cur_line = -1;
|
|
}
|
|
|
|
const char *
|
|
rdline_get_buffer(struct rdline *rdl)
|
|
{
|
|
if (!rdl)
|
|
return NULL;
|
|
unsigned int len_l, len_r;
|
|
cirbuf_align_left(&rdl->left);
|
|
cirbuf_align_left(&rdl->right);
|
|
|
|
len_l = CIRBUF_GET_LEN(&rdl->left);
|
|
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';
|
|
return rdl->left_buf;
|
|
}
|
|
|
|
static void
|
|
display_right_buffer(struct rdline *rdl, int force)
|
|
{
|
|
unsigned int i;
|
|
char tmp;
|
|
|
|
if (!force && CIRBUF_IS_EMPTY(&rdl->right))
|
|
return;
|
|
|
|
rdline_puts(rdl, vt100_clear_right);
|
|
CIRBUF_FOREACH(&rdl->right, i, tmp) {
|
|
rdl->write_char(rdl, tmp);
|
|
}
|
|
if (!CIRBUF_IS_EMPTY(&rdl->right))
|
|
rdline_miniprintf(rdl, vt100_multi_left,
|
|
CIRBUF_GET_LEN(&rdl->right));
|
|
}
|
|
|
|
void
|
|
rdline_redisplay(struct rdline *rdl)
|
|
{
|
|
unsigned int i;
|
|
char tmp;
|
|
|
|
if (!rdl)
|
|
return;
|
|
|
|
rdline_puts(rdl, vt100_home);
|
|
for (i=0 ; i<rdl->prompt_size ; i++)
|
|
rdl->write_char(rdl, rdl->prompt[i]);
|
|
CIRBUF_FOREACH(&rdl->left, i, tmp) {
|
|
rdl->write_char(rdl, tmp);
|
|
}
|
|
display_right_buffer(rdl, 1);
|
|
}
|
|
|
|
int
|
|
rdline_char_in(struct rdline *rdl, char c)
|
|
{
|
|
unsigned int i;
|
|
int cmd;
|
|
char tmp;
|
|
char *buf;
|
|
|
|
if (!rdl)
|
|
return -EINVAL;
|
|
|
|
if (rdl->status == RDLINE_EXITED)
|
|
return RDLINE_RES_EXITED;
|
|
if (rdl->status != RDLINE_RUNNING)
|
|
return RDLINE_RES_NOT_RUNNING;
|
|
|
|
cmd = vt100_parser(&rdl->vt100, c);
|
|
if (cmd == -2)
|
|
return RDLINE_RES_SUCCESS;
|
|
|
|
if (cmd >= 0) {
|
|
switch (cmd) {
|
|
/* move caret 1 char to the left */
|
|
case CMDLINE_KEY_CTRL_B:
|
|
case CMDLINE_KEY_LEFT_ARR:
|
|
if (CIRBUF_IS_EMPTY(&rdl->left))
|
|
break;
|
|
tmp = cirbuf_get_tail(&rdl->left);
|
|
cirbuf_del_tail(&rdl->left);
|
|
cirbuf_add_head(&rdl->right, tmp);
|
|
rdline_puts(rdl, vt100_left_arr);
|
|
break;
|
|
|
|
/* move caret 1 char to the right */
|
|
case CMDLINE_KEY_CTRL_F:
|
|
case CMDLINE_KEY_RIGHT_ARR:
|
|
if (CIRBUF_IS_EMPTY(&rdl->right))
|
|
break;
|
|
tmp = cirbuf_get_head(&rdl->right);
|
|
cirbuf_del_head(&rdl->right);
|
|
cirbuf_add_tail(&rdl->left, tmp);
|
|
rdline_puts(rdl, vt100_right_arr);
|
|
break;
|
|
|
|
/* move caret 1 word to the left */
|
|
/* keyboard equivalent: Alt+B */
|
|
case CMDLINE_KEY_WLEFT:
|
|
while (! CIRBUF_IS_EMPTY(&rdl->left) &&
|
|
(tmp = cirbuf_get_tail(&rdl->left)) &&
|
|
isblank2(tmp)) {
|
|
rdline_puts(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);
|
|
cirbuf_del_tail(&rdl->left);
|
|
cirbuf_add_head(&rdl->right, tmp);
|
|
}
|
|
break;
|
|
|
|
/* move caret 1 word to the right */
|
|
/* keyboard equivalent: Alt+F */
|
|
case CMDLINE_KEY_WRIGHT:
|
|
while (! CIRBUF_IS_EMPTY(&rdl->right) &&
|
|
(tmp = cirbuf_get_head(&rdl->right)) &&
|
|
isblank2(tmp)) {
|
|
rdline_puts(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);
|
|
cirbuf_del_head(&rdl->right);
|
|
cirbuf_add_tail(&rdl->left, tmp);
|
|
}
|
|
break;
|
|
|
|
/* move caret to the left */
|
|
case CMDLINE_KEY_CTRL_A:
|
|
if (CIRBUF_IS_EMPTY(&rdl->left))
|
|
break;
|
|
rdline_miniprintf(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);
|
|
cirbuf_add_head(&rdl->right, tmp);
|
|
}
|
|
break;
|
|
|
|
/* move caret to the right */
|
|
case CMDLINE_KEY_CTRL_E:
|
|
if (CIRBUF_IS_EMPTY(&rdl->right))
|
|
break;
|
|
rdline_miniprintf(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_add_tail(&rdl->left, tmp);
|
|
}
|
|
break;
|
|
|
|
/* delete 1 char from the left */
|
|
case CMDLINE_KEY_BKSPACE:
|
|
case CMDLINE_KEY_BKSPACE2:
|
|
if(!cirbuf_del_tail_safe(&rdl->left)) {
|
|
rdline_puts(rdl, vt100_bs);
|
|
display_right_buffer(rdl, 1);
|
|
}
|
|
break;
|
|
|
|
/* delete 1 char from the right */
|
|
case CMDLINE_KEY_SUPPR:
|
|
case CMDLINE_KEY_CTRL_D:
|
|
if (cmd == CMDLINE_KEY_CTRL_D &&
|
|
CIRBUF_IS_EMPTY(&rdl->left) &&
|
|
CIRBUF_IS_EMPTY(&rdl->right)) {
|
|
return RDLINE_RES_EOF;
|
|
}
|
|
if (!cirbuf_del_head_safe(&rdl->right)) {
|
|
display_right_buffer(rdl, 1);
|
|
}
|
|
break;
|
|
|
|
/* delete 1 word from the left */
|
|
case CMDLINE_KEY_META_BKSPACE:
|
|
case CMDLINE_KEY_CTRL_W:
|
|
while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
|
|
rdline_puts(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);
|
|
cirbuf_del_tail(&rdl->left);
|
|
}
|
|
display_right_buffer(rdl, 1);
|
|
break;
|
|
|
|
/* delete 1 word from the right */
|
|
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;
|
|
|
|
/* set kill buffer to contents on the right side of caret */
|
|
case CMDLINE_KEY_CTRL_K:
|
|
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);
|
|
break;
|
|
|
|
/* paste contents of kill buffer to the left side of caret */
|
|
case CMDLINE_KEY_CTRL_Y:
|
|
i=0;
|
|
while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
|
|
RDLINE_BUF_SIZE &&
|
|
i < rdl->kill_size) {
|
|
cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
|
|
rdl->write_char(rdl, rdl->kill_buf[i]);
|
|
i++;
|
|
}
|
|
display_right_buffer(rdl, 0);
|
|
break;
|
|
|
|
/* clear and newline */
|
|
case CMDLINE_KEY_CTRL_C:
|
|
rdline_puts(rdl, "\r\n");
|
|
rdline_newline(rdl, rdl->prompt);
|
|
break;
|
|
|
|
/* redisplay (helps when prompt is lost in other output) */
|
|
case CMDLINE_KEY_CTRL_L:
|
|
rdline_redisplay(rdl);
|
|
break;
|
|
|
|
/* autocomplete */
|
|
case CMDLINE_KEY_TAB:
|
|
case CMDLINE_KEY_HELP:
|
|
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 = strnlen(tmp_buf, sizeof(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, 1);
|
|
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_redisplay(rdl);
|
|
}
|
|
return RDLINE_RES_COMPLETE;
|
|
|
|
/* complete buffer */
|
|
case CMDLINE_KEY_RETURN:
|
|
case CMDLINE_KEY_RETURN2:
|
|
rdline_get_buffer(rdl);
|
|
rdl->status = RDLINE_INIT;
|
|
rdline_puts(rdl, "\r\n");
|
|
if (rdl->history_cur_line != -1)
|
|
rdline_remove_first_history_item(rdl);
|
|
|
|
if (rdl->validate)
|
|
rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
|
|
/* user may have stopped rdline */
|
|
if (rdl->status == RDLINE_EXITED)
|
|
return RDLINE_RES_EXITED;
|
|
return RDLINE_RES_VALIDATED;
|
|
|
|
/* previous element in history */
|
|
case CMDLINE_KEY_UP_ARR:
|
|
case CMDLINE_KEY_CTRL_P:
|
|
if (rdl->history_cur_line == 0) {
|
|
rdline_remove_first_history_item(rdl);
|
|
}
|
|
if (rdl->history_cur_line <= 0) {
|
|
rdline_add_history(rdl, rdline_get_buffer(rdl));
|
|
rdl->history_cur_line = 0;
|
|
}
|
|
|
|
buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
|
|
if (!buf)
|
|
break;
|
|
|
|
rdl->history_cur_line ++;
|
|
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);
|
|
cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
|
|
rdline_redisplay(rdl);
|
|
break;
|
|
|
|
/* next element in history */
|
|
case CMDLINE_KEY_DOWN_ARR:
|
|
case CMDLINE_KEY_CTRL_N:
|
|
if (rdl->history_cur_line - 1 < 0)
|
|
break;
|
|
|
|
rdl->history_cur_line --;
|
|
buf = rdline_get_history_item(rdl, rdl->history_cur_line);
|
|
if (!buf)
|
|
break;
|
|
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);
|
|
cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
|
|
rdline_redisplay(rdl);
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return RDLINE_RES_SUCCESS;
|
|
}
|
|
|
|
if (!isprint((int)c))
|
|
return RDLINE_RES_SUCCESS;
|
|
|
|
/* standard chars */
|
|
if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
|
|
return RDLINE_RES_SUCCESS;
|
|
|
|
if (cirbuf_add_tail_safe(&rdl->left, c))
|
|
return RDLINE_RES_SUCCESS;
|
|
|
|
rdl->write_char(rdl, c);
|
|
display_right_buffer(rdl, 0);
|
|
|
|
return RDLINE_RES_SUCCESS;
|
|
}
|
|
|
|
|
|
/* HISTORY */
|
|
|
|
static void
|
|
rdline_remove_old_history_item(struct rdline * rdl)
|
|
{
|
|
char tmp;
|
|
|
|
while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
|
|
tmp = cirbuf_get_head(&rdl->history);
|
|
cirbuf_del_head(&rdl->history);
|
|
if (!tmp)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
rdline_remove_first_history_item(struct rdline * rdl)
|
|
{
|
|
char tmp;
|
|
|
|
if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
|
|
return;
|
|
}
|
|
else {
|
|
cirbuf_del_tail(&rdl->history);
|
|
}
|
|
|
|
while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
|
|
tmp = cirbuf_get_tail(&rdl->history);
|
|
if (!tmp)
|
|
break;
|
|
cirbuf_del_tail(&rdl->history);
|
|
}
|
|
}
|
|
|
|
static unsigned int
|
|
rdline_get_history_size(struct rdline * rdl)
|
|
{
|
|
unsigned int i, tmp, ret=0;
|
|
|
|
CIRBUF_FOREACH(&rdl->history, i, tmp) {
|
|
if (tmp == 0)
|
|
ret ++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
rdline_get_history_item(struct rdline * rdl, unsigned int idx)
|
|
{
|
|
unsigned int len, i, tmp;
|
|
|
|
if (!rdl)
|
|
return NULL;
|
|
|
|
len = rdline_get_history_size(rdl);
|
|
if ( idx >= len ) {
|
|
return NULL;
|
|
}
|
|
|
|
cirbuf_align_left(&rdl->history);
|
|
|
|
CIRBUF_FOREACH(&rdl->history, i, tmp) {
|
|
if ( idx == len - 1) {
|
|
return rdl->history_buf + i;
|
|
}
|
|
if (tmp == 0)
|
|
len --;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
size_t
|
|
rdline_get_history_buffer_size(struct rdline *rdl)
|
|
{
|
|
return sizeof(rdl->history_buf);
|
|
}
|
|
|
|
void *
|
|
rdline_get_opaque(struct rdline *rdl)
|
|
{
|
|
return rdl != NULL ? rdl->opaque : NULL;
|
|
}
|
|
|
|
int
|
|
rdline_add_history(struct rdline * rdl, const char * buf)
|
|
{
|
|
unsigned int len, i;
|
|
|
|
if (!rdl || !buf)
|
|
return -EINVAL;
|
|
|
|
len = strnlen(buf, RDLINE_BUF_SIZE);
|
|
for (i=0; i<len ; i++) {
|
|
if (buf[i] == '\n') {
|
|
len = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( len >= RDLINE_HISTORY_BUF_SIZE )
|
|
return -1;
|
|
|
|
while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
|
|
rdline_remove_old_history_item(rdl);
|
|
}
|
|
|
|
cirbuf_add_buf_tail(&rdl->history, buf, len);
|
|
cirbuf_add_tail(&rdl->history, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rdline_clear_history(struct rdline * rdl)
|
|
{
|
|
if (!rdl)
|
|
return;
|
|
cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
|
|
}
|
|
|
|
|
|
/* STATIC USEFUL FUNCS */
|
|
|
|
static void
|
|
rdline_puts(struct rdline * rdl, const char * buf)
|
|
{
|
|
char c;
|
|
while ( (c = *(buf++)) != '\0' ) {
|
|
rdl->write_char(rdl, c);
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
char c, started=0, div=100;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|