This patchset add tab completion to haltest so using this tool
will be much easier.
It allows to build for andriod 4.3 and for host with autotools.
Jerzy Kasenberg (3):
android: Add tab completion to haltest
android: Fix error in draw line in haltest tool
android: Add completion for adapter to haltest
Makefile.android | 2 +
android/Android.mk | 1 +
android/client/haltest.c | 11 +-
android/client/if-bt.c | 331 ++++++++++++++++++++++++++++++++++------
android/client/if-main.h | 44 +++++-
android/client/tabcompletion.c | 313 +++++++++++++++++++++++++++++++++++++
android/client/terminal.c | 4 +-
android/client/textconv.c | 21 +++
android/client/textconv.h | 4 +
9 files changed, 673 insertions(+), 58 deletions(-)
create mode 100644 android/client/tabcompletion.c
--
1.7.9.5
Hi Jerzy,
On Fri, Oct 18, 2013, Jerzy Kasenberg wrote:
> This patchset add tab completion to haltest so using this tool
> will be much easier.
>
> It allows to build for andriod 4.3 and for host with autotools.
>
> Jerzy Kasenberg (3):
> android: Add tab completion to haltest
> android: Fix error in draw line in haltest tool
> android: Add completion for adapter to haltest
>
> Makefile.android | 2 +
> android/Android.mk | 1 +
> android/client/haltest.c | 11 +-
> android/client/if-bt.c | 331 ++++++++++++++++++++++++++++++++++------
> android/client/if-main.h | 44 +++++-
> android/client/tabcompletion.c | 313 +++++++++++++++++++++++++++++++++++++
> android/client/terminal.c | 4 +-
> android/client/textconv.c | 21 +++
> android/client/textconv.h | 4 +
> 9 files changed, 673 insertions(+), 58 deletions(-)
> create mode 100644 android/client/tabcompletion.c
All three patches have been applied. Thanks.
Johan
This patch adds all completion functions for adapter methods.
It also adds short help lines for all methods that require arguments.
---
android/client/haltest.c | 11 +-
android/client/if-bt.c | 331 ++++++++++++++++++++++++++++++++++++++-------
android/client/if-main.h | 16 +++
android/client/textconv.c | 21 +++
android/client/textconv.h | 4 +
5 files changed, 330 insertions(+), 53 deletions(-)
diff --git a/android/client/haltest.c b/android/client/haltest.c
index 6dcabbc..6b37f33 100644
--- a/android/client/haltest.c
+++ b/android/client/haltest.c
@@ -103,11 +103,12 @@ static void process_line(char *line_buffer)
continue;
}
if (argc < 2 || strcmp(argv[1], "?") == 0) {
- j = 0;
- while (strcmp(interfaces[i]->methods[j].name, "")) {
- haltest_info("%s %s\n", argv[0],
- interfaces[i]->methods[j].name);
- ++j;
+ struct method *m = &interfaces[i]->methods[0];
+ while (strcmp(m->name, "")) {
+ haltest_info("%s %s %s\n", argv[0],
+ m->name,
+ (m->help ? m->help : ""));
+ m++;
}
return;
}
diff --git a/android/client/if-bt.c b/android/client/if-bt.c
index 90ef732..30b41cd 100644
--- a/android/client/if-bt.c
+++ b/android/client/if-bt.c
@@ -19,6 +19,16 @@
const bt_interface_t *if_bluetooth;
+#define VERIFY_PROP_TYPE_ARG(n, typ) \
+ do { \
+ if (n < argc) \
+ typ = str2btpropertytype(argv[n]); \
+ else { \
+ haltest_error("No property type specified\n"); \
+ return;\
+ } \
+ } while (0)
+
static char *bdaddr2str(const bt_bdaddr_t *bd_addr)
{
static char buf[18];
@@ -177,6 +187,84 @@ static void dump_properties(int num_properties, bt_property_t *properties)
}
}
+/*
+ * Cache for remote devices, stored in sorted array
+ */
+static bt_bdaddr_t *remote_devices = NULL;
+static int remote_devices_cnt = 0;
+static int remote_devices_capacity = 0;
+
+/* Adds address to remote device set so it can be used in tab completion */
+void add_remote_device(const bt_bdaddr_t *addr)
+{
+ int i;
+
+ if (remote_devices == NULL) {
+ remote_devices = malloc(4 * sizeof(bt_bdaddr_t));
+ remote_devices_cnt = 0;
+ if (remote_devices == NULL) {
+ remote_devices_capacity = 0;
+ return;
+ }
+ remote_devices_capacity = 4;
+ }
+
+ /* Array is sorted, search for right place */
+ for (i = 0; i < remote_devices_cnt; ++i) {
+ int res = memcmp(&remote_devices[i], addr, sizeof(*addr));
+ if (res == 0)
+ return; /* Already added */
+ else if (res > 0)
+ break;
+ }
+
+ /* Realloc space if needed */
+ if (remote_devices_cnt >= remote_devices_capacity) {
+ remote_devices_capacity *= 2;
+ remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+ remote_devices_capacity);
+ if (remote_devices == NULL) {
+ remote_devices_capacity = 0;
+ remote_devices_cnt = 0;
+ return;
+ }
+ }
+
+ if (i < remote_devices_cnt)
+ memmove(remote_devices + i + 1, remote_devices + i,
+ (remote_devices_cnt - i) * sizeof(bt_bdaddr_t));
+ remote_devices[i] = *addr;
+ remote_devices_cnt++;
+}
+
+const char *enum_devices(void *v, int i)
+{
+ static char buf[19];
+
+ if (i >= remote_devices_cnt)
+ return NULL;
+
+ bt_bdaddr_t2str(&remote_devices[i], buf);
+ return buf;
+}
+
+static void add_remote_device_from_props(int num_properties,
+ const bt_property_t *properties)
+{
+ int i;
+
+ for (i = 0; i < num_properties; i++) {
+ /*
+ * properities sometimes come unaligned hence memcp to
+ * aligned buffer
+ */
+ bt_property_t property;
+ memcpy(&property, properties + i, sizeof(property));
+ if (property.type == BT_PROPERTY_BDADDR)
+ add_remote_device((bt_bdaddr_t *) property.val);
+ }
+}
+
static void adapter_state_changed_cb(bt_state_t state)
{
haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state));
@@ -198,6 +286,8 @@ static void remote_device_properties_cb(bt_status_t status,
__func__, bt_status_t2str(status), bdaddr2str(bd_addr),
num_properties);
+ add_remote_device(bd_addr);
+
dump_properties(num_properties, properties);
}
@@ -205,6 +295,8 @@ static void device_found_cb(int num_properties, bt_property_t *properties)
{
haltest_info("%s: num_properties=%d\n", __func__, num_properties);
+ add_remote_device_from_props(num_properties, properties);
+
dump_properties(num_properties, properties);
}
@@ -214,19 +306,33 @@ static void discovery_state_changed_cb(bt_discovery_state_t state)
bt_discovery_state_t2str(state));
}
+/*
+ * Buffer for remote addres that came from one of bind request.
+ * It's stored for command completion.
+ */
+static char last_remote_addr[18];
+static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t)-1;
+
static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
uint32_t cod)
{
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+
haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__,
- bdaddr2str(remote_bd_addr), bd_name->name, cod);
+ last_remote_addr, bd_name->name, cod);
}
static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
uint32_t cod, bt_ssp_variant_t pairing_variant,
uint32_t pass_key)
{
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+ last_ssp_variant = pairing_variant;
+
haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n",
- __func__, bdaddr2str(remote_bd_addr), bd_name->name, cod,
+ __func__, last_remote_addr, bd_name->name, cod,
bt_ssp_variant_t2str(pairing_variant), pass_key);
}
@@ -338,15 +444,45 @@ static void get_adapter_properties_p(int argc, const char **argv)
EXEC(if_bluetooth->get_adapter_properties);
}
+static void get_adapter_property_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = TYPE_ENUM(bt_property_type_t);
+ *penum_func = enum_defines;
+ }
+}
+
static void get_adapter_property_p(int argc, const char **argv)
{
- int type = str2btpropertytype(argv[2]);
+ int type;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, type);
EXEC(if_bluetooth->get_adapter_property, type);
}
+static const char * const names[] = {
+ "BT_PROPERTY_BDNAME",
+ "BT_PROPERTY_ADAPTER_SCAN_MODE",
+ "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT", NULL
+};
+
+static void set_adapter_property_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = (void *) names;
+ *penum_func = enum_strings;
+ } else if (argc == 4) {
+ if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) {
+ *puser = TYPE_ENUM(bt_scan_mode_t);
+ *penum_func = enum_defines;
+ }
+ }
+}
+
static void set_adapter_property_p(int argc, const char **argv)
{
bt_property_t property;
@@ -354,9 +490,12 @@ static void set_adapter_property_p(int argc, const char **argv)
int timeout;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, property.type);
- property.type = str2btpropertytype(argv[2]);
-
+ if (argc <= 3) {
+ haltest_error("No property value specified\n");
+ return;
+ }
switch (property.type) {
case BT_PROPERTY_BDNAME:
property.len = strlen(argv[3]) + 1;
@@ -383,39 +522,70 @@ static void set_adapter_property_p(int argc, const char **argv)
EXEC(if_bluetooth->set_adapter_property, &property);
}
+/*
+ * This function is to be used for completion methods that need only address
+ */
+static void complete_addr_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = NULL;
+ *penum_func = enum_devices;
+ }
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_device_properties_c complete_addr_c
+
static void get_remote_device_properties_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->get_remote_device_properties, &addr);
}
+static void get_remote_device_property_c(int argc, const char **argv,
+ enum_func *penum_func,
+ void **puser)
+{
+ if (argc == 3) {
+ *puser = NULL;
+ *penum_func = enum_devices;
+ } else if (argc == 4) {
+ *puser = TYPE_ENUM(bt_property_type_t);
+ *penum_func = enum_defines;
+ }
+}
+
static void get_remote_device_property_p(int argc, const char **argv)
{
bt_property_type_t type;
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
- type = str2btpropertytype(argv[3]);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, type);
EXEC(if_bluetooth->get_remote_device_property, &addr, type);
}
+/*
+ * Same completion as for get_remote_device_property_c can be used for
+ * set_remote_device_property_c. No need to create separate function.
+ */
+#define set_remote_device_property_c get_remote_device_property_c
+
static void set_remote_device_property_p(int argc, const char **argv)
{
bt_property_t property;
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
- property.type = str2btpropertytype(argv[3]);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, property.type);
switch (property.type) {
case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
@@ -430,26 +600,37 @@ static void set_remote_device_property_p(int argc, const char **argv)
EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
}
+/*
+ * For now uuid is not autocompleted. Use routine for complete_addr_c
+ */
+#define get_remote_service_record_c complete_addr_c
+
static void get_remote_service_record_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
bt_uuid_t uuid;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- str2bt_bdaddr_t(argv[2], &addr);
+ if (argc <= 3) {
+ haltest_error("No uuid specified\n");
+ return;
+ }
str2bt_uuid_t(argv[3], &uuid);
EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid);
}
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_services_c complete_addr_c
+
static void get_remote_services_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->get_remote_services, &addr);
}
@@ -468,39 +649,55 @@ static void cancel_discovery_p(int argc, const char **argv)
EXEC(if_bluetooth->cancel_discovery);
}
+/* Just addres to complete, use complete_addr_c */
+#define create_bond_c complete_addr_c
+
static void create_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->create_bond, &addr);
}
+/* Just addres to complete, use complete_addr_c */
+#define remove_bond_c complete_addr_c
+
static void remove_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->remove_bond, &addr);
}
+/* Just addres to complete, use complete_addr_c */
+#define cancel_bond_c complete_addr_c
+
static void cancel_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->cancel_bond, &addr);
}
+static void pin_reply_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ static const char *const completions[] = { last_remote_addr, NULL };
+ if (argc == 3) {
+ *puser = (void *) completions;
+ *penum_func = enum_strings;
+ }
+}
+
static void pin_reply_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
@@ -509,14 +706,9 @@ static void pin_reply_p(int argc, const char **argv)
int accept;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- if (argc < 3) {
- haltest_error("No address specified\n");
- return;
- }
- str2bt_bdaddr_t(argv[2], &addr);
-
- if (argc >= 4) {
+ if (argc > 3) {
accept = 1;
pin_len = strlen(argv[3]);
memcpy(pin.pin, argv[3], pin_len);
@@ -525,6 +717,26 @@ static void pin_reply_p(int argc, const char **argv)
EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin);
}
+static void ssp_reply_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = last_remote_addr;
+ *penum_func = enum_one_string;
+ } else if (argc == 5) {
+ *puser = "1";
+ *penum_func = enum_one_string;
+ } else if (argc == 4) {
+ if (-1 != (int) last_ssp_variant) {
+ *puser = (void *) bt_ssp_variant_t2str(last_ssp_variant);
+ *penum_func = enum_one_string;
+ } else {
+ *puser = TYPE_ENUM(bt_ssp_variant_t);
+ *penum_func = enum_defines;
+ }
+ }
+}
+
static void ssp_reply_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
@@ -533,12 +745,8 @@ static void ssp_reply_p(int argc, const char **argv)
int passkey;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- if (argc < 3) {
- haltest_error("No address specified\n");
- return;
- }
- str2bt_bdaddr_t(argv[2], &addr);
if (argc < 4) {
haltest_error("No ssp variant specified\n");
return;
@@ -557,6 +765,29 @@ static void ssp_reply_p(int argc, const char **argv)
EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey);
}
+static void get_profile_interface_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ static const char *const profile_ids[] = {
+ BT_PROFILE_HANDSFREE_ID,
+ BT_PROFILE_ADVANCED_AUDIO_ID,
+ BT_PROFILE_HEALTH_ID,
+ BT_PROFILE_SOCKETS_ID,
+ BT_PROFILE_HIDHOST_ID,
+ BT_PROFILE_PAN_ID,
+#if PLATFORM_SDK_VERSION >= 18
+ BT_PROFILE_GATT_ID,
+#endif
+ BT_PROFILE_AV_RC_ID,
+ NULL
+ };
+
+ if (argc == 3) {
+ *puser = (void *) profile_ids;
+ *penum_func = enum_strings;
+ }
+}
+
static void get_profile_interface_p(int argc, const char **argv)
{
const char *id = argv[2];
@@ -595,6 +826,10 @@ static void dut_mode_configure_p(int argc, const char **argv)
RETURN_IF_NULL(if_bluetooth);
+ if (argc <= 2) {
+ haltest_error("No dut mode specified\n");
+ return;
+ }
mode = strtol(argv[2], NULL, 0);
EXEC(if_bluetooth->dut_mode_configure, mode);
@@ -606,22 +841,22 @@ static struct method methods[] = {
STD_METHOD(enable),
STD_METHOD(disable),
STD_METHOD(get_adapter_properties),
- STD_METHOD(get_adapter_property),
- STD_METHOD(set_adapter_property),
- STD_METHOD(get_remote_device_properties),
- STD_METHOD(get_remote_device_property),
- STD_METHOD(set_remote_device_property),
- STD_METHOD(get_remote_service_record),
- STD_METHOD(get_remote_services),
+ STD_METHODCH(get_adapter_property, "<prop_type>"),
+ STD_METHODCH(set_adapter_property, "<prop_type> <prop_value>"),
+ STD_METHODCH(get_remote_device_properties, "<addr>"),
+ STD_METHODCH(get_remote_device_property, "<addr> <property_type>"),
+ STD_METHODCH(set_remote_device_property, "<addr> <property_type> <value>"),
+ STD_METHODCH(get_remote_service_record, "<addr> <uuid>"),
+ STD_METHODCH(get_remote_services, "<addr>"),
STD_METHOD(start_discovery),
STD_METHOD(cancel_discovery),
- STD_METHOD(create_bond),
- STD_METHOD(remove_bond),
- STD_METHOD(cancel_bond),
- STD_METHOD(pin_reply),
- STD_METHOD(ssp_reply),
- STD_METHOD(get_profile_interface),
- STD_METHOD(dut_mode_configure),
+ STD_METHODCH(create_bond, "<addr>"),
+ STD_METHODCH(remove_bond, "<addr>"),
+ STD_METHODCH(cancel_bond, "<addr>"),
+ STD_METHODCH(pin_reply, "<address> [<pin>]"),
+ STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
+ STD_METHODCH(get_profile_interface, "<profile id>"),
+ STD_METHODH(dut_mode_configure, "<dut mode>"),
END_METHOD
};
diff --git a/android/client/if-main.h b/android/client/if-main.h
index 3fb3007..b6bbf05 100644
--- a/android/client/if-main.h
+++ b/android/client/if-main.h
@@ -103,6 +103,12 @@ int haltest_error(const char *format, ...);
int haltest_info(const char *format, ...);
int haltest_warn(const char *format, ...);
+/*
+ * Enumerator for discovered devices, to be used as tab completion enum_func
+ */
+const char *enum_devices(void *v, int i);
+void add_remote_device(const bt_bdaddr_t *addr);
+
/* Helper macro for executing function on interface and printing BT_STATUS */
#define EXEC(f, ...) \
{ \
@@ -119,3 +125,13 @@ int haltest_warn(const char *format, ...);
#define RETURN_IF_NULL(x) \
do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0)
+
+#define VERIFY_ADDR_ARG(n, adr) \
+ do { \
+ if (n < argc) \
+ str2bt_bdaddr_t(argv[n], adr); \
+ else { \
+ haltest_error("No address specified\n");\
+ return;\
+ } \
+ } while (0)
diff --git a/android/client/textconv.c b/android/client/textconv.c
index eebad70..3493b1c 100644
--- a/android/client/textconv.c
+++ b/android/client/textconv.c
@@ -203,3 +203,24 @@ void str2bt_uuid_t(const char *str, bt_uuid_t *uuid)
str += 2;
}
}
+
+const char *enum_defines(void *v, int i)
+{
+ const struct int2str *m = v;
+
+ return m[i].str != NULL ? m[i].str : NULL;
+}
+
+const char *enum_strings(void *v, int i)
+{
+ const char **m = v;
+
+ return m[i] != NULL ? m[i] : NULL;
+}
+
+const char *enum_one_string(void *v, int i)
+{
+ const char *m = v;
+
+ return (i == 0) && (m[0] != 0) ? m : NULL;
+}
diff --git a/android/client/textconv.h b/android/client/textconv.h
index 8fe976c..88da641 100644
--- a/android/client/textconv.h
+++ b/android/client/textconv.h
@@ -54,7 +54,11 @@ struct int2str {
int int2str_findint(int v, const struct int2str m[]);
int int2str_findstr(const char *str, const struct int2str m[]);
+const char *enum_defines(void *v, int i);
+const char *enum_strings(void *v, int i);
+const char *enum_one_string(void *v, int i);
+#define TYPE_ENUM(type) ((void *)&__##type##2str[0])
#define DECINTMAP(type) \
extern struct int2str __##type##2str[]; \
const char *type##2##str(type v); \
--
1.7.9.5
This fixes small error that showed up when tab completion inserted
characters before end of line.
Cursor in line was not moved correctly.
---
android/client/terminal.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/android/client/terminal.c b/android/client/terminal.c
index 0421633..8dd3a25 100644
--- a/android/client/terminal.c
+++ b/android/client/terminal.c
@@ -140,7 +140,7 @@ void terminal_draw_command_line(void)
putchar('>');
/* move cursor to it's place */
- terminal_move_cursor(line_len - line_buf_ix);
+ terminal_move_cursor(line_buf_ix - line_len);
}
/* inserts string into command line at cursor position */
--
1.7.9.5
This patch adds tab completion to interfaces, methods and parameters
in haltest.
---
Makefile.android | 2 +
android/Android.mk | 1 +
android/client/if-main.h | 28 +++-
android/client/tabcompletion.c | 313 ++++++++++++++++++++++++++++++++++++++++
android/client/terminal.c | 2 +-
5 files changed, 342 insertions(+), 4 deletions(-)
create mode 100644 android/client/tabcompletion.c
diff --git a/Makefile.android b/Makefile.android
index 56caf78..dcaca3c 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -42,6 +42,7 @@ android_haltest_SOURCES = android/client/haltest.c \
android/client/terminal.c \
android/client/history.c \
android/client/textconv.c \
+ android/client/tabcompletion.c \
android/client/if-bt.c \
android/client/hwmodule.c
@@ -67,6 +68,7 @@ EXTRA_DIST += android/client/terminal.c \
android/client/history.c \
android/client/if-bt.c \
android/client/textconv.c \
+ android/client/tabcompletion.c \
android/client/textconv.h \
android/client/if-main.h \
android/client/pollhandler.h \
diff --git a/android/Android.mk b/android/Android.mk
index 5858c3d..30b2169 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -78,6 +78,7 @@ LOCAL_SRC_FILES := \
client/terminal.c \
client/history.c \
client/textconv.c \
+ client/tabcompletion.c \
client/if-bt.c \
LOCAL_SHARED_LIBRARIES := libhardware
diff --git a/android/client/if-main.h b/android/client/if-main.h
index 9cac7ef..3fb3007 100644
--- a/android/client/if-main.h
+++ b/android/client/if-main.h
@@ -56,9 +56,12 @@ extern const struct interface bluetooth_if;
/* Interfaces that will show up in tool (first part of command line) */
extern const struct interface *interfaces[];
-#define METHOD(name, func) {name, func}
-#define STD_METHOD(m) {#m, m##_p}
-#define END_METHOD {"", NULL}
+#define METHOD(name, func, comp, help) {name, func, comp, help}
+#define STD_METHOD(m) {#m, m##_p, NULL, NULL}
+#define STD_METHODC(m) {#m, m##_p, m##_c, NULL}
+#define STD_METHODH(m, h) {#m, m##_p, NULL, h}
+#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h}
+#define END_METHOD {"", NULL, NULL, NULL}
/*
* Function to parse argument for function, argv[0] and argv[1] are already
@@ -69,12 +72,31 @@ extern const struct interface *interfaces[];
typedef void (*parse_and_call)(int argc, const char **argv);
/*
+ * This is prototype of function that will return string for given number.
+ * Purpose is to enumerate string for auto completion.
+ * Function of this type will always be called in loop.
+ * First time function is called i = 0, then if function returns non-NULL
+ * it will be called again till for some value of i it will return NULL
+ */
+typedef const char *(*enum_func)(void *user, int i);
+
+/*
+ * This is prototype of function that when given argc, argv will
+ * fill penum_func with pointer to function that will enumerate
+ * parameters for argc argument, puser will be passed to penum_func.
+ */
+typedef void (*tab_complete)(int argc, const char **argv,
+ enum_func *penum_func, void **puser);
+
+/*
* For each method there is name and two functions to parse command line
* and call proper hal function on.
*/
struct method {
const char *name;
parse_and_call func;
+ tab_complete complete;
+ const char *help;
};
int haltest_error(const char *format, ...);
diff --git a/android/client/tabcompletion.c b/android/client/tabcompletion.c
new file mode 100644
index 0000000..e9c9921
--- /dev/null
+++ b/android/client/tabcompletion.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "if-main.h"
+#include "terminal.h"
+
+/* how many times tab was hit */
+static int tab_hit_count;
+
+typedef struct split_arg {
+ struct split_arg *next; /* next argument in buffer */
+ const char *origin; /* pointer to original argument */
+ char ntcopy[1]; /* null terminated copy of argument */
+} split_arg_t;
+
+/* function returns interface of given name or NULL if not found */
+static const struct interface *get_interface(const char *name)
+{
+ int i;
+
+ for (i = 0; interfaces[i] != NULL; ++i) {
+ if (strcmp(interfaces[i]->name, name) == 0)
+ break;
+ }
+
+ return interfaces[i];
+}
+
+/* function returns method of given name or NULL if not found */
+static const struct method *get_method(const char *iname, const char *mname)
+{
+ int i;
+ const struct interface *iface = get_interface(iname);
+
+ if (iface == NULL)
+ return NULL;
+
+ for (i = 0; iface->methods[i].name[0]; ++i) {
+ if (0 == strcmp(iface->methods[i].name, mname))
+ return &iface->methods[i];
+ }
+ return NULL;
+}
+
+/* prints matching elements */
+static void print_matches(enum_func f, void *user, const char *prefix, int len)
+{
+ int i;
+ const char *enum_name;
+
+ putchar('\n');
+ for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
+ if (strncmp(enum_name, prefix, len) == 0)
+ printf("%s\t", enum_name);
+ }
+ putchar('\n');
+ terminal_draw_command_line();
+}
+
+/*
+ * This function splits command line into linked list of arguments.
+ * line_buffer - pointer to input comman line
+ * size - size of command line to parse
+ * buf - output buffer to keep splited arguments list
+ * buf_size_in_bytes - size of buf
+ */
+static int split_command(const char *line_buffer, int size,
+ split_arg_t *buf, int buf_size_in_bytes)
+{
+ split_arg_t *prev = NULL;
+ split_arg_t *arg = buf;
+ int argc = 0;
+ const char *p = line_buffer;
+ const char *e = p + (size > 0 ? size : (int) strlen(p));
+ int len;
+
+ do {
+ while (p < e && isspace(*p))
+ p++;
+ arg->origin = p;
+ arg->next = NULL;
+ while (p < e && !isspace(*p))
+ p++;
+ len = p - arg->origin;
+ if (&arg->ntcopy[0] + len + 1 >
+ (const char *) buf + buf_size_in_bytes)
+ break;
+ strncpy(arg->ntcopy, arg->origin, len);
+ arg->ntcopy[len] = 0;
+ if (prev != NULL)
+ prev->next = arg;
+ prev = arg;
+ arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
+ argc++;
+ } while (p < e);
+
+ return argc;
+}
+
+/* Function to enumerate interface names */
+static const char *interface_name(void *v, int i)
+{
+ return interfaces[i] ? interfaces[i]->name : NULL;
+}
+
+/* Function to enumerate method names */
+static const char *methods_name(void *v, int i)
+{
+ const struct interface *iface = v;
+
+ return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
+}
+
+struct command_completion_args;
+typedef void (*short_help)(struct command_completion_args *args);
+
+struct command_completion_args {
+ const split_arg_t *arg; /* list of arguments */
+ const char *typed; /* last typed element */
+ enum_func func; /* enumerating function */
+ void *user; /* argument to enumerating function */
+ short_help help; /* help function */
+ void *user_help; /* additional data (used by short_help) */
+};
+
+/*
+ * complete command line
+ */
+static void command_completion(struct command_completion_args *args)
+{
+ const char *name = args->typed;
+ const int len = strlen(name);
+ int i;
+ int j;
+ char prefix[128] = {0};
+ int prefix_len = 0;
+ int count = 0;
+ const char *enum_name;
+
+ for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
+ /* prefix does not match */
+ if (strncmp(enum_name, name, len) != 0)
+ continue;
+ /* prefix matches first time */
+ if (count++ == 0) {
+ strcpy(prefix, enum_name);
+ prefix_len = strlen(prefix);
+ continue;
+ }
+ /*
+ * Prefix matches next time
+ * reduce prefix to common part
+ */
+ for (j = 0; prefix[j] != 0
+ && prefix[j] == enum_name[j];)
+ ++j;
+ prefix_len = j;
+ prefix[j] = 0;
+ }
+
+ if (count == 0) {
+ /* no matches */
+ if (args->help != NULL)
+ args->help(args);
+ tab_hit_count = 0;
+ return;
+ }
+ /* len == prefix_len => nothing new was added */
+ if (len == prefix_len) {
+ if (count != 1) {
+ if (tab_hit_count == 1)
+ putchar('\a');
+ else if (tab_hit_count == 2 ||
+ args->help == NULL) {
+ print_matches(args->func,
+ args->user, name, len);
+ } else {
+ args->help(args);
+ tab_hit_count = 1;
+ }
+ } else if (count == 1) {
+ /* nothing to add, exact match add space */
+ terminal_insert_into_command_line(" ");
+ }
+ } else {
+ /* new chars can be added from some interface name(s) */
+ if (count == 1) {
+ /* exact match, add space */
+ prefix[prefix_len++] = ' ';
+ prefix[prefix_len] = '\0';
+ }
+ terminal_insert_into_command_line(prefix + len);
+ tab_hit_count = 0;
+ }
+}
+
+/* interface completion */
+static void interface_completion(split_arg_t *arg)
+{
+ struct command_completion_args args = {
+ .arg = arg,
+ .typed = arg->ntcopy,
+ .func = interface_name
+ };
+
+ command_completion(&args);
+}
+
+/* method completion */
+static void method_completion(const struct interface *iface, split_arg_t *arg)
+{
+ struct command_completion_args args = {
+ .arg = arg,
+ .typed = arg->next->ntcopy,
+ .func = methods_name,
+ .user = (void *) iface
+ };
+
+ if (iface == NULL)
+ return;
+
+ command_completion(&args);
+}
+
+/* prints short help on method for interface */
+static void method_help(struct command_completion_args *args)
+{
+ if (args->user_help == NULL)
+ return;
+
+ haltest_info("%s %s %s\n", args->arg->ntcopy,
+ args->arg->next->ntcopy, args->user_help);
+}
+
+/* So we have empty enumeration */
+static const char *return_null(void *user, int i)
+{
+ return NULL;
+}
+
+/* parameter completion function */
+static void param_completion(int argc, const split_arg_t *arg)
+{
+ const struct method *method;
+ int i;
+ const char *argv[argc];
+ const split_arg_t *tmp = arg;
+ struct command_completion_args args = {
+ .arg = arg,
+ .func = return_null
+ };
+
+ /* prepare standard argv from arg */
+ for (i = 0; i < argc; ++i) {
+ argv[i] = tmp->ntcopy;
+ tmp = tmp->next;
+ }
+
+ /* Find method for <interface, name> pair */
+ method = get_method(argv[0], argv[1]);
+
+ if (method != NULL && method->complete != NULL) {
+ /* ask method for completion function */
+ method->complete(argc, argv, &args.func, &args.user);
+ }
+
+ /* If method provided enumeration function call try to complete */
+ if (args.func != NULL) {
+ args.typed = argv[argc - 1];
+ args.help = method_help;
+ args.user_help = (void *) method->help;
+
+ command_completion(&args);
+ }
+}
+
+/*
+ * This methd gets called when user tapped tab key.
+ * line - points to comman line
+ * len - size of line that should be used for comletions. This should be
+ * cursor position during tab hit.
+ */
+void process_tab(const char *line, int len)
+{
+ int argc;
+ static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
+
+ argc = split_command(line, len, buf, sizeof(buf));
+ tab_hit_count++;
+
+ if (argc == 1)
+ interface_completion(buf);
+ else if (argc == 2)
+ method_completion(get_interface(buf[0].ntcopy), buf);
+ else if (argc > 2)
+ param_completion(argc, buf);
+}
diff --git a/android/client/terminal.c b/android/client/terminal.c
index b484ef6..0421633 100644
--- a/android/client/terminal.c
+++ b/android/client/terminal.c
@@ -453,7 +453,7 @@ void terminal_process_char(int c, void (*process_line)(char *line))
break;
case '\t':
/* tab processing */
- /* TODO Add completion here */
+ process_tab(line_buf, line_buf_ix);
break;
case KEY_BACKSPACE:
if (line_buf_ix <= 0)
--
1.7.9.5