]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add tab expansion for commands
authorAlan T. DeKok <aland@freeradius.org>
Wed, 11 Jul 2018 14:12:36 +0000 (10:12 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 11 Jul 2018 14:12:36 +0000 (10:12 -0400)
right now, only for fixed strings, and not for syntax in commands.
But it's a nice step forward.

src/include/command.h
src/main/command.c
src/main/radmin.c

index 97ae4363bc787bfca1b0382c98b414780ea18aed..2934afc53a818966a1257197276ae03474f49926 100644 (file)
@@ -87,6 +87,9 @@ int fr_command_clear(int new_argc, fr_cmd_info_t *info) CC_HINT(nonnull);
 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
index f187aab34900f2daf9a01e39dcb29426776fa2c5..158e6f84a63f0763271797b4424dbc7b047c3689 100644 (file)
@@ -1935,3 +1935,103 @@ void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info)
        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;
+}
index a0e55307b4ee1feeda3cb4c750ba2c1c940dd617..714215f355994c352ab26e1573474cb933c562dd 100644 (file)
@@ -131,10 +131,52 @@ static void add_history(UNUSED char *line)
 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;
@@ -161,6 +203,8 @@ static void *fr_radmin(UNUSED void *input_ctx)
 
        fflush(stdout);
 
+       rl_attempted_completion_function = radmin_completion;
+
        while (true) {
                char *line;