summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/baresip.h11
-rw-r--r--modules/auloop/auloop.c4
-rw-r--r--modules/contact/contact.c10
-rw-r--r--modules/debug_cmd/debug_cmd.c18
-rw-r--r--modules/gtk/gtk_mod.c2
-rw-r--r--modules/menu/menu.c125
-rw-r--r--modules/natbd/natbd.c2
-rw-r--r--modules/presence/presence.c4
-rw-r--r--modules/vidloop/vidloop.c4
-rw-r--r--modules/zrtp/zrtp.c2
-rw-r--r--src/audio.c2
-rw-r--r--src/cmd.c370
-rw-r--r--src/ua.c2
-rw-r--r--test/cmd.c70
-rw-r--r--test/main.c1
-rw-r--r--test/test.h1
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,
diff --git a/src/cmd.c b/src/cmd.c
index cd70f5b..5d4e0c8 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -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;
diff --git a/src/ua.c b/src/ua.c
index 41a80e7..7f0f1b0 100644
--- a/src/ua.c
+++ b/src/ua.c
@@ -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 },
};
diff --git a/test/cmd.c b/test/cmd.c
index 92325b7..f4c738c 100644
--- a/test/cmd.c
+++ b/test/cmd.c
@@ -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);