diff --git a/pjlib-util/include/pjlib-util/cli_console.h b/pjlib-util/include/pjlib-util/cli_console.h index 8b185da52..41f3b5494 100644 --- a/pjlib-util/include/pjlib-util/cli_console.h +++ b/pjlib-util/include/pjlib-util/cli_console.h @@ -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); /** * @} diff --git a/pjlib-util/src/pjlib-util/cli.c b/pjlib-util/src/pjlib-util/cli.c index 0466d463c..dffe96c7a 100644 --- a/pjlib-util/src/pjlib-util/cli.c +++ b/pjlib-util/src/pjlib-util/cli.c @@ -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; diff --git a/pjlib-util/src/pjlib-util/cli_console.c b/pjlib-util/src/pjlib-util/cli_console.c index 1bd4eb220..067e498e6 100644 --- a/pjlib-util/src/pjlib-util/cli_console.c +++ b/pjlib-util/src/pjlib-util/cli_console.c @@ -27,57 +27,6 @@ #include #include -#if defined(PJ_LINUX) && PJ_LINUX != 0 || \ - defined(PJ_DARWINOS) && PJ_DARWINOS != 0 -#include - -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; diff --git a/pjlib-util/src/pjlib-util/cli_telnet.c b/pjlib-util/src/pjlib-util/cli_telnet.c index f8b524856..52d126388 100644 --- a/pjlib-util/src/pjlib-util/cli_telnet.c +++ b/pjlib-util/src/pjlib-util/cli_telnet.c @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #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; } diff --git a/pjsip-apps/src/samples/clidemo.c b/pjsip-apps/src/samples/clidemo.c index 2d68af772..a5aee95e3 100644 --- a/pjsip-apps/src/samples/clidemo.c +++ b/pjsip-apps/src/samples/clidemo.c @@ -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;