From aa737d9d9cc8ac743a8c8ac0eba76f0451df5894 Mon Sep 17 00:00:00 2001 From: kivkiv12345 Date: Fri, 19 Jul 2024 10:20:14 +0200 Subject: [PATCH 1/2] Don't tab complete to parameters on other nodes Also inform the user when they haven't downloaded any parameters from the specified node --- src/param/param_slash.c | 90 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/src/param/param_slash.c b/src/param/param_slash.c index 9afea414..fdd4badf 100644 --- a/src/param/param_slash.c +++ b/src/param/param_slash.c @@ -5,9 +5,11 @@ * Author: johan */ +#include #include #include #include +#include #include #include #include @@ -60,6 +62,8 @@ static void param_slash_parse(char * arg, int node, param_t **param, int *offset static void param_completer(struct slash *slash, char * token) { + int node = slash_dfl_node; + int matches = 0; size_t prefixlen = -1; param_t *prefix = NULL; @@ -75,6 +79,45 @@ static void param_completer(struct slash *slash, char * token) { skip_prefix = "cmd add"; } + if (skip_prefix == NULL) { + fprintf(stderr, "%s() does not support the line/command: '%s'", __func__, slash->buffer); + assert(false); // Assert because this should be a compile-time error on part of the programmer, not the user. + return; + } + + { /* Keep optparse parser in local scope, to prevent use-after-free */ + + #define SLASH_ARG_MAX 16 /* Maximum number of arguments */ + char *argv[SLASH_ARG_MAX]; + int argc = 0; + char *args = strdup(slash->buffer + strlen(skip_prefix)); + if (args == NULL) { + fprintf(stderr, "No memory for tab-completion\n"); + return; + } + + /* Build args */ + int slash_build_args(char *args, char **argv, int *argc); + if (slash_build_args(args, argv, &argc) < 0) { + argv[argc] = NULL; + slash_printf(slash, "Mismatched quotes\n"); + //return -EINVAL; + } + + optparse_t * parser = optparse_new(skip_prefix, "[offset] "); + optparse_add_int(parser, 'n', "node", "NUM", 0, &node, "node (default = )"); + + /* TODO: Slash doesn't prepare arguments for completer functions. + A quick attempt to make it do this, seems to cause other problems for tab-completion. + So we just do it ourselves here, for now. */ + //int argi = optparse_parse(parser, slash->argc - 1, (const char **) slash->argv + 1); + int argi = optparse_parse(parser, argc - 1, (const char **) argv + 1); + optparse_del(parser); + if (argi < 0) { + return; + } + } + if (skip_prefix) { orig_slash_buf = slash->buffer; slash_completer_skip_flagged_prefix(slash, skip_prefix); @@ -83,17 +126,32 @@ static void param_completer(struct slash *slash, char * token) { size_t tokenlen = strlen(token); + const bool token_has_wildcard = has_wildcard(token, strlen(token)); // This will be true for every iteration, so we store it here. + unsigned int num_params_on_node = 0; // Used to inform the user when they've forgotten to 'list download' :) + param_t * param; param_list_iterator i = { }; - if (has_wildcard(token, strlen(token))) { - // Only print parameters when globbing is involved. - while ((param = param_list_iterate(&i)) != NULL) - if (strmatch(param->name, token, strlen(param->name), strlen(token))) + while ((param = param_list_iterate(&i)) != NULL) { + + /* Don't print or complete parameters from other nodes. + If the user wants to see all parameters, they can do 'list -n -1' */ + if (param-> node != node) + continue; + + num_params_on_node++; + + /* Don't complete parameters when globbing is involved */ + if (token_has_wildcard) { + + /* Only print parameters where name matches (with or w/o wildcards) */ + if (strmatch(param->name, token, strlen(param->name), strlen(token))) { param_print(param, -1, NULL, 0, 2, 0); - return; - } + } - while ((param = param_list_iterate(&i)) != NULL) { + /* We don't actually tab-complete with globbing, + because it's ambiguous with multiple matches. */ + continue; + } if (tokenlen > strlen(param->name)) continue; @@ -123,7 +181,25 @@ static void param_completer(struct slash *slash, char * token) { param_print(param, -1, NULL, 0, 2, 0); } + } + + if (num_params_on_node == 0) { + static unsigned int tab_count = 1; + static time_t tab_expiration = 0; + + if (tab_expiration == 0) { + tab_expiration = time(NULL) + 60; // User has to press 23 times, within 60 seconds. + } else if (time(NULL) > tab_expiration) { + tab_expiration = 0; + tab_count = 1; + } + if (tab_count++ % 23 == 0) { + /* No I'm not trying to introduce easter eggs into CSH, I don't know what you're talking about... */ + printf("♫ Ain't nobody here but us chickens ♫ Maybe do a little 'list download [node]'?\n"); + } else { + printf("No known parameters on node %d, perhaps you've forgotten to 'list download [node]'?\n", node); + } } if (!matches) { From 8962b6797d3fa2f63b4801624a4f7bcf5770eb45 Mon Sep 17 00:00:00 2001 From: kivkiv12345 Date: Mon, 19 Aug 2024 13:11:36 +0200 Subject: [PATCH 2/2] Fix param_completer() optparse memory leak --- src/param/param_slash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/param/param_slash.c b/src/param/param_slash.c index fdd4badf..f39af5b1 100644 --- a/src/param/param_slash.c +++ b/src/param/param_slash.c @@ -90,7 +90,7 @@ static void param_completer(struct slash *slash, char * token) { #define SLASH_ARG_MAX 16 /* Maximum number of arguments */ char *argv[SLASH_ARG_MAX]; int argc = 0; - char *args = strdup(slash->buffer + strlen(skip_prefix)); + char *args = strdup(slash->buffer + strlen(skip_prefix)); // Must be freed for now, but not when we replace it with a fixed slash->arg*. if (args == NULL) { fprintf(stderr, "No memory for tab-completion\n"); return; @@ -113,6 +113,7 @@ static void param_completer(struct slash *slash, char * token) { //int argi = optparse_parse(parser, slash->argc - 1, (const char **) slash->argv + 1); int argi = optparse_parse(parser, argc - 1, (const char **) argv + 1); optparse_del(parser); + free(args); if (argi < 0) { return; }