diff options
-rw-r--r-- | include/baresip.h | 11 | ||||
-rw-r--r-- | modules/auloop/auloop.c | 4 | ||||
-rw-r--r-- | modules/contact/contact.c | 10 | ||||
-rw-r--r-- | modules/debug_cmd/debug_cmd.c | 18 | ||||
-rw-r--r-- | modules/gtk/gtk_mod.c | 2 | ||||
-rw-r--r-- | modules/menu/menu.c | 125 | ||||
-rw-r--r-- | modules/natbd/natbd.c | 2 | ||||
-rw-r--r-- | modules/presence/presence.c | 4 | ||||
-rw-r--r-- | modules/vidloop/vidloop.c | 4 | ||||
-rw-r--r-- | modules/zrtp/zrtp.c | 2 | ||||
-rw-r--r-- | src/audio.c | 2 | ||||
-rw-r--r-- | src/cmd.c | 370 | ||||
-rw-r--r-- | src/ua.c | 2 | ||||
-rw-r--r-- | test/cmd.c | 70 | ||||
-rw-r--r-- | test/main.c | 1 | ||||
-rw-r--r-- | test/test.h | 1 |
16 files changed, 514 insertions, 114 deletions
diff --git a/include/baresip.h b/include/baresip.h index dfd36cc..ef42f2f 100644 --- a/include/baresip.h +++ b/include/baresip.h @@ -642,8 +642,9 @@ int ui_password_prompt(char **passwordp); */ /* special keys */ -#define KEYCODE_NONE (0x00) -#define KEYCODE_REL (-1) /* Key was released */ +#define KEYCODE_NONE (0x00) +#define KEYCODE_REL (-1) /* Key was released */ +#define KEYCODE_ESC (0x1b) /** Command flags */ @@ -664,7 +665,8 @@ struct cmd_arg { /** Defines a command */ struct cmd { - char key; /**< Input character */ + const char *name; /**< Long command */ + char key; /**< Short command */ int flags; /**< Optional command flags */ const char *desc; /**< Description string */ re_printf_h *h; /**< Command handler */ @@ -676,7 +678,10 @@ int cmd_register(const struct cmd *cmdv, size_t cmdc); void cmd_unregister(const struct cmd *cmdv); int cmd_process(struct cmd_ctx **ctxp, char key, struct re_printf *pf, void *data); +int cmd_process_long(const char *str, size_t len, + struct re_printf *pf_resp, void *data); int cmd_print(struct re_printf *pf, void *unused); +const struct cmd *cmd_find_long(const char *name); /* diff --git a/modules/auloop/auloop.c b/modules/auloop/auloop.c index 05ee623..5d1d9e8 100644 --- a/modules/auloop/auloop.c +++ b/modules/auloop/auloop.c @@ -361,8 +361,8 @@ static int auloop_stop(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'a', 0, "Start audio-loop", auloop_start }, - {'A', 0, "Stop audio-loop", auloop_stop }, + {"auloop", 'a', 0, "Start audio-loop", auloop_start }, + {"auloop_stop", 'A', 0, "Stop audio-loop", auloop_stop }, }; diff --git a/modules/contact/contact.c b/modules/contact/contact.c index c71c370..bc2f09e 100644 --- a/modules/contact/contact.c +++ b/modules/contact/contact.c @@ -77,7 +77,7 @@ static int cmd_contact(struct re_printf *pf, void *arg) switch (carg->key) { - case '/': + case '|': err = ua_connect(uag_current(), NULL, NULL, contact_str(cnt), NULL, VIDMODE_ON); if (err) { @@ -127,10 +127,10 @@ static int cmd_message(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'/', CMD_IPRM, "Dial from contacts", cmd_contact }, - {'=', CMD_IPRM, "Select chat peer", cmd_contact }, - {'C', 0, "List contacts", print_contacts }, - {'-', CMD_PRM, cmd_desc, cmd_message }, +{"dialcontact", '|', CMD_IPRM, "Dial from contacts", cmd_contact }, +{"chatpeer", '=', CMD_IPRM, "Select chat peer", cmd_contact }, +{"contacts", 'C', 0, "List contacts", print_contacts }, +{"message", '-', CMD_PRM, cmd_desc, cmd_message }, }; diff --git a/modules/debug_cmd/debug_cmd.c b/modules/debug_cmd/debug_cmd.c index aee65cc..0d6c375 100644 --- a/modules/debug_cmd/debug_cmd.c +++ b/modules/debug_cmd/debug_cmd.c @@ -63,15 +63,15 @@ static int cmd_ua_debug(struct re_printf *pf, void *unused) static const struct cmd debugcmdv[] = { - {'M', 0, "Main loop debug", re_debug }, - {'g', 0, "Print configuration", cmd_config_print }, - {'i', 0, "SIP debug", ua_print_sip_status }, - {'m', 0, "Module debug", mod_debug }, - {'n', 0, "Network debug", cmd_net_debug }, - {'s', 0, "System info", print_system_info }, - {'t', 0, "Timer debug", tmr_status }, - {'u', 0, "UA debug", cmd_ua_debug }, - {'y', 0, "Memory status", mem_status }, +{"main", 0, 0, "Main loop debug", re_debug }, +{"config", 'g', 0, "Print configuration", cmd_config_print }, +{"sipstat", 'i', 0, "SIP debug", ua_print_sip_status }, +{"modules", 'm', 0, "Module debug", mod_debug }, +{"netstat", 'n', 0, "Network debug", cmd_net_debug }, +{"sysinfo", 's', 0, "System info", print_system_info }, +{"timers", 0, 0, "Timer debug", tmr_status }, +{"uastat", 'u', 0, "UA debug", cmd_ua_debug }, +{"memstat", 'y', 0, "Memory status", mem_status }, }; diff --git a/modules/gtk/gtk_mod.c b/modules/gtk/gtk_mod.c index 55559d8..7397b9f 100644 --- a/modules/gtk/gtk_mod.c +++ b/modules/gtk/gtk_mod.c @@ -974,7 +974,7 @@ static int cmd_popup_menu(struct re_printf *pf, void *unused) static const struct cmd cmdv[] = { - {'G', 0, "Pop up GTK+ menu", cmd_popup_menu }, + {"gtk", 'G', 0, "Pop up GTK+ menu", cmd_popup_menu }, }; diff --git a/modules/menu/menu.c b/modules/menu/menu.c index 4349f48..619e88c 100644 --- a/modules/menu/menu.c +++ b/modules/menu/menu.c @@ -400,31 +400,34 @@ static int cmd_print_calls(struct re_printf *pf, void *unused) static const struct cmd cmdv[] = { - {'\n', 0, "Accept incoming call", cmd_answer }, - {'D', 0, "Accept incoming call", cmd_answer }, - {'b', 0, "Hangup call", cmd_hangup }, - {'c', 0, "Call status", ua_print_call_status }, - {'d', CMD_PRM, "Dial", dial_handler }, - {'h', 0, "Help menu", cmd_print }, - {'l', 0, "List active calls", cmd_print_calls }, - {'o', CMD_PRM, "Options", options_command }, - {'r', 0, "Registration info", ua_print_reg_status }, - {0x1b, 0, "Hangup call", cmd_hangup }, - {' ', 0, "Toggle UAs", cmd_ua_next }, - {'T', 0, "Toggle UAs", cmd_ua_next }, - {'R', CMD_PRM, "Create User-Agent", create_ua }, - {'#', CMD_PRM, NULL, dial_handler }, - {'*', CMD_PRM, NULL, dial_handler }, - {'0', CMD_PRM, NULL, dial_handler }, - {'1', CMD_PRM, NULL, dial_handler }, - {'2', CMD_PRM, NULL, dial_handler }, - {'3', CMD_PRM, NULL, dial_handler }, - {'4', CMD_PRM, NULL, dial_handler }, - {'5', CMD_PRM, NULL, dial_handler }, - {'6', CMD_PRM, NULL, dial_handler }, - {'7', CMD_PRM, NULL, dial_handler }, - {'8', CMD_PRM, NULL, dial_handler }, - {'9', CMD_PRM, NULL, dial_handler }, + +{NULL, '\n', 0, "Accept incoming call", cmd_answer }, +{"accept", 'D', 0, "Accept incoming call", cmd_answer }, +{"hangup", 'b', 0, "Hangup call", cmd_hangup }, +{"callstat", 'c', 0, "Call status", ua_print_call_status }, +{"dial", 'd', CMD_PRM, "Dial", dial_handler }, +{"help", 'h', 0, "Help menu", cmd_print }, +{"listcalls", 'l', 0, "List active calls", cmd_print_calls }, +{"options", 'o', CMD_PRM, "Options", options_command }, +{"reginfo", 'r', 0, "Registration info", ua_print_reg_status }, +{NULL, KEYCODE_ESC,0, "Hangup call", cmd_hangup }, +{NULL, ' ', 0, "Toggle UAs", cmd_ua_next }, +{NULL, 'T', 0, "Toggle UAs", cmd_ua_next }, +{NULL, 'R', CMD_PRM, "Create User-Agent", create_ua }, + +/* Numeric keypad inputs: */ +{NULL, '#', CMD_PRM, NULL, dial_handler }, +{NULL, '*', CMD_PRM, NULL, dial_handler }, +{NULL, '0', CMD_PRM, NULL, dial_handler }, +{NULL, '1', CMD_PRM, NULL, dial_handler }, +{NULL, '2', CMD_PRM, NULL, dial_handler }, +{NULL, '3', CMD_PRM, NULL, dial_handler }, +{NULL, '4', CMD_PRM, NULL, dial_handler }, +{NULL, '5', CMD_PRM, NULL, dial_handler }, +{NULL, '6', CMD_PRM, NULL, dial_handler }, +{NULL, '7', CMD_PRM, NULL, dial_handler }, +{NULL, '8', CMD_PRM, NULL, dial_handler }, +{NULL, '9', CMD_PRM, NULL, dial_handler }, }; @@ -487,12 +490,21 @@ static int call_xfer(struct re_printf *pf, void *arg) } -static int call_holdresume(struct re_printf *pf, void *arg) +static int cmd_call_hold(struct re_printf *pf, void *arg) +{ + (void)pf; + (void)arg; + + return call_hold(ua_call(uag_cur()), true); +} + + +static int cmd_call_resume(struct re_printf *pf, void *arg) { - const struct cmd_arg *carg = arg; (void)pf; + (void)arg; - return call_hold(ua_call(uag_cur()), 'x' == carg->key); + return call_hold(ua_call(uag_cur()), false); } @@ -663,38 +675,39 @@ static int set_current_call(struct re_printf *pf, void *arg) static const struct cmd callcmdv[] = { - {'I', 0, "Send re-INVITE", call_reinvite }, - {'X', 0, "Call resume", call_holdresume }, - {'a', 0, "Audio stream", call_audio_debug }, - {'e', 0, "Cycle audio encoder", call_audioenc_cycle }, - {'m', 0, "Call mute/un-mute", call_mute }, - {'r', CMD_IPRM,"Transfer call", call_xfer }, - {'x', 0, "Call hold", call_holdresume }, - {'H', 0, "Hold previous call", hold_prev_call }, - {'L', 0, "Resume previous call",hold_prev_call }, - {'A', CMD_IPRM,"Switch audio device", switch_audio_dev }, +{"", 'I', 0, "Send re-INVITE", call_reinvite }, +{"resume", 'X', 0, "Call resume", cmd_call_resume }, +{"", 'a', 0, "Audio stream", call_audio_debug }, +{"", 'e', 0, "Cycle audio encoder", call_audioenc_cycle }, +{"mute", 'm', 0, "Call mute/un-mute", call_mute }, +{"transfer", 'r', CMD_IPRM, "Transfer call", call_xfer }, +{"hold", 'x', 0, "Call hold", cmd_call_hold }, +{"", 'H', 0, "Hold previous call", hold_prev_call }, +{"", 'L', 0, "Resume previous call",hold_prev_call }, +{"", 'A', CMD_IPRM, "Switch audio device", switch_audio_dev }, #ifdef USE_VIDEO - {'E', 0, "Cycle video encoder", call_videoenc_cycle }, - {'v', 0, "Video stream", call_video_debug }, +{"", 'E', 0, "Cycle video encoder", call_videoenc_cycle }, +{"", 'v', 0, "Video stream", call_video_debug }, #endif - {'#', 0, NULL, digit_handler }, - {'*', 0, NULL, digit_handler }, - {'0', 0, NULL, digit_handler }, - {'1', 0, NULL, digit_handler }, - {'2', 0, NULL, digit_handler }, - {'3', 0, NULL, digit_handler }, - {'4', 0, NULL, digit_handler }, - {'5', 0, NULL, digit_handler }, - {'6', 0, NULL, digit_handler }, - {'7', 0, NULL, digit_handler }, - {'8', 0, NULL, digit_handler }, - {'9', 0, NULL, digit_handler }, - {KEYCODE_REL,0,NULL, digit_handler }, - - {'S', 0, "Statusmode toggle", toggle_statmode }, - {'@', CMD_PRM, "Set current call <line>", set_current_call }, +/* Numeric keypad for DTMF events: */ +{NULL, '#', 0, NULL, digit_handler }, +{NULL, '*', 0, NULL, digit_handler }, +{NULL, '0', 0, NULL, digit_handler }, +{NULL, '1', 0, NULL, digit_handler }, +{NULL, '2', 0, NULL, digit_handler }, +{NULL, '3', 0, NULL, digit_handler }, +{NULL, '4', 0, NULL, digit_handler }, +{NULL, '5', 0, NULL, digit_handler }, +{NULL, '6', 0, NULL, digit_handler }, +{NULL, '7', 0, NULL, digit_handler }, +{NULL, '8', 0, NULL, digit_handler }, +{NULL, '9', 0, NULL, digit_handler }, +{NULL, KEYCODE_REL, 0, NULL, digit_handler }, + +{NULL, 'S', 0, "Statusmode toggle", toggle_statmode }, +{NULL, '@', CMD_PRM, "Set current call <line>", set_current_call }, }; diff --git a/modules/natbd/natbd.c b/modules/natbd/natbd.c index 428a51a..3c45ba4 100644 --- a/modules/natbd/natbd.c +++ b/modules/natbd/natbd.c @@ -451,7 +451,7 @@ static int status(struct re_printf *pf, void *unused) static const struct cmd cmdv[] = { - {'z', 0, "NAT status", status} + {"natbd", 'z', 0, "NAT status", status} }; diff --git a/modules/presence/presence.c b/modules/presence/presence.c index f343365..bfde2cd 100644 --- a/modules/presence/presence.c +++ b/modules/presence/presence.c @@ -47,8 +47,8 @@ static int cmd_offline(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'[', 0, "Set presence online", cmd_online }, - {']', 0, "Set presence offline", cmd_offline }, + {"presence_online", '[', 0, "Set presence online", cmd_online }, + {"presence_offline", ']', 0, "Set presence offline", cmd_offline }, }; diff --git a/modules/vidloop/vidloop.c b/modules/vidloop/vidloop.c index 59a5ed3..a9f44fa 100644 --- a/modules/vidloop/vidloop.c +++ b/modules/vidloop/vidloop.c @@ -469,8 +469,8 @@ static int vidloop_stop(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'v', 0, "Start video-loop", vidloop_start }, - {'V', 0, "Stop video-loop", vidloop_stop }, + {"vidloop", 'v', 0, "Start video-loop", vidloop_start }, + {"vidloop_stop", 'V', 0, "Stop video-loop", vidloop_stop }, }; diff --git a/modules/zrtp/zrtp.c b/modules/zrtp/zrtp.c index eddb364..9688545 100644 --- a/modules/zrtp/zrtp.c +++ b/modules/zrtp/zrtp.c @@ -311,7 +311,7 @@ static int verify_sas(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'Z', CMD_PRM, "Verify ZRTP SAS", verify_sas }, + {"zrtp", 'Z', CMD_PRM, "Verify ZRTP SAS", verify_sas }, }; diff --git a/src/audio.c b/src/audio.c index c373cb8..f330864 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1321,7 +1321,7 @@ int audio_send_digit(struct audio *a, char key) info("audio: send DTMF digit: '%c'\n", key); err = telev_send(a->telev, telev_digit2code(key), false); } - else if (a->tx.cur_key) { + else if (a->tx.cur_key != KEYCODE_REL) { /* Key release */ info("audio: send DTMF digit end: '%c'\n", a->tx.cur_key); err = telev_send(a->telev, @@ -11,8 +11,8 @@ enum { - ESC = 0x1b, - DEL = 0x7f, + KEYCODE_DEL = 0x7f, + LONG_PREFIX = '/' }; @@ -25,12 +25,18 @@ struct cmds { struct cmd_ctx { struct mbuf *mb; const struct cmd *cmd; + bool is_long; }; static struct list cmdl; /**< List of command blocks (struct cmds) */ +static int cmd_print_all(struct re_printf *pf, + bool print_long, bool print_short, + const char *match, size_t match_len); + + static void destructor(void *arg) { struct cmds *cmds = arg; @@ -115,7 +121,7 @@ static const char *cmd_name(char *buf, size_t sz, const struct cmd *cmd) case ' ': return "SPACE"; case '\n': return "ENTER"; - case ESC: return "ESC"; + case KEYCODE_ESC: return "ESC"; } buf[0] = cmd->key; @@ -128,14 +134,45 @@ static const char *cmd_name(char *buf, size_t sz, const struct cmd *cmd) } +static size_t print_match(const struct cmd **cmdp, struct re_printf *pf, + const char *str, size_t len) +{ + struct le *le; + size_t nmatch = 0; + + for (le = cmdl.head; le; le = le->next) { + + struct cmds *cmds = le->data; + size_t i; + + for (i=0; i<cmds->cmdc; i++) { + + const struct cmd *cmd = &cmds->cmdv[i]; + + if (!str_isset(cmd->name)) + continue; + + if (str_len(cmd->name) >= len && + 0 == memcmp(cmd->name, str, len)) { + + ++nmatch; + *cmdp = cmd; + } + } + } + + return nmatch; +} + + static int editor_input(struct mbuf *mb, char key, - struct re_printf *pf, bool *del) + struct re_printf *pf, bool *del, bool is_long) { int err = 0; switch (key) { - case ESC: + case KEYCODE_ESC: *del = true; return re_hprintf(pf, "\nCancel\n"); @@ -147,9 +184,52 @@ static int editor_input(struct mbuf *mb, char key, return re_hprintf(pf, "\n"); case '\b': - case DEL: - if (mb->pos > 0) + case KEYCODE_DEL: + if (mb->pos > 0) { + err |= re_hprintf(pf, "\b "); mb->pos = mb->end = (mb->pos - 1); + } + break; + + case '\t': + if (is_long) { + const struct cmd *cmd = NULL; + size_t n; + + err = re_hprintf(pf, + "TAB completion for \"%b\":\n", + mb->buf, mb->end); + if (err) + return err; + + /* Find all long commands that matches the N + * first characters of the input string. + * + * If the number of matches is exactly one, + * we can regard it as TAB completion. + */ + + err = cmd_print_all(pf, true, false, + (char *)mb->buf, mb->end); + if (err) + return err; + + n = print_match(&cmd, pf, (char *)mb->buf, mb->end); + if (n == 1 && cmd) { + + re_printf("replace: %b -> %s\n", + mb->buf, mb->end, cmd->name); + + mb->pos = 0; + mbuf_write_str(mb, cmd->name); + } + else if (n == 0) { + err = re_hprintf(pf, "(none)\n"); + } + } + else { + err = mbuf_write_u8(mb, key); + } break; default: @@ -157,7 +237,12 @@ static int editor_input(struct mbuf *mb, char key, break; } - err |= re_hprintf(pf, "\r> %32b", mb->buf, mb->end); + if (is_long) { + err |= re_hprintf(pf, "\r/%b", + mb->buf, mb->end); + } + else + err |= re_hprintf(pf, "\r> %32b", mb->buf, mb->end); return err; } @@ -169,6 +254,8 @@ static int cmd_report(const struct cmd *cmd, struct re_printf *pf, struct cmd_arg arg; int err; + memset(&arg, 0, sizeof(arg)); + mb->pos = 0; err = mbuf_strdup(mb, &arg.prm, mb->end); if (err) @@ -186,6 +273,54 @@ static int cmd_report(const struct cmd *cmd, struct re_printf *pf, } +int cmd_process_long(const char *str, size_t len, + struct re_printf *pf_resp, void *data) +{ + struct cmd_arg arg; + const struct cmd *cmd_long; + char *name = NULL, *prm = NULL; + struct pl pl_name, pl_prm; + int err; + + if (!str || !len) + return EINVAL; + + memset(&arg, 0, sizeof(arg)); + + err = re_regex(str, len, "[^ ]+[ ]*[~]*", &pl_name, NULL, &pl_prm); + if (err) { + return err; + } + + err = pl_strdup(&name, &pl_name); + if (pl_isset(&pl_prm)) + err |= pl_strdup(&prm, &pl_prm); + if (err) + goto out; + + cmd_long = cmd_find_long(name); + if (cmd_long) { + + arg.key = LONG_PREFIX; + arg.prm = prm; + arg.complete = true; + arg.data = data; + + if (cmd_long->h) + err = cmd_long->h(pf_resp, &arg); + } + else { + err = re_hprintf(pf_resp, "command not found (%s)\n", name); + } + + out: + mem_deref(name); + mem_deref(prm); + + return err; +} + + static int cmd_process_edit(struct cmd_ctx **ctxp, char key, struct re_printf *pf, void *data) { @@ -198,12 +333,24 @@ static int cmd_process_edit(struct cmd_ctx **ctxp, char key, ctx = *ctxp; - err = editor_input(ctx->mb, key, pf, &del); + err = editor_input(ctx->mb, key, pf, &del, ctx->is_long); if (err) return err; - if (compl || ctx->cmd->flags & CMD_PROG) - err = cmd_report(ctx->cmd, pf, ctx->mb, compl, data); + if (ctx->is_long) { + + if (compl) { + + err = cmd_process_long((char *)ctx->mb->buf, + ctx->mb->end, + pf, data); + } + } + else { + if (compl || + (ctx->cmd && ctx->cmd->flags & CMD_PROG)) + err = cmd_report(ctx->cmd, pf, ctx->mb, compl, data); + } if (del) *ctxp = mem_deref(*ctxp); @@ -223,6 +370,7 @@ static int cmd_process_edit(struct cmd_ctx **ctxp, char key, int cmd_register(const struct cmd *cmdv, size_t cmdc) { struct cmds *cmds; + size_t i; if (!cmdv || !cmdc) return EINVAL; @@ -231,6 +379,23 @@ int cmd_register(const struct cmd *cmdv, size_t cmdc) if (cmds) return EALREADY; + /* verify that command is not registered */ + for (i=0; i<cmdc; i++) { + const struct cmd *cmd = &cmdv[i]; + + if (cmd->key == LONG_PREFIX) { + warning("cmd: cannot register command with" + " short key '%c'\n", cmd->key); + return EINVAL; + } + + if (str_isset(cmd->name) && cmd_find_long(cmd->name)) { + warning("cmd: long command '%s' already registered\n", + cmd->name); + return EINVAL; + } + } + cmds = mem_zalloc(sizeof(*cmds), destructor); if (!cmds) return ENOMEM; @@ -255,6 +420,31 @@ void cmd_unregister(const struct cmd *cmdv) } +const struct cmd *cmd_find_long(const char *name) +{ + struct le *le; + + if (!name) + return NULL; + + for (le = cmdl.tail; le; le = le->prev) { + + struct cmds *cmds = le->data; + size_t i; + + for (i=0; i<cmds->cmdc; i++) { + + const struct cmd *cmd = &cmds->cmdv[i]; + + if (0 == str_casecmp(name, cmd->name) && cmd->h) + return cmd; + } + } + + return NULL; +} + + /** * Process input characters to the command system * @@ -306,6 +496,30 @@ int cmd_process(struct cmd_ctx **ctxp, char key, struct re_printf *pf, return cmd->h(pf, &arg); } + else if (key == LONG_PREFIX) { + + int err; + + err = re_hprintf(pf, "\n/"); + if (err) + return err; + + if (!ctxp) { + warning("cmd: ctxp is required\n"); + return EINVAL; + } + + err = ctx_alloc(ctxp, cmd); + if (err) + return err; + + (*ctxp)->is_long = true; + + return 0; + } + else if (key == '\t') { + return cmd_print_all(pf, false, true, NULL, 0); + } if (key == KEYCODE_REL) return 0; @@ -314,6 +528,121 @@ int cmd_process(struct cmd_ctx **ctxp, char key, struct re_printf *pf, } +struct cmd_sort { + struct le le; + const struct cmd *cmd; +}; + + +static bool sort_handler(struct le *le1, struct le *le2, void *arg) +{ + struct cmd_sort *cs1 = le1->data; + struct cmd_sort *cs2 = le2->data; + const struct cmd *cmd1 = cs1->cmd; + const struct cmd *cmd2 = cs2->cmd; + bool print_long = *(bool *)arg; + + if (print_long) { + return str_casecmp(cs2->cmd->name ? cs2->cmd->name : "", + cs1->cmd->name ? cs1->cmd->name : "") >= 0; + } + else { + return tolower(cmd2->key) >= tolower(cmd1->key); + } +} + + +static int cmd_print_all(struct re_printf *pf, + bool print_long, bool print_short, + const char *match, size_t match_len) +{ + struct list sortedl = LIST_INIT; + struct le *le; + size_t width_long = 1; + size_t width_short = 5; + char fmt[64]; + char buf[16]; + int err = 0; + + for (le = cmdl.head; le; le = le->next) { + + struct cmds *cmds = le->data; + size_t i; + + for (i=0; i<cmds->cmdc; i++) { + + const struct cmd *cmd = &cmds->cmdv[i]; + struct cmd_sort *cs; + + if (match && match_len) { + + if (str_len(cmd->name) >= match_len && + 0 == memcmp(cmd->name, match, match_len)) { + /* Match */ + } + else { + continue; + } + } + + if (!str_isset(cmd->desc)) + continue; + + if (print_short && !print_long) { + + if (cmd->key == KEYCODE_NONE) + continue; + } + + cs = mem_zalloc(sizeof(*cs), NULL); + if (!cs) { + err = ENOMEM; + goto out; + } + cs->cmd = cmd; + + list_append(&sortedl, &cs->le, cs); + + width_long = max(width_long, 1+str_len(cmd->name)+2); + } + } + + list_sort(&sortedl, sort_handler, &print_long); + + if (re_snprintf(fmt, sizeof(fmt), + " %%-%zus %%-%zus %%s\n", + width_long, width_short) < 0) { + err = ENOMEM; + goto out; + } + + for (le = sortedl.head; le; le = le->next) { + struct cmd_sort *cs = le->data; + const struct cmd *cmd = cs->cmd; + char namep[64] = ""; + + if (print_long && str_isset(cmd->name)) { + re_snprintf(namep, sizeof(namep), "%c%s%s", + LONG_PREFIX, cmd->name, + (cmd->flags & CMD_PRM) ? " .." : ""); + } + + err |= re_hprintf(pf, fmt, + namep, + print_short + ? cmd_name(buf, sizeof(buf), cmd) + : "", + cmd->desc); + } + + err |= re_hprintf(pf, "\n"); + + out: + list_flush(&sortedl); + return err; +} + + /** * Print a list of available commands * @@ -324,32 +653,15 @@ int cmd_process(struct cmd_ctx **ctxp, char key, struct re_printf *pf, */ int cmd_print(struct re_printf *pf, void *unused) { - size_t width = 5; - char fmt[32], buf[8]; int err = 0; - int key; (void)unused; if (!pf) return EINVAL; - (void)re_snprintf(fmt, sizeof(fmt), " %%-%zus %%s\n", width); - err |= re_hprintf(pf, "--- Help ---\n"); - - /* print in alphabetical order */ - for (key = 1; key <= 0x80; key++) { - - const struct cmd *cmd = cmd_find_by_key(key); - if (!cmd || !str_isset(cmd->desc)) - continue; - - err |= re_hprintf(pf, fmt, cmd_name(buf, sizeof(buf), cmd), - cmd->desc); - - } - + err |= cmd_print_all(pf, true, true, NULL, 0); err |= re_hprintf(pf, "\n"); return err; @@ -1284,7 +1284,7 @@ static int cmd_quit(struct re_printf *pf, void *unused) static const struct cmd cmdv[] = { - {'q', 0, "Quit", cmd_quit }, + {"quit", 'q', 0, "Quit", cmd_quit }, }; @@ -32,7 +32,7 @@ static int cmd_test(struct re_printf *pf, void *arg) static const struct cmd cmdv[] = { - {'@', 0, "Test command", cmd_test}, + {NULL, '@', 0, "Test command", cmd_test}, }; @@ -77,3 +77,71 @@ int test_cmd(void) out: return err; } + + +static int long_handler(struct re_printf *pf, void *arg) +{ + struct cmd_arg *carg = arg; + struct test *test = carg->data; + int err = 0; + (void)pf; + + ASSERT_STREQ("123", carg->prm); + + ++test->cmd_called; + + out: + return err; +} + + +static const struct cmd longcmdv[] = { + { "test", 0, 0, "Test Command", long_handler}, +}; + + +int test_cmd_long(void) +{ + struct test test; + const struct cmd *cmd; + static const char *input_str = "/test 123\n"; + struct cmd_ctx *ctx = NULL; + size_t i; + int err; + + memset(&test, 0, sizeof(test)); + + /* Verify that the command does not exist */ + cmd = cmd_find_long("test"); + ASSERT_TRUE(cmd == NULL); + + /* Register and verify command */ + err = cmd_register(longcmdv, ARRAY_SIZE(longcmdv)); + ASSERT_EQ(0, err); + + cmd = cmd_find_long("test"); + ASSERT_TRUE(cmd != NULL); + + /* Feed it some input data .. */ + + for (i=0; i<strlen(input_str); i++) { + + err = cmd_process(&ctx, input_str[i], &pf_null, &test); + ASSERT_EQ(0, err); + } + + err = cmd_process_long("test 123", 8, &pf_null, &test); + ASSERT_EQ(0, err); + + ASSERT_EQ(2, test.cmd_called); + + /* Cleanup .. */ + + cmd_unregister(longcmdv); + + cmd = cmd_find_long("test"); + ASSERT_TRUE(cmd == NULL); + + out: + return err; +} diff --git a/test/main.c b/test/main.c index 6991671..3a5dd77 100644 --- a/test/main.c +++ b/test/main.c @@ -29,6 +29,7 @@ static const struct test tests[] = { TEST(test_call_max), TEST(test_call_dtmf), TEST(test_cmd), + TEST(test_cmd_long), TEST(test_contact), TEST(test_cplusplus), TEST(test_mos), diff --git a/test/test.h b/test/test.h index 4c6c11b..bd41c82 100644 --- a/test/test.h +++ b/test/test.h @@ -95,6 +95,7 @@ int mock_ausrc_register(struct ausrc **ausrcp); /* test cases */ int test_cmd(void); +int test_cmd_long(void); int test_contact(void); int test_ua_alloc(void); int test_uag_find_param(void); |