Wireless Daemon for Linux
 help / color / mirror / Atom feed
From: James Prestwood <prestwoj@gmail.com>
To: iwd@lists.linux.dev
Cc: James Prestwood <prestwoj@gmail.com>
Subject: [PATCH v3 3/6] client: allow entity name to be passed to completion
Date: Thu, 11 Aug 2022 11:47:32 -0700	[thread overview]
Message-ID: <20220811184735.465951-3-prestwoj@gmail.com> (raw)
In-Reply-To: <20220811184735.465951-1-prestwoj@gmail.com>

There is a limitation of libreadline where no context/userdata
can be passed to completion functions. This effects iwctl since
the entity value isn't known to completion functions.

Workarounds such as getting the default device are employed but
its not a great solution.

Instead hack around this limitation by parsing the prompt to
extract the entity (second arg). Then use a generic match function
given to readline which can call the actual match function and
include the entity.
---
 client/adapter.c        |  3 ++-
 client/command.c        | 39 ++++++++++++++++++++++++++++++++++++++-
 client/command.h        |  9 ++++++---
 client/device.c         |  3 ++-
 client/known-networks.c |  3 ++-
 client/station.c        |  6 ++++--
 6 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/client/adapter.c b/client/adapter.c
index 394eae0d..1719b970 100644
--- a/client/adapter.c
+++ b/client/adapter.c
@@ -319,7 +319,8 @@ static enum cmd_status cmd_set_property(const char *adapter_name,
 	return CMD_STATUS_TRIGGERED;
 }
 
-static char *set_property_cmd_arg_completion(const char *text, int state)
+static char *set_property_cmd_arg_completion(const char *text, int state,
+						const char *phy)
 {
 	return proxy_property_completion(adapter_properties, text, state);
 }
diff --git a/client/command.c b/client/command.c
index c0576e97..46752c72 100644
--- a/client/command.c
+++ b/client/command.c
@@ -213,20 +213,57 @@ bool command_line_find_token(const char *token, uint8_t num_to_inspect)
 	return false;
 }
 
+/*
+ * Work around readline limitations of not being able to pass a context pointer
+ * to match functions. Set the command match function/entity to these globals
+ * and call a generic match function which can call the _real_ match function
+ * and include the entity.
+ */
+static command_completion_func_t cmd_current_completion_func = NULL;
+static const char *cmd_current_entity = NULL;
+
+static char *cmd_completion_generic(const char *text, int state)
+{
+	return cmd_current_completion_func(text, state, cmd_current_entity);
+}
+
 static char **cmd_completion_match_entity_cmd(const char *cmd, const char *text,
 						const struct command *cmd_list)
 {
 	char **matches = NULL;
 	size_t i;
+	char *family = NULL;
+	char *entity = NULL;
+	char *prompt = NULL;
 
 	for (i = 0; cmd_list[i].cmd; i++) {
+		char *tmp;
+
 		if (strcmp(cmd_list[i].cmd, cmd))
 			continue;
 
 		if (!cmd_list[i].completion)
 			break;
 
-		matches = rl_completion_matches(text, cmd_list[i].completion);
+		if (cmd_list[i].entity) {
+			prompt = rl_copy_text(0, rl_end);
+
+			family = strtok_r(prompt, " ", &tmp);
+			if (!family)
+				goto done;
+
+			entity = strtok_r(NULL, " ", &tmp);
+		}
+
+done:
+		cmd_current_completion_func = cmd_list[i].completion;
+		cmd_current_entity = entity;
+
+		matches = rl_completion_matches(text, cmd_completion_generic);
+
+		l_free(prompt);
+		cmd_current_completion_func = NULL;
+		cmd_current_entity = NULL;
 
 		break;
 	}
diff --git a/client/command.h b/client/command.h
index 23f23dc9..1f841689 100644
--- a/client/command.h
+++ b/client/command.h
@@ -25,7 +25,10 @@
 #define COMMAND_OPTION_PASSPHRASE	"passphrase"
 #define COMMAND_OPTION_DONTASK		"dont-ask"
 
-typedef char *(*command_completion_func_t) (const char *text, int state);
+typedef char *(*command_completion_func_t)(const char *text, int state,
+						const char *entity);
+typedef char *(*command_rl_completion_func_t)(const char *text, int state);
+
 
 enum cmd_status {
 	CMD_STATUS_TRIGGERED,
@@ -51,8 +54,8 @@ struct command_family {
 	const char *caption;
 	const char *name;
 	const struct command *command_list;
-	command_completion_func_t family_arg_completion;
-	command_completion_func_t entity_arg_completion;
+	command_rl_completion_func_t family_arg_completion;
+	command_rl_completion_func_t entity_arg_completion;
 	void (*set_default_entity)(const char *entity);
 	void (*reset_default_entity)(void);
 };
diff --git a/client/device.c b/client/device.c
index 9f8d731c..8b27ddd2 100644
--- a/client/device.c
+++ b/client/device.c
@@ -398,7 +398,8 @@ static enum cmd_status cmd_set_property(const char *device_name,
 	return CMD_STATUS_TRIGGERED;
 }
 
-static char *set_property_cmd_arg_completion(const char *text, int state)
+static char *set_property_cmd_arg_completion(const char *text, int state,
+						const char *device_name)
 {
 	return proxy_property_completion(device_properties, text, state);
 }
diff --git a/client/known-networks.c b/client/known-networks.c
index 56c34a2e..b2023e35 100644
--- a/client/known-networks.c
+++ b/client/known-networks.c
@@ -387,7 +387,8 @@ static enum cmd_status cmd_set_property(const char *network_name,
 	return CMD_STATUS_TRIGGERED;
 }
 
-static char *set_property_cmd_arg_completion(const char *text, int state)
+static char *set_property_cmd_arg_completion(const char *text, int state,
+						const char *network_name)
 {
 	return proxy_property_completion(known_network_properties, text, state);
 }
diff --git a/client/station.c b/client/station.c
index 64becdbd..90291c88 100644
--- a/client/station.c
+++ b/client/station.c
@@ -277,7 +277,8 @@ static enum cmd_status cmd_list(const char *device_name, char **argv, int argc)
 	return CMD_STATUS_DONE;
 }
 
-static char *connect_cmd_arg_completion(const char *text, int state)
+static char *connect_cmd_arg_completion(const char *text, int state,
+					const char *device_name)
 {
 	const struct proxy_interface *device = device_get_default();
 
@@ -489,7 +490,8 @@ static void ordered_networks_callback(struct l_dbus_message *message,
 	l_queue_destroy(networks, ordered_networks_destroy);
 }
 
-static char *get_networks_cmd_arg_completion(const char *text, int state)
+static char *get_networks_cmd_arg_completion(const char *text, int state,
+						const char *device_name)
 {
 	static int index;
 	static int len;
-- 
2.34.3


  parent reply	other threads:[~2022-08-11 18:47 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-11 18:47 [PATCH v3 1/6] doc: add documentation for StationDebug James Prestwood
2022-08-11 18:47 ` [PATCH v3 2/6] station: add debug method GetNetworks James Prestwood
2022-08-11 18:47 ` James Prestwood [this message]
2022-08-11 18:47 ` [PATCH v3 4/6] client: add station-debug command interface James Prestwood
2022-08-11 18:47 ` [PATCH v3 5/6] client: fix station autocomplete with multiple phys James Prestwood
2022-08-11 18:47 ` [PATCH v3 6/6] device: command: remove default device concept James Prestwood
2022-08-11 20:48 ` [PATCH v3 1/6] doc: add documentation for StationDebug Denis Kenzior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220811184735.465951-3-prestwoj@gmail.com \
    --to=prestwoj@gmail.com \
    --cc=iwd@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox