summaryrefslogtreecommitdiff
path: root/src/cmd.c
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
commit98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch)
treeebc6ec71f44bff8c42e4eefced61948623df02fc /src/cmd.c
parente6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff)
baresip 0.4.10
Diffstat (limited to 'src/cmd.c')
-rw-r--r--src/cmd.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/cmd.c b/src/cmd.c
new file mode 100644
index 0000000..7562081
--- /dev/null
+++ b/src/cmd.c
@@ -0,0 +1,351 @@
+/**
+ * @file src/cmd.c Command Interface
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <ctype.h>
+#include <string.h>
+#include <re.h>
+#include <baresip.h>
+#include "core.h"
+
+
+enum {
+ REL = 0x00,
+ ESC = 0x1b,
+ DEL = 0x7f,
+};
+
+
+struct cmds {
+ struct le le;
+ const struct cmd *cmdv;
+ size_t cmdc;
+};
+
+struct cmd_ctx {
+ struct mbuf *mb;
+ const struct cmd *cmd;
+};
+
+
+static struct list cmdl; /**< List of command blocks (struct cmds) */
+
+
+static void destructor(void *arg)
+{
+ struct cmds *cmds = arg;
+
+ list_unlink(&cmds->le);
+}
+
+
+static void ctx_destructor(void *arg)
+{
+ struct cmd_ctx *ctx = arg;
+
+ mem_deref(ctx->mb);
+}
+
+
+static int ctx_alloc(struct cmd_ctx **ctxp, const struct cmd *cmd)
+{
+ struct cmd_ctx *ctx;
+
+ ctx = mem_zalloc(sizeof(*ctx), ctx_destructor);
+ if (!ctx)
+ return ENOMEM;
+
+ ctx->mb = mbuf_alloc(32);
+ if (!ctx->mb) {
+ mem_deref(ctx);
+ return ENOMEM;
+ }
+
+ ctx->cmd = cmd;
+
+ *ctxp = ctx;
+
+ return 0;
+}
+
+
+static struct cmds *cmds_find(const struct cmd *cmdv)
+{
+ struct le *le;
+
+ if (!cmdv)
+ return NULL;
+
+ for (le = cmdl.head; le; le = le->next) {
+ struct cmds *cmds = le->data;
+
+ if (cmds->cmdv == cmdv)
+ return cmds;
+ }
+
+ return NULL;
+}
+
+
+static const struct cmd *cmd_find_by_key(char key)
+{
+ struct le *le;
+
+ 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 (cmd->key == key && cmd->h)
+ return cmd;
+ }
+ }
+
+ return NULL;
+}
+
+
+static const char *cmd_name(char *buf, size_t sz, const struct cmd *cmd)
+{
+ switch (cmd->key) {
+
+ case ' ': return "SPACE";
+ case '\n': return "ENTER";
+ case ESC: return "ESC";
+ }
+
+ buf[0] = cmd->key;
+ buf[1] = '\0';
+
+ if (cmd->flags & CMD_PRM)
+ strncat(buf, " ..", sz-1);
+
+ return buf;
+}
+
+
+static int editor_input(struct mbuf *mb, char key,
+ struct re_printf *pf, bool *del)
+{
+ int err = 0;
+
+ switch (key) {
+
+ case ESC:
+ *del = true;
+ return re_hprintf(pf, "\nCancel\n");
+
+ case REL:
+ break;
+
+ case '\n':
+ *del = true;
+ return re_hprintf(pf, "\n");
+
+ case '\b':
+ case DEL:
+ if (mb->pos > 0)
+ mb->pos = mb->end = (mb->pos - 1);
+ break;
+
+ default:
+ err = mbuf_write_u8(mb, key);
+ break;
+ }
+
+ err |= re_hprintf(pf, "\r> %32b", mb->buf, mb->end);
+
+ return err;
+}
+
+
+static int cmd_report(const struct cmd *cmd, struct re_printf *pf,
+ struct mbuf *mb, bool compl)
+{
+ struct cmd_arg arg;
+ int err;
+
+ mb->pos = 0;
+ err = mbuf_strdup(mb, &arg.prm, mb->end);
+ if (err)
+ return err;
+
+ arg.key = cmd->key;
+ arg.complete = compl;
+
+ err = cmd->h(pf, &arg);
+
+ mem_deref(arg.prm);
+
+ return err;
+}
+
+
+static int cmd_process_edit(struct cmd_ctx **ctxp, char key,
+ struct re_printf *pf)
+{
+ struct cmd_ctx *ctx;
+ bool compl = (key == '\n'), del = false;
+ int err;
+
+ if (!ctxp)
+ return EINVAL;
+
+ ctx = *ctxp;
+
+ err = editor_input(ctx->mb, key, pf, &del);
+ if (err)
+ return err;
+
+ if (compl || ctx->cmd->flags & CMD_PROG)
+ err = cmd_report(ctx->cmd, pf, ctx->mb, compl);
+
+ if (del)
+ *ctxp = mem_deref(*ctxp);
+
+ return err;
+}
+
+
+/**
+ * Register commands
+ *
+ * @param cmdv Array of commands
+ * @param cmdc Number of commands
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int cmd_register(const struct cmd *cmdv, size_t cmdc)
+{
+ struct cmds *cmds;
+
+ if (!cmdv || !cmdc)
+ return EINVAL;
+
+ cmds = cmds_find(cmdv);
+ if (cmds)
+ return EALREADY;
+
+ cmds = mem_zalloc(sizeof(*cmds), destructor);
+ if (!cmds)
+ return ENOMEM;
+
+ cmds->cmdv = cmdv;
+ cmds->cmdc = cmdc;
+
+ list_append(&cmdl, &cmds->le, cmds);
+
+ return 0;
+}
+
+
+/**
+ * Unregister commands
+ *
+ * @param cmdv Array of commands
+ */
+void cmd_unregister(const struct cmd *cmdv)
+{
+ mem_deref(cmds_find(cmdv));
+}
+
+
+/**
+ * Process input characters to the command system
+ *
+ * @param ctxp Pointer to context for editor (optional)
+ * @param key Input character
+ * @param pf Print function
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int cmd_process(struct cmd_ctx **ctxp, char key, struct re_printf *pf)
+{
+ const struct cmd *cmd;
+
+ /* are we in edit-mode? */
+ if (ctxp && *ctxp) {
+
+ if (key == REL)
+ return 0;
+
+ return cmd_process_edit(ctxp, key, pf);
+ }
+
+ cmd = cmd_find_by_key(key);
+ if (cmd) {
+ struct cmd_arg arg;
+
+ /* check for parameters */
+ if (cmd->flags & CMD_PRM) {
+
+ if (ctxp) {
+ int err = ctx_alloc(ctxp, cmd);
+ if (err)
+ return err;
+ }
+
+ return cmd_process_edit(ctxp,
+ isdigit(key) ? key : 0,
+ pf);
+ }
+
+ arg.key = key;
+ arg.prm = NULL;
+ arg.complete = true;
+
+ return cmd->h(pf, &arg);
+ }
+
+ if (key == REL)
+ return 0;
+
+ return cmd_print(pf, NULL);
+}
+
+
+/**
+ * Print a list of available commands
+ *
+ * @param pf Print function
+ * @param unused Unused variable
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+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 |= re_hprintf(pf, "\n");
+
+ return err;
+}