diff --git a/client/main.c b/client/main.c
index 034c500..63612d8 100644
--- a/client/main.c
+++ b/client/main.c
ad_advertise_timeout(dbus_conn, value);
}
-static const struct bt_shell_menu_entry cmd_table[] = {
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
{ "list", NULL, cmd_list, "List available controllers" },
{ "show", "[ctrl]", cmd_show, "Controller information",
ctrl_generator },
{ "unregister-descriptor", "<UUID/object>",
cmd_unregister_descriptor,
"Unregister application descriptor" },
- { }
+ { } },
};
static gboolean parse_agent(const char *key, const char *value,
g_option_context_free(context);
bt_shell_init(&argc, &argv);
- bt_shell_set_menu(cmd_table);
+ bt_shell_set_menu(&main_menu);
bt_shell_set_prompt(PROMPT_OFF);
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
diff --git a/src/shared/shell.c b/src/shared/shell.c
index 7db629b..8c29dd7 100644
--- a/src/shared/shell.c
+++ b/src/shared/shell.c
#define print_menu(cmd, args, desc) \
printf(COLOR_HIGHLIGHT "%s %-*s " COLOR_OFF "%s\n", \
cmd, (int)(CMD_LENGTH - strlen(cmd)), args, desc)
+#define print_submenu(cmd, desc) \
+ printf(COLOR_BLUE "%s %-*s " COLOR_OFF "%s\n", \
+ cmd, (int)(CMD_LENGTH - strlen(cmd)), "", desc)
static GMainLoop *main_loop;
static gboolean option_version = FALSE;
bt_shell_prompt_input_func saved_func;
void *saved_user_data;
- const struct bt_shell_menu_entry *menu;
- /* TODO: Add submenus support */
+ const struct bt_shell_menu *menu;
+ const struct bt_shell_menu *main;
+ struct queue *submenus;
} data;
static void shell_print_menu(void);
shell_print_menu();
}
+static const struct bt_shell_menu *find_menu(const char *name)
+{
+ const struct queue_entry *entry;
+
+ for (entry = queue_get_entries(data.submenus); entry;
+ entry = entry->next) {
+ struct bt_shell_menu *menu = entry->data;
+
+ if (!strcmp(menu->name, name))
+ return menu;
+ }
+
+ return NULL;
+}
+
+static char *menu_generator(const char *text, int state)
+{
+ static unsigned int index, len;
+ static struct queue_entry *entry;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ entry = (void *) queue_get_entries(data.submenus);
+ }
+
+ for (; entry; entry = entry->next) {
+ struct bt_shell_menu *menu = entry->data;
+
+ index++;
+
+ if (!strncmp(menu->name, text, len)) {
+ entry = entry->next;
+ return strdup(menu->name);
+ }
+ }
+
+ return NULL;
+}
+
+static void cmd_menu(const char *arg)
+{
+ const struct bt_shell_menu *menu;
+
+ if (!arg || !strlen(arg)) {
+ bt_shell_printf("Missing name argument\n");
+ return;
+ }
+
+ menu = find_menu(arg);
+ if (!menu) {
+ bt_shell_printf("Unable find menu with name: %s\n", arg);
+ return;
+ }
+
+ bt_shell_set_menu(menu);
+
+ shell_print_menu();
+}
+
+static void cmd_back(const char *arg)
+{
+ if (data.menu == data.main) {
+ bt_shell_printf("Already on main menu\n");
+ return;
+ }
+
+ bt_shell_set_menu(data.main);
+
+ shell_print_menu();
+}
+
static const struct bt_shell_menu_entry default_menu[] = {
+ { "back", NULL, cmd_back, "Return to main menu" },
+ { "menu", "<name>", cmd_menu, "Select submenu",
+ menu_generator },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit, "Quit program" },
static void shell_print_menu(void)
{
const struct bt_shell_menu_entry *entry;
+ const struct queue_entry *submenu;
if (!data.menu)
return;
+ print_text(COLOR_HIGHLIGHT, "Menu %s:", data.menu->name);
print_text(COLOR_HIGHLIGHT, "Available commands:");
print_text(COLOR_HIGHLIGHT, "-------------------");
- for (entry = data.menu; entry->cmd; entry++) {
+
+ if (data.menu == data.main) {
+ for (submenu = queue_get_entries(data.submenus); submenu;
+ submenu = submenu->next) {
+ struct bt_shell_menu *menu = submenu->data;
+
+ print_submenu(menu->name, "Submenu");
+ }
+ }
+
+ for (entry = data.menu->entries; entry->cmd; entry++) {
print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
}
for (entry = default_menu; entry->cmd; entry++) {
+ /* Skip menu command if not on main menu */
+ if (data.menu != data.main && !strcmp(entry->cmd, "menu"))
+ continue;
+
+ /* Skip back command if on main menu */
+ if (data.menu == data.main && !strcmp(entry->cmd, "back"))
+ continue;
+
print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
}
}
-static void shell_exec(const char *cmd, const char *arg)
+static int menu_exec(const struct bt_shell_menu_entry *entry,
+ const char *cmd, const char *arg)
{
- const struct bt_shell_menu_entry *entry;
-
- if (!data.menu || !cmd)
- return;
-
- for (entry = data.menu; entry->cmd; entry++) {
+ for (; entry->cmd; entry++) {
if (strcmp(cmd, entry->cmd))
continue;
- if (entry->func) {
- entry->func(arg);
- return;
- }
- }
+ /* Skip menu command if not on main menu */
+ if (data.menu != data.main && !strcmp(entry->cmd, "menu"))
+ continue;
- for (entry = default_menu; entry->cmd; entry++) {
- if (strcmp(cmd, entry->cmd))
+ /* Skip back command if on main menu */
+ if (data.menu == data.main && !strcmp(entry->cmd, "back"))
continue;
if (entry->func) {
entry->func(arg);
- return;
+ return 0;
}
}
- print_text(COLOR_HIGHLIGHT, "Invalid command");
+ return -ENOENT;
+}
+
+static void shell_exec(const char *cmd, const char *arg)
+{
+ if (!data.menu || !cmd)
+ return;
+
+ if (menu_exec(default_menu, cmd, arg) < 0) {
+ if (menu_exec(data.menu->entries, cmd, arg) < 0)
+ print_text(COLOR_HIGHLIGHT, "Invalid command");
+ }
}
void bt_shell_printf(const char *fmt, ...)
if (state)
return NULL;
- entry = data.menu;
+ entry = data.menu->entries;
index = 0;
return cmd_generator(text, 1);
{
char **matches = NULL;
- for (entry = data.menu; entry->cmd; entry++) {
+ for (; entry->cmd; entry++) {
if (strcmp(entry->cmd, input_cmd))
continue;
input_cmd = strndup(rl_line_buffer, start - 1);
matches = menu_completion(default_menu, text, input_cmd);
if (!matches)
- matches = menu_completion(data.menu, text,
+ matches = menu_completion(data.menu->entries, text,
input_cmd);
free(input_cmd);
rl_cleanup();
}
-bool bt_shell_set_menu(const struct bt_shell_menu_entry *menu)
+bool bt_shell_set_menu(const struct bt_shell_menu *menu)
{
- if (data.menu || !menu)
+ if (!menu)
return false;
data.menu = menu;
+ if (!data.main)
+ data.main = menu;
+
+ return true;
+}
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu)
+{
+ if (!menu)
+ return false;
+
+ if (!data.submenus)
+ data.submenus = queue_new();
+
+ queue_push_tail(data.submenus, (void *) menu);
+
return true;
}
diff --git a/src/shared/shell.h b/src/shared/shell.h
index 8433357..114219c 100644
--- a/src/shared/shell.h
+++ b/src/shared/shell.h
bt_shell_menu_disp_t disp;
};
+struct bt_shell_menu {
+ const char *name;
+ const struct bt_shell_menu_entry entries[];
+};
+
void bt_shell_init(int *argc, char ***argv);
void bt_shell_run(void);
-bool bt_shell_set_menu(const struct bt_shell_menu_entry *menu);
+bool bt_shell_set_menu(const struct bt_shell_menu *menu);
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu);
+
+bool bt_shell_remove_submenu(const struct bt_shell_menu *menu);
void bt_shell_set_prompt(const char *string);