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:
Riza Sulistyo 2012-11-30 08:57:18 +00:00
parent e02d0df8f6
commit b23df5e0d3
5 changed files with 130 additions and 511 deletions

View File

@ -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);
/**
* @}

View File

@ -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;

View File

@ -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, &param->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;

View File

@ -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;
}

View File

@ -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;