void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, int options);
void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info);
+int fr_command_complete(fr_cmd_t *head, char const *text, int start,
+ int max_expansions, char const **expansions);
+
#ifdef __cplusplus
}
#endif
info->box = talloc_zero_array(ctx, fr_value_box_t *, CMD_MAX_ARGV);
info->cmd = talloc_zero_array(ctx, fr_cmd_t *, CMD_MAX_ARGV);
}
+
+
+/** Do readline-style command completions
+ *
+ * Most useful as part of readline tab expansions.
+ *
+ * @todo - call strdup() on the names, because that's what readline
+ * expects. <sigh>
+ *
+ * @param head of the command tree
+ * @param text the text to check
+ * @param start offset in the text where the completions should start
+ * @param max_expansions how many entries in the "expansions" array.
+ * @param expansions where the expansions are stored.
+ */
+int fr_command_complete(fr_cmd_t *head, char const *text, int start,
+ int max_expansions, char const **expansions)
+{
+ int count;
+ char const *word, *p, *q;
+ fr_cmd_t *cmd;
+
+ cmd = head;
+ word = text;
+ count = 0;
+
+ /*
+ * Try to do this without mangling "text".
+ */
+ while (cmd) {
+ while (isspace((int) *word)) word++;
+
+ /*
+ * End of the input. Tab expand everything here.
+ */
+ if (!*word) {
+ expand:
+ while (cmd && (count < max_expansions)) {
+ expansions[count] = cmd->name;
+ count++;
+ cmd = cmd->next;
+ }
+ return count;
+ }
+
+ /*
+ * Try to find a matching cmd->name
+ */
+ p = word;
+ q = cmd->name;
+
+ while (*p == *q) {
+ p++;
+ q++;
+ }
+
+ /*
+ * We're supposed to expand the text at this
+ * location, go do so. Even if it doesn't match.
+ */
+ if (((text + start) >= word) && ((text + start) <= p)) {
+ goto expand;
+ }
+
+ /*
+ * The only matching exit condition is *p is a
+ * space, and *q is the NUL character.
+ */
+ if (!(isspace((int) *p) && !*q)) {
+ cmd = cmd->next;
+ continue;
+ }
+
+ if (cmd->intermediate) {
+ rad_assert(cmd->child != NULL);
+ word = p;
+ cmd = cmd->child;
+ continue;
+ }
+
+ break;
+ }
+
+ /*
+ * No match, can't do anything.
+ */
+ if (!cmd) return count;
+
+ /*
+ * No syntax, can't do anything.
+ */
+ if (!cmd->syntax) return count;
+
+ /*
+ * @todo - loop over the command syntax, looking for
+ * matches.
+ */
+
+ return count;
+}
static fr_cmd_t *radmin_cmd = NULL;
#define CMD_MAX_ARGV (32)
+#define CMD_MAX_EXPANSIONS (128)
static int cmd_help(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info);
static int cmd_exit(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info);
+static int radmin_num_expansions;
+static char const *radmin_expansions[CMD_MAX_EXPANSIONS] = {0};
+
+static char *
+radmin_expansion_walk(const char *text, int state)
+{
+ static int current, len;
+ char const *name;
+
+ if (!state) {
+ current = 0;
+ len = strlen(text);
+ }
+
+ if (current >= radmin_num_expansions) return 0;
+
+ while ((name = radmin_expansions[current++])) {
+ if (strncmp(name, text, len) == 0) {
+ return strdup(name);
+ }
+ }
+
+ return NULL;
+}
+
+
+static char **
+radmin_completion(const char *text, int start, UNUSED int end)
+{
+ int num;
+
+ rl_attempted_completion_over = 1;
+
+ num = fr_command_complete(radmin_cmd, rl_line_buffer, start, CMD_MAX_EXPANSIONS, radmin_expansions);
+ if (num <= 0) return NULL;
+
+ radmin_num_expansions = num;
+
+ return rl_completion_matches(text, radmin_expansion_walk);
+}
+
static void *fr_radmin(UNUSED void *input_ctx)
{
int argc;
fflush(stdout);
+ rl_attempted_completion_function = radmin_completion;
+
while (true) {
char *line;