bool runnable; //!< is the command runnable?
char **argv; //!< text version of commands
fr_value_box_t **box; //!< value_box version of commands.
+ fr_cmd_t **cmd; //!< cached commands at each offset
} fr_cmd_info_t;
typedef int (*fr_cmd_func_t)(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info);
char const *fr_command_help(fr_cmd_t *head, int argc, char *argv[]);
int fr_command_run(FILE *fp, FILE *fp_err, fr_cmd_t *head, fr_cmd_info_t *info);
void fr_command_debug(FILE *fp, fr_cmd_t *head);
-void fr_command_list(FILE *fp, fr_cmd_t *head);
+void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, bool is_head);
int fr_command_str_to_argv(fr_cmd_t *head, fr_cmd_info_t *info, char *str);
#ifdef __cplusplus
}
}
-static void fr_command_list_internal(FILE *fp, fr_cmd_t *head, int depth, char const **argv)
+static void fr_command_list_internal(FILE *fp, fr_cmd_t *head, int depth, int max_depth, char const **argv)
{
fr_cmd_t *cmd;
for (cmd = head; cmd != NULL; cmd = cmd->next) {
- if (cmd->child) {
+ if (cmd->child && ((depth + 1) < max_depth)) {
argv[depth] = cmd->name;
- fr_command_list_internal(fp, cmd->child, depth + 1, argv);
+ fr_command_list_internal(fp, cmd->child, depth + 1, max_depth, argv);
} else {
fr_command_list_node(fp, cmd, depth, argv);
}
}
}
-void fr_command_list(FILE *fp, fr_cmd_t *head)
+void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, bool is_head)
{
char const *argv[CMD_MAX_ARGV];
- fr_command_list_internal(fp, head, 0, argv);
+ if ((max_depth <= 0) || !head) return;
+ if (max_depth > CMD_MAX_ARGV) max_depth = CMD_MAX_ARGV;
+
+ if (!is_head) {
+ if (!head->child) return;
+ head = head->child;
+ }
+
+ fr_command_list_internal(fp, head, 0, max_depth, argv);
}
fr_cmd_t *cmd, *start;
fr_cmd_argv_t *argv;
- if ((info->argc < 0) || (info->max_argc <= 0) || !str) {
+ if ((info->argc < 0) || (info->max_argc <= 0) || !str || !head) {
fr_strerror_printf("Invalid arguments passed to parse routine.");
return -1;
}
return -1;
}
+ /*
+ * Cache the command for later consumption.
+ */
+ if (info->cmd) info->cmd[i] = cmd;
+
/*
* There's a child. Go match it.
*/
static pthread_t pthread_id;
static bool stop = false;
-static int context = 0;
+static int context;
static fr_cmd_info_t radmin_info;
#ifndef USE_READLINE
#define CMD_MAX_ARGV (32)
static int cmd_help(FILE *fp, FILE *fp_err, UNUSED 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 void *fr_radmin(UNUSED void *input_ctx)
{
goto next;
}
+ /*
+ * Special-case "quit", which works everywhere.
+ * It closes the CLI immediately.
+ */
+ if (strcmp(line, "quit") == 0) {
+ cmd_exit(stdout, stderr, NULL, info);
+ goto next;
+ }
+
/*
* Allow exiting from the current context.
*/
static int cmd_help(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
{
- char const *help;
+// char const *help;
if (info->argc == 0) {
- fr_command_list(fp, radmin_cmd);
- return 0;
+ /*
+ * List only the top-level commands
+ */
+ if (radmin_info.argc == 1) {
+ fr_command_list(fp, 1, radmin_cmd, true);
+ return 0;
+ }
+
}
+ /*
+ * List the current command, but it's children instead of
+ * itself.
+ */
+ fr_command_list(fp, 1, radmin_info.cmd[radmin_info.argc - 1], false);
+
+#if 0
+ // @todo - print out actual help from the above commands
help = fr_command_help(radmin_cmd, info->argc, info->argv);
if (help) {
fprintf(fp, "%s\n", help);
return 0;
}
+#endif
return 0;
}
{
.syntax = "exit",
.func = cmd_exit,
- .help = "Tell the server to exit immediately.",
+ .help = "Exit from the current context.",
+ .read_only = false
+ },
+
+ {
+ .syntax = "quit",
+ .func = cmd_exit,
+ .help = "Quit and close the command line immediately.",
.read_only = false
},