Re #1098: Modify input method for console, from character based to line based
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/cli@4303 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
e02d0df8f6
commit
b23df5e0d3
|
@ -97,7 +97,9 @@ PJ_DECL(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
|
|||
*
|
||||
* @return PJ_SUCCESS if an input was read
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess);
|
||||
PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess,
|
||||
char *buf,
|
||||
unsigned maxlen);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
|
|
@ -164,14 +164,14 @@ typedef enum pj_cli_arg_type
|
|||
|
||||
} pj_cli_arg_type;
|
||||
|
||||
static const struct
|
||||
struct arg_type
|
||||
{
|
||||
const pj_str_t msg;
|
||||
} arg_type[] =
|
||||
} arg_type[3] =
|
||||
{
|
||||
{"Text", 4},
|
||||
{"Int", 3},
|
||||
{"Choice", 6}
|
||||
{{"Text", 4}},
|
||||
{{"Int", 3}},
|
||||
{{"Choice", 6}}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -735,8 +735,6 @@ static pj_status_t pj_cli_add_cmd_node(pj_cli_t *cli,
|
|||
sizeof(pj_cli_arg_spec));
|
||||
|
||||
for (i = 0; i < cmd->arg_cnt; i++) {
|
||||
unsigned j;
|
||||
|
||||
pj_strdup(cli->pool, &cmd->arg[i].name, &args[i].name);
|
||||
pj_strdup(cli->pool, &cmd->arg[i].desc, &args[i].desc);
|
||||
cmd->arg[i].id = args[i].id;
|
||||
|
@ -834,7 +832,7 @@ PJ_DEF(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess,
|
|||
|
||||
/* Set the parse mode based on the latest char. */
|
||||
len = pj_ansi_strlen(cmdline);
|
||||
if (len > 0 && cmdline[len - 1] == '\r') {
|
||||
if (len > 0 && ((cmdline[len - 1] == '\r')||(cmdline[len - 1] == '\n'))) {
|
||||
cmdline[--len] = 0;
|
||||
parse_mode = PARSE_EXEC;
|
||||
} else if (len > 0 &&
|
||||
|
@ -920,16 +918,8 @@ PJ_DEF(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess,
|
|||
info->err_pos = pj_ansi_strlen(cmdline);
|
||||
}
|
||||
|
||||
} else if (parse_mode == PARSE_COMPLETION) {
|
||||
if (info->hint[0].name.slen > str.slen) {
|
||||
pj_str_t *hint_info = &info->hint[0].name;
|
||||
pj_memmove(&hint_info->ptr[0], &hint_info->ptr[str.slen],
|
||||
info->hint[0].name.slen-str.slen);
|
||||
hint_info->slen = info->hint[0].name.slen-str.slen;
|
||||
} else {
|
||||
info->hint[0].name.slen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val->sess = sess;
|
||||
return status;
|
||||
}
|
||||
|
@ -1089,7 +1079,7 @@ static pj_status_t get_match_args(pj_cli_sess *sess,
|
|||
}
|
||||
if (arg->get_dyn_choice) {
|
||||
pj_cli_dyn_choice_param dyn_choice_param;
|
||||
static const pj_str_t choice_str = {"choice", 6};
|
||||
static pj_str_t choice_str = {"choice", 6};
|
||||
|
||||
/* Get the dynamic choice values */
|
||||
dyn_choice_param.sess = sess;
|
||||
|
|
|
@ -27,57 +27,6 @@
|
|||
#include <pj/string.h>
|
||||
#include <pjlib-util/errno.h>
|
||||
|
||||
#if defined(PJ_LINUX) && PJ_LINUX != 0 || \
|
||||
defined(PJ_DARWINOS) && PJ_DARWINOS != 0
|
||||
#include <termios.h>
|
||||
|
||||
static struct termios old, new;
|
||||
|
||||
/* Initialize new terminal i/o settings */
|
||||
void initTermios(int echo)
|
||||
{
|
||||
tcgetattr(0, &old);
|
||||
new = old;
|
||||
new.c_lflag &= ~ICANON;
|
||||
new.c_lflag &= echo ? ECHO : ~ECHO;
|
||||
tcsetattr(0, TCSANOW, &new);
|
||||
}
|
||||
|
||||
/* Restore old terminal i/o settings */
|
||||
void resetTermios(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &old);
|
||||
}
|
||||
|
||||
/* Read 1 character - echo defines echo mode */
|
||||
char getch_(int echo)
|
||||
{
|
||||
char ch;
|
||||
initTermios(echo);
|
||||
ch = getchar();
|
||||
resetTermios();
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* Read 1 character without echo */
|
||||
char getch(void)
|
||||
{
|
||||
return getch_(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This specify the state of input character parsing.
|
||||
*/
|
||||
typedef enum cmd_parse_state
|
||||
{
|
||||
ST_NORMAL,
|
||||
ST_SCANMODE,
|
||||
ST_ESC,
|
||||
ST_ARROWMODE
|
||||
} cmd_parse_state;
|
||||
|
||||
/**
|
||||
* This specify the state of output character parsing.
|
||||
*/
|
||||
|
@ -89,32 +38,6 @@ typedef enum out_parse_state
|
|||
OP_CHOICE
|
||||
} out_parse_state;
|
||||
|
||||
/**
|
||||
* This structure contains the command line shown to the user.
|
||||
*/
|
||||
typedef struct console_recv_buf {
|
||||
/**
|
||||
* Buffer containing the characters, NULL terminated.
|
||||
*/
|
||||
unsigned char rbuf[PJ_CLI_MAX_CMDBUF];
|
||||
|
||||
/**
|
||||
* Current length of the command line.
|
||||
*/
|
||||
unsigned len;
|
||||
|
||||
/**
|
||||
* Current cursor position.
|
||||
*/
|
||||
unsigned cur_pos;
|
||||
} console_recv_buf;
|
||||
|
||||
typedef struct cmd_history
|
||||
{
|
||||
PJ_DECL_LIST_MEMBER(struct cmd_history);
|
||||
pj_str_t command;
|
||||
} cmd_history;
|
||||
|
||||
struct cli_console_fe
|
||||
{
|
||||
pj_cli_front_end base;
|
||||
|
@ -123,69 +46,16 @@ struct cli_console_fe
|
|||
pj_thread_t *input_thread;
|
||||
pj_bool_t thread_quit;
|
||||
pj_sem_t *thread_sem;
|
||||
pj_cli_console_cfg cfg;
|
||||
pj_cli_console_cfg cfg;
|
||||
|
||||
struct async_input_t
|
||||
{
|
||||
console_recv_buf recv_buf;
|
||||
pj_sem_t *sem;
|
||||
char *buf;
|
||||
unsigned maxlen;
|
||||
pj_sem_t *sem;
|
||||
} input;
|
||||
|
||||
cmd_history *history;
|
||||
cmd_history *active_history;
|
||||
};
|
||||
|
||||
static unsigned recv_buf_right_len(console_recv_buf *recv_buf)
|
||||
{
|
||||
return (recv_buf->len - recv_buf->cur_pos);
|
||||
}
|
||||
|
||||
static pj_bool_t recv_buf_insert(console_recv_buf *recv_buf,
|
||||
unsigned char *data)
|
||||
{
|
||||
if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) {
|
||||
return PJ_FALSE;
|
||||
} else {
|
||||
if (*data == '\t' || *data == '?' || *data == '\r') {
|
||||
/* Always insert to the end of line */
|
||||
recv_buf->rbuf[recv_buf->len] = *data;
|
||||
} else {
|
||||
/* Insert based on the current cursor pos */
|
||||
unsigned cur_pos = recv_buf->cur_pos;
|
||||
unsigned rlen = recv_buf_right_len(recv_buf);
|
||||
if (rlen > 0) {
|
||||
/* Shift right characters */
|
||||
pj_memmove(&recv_buf->rbuf[cur_pos+1],
|
||||
&recv_buf->rbuf[cur_pos],
|
||||
rlen+1);
|
||||
}
|
||||
recv_buf->rbuf[cur_pos] = *data;
|
||||
}
|
||||
++recv_buf->cur_pos;
|
||||
++recv_buf->len;
|
||||
recv_buf->rbuf[recv_buf->len] = 0;
|
||||
}
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
static pj_bool_t recv_buf_backspace(console_recv_buf *recv_buf)
|
||||
{
|
||||
if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) {
|
||||
return PJ_FALSE;
|
||||
} else {
|
||||
unsigned rlen = recv_buf_right_len(recv_buf);
|
||||
if (rlen) {
|
||||
unsigned cur_pos = recv_buf->cur_pos;
|
||||
pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos],
|
||||
rlen);
|
||||
}
|
||||
--recv_buf->cur_pos;
|
||||
--recv_buf->len;
|
||||
recv_buf->rbuf[recv_buf->len] = 0;
|
||||
}
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
static void cli_console_write_log(pj_cli_front_end *fe, int level,
|
||||
const char *data, int len)
|
||||
{
|
||||
|
@ -195,7 +65,6 @@ static void cli_console_write_log(pj_cli_front_end *fe, int level,
|
|||
printf("%.*s", len, data);
|
||||
}
|
||||
|
||||
|
||||
static void cli_console_quit(pj_cli_front_end *fe, pj_cli_sess *req)
|
||||
{
|
||||
struct cli_console_fe * cfe = (struct cli_console_fe *)fe;
|
||||
|
@ -228,7 +97,8 @@ PJ_DEF(void) pj_cli_console_cfg_default(pj_cli_console_cfg *param)
|
|||
{
|
||||
pj_assert(param);
|
||||
|
||||
param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL;
|
||||
param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL;
|
||||
param->prompt_str.slen = 0;
|
||||
}
|
||||
|
||||
PJ_DEF(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
|
||||
|
@ -250,16 +120,13 @@ PJ_DEF(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
|
|||
return PJ_ENOMEM;
|
||||
sess = PJ_POOL_ZALLOC_T(pool, pj_cli_sess);
|
||||
fe = PJ_POOL_ZALLOC_T(pool, struct cli_console_fe);
|
||||
fe->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history);
|
||||
pj_list_init(fe->history);
|
||||
fe->active_history = fe->history;
|
||||
|
||||
if (!param) {
|
||||
pj_cli_console_cfg_default(&cfg);
|
||||
param = &cfg;
|
||||
}
|
||||
sess->fe = &fe->base;
|
||||
sess->log_level = param->log_level;
|
||||
sess->log_level = param->log_level;
|
||||
sess->op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_sess_op);
|
||||
fe->base.op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_front_end_op);
|
||||
fe->base.cli = cli;
|
||||
|
@ -273,14 +140,16 @@ PJ_DEF(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
|
|||
pj_sem_create(pool, "console_fe", 0, 1, &fe->input.sem);
|
||||
pj_cli_register_front_end(cli, &fe->base);
|
||||
|
||||
if (fe->cfg.prompt_str.slen == 0) {
|
||||
if (param->prompt_str.slen == 0) {
|
||||
pj_str_t prompt_sign = pj_str(">>> ");
|
||||
char *prompt_data = pj_pool_alloc(fe->pool, 5);
|
||||
fe->cfg.prompt_str.ptr = prompt_data;
|
||||
|
||||
pj_strcpy(&fe->cfg.prompt_str, &prompt_sign);
|
||||
prompt_data[4] = 0;
|
||||
}
|
||||
fe->cfg.prompt_str.ptr = pj_pool_alloc(fe->pool, prompt_sign.slen+1);
|
||||
pj_strcpy(&fe->cfg.prompt_str, &prompt_sign);
|
||||
} else {
|
||||
fe->cfg.prompt_str.ptr = pj_pool_alloc(fe->pool,
|
||||
param->prompt_str.slen+1);
|
||||
pj_strcpy(&fe->cfg.prompt_str, ¶m->prompt_str);
|
||||
}
|
||||
fe->cfg.prompt_str.ptr[fe->cfg.prompt_str.slen] = 0;
|
||||
|
||||
*p_sess = sess;
|
||||
if (p_fe)
|
||||
|
@ -307,15 +176,13 @@ static void send_prompt_str(pj_cli_sess *sess)
|
|||
static void send_err_arg(pj_cli_sess *sess,
|
||||
const pj_cli_exec_info *info,
|
||||
const pj_str_t *msg,
|
||||
pj_bool_t with_return,
|
||||
pj_bool_t with_last_cmd)
|
||||
pj_bool_t with_return)
|
||||
{
|
||||
pj_str_t send_data;
|
||||
char data_str[256];
|
||||
unsigned len;
|
||||
unsigned i;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
|
||||
send_data.ptr = &data_str[0];
|
||||
send_data.slen = 0;
|
||||
|
@ -332,41 +199,30 @@ static void send_err_arg(pj_cli_sess *sess,
|
|||
pj_strcat2(&send_data, "\r\n");
|
||||
pj_strcat(&send_data, msg);
|
||||
pj_strcat(&send_data, &fe->cfg.prompt_str);
|
||||
if (with_last_cmd)
|
||||
pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]);
|
||||
|
||||
send_data.ptr[send_data.slen] = 0;
|
||||
printf("%s", send_data.ptr);
|
||||
}
|
||||
|
||||
static void send_return_key(pj_cli_sess *sess)
|
||||
{
|
||||
PJ_UNUSED_ARG(sess);
|
||||
printf("\r\n");
|
||||
printf("%s", send_data.ptr);
|
||||
}
|
||||
|
||||
static void send_inv_arg(pj_cli_sess *sess,
|
||||
const pj_cli_exec_info *info,
|
||||
pj_bool_t with_return,
|
||||
pj_bool_t with_last_cmd)
|
||||
pj_bool_t with_return)
|
||||
{
|
||||
static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28};
|
||||
send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
|
||||
send_err_arg(sess, info, &ERR_MSG, with_return);
|
||||
}
|
||||
|
||||
static void send_too_many_arg(pj_cli_sess *sess,
|
||||
const pj_cli_exec_info *info,
|
||||
pj_bool_t with_return,
|
||||
pj_bool_t with_last_cmd)
|
||||
pj_bool_t with_return)
|
||||
{
|
||||
static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29};
|
||||
send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
|
||||
send_err_arg(sess, info, &ERR_MSG, with_return);
|
||||
}
|
||||
|
||||
static void send_ambi_arg(pj_cli_sess *sess,
|
||||
const pj_cli_exec_info *info,
|
||||
pj_bool_t with_return,
|
||||
pj_bool_t with_last_cmd)
|
||||
pj_bool_t with_return)
|
||||
{
|
||||
unsigned i;
|
||||
pj_ssize_t j;
|
||||
|
@ -374,7 +230,6 @@ static void send_ambi_arg(pj_cli_sess *sess,
|
|||
pj_str_t send_data;
|
||||
char data[1028];
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
const pj_cli_hint_info *hint = info->hint;
|
||||
out_parse_state parse_state = OP_NORMAL;
|
||||
pj_ssize_t max_length = 0;
|
||||
|
@ -443,210 +298,58 @@ static void send_ambi_arg(pj_cli_sess *sess,
|
|||
}
|
||||
pj_strcat2(&send_data, "\r\n");
|
||||
pj_strcat(&send_data, &fe->cfg.prompt_str);
|
||||
if (with_last_cmd)
|
||||
pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]);
|
||||
|
||||
send_data.ptr[send_data.slen] = 0;
|
||||
printf("%s", send_data.ptr);
|
||||
printf("%s", send_data.ptr);
|
||||
}
|
||||
|
||||
static void send_comp_arg(pj_cli_exec_info *info)
|
||||
{
|
||||
pj_str_t send_data;
|
||||
char data[128];
|
||||
|
||||
pj_strcat2(&info->hint[0].name, " ");
|
||||
|
||||
send_data.ptr = &data[0];
|
||||
send_data.slen = 0;
|
||||
|
||||
pj_strcat(&send_data, &info->hint[0].name);
|
||||
|
||||
send_data.ptr[send_data.slen] = 0;
|
||||
printf("%s", send_data.ptr);
|
||||
}
|
||||
|
||||
static int compare_str(void *value, const pj_list_type *nd)
|
||||
{
|
||||
cmd_history *node = (cmd_history*)nd;
|
||||
return (pj_strcmp((pj_str_t *)value, &node->command));
|
||||
}
|
||||
|
||||
static pj_status_t insert_history(pj_cli_sess *sess,
|
||||
char *cmd_val)
|
||||
{
|
||||
cmd_history *in_history;
|
||||
pj_str_t cmd;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
cmd.ptr = cmd_val;
|
||||
cmd.slen = pj_ansi_strlen(cmd_val)-1;
|
||||
|
||||
if (cmd.slen == 0)
|
||||
return PJ_SUCCESS;
|
||||
|
||||
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
|
||||
|
||||
/* Find matching history */
|
||||
in_history = pj_list_search(fe->history, (void*)&cmd, compare_str);
|
||||
if (!in_history) {
|
||||
if (pj_list_size(fe->history) < PJ_CLI_MAX_CMD_HISTORY) {
|
||||
char *data_history;
|
||||
in_history = PJ_POOL_ZALLOC_T(fe->pool, cmd_history);
|
||||
pj_list_init(in_history);
|
||||
data_history = (char *)pj_pool_calloc(fe->pool,
|
||||
sizeof(char), PJ_CLI_MAX_CMDBUF);
|
||||
in_history->command.ptr = data_history;
|
||||
in_history->command.slen = 0;
|
||||
} else {
|
||||
/* Get the oldest history */
|
||||
in_history = fe->history->prev;
|
||||
}
|
||||
} else {
|
||||
pj_list_insert_nodes_after(in_history->prev, in_history->next);
|
||||
}
|
||||
pj_strcpy(&in_history->command, pj_strtrim(&cmd));
|
||||
pj_list_push_front(fe->history, in_history);
|
||||
fe->active_history = fe->history;
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_str_t* get_prev_history(pj_cli_sess *sess, pj_bool_t is_forward)
|
||||
{
|
||||
pj_str_t *retval;
|
||||
pj_size_t history_size;
|
||||
cmd_history *node;
|
||||
cmd_history *root;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
|
||||
PJ_ASSERT_RETURN(sess && fe, NULL);
|
||||
|
||||
node = fe->active_history;
|
||||
root = fe->history;
|
||||
history_size = pj_list_size(fe->history);
|
||||
|
||||
if (history_size == 0) {
|
||||
return NULL;
|
||||
} else {
|
||||
if (is_forward) {
|
||||
node = (node->next==root)?node->next->next:node->next;
|
||||
} else {
|
||||
node = (node->prev==root)?node->prev->prev:node->prev;
|
||||
}
|
||||
retval = &node->command;
|
||||
fe->active_history = node;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_alfa_num(console_recv_buf *recv_buf,
|
||||
unsigned char *cdata)
|
||||
{
|
||||
if (recv_buf_right_len(recv_buf) > 0) {
|
||||
char out_str[255];
|
||||
pj_memset(&out_str[0], 0, 255);
|
||||
out_str[0] = *cdata;
|
||||
pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos],
|
||||
recv_buf_right_len(recv_buf));
|
||||
pj_memset(&out_str[recv_buf_right_len(recv_buf)+1], '\b',
|
||||
recv_buf_right_len(recv_buf));
|
||||
printf("%s", out_str);
|
||||
} else {
|
||||
printf("%c", *cdata);
|
||||
}
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_backspace(console_recv_buf *recv_buf)
|
||||
{
|
||||
if (recv_buf_backspace(recv_buf)) {
|
||||
if(recv_buf_right_len(recv_buf) > 0) {
|
||||
char out_str[255];
|
||||
pj_memset(&out_str[0], 0, 255);
|
||||
out_str[0] = '\b';
|
||||
pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos],
|
||||
recv_buf_right_len(recv_buf));
|
||||
out_str[recv_buf_right_len(recv_buf)+1] = ' ';
|
||||
pj_memset(&out_str[recv_buf_right_len(recv_buf)+2], '\b',
|
||||
recv_buf_right_len(recv_buf)+1);
|
||||
printf("%s", out_str);
|
||||
} else {
|
||||
char out_str[4];
|
||||
out_str[0] = '\b';
|
||||
out_str[1] = ' ';
|
||||
out_str[2] = '\b';
|
||||
out_str[3] = 0;
|
||||
printf("%s", out_str);
|
||||
}
|
||||
return PJ_TRUE;
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_tab(pj_cli_sess *sess)
|
||||
static pj_bool_t handle_hint(pj_cli_sess *sess)
|
||||
{
|
||||
pj_status_t status;
|
||||
pj_bool_t retval = PJ_TRUE;
|
||||
unsigned len;
|
||||
|
||||
pj_pool_t *pool;
|
||||
pj_cli_cmd_val *cmd_val;
|
||||
pj_cli_exec_info info;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
char *recv_buf = fe->input.buf;
|
||||
pj_cli_t *cli = sess->fe->cli;
|
||||
|
||||
pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_tab",
|
||||
pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_hint",
|
||||
PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC,
|
||||
NULL);
|
||||
|
||||
cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val);
|
||||
|
||||
status = pj_cli_sess_parse(sess, (char *)recv_buf->rbuf, cmd_val,
|
||||
pool, &info);
|
||||
|
||||
len = pj_ansi_strlen((char *)recv_buf->rbuf);
|
||||
status = pj_cli_sess_parse(sess, recv_buf, cmd_val,
|
||||
pool, &info);
|
||||
|
||||
switch (status) {
|
||||
case PJ_CLI_EINVARG:
|
||||
send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);
|
||||
send_inv_arg(sess, &info, PJ_TRUE);
|
||||
break;
|
||||
case PJ_CLI_ETOOMANYARGS:
|
||||
send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE);
|
||||
send_too_many_arg(sess, &info, PJ_TRUE);
|
||||
break;
|
||||
case PJ_CLI_EMISSINGARG:
|
||||
case PJ_CLI_EAMBIGUOUS:
|
||||
send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE);
|
||||
send_ambi_arg(sess, &info, PJ_TRUE);
|
||||
break;
|
||||
case PJ_SUCCESS:
|
||||
if (len > recv_buf->cur_pos)
|
||||
{
|
||||
/* Send the cursor to EOL */
|
||||
unsigned char *data_sent = &recv_buf->rbuf[recv_buf->cur_pos-1];
|
||||
printf("%s", data_sent);
|
||||
}
|
||||
case PJ_SUCCESS:
|
||||
if (info.hint_cnt > 0) {
|
||||
/* Compelete command */
|
||||
send_comp_arg(&info);
|
||||
|
||||
pj_memcpy(&recv_buf->rbuf[len],
|
||||
&info.hint[0].name.ptr[0], info.hint[0].name.slen);
|
||||
|
||||
len += info.hint[0].name.slen;
|
||||
recv_buf->rbuf[len] = 0;
|
||||
/* Compelete command */
|
||||
send_ambi_arg(sess, &info, PJ_TRUE);
|
||||
} else {
|
||||
retval = PJ_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
recv_buf->len = len;
|
||||
recv_buf->cur_pos = len;
|
||||
|
||||
pj_pool_release(pool);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_return(pj_cli_sess *sess)
|
||||
static pj_bool_t handle_exec(pj_cli_sess *sess)
|
||||
{
|
||||
pj_status_t status;
|
||||
pj_bool_t retval = PJ_TRUE;
|
||||
|
@ -655,187 +358,59 @@ static pj_bool_t handle_return(pj_cli_sess *sess)
|
|||
pj_cli_exec_info info;
|
||||
pj_cli_t *cli = sess->fe->cli;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
|
||||
send_return_key(sess);
|
||||
insert_history(sess, (char *)&recv_buf->rbuf[0]);
|
||||
char *recv_buf = fe->input.buf;
|
||||
|
||||
printf("\r\n");
|
||||
|
||||
pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_return",
|
||||
pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_exec",
|
||||
PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC,
|
||||
NULL);
|
||||
|
||||
status = pj_cli_sess_exec(sess, (char *)&recv_buf->rbuf[0],
|
||||
status = pj_cli_sess_exec(sess, recv_buf,
|
||||
pool, &info);
|
||||
|
||||
switch (status) {
|
||||
case PJ_CLI_EINVARG:
|
||||
send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);
|
||||
send_inv_arg(sess, &info, PJ_FALSE);
|
||||
break;
|
||||
case PJ_CLI_ETOOMANYARGS:
|
||||
send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE);
|
||||
send_too_many_arg(sess, &info, PJ_FALSE);
|
||||
break;
|
||||
case PJ_CLI_EAMBIGUOUS:
|
||||
case PJ_CLI_EMISSINGARG:
|
||||
send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE);
|
||||
send_ambi_arg(sess, &info, PJ_FALSE);
|
||||
break;
|
||||
case PJ_CLI_EEXIT:
|
||||
retval = PJ_FALSE;
|
||||
break;
|
||||
case PJ_SUCCESS:
|
||||
send_prompt_str(sess);
|
||||
send_prompt_str(sess);
|
||||
break;
|
||||
}
|
||||
if (retval) {
|
||||
recv_buf->rbuf[0] = 0;
|
||||
recv_buf->len = 0;
|
||||
recv_buf->cur_pos = 0;
|
||||
}
|
||||
|
||||
pj_pool_release(pool);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_up_down(pj_cli_sess *sess, pj_bool_t is_up)
|
||||
{
|
||||
pj_str_t *history;
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
|
||||
PJ_ASSERT_RETURN(sess && fe, PJ_FALSE);
|
||||
|
||||
history = get_prev_history(sess, is_up);
|
||||
if (history) {
|
||||
pj_str_t send_data;
|
||||
char str[PJ_CLI_MAX_CMDBUF];
|
||||
send_data.ptr = &str[0];
|
||||
send_data.slen = 0;
|
||||
|
||||
if (recv_buf->cur_pos > 0) {
|
||||
pj_memset(send_data.ptr, 0x08, recv_buf->cur_pos);
|
||||
send_data.slen = recv_buf->cur_pos;
|
||||
}
|
||||
|
||||
if (recv_buf->len > (unsigned)history->slen) {
|
||||
unsigned buf_len = recv_buf->len;
|
||||
pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len);
|
||||
send_data.slen += buf_len;
|
||||
pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len);
|
||||
send_data.slen += buf_len;
|
||||
}
|
||||
/* Send data */
|
||||
pj_strcat(&send_data, history);
|
||||
send_data.ptr[send_data.slen] = 0;
|
||||
printf("%s", send_data.ptr);
|
||||
pj_ansi_strncpy((char*)&recv_buf->rbuf, history->ptr, history->slen);
|
||||
recv_buf->rbuf[history->slen] = 0;
|
||||
recv_buf->len = history->slen;
|
||||
recv_buf->cur_pos = recv_buf->len;
|
||||
return PJ_TRUE;
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_left_key(console_recv_buf *recv_buf)
|
||||
{
|
||||
const static unsigned char BACK_SPACE = 0x08;
|
||||
if (recv_buf->cur_pos) {
|
||||
printf("%c", BACK_SPACE);
|
||||
--recv_buf->cur_pos;
|
||||
return PJ_TRUE;
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_right_key(console_recv_buf *recv_buf)
|
||||
{
|
||||
if (recv_buf_right_len(recv_buf)) {
|
||||
unsigned char *data = &recv_buf->rbuf[recv_buf->cur_pos++];
|
||||
printf("%c", *data);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static int readchar_thread(void * p)
|
||||
static int readline_thread(void * p)
|
||||
{
|
||||
struct cli_console_fe * fe = (struct cli_console_fe *)p;
|
||||
cmd_parse_state parse_state = ST_NORMAL;
|
||||
|
||||
printf("%s", fe->cfg.prompt_str.ptr);
|
||||
|
||||
while (!fe->thread_quit) {
|
||||
unsigned char cdata;
|
||||
console_recv_buf *recv_buf = &fe->input.recv_buf;
|
||||
unsigned input_len = 0;
|
||||
char *recv_buf = fe->input.buf;
|
||||
pj_bool_t is_valid = PJ_TRUE;
|
||||
|
||||
cdata = (unsigned char)getch();
|
||||
|
||||
switch (parse_state) {
|
||||
case ST_NORMAL:
|
||||
if (cdata == '\b') {
|
||||
is_valid = handle_backspace(recv_buf);
|
||||
} else if (cdata == 224) {
|
||||
parse_state = ST_SCANMODE;
|
||||
} else if (cdata == 27) {
|
||||
parse_state = ST_ESC;
|
||||
} else {
|
||||
if (cdata == '\n')
|
||||
cdata = '\r';
|
||||
if (recv_buf_insert(recv_buf, &cdata)) {
|
||||
if (cdata == '\r') {
|
||||
is_valid = handle_return(fe->sess);
|
||||
} else if ((cdata == '\t') || (cdata == '?')) {
|
||||
is_valid = handle_tab(fe->sess);
|
||||
} else if (cdata > 31 && cdata < 127) {
|
||||
is_valid = handle_alfa_num(recv_buf, &cdata);
|
||||
}
|
||||
} else {
|
||||
is_valid = PJ_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ST_SCANMODE:
|
||||
switch (cdata) {
|
||||
case 72:
|
||||
//UP
|
||||
case 80:
|
||||
//DOwN
|
||||
is_valid = handle_up_down(fe->sess, (cdata==72));
|
||||
break;
|
||||
case 75:
|
||||
is_valid = handle_left_key(recv_buf);
|
||||
//LEFT
|
||||
break;
|
||||
case 77:
|
||||
is_valid = handle_right_key(recv_buf);
|
||||
//RIGHT
|
||||
break;
|
||||
};
|
||||
parse_state = ST_NORMAL;
|
||||
break;
|
||||
case ST_ESC:
|
||||
parse_state = (cdata == 91)?ST_ARROWMODE:ST_NORMAL;
|
||||
break;
|
||||
case ST_ARROWMODE:
|
||||
switch (cdata) {
|
||||
case 65:
|
||||
//UP
|
||||
case 66:
|
||||
//DOwN
|
||||
is_valid = handle_up_down(fe->sess, (cdata==65));
|
||||
break;
|
||||
case 68:
|
||||
is_valid = handle_left_key(recv_buf);
|
||||
//LEFT
|
||||
break;
|
||||
case 67:
|
||||
is_valid = handle_right_key(recv_buf);
|
||||
//RIGHT
|
||||
break;
|
||||
};
|
||||
parse_state = ST_NORMAL;
|
||||
break;
|
||||
};
|
||||
fgets(recv_buf, fe->input.maxlen, stdin);
|
||||
input_len = pj_ansi_strlen(fe->input.buf);
|
||||
if ((input_len > 1) && (fe->input.buf[input_len-2] == '?')) {
|
||||
fe->input.buf[input_len-1] = 0;
|
||||
is_valid = handle_hint(fe->sess);
|
||||
} else {
|
||||
is_valid = handle_exec(fe->sess);
|
||||
}
|
||||
|
||||
pj_sem_post(fe->input.sem);
|
||||
pj_sem_wait(fe->thread_sem);
|
||||
|
@ -845,16 +420,21 @@ static int readchar_thread(void * p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
PJ_DEF(pj_status_t) pj_cli_console_process(pj_cli_sess *sess)
|
||||
PJ_DEF(pj_status_t) pj_cli_console_process(pj_cli_sess *sess,
|
||||
char *buf,
|
||||
unsigned maxlen)
|
||||
{
|
||||
struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
|
||||
|
||||
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
|
||||
|
||||
fe->input.buf = buf;
|
||||
fe->input.maxlen = maxlen;
|
||||
|
||||
if (!fe->input_thread) {
|
||||
pj_status_t status;
|
||||
|
||||
status = pj_thread_create(fe->pool, NULL, &readchar_thread, fe,
|
||||
status = pj_thread_create(fe->pool, NULL, &readline_thread, fe,
|
||||
0, 0, &fe->input_thread);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
#include <pj/os.h>
|
||||
#include <pj/pool.h>
|
||||
#include <pj/string.h>
|
||||
#include <pj/except.h>
|
||||
#include <pjlib-util/errno.h>
|
||||
#include <pjlib-util/scanner.h>
|
||||
|
||||
#define CLI_TELNET_BUF_SIZE 256
|
||||
|
||||
|
@ -888,6 +890,32 @@ static pj_bool_t handle_backspace(cli_telnet_sess *sess, unsigned char *data)
|
|||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
/* Syntax error handler for parser. */
|
||||
static void on_syntax_error(pj_scanner *scanner)
|
||||
{
|
||||
PJ_UNUSED_ARG(scanner);
|
||||
PJ_THROW(PJ_EINVAL);
|
||||
}
|
||||
|
||||
static pj_status_t get_last_token(pj_str_t *cmd, pj_str_t *str)
|
||||
{
|
||||
pj_scanner scanner;
|
||||
PJ_USE_EXCEPTION;
|
||||
pj_scan_init(&scanner, cmd->ptr, cmd->slen, PJ_SCAN_AUTOSKIP_WS,
|
||||
&on_syntax_error);
|
||||
PJ_TRY {
|
||||
while (!pj_scan_is_eof(&scanner)) {
|
||||
pj_scan_get_until_chr(&scanner, " \t\r\n", str);
|
||||
}
|
||||
}
|
||||
PJ_CATCH_ANY {
|
||||
pj_scan_fini(&scanner);
|
||||
return PJ_GET_EXCEPTION();
|
||||
}
|
||||
PJ_END;
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_bool_t handle_tab(cli_telnet_sess *sess)
|
||||
{
|
||||
pj_status_t status;
|
||||
|
@ -929,13 +957,25 @@ static pj_bool_t handle_tab(cli_telnet_sess *sess)
|
|||
}
|
||||
if (info.hint_cnt > 0) {
|
||||
/* Complete command */
|
||||
send_comp_arg(sess, &info);
|
||||
pj_str_t cmd = pj_str((char *)&sess->rcmd->rbuf[0]);
|
||||
pj_str_t last_token;
|
||||
|
||||
pj_memcpy(&sess->rcmd->rbuf[len],
|
||||
&info.hint[0].name.ptr[0], info.hint[0].name.slen);
|
||||
if (get_last_token(&cmd, &last_token) == PJ_SUCCESS) {
|
||||
pj_str_t *hint_info = &info.hint[0].name;
|
||||
pj_strtrim(&last_token);
|
||||
if (hint_info->slen > last_token.slen) {
|
||||
hint_info->slen -= last_token.slen;
|
||||
pj_memmove(&hint_info->ptr[0], &hint_info->ptr[last_token.slen],
|
||||
hint_info->slen);
|
||||
}
|
||||
send_comp_arg(sess, &info);
|
||||
|
||||
len += info.hint[0].name.slen;
|
||||
sess->rcmd->rbuf[len] = 0;
|
||||
pj_memcpy(&sess->rcmd->rbuf[len], &info.hint[0].name.ptr[0],
|
||||
info.hint[0].name.slen);
|
||||
|
||||
len += info.hint[0].name.slen;
|
||||
sess->rcmd->rbuf[len] = 0;
|
||||
}
|
||||
} else {
|
||||
retval = PJ_FALSE;
|
||||
}
|
||||
|
|
|
@ -189,6 +189,8 @@ int main()
|
|||
#if USE_RANDOM_PORT
|
||||
tcfg.port = 0;
|
||||
#endif
|
||||
tcfg.port = 2233;
|
||||
tcfg.prompt_str = pj_str("CoolWater% ");
|
||||
status = pj_cli_telnet_create(cli, &tcfg, NULL);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
@ -220,11 +222,15 @@ pj_status_t app_main(pj_cli_t *cli)
|
|||
{
|
||||
pj_status_t status;
|
||||
pj_cli_sess *sess;
|
||||
pj_cli_console_cfg console_cfg;
|
||||
|
||||
pj_cli_console_cfg_default(&console_cfg);
|
||||
console_cfg.prompt_str = pj_str("HotWater> ");
|
||||
|
||||
/*
|
||||
* Create the console front end
|
||||
*/
|
||||
status = pj_cli_console_create(cli, NULL, &sess, NULL);
|
||||
status = pj_cli_console_create(cli, &console_cfg, &sess, NULL);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
|
@ -234,9 +240,10 @@ pj_status_t app_main(pj_cli_t *cli)
|
|||
* Main loop.
|
||||
*/
|
||||
for (;;) {
|
||||
char cmdline[PJ_CLI_MAX_CMDBUF];
|
||||
pj_status_t status;
|
||||
|
||||
status = pj_cli_console_process(sess);
|
||||
status = pj_cli_console_process(sess, &cmdline[0], sizeof(cmdline));
|
||||
if (status != PJ_SUCCESS)
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in New Issue