diff options
Diffstat (limited to 'common/complete.c')
-rw-r--r-- | common/complete.c | 152 |
1 files changed, 99 insertions, 53 deletions
diff --git a/common/complete.c b/common/complete.c index 919e5abc6a..3911535621 100644 --- a/common/complete.c +++ b/common/complete.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * complete.c - functions for TAB completion * * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -23,17 +14,39 @@ #include <command.h> #include <environment.h> -static int file_complete(struct string_list *sl, char *instr, int exec) +static bool is_valid_escape(const char *str) +{ + return str[0] == '\\' && (str[1] == ' ' || str[1] == '\\'); +} + +static bool strstarts_escaped(const char *whole, const char *prefix_escaped) +{ + if (!prefix_escaped) + return true; + + while (*prefix_escaped) { + if (is_valid_escape(prefix_escaped)) + prefix_escaped++; + + if (*whole++ != *prefix_escaped++) + return false; + } + + return true; +} + +static int file_complete(struct string_list *sl, char *instr, + const char *dirn, int exec) { char *path = strdup(instr); struct stat s; DIR *dir; struct dirent *d; char tmp[PATH_MAX]; - char *base, *dirn; + char *base; base = basename(instr); - dirn = dirname(path); + dirn = dirn ?: dirname(path); dir = opendir(dirn); if (!dir) @@ -43,7 +56,7 @@ static int file_complete(struct string_list *sl, char *instr, int exec) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; - if (strncmp(base, d->d_name, strlen(base))) + if (!strstarts_escaped(d->d_name, base)) continue; strcpy(tmp, instr); @@ -102,7 +115,7 @@ static int path_command_complete(struct string_list *sl, char *instr) !strcmp(d->d_name, "..")) continue; - if (!strncmp(instr, d->d_name, strlen(instr))) { + if (strstarts_escaped(d->d_name, instr)) { strcpy(tmp, d->d_name); if (!stat(tmp, &s) && S_ISDIR(s.st_mode)) @@ -110,14 +123,7 @@ static int path_command_complete(struct string_list *sl, char *instr) else strcat(tmp, " "); - /* This function is called - * after command_complete, - * so we check if a double - * entry exist */ - if (string_list_contains - (sl, tmp) == 0) { - string_list_add_sorted(sl, tmp); - } + string_list_add_sort_uniq(sl, tmp); } } @@ -150,16 +156,10 @@ EXPORT_SYMBOL(command_complete); int device_complete(struct string_list *sl, char *instr) { - struct device_d *dev; - int len; - - if (!instr) - instr = ""; - - len = strlen(instr); + struct device *dev; for_each_device(dev) { - if (strncmp(instr, dev_name(dev), len)) + if (!strstarts_escaped(dev_name(dev), instr)) continue; string_list_add_asprintf(sl, "%s ", dev_name(dev)); @@ -169,26 +169,38 @@ int device_complete(struct string_list *sl, char *instr) } EXPORT_SYMBOL(device_complete); -static int device_param_complete(struct device_d *dev, struct string_list *sl, - char *instr, int eval) +static int device_param_complete(struct device *dev, const char *devname, + struct string_list *sl, char *instr, int eval) { struct param_d *param; - int len; - - len = strlen(instr); list_for_each_entry(param, &dev->parameters, list) { - if (strncmp(instr, param->name, len)) + if (!strstarts_escaped(param->name, instr)) continue; string_list_add_asprintf(sl, "%s%s.%s%c", - eval ? "$" : "", dev_name(dev), param->name, + eval ? "$" : "", devname, param->name, eval ? ' ' : '='); } return 0; } +int driver_complete(struct string_list *sl, char *instr) +{ + struct driver_d *drv; + + for_each_driver(drv) { + if (!strstarts_escaped(drv->name, instr)) + continue; + + string_list_add_asprintf(sl, "%s ", drv->name); + } + + return COMPLETE_CONTINUE; +} +EXPORT_SYMBOL(driver_complete); + int empty_complete(struct string_list *sl, char *instr) { return COMPLETE_END; @@ -245,7 +257,7 @@ int devicetree_nodepath_complete(struct string_list *sl, char *instr) for_each_child_of_node(node, child) { if (strncmp(base, child->name, strlen(base))) continue; - string_list_add_asprintf(sl, "%s/", child->full_name); + string_list_add_asprintf(sl, "%pOF/", child); } out: free(path); @@ -266,15 +278,23 @@ EXPORT_SYMBOL(devicetree_complete); int devicetree_file_complete(struct string_list *sl, char *instr) { devicetree_complete(sl, instr); - file_complete(sl, instr, 0); + file_complete(sl, instr, NULL, 0); return 0; } EXPORT_SYMBOL(devicetree_file_complete); +int tutorial_complete(struct string_list *sl, char *instr) +{ + file_complete(sl, instr, "/env/data/tutorial", 0); + + return 0; +} +EXPORT_SYMBOL(tutorial_complete); + static int env_param_complete(struct string_list *sl, char *instr, int eval) { - struct device_d *dev; + struct device *dev; struct variable_d *var; struct env_context *c; int len; @@ -315,14 +335,12 @@ static int env_param_complete(struct string_list *sl, char *instr, int eval) char *devname; devname = xstrndup(instr, dot - instr); - - dev = get_device_by_name(devname); - free(devname); if (dev) - device_param_complete(dev, sl, dot + 1, eval); + device_param_complete(dev, devname, sl, dot + 1, eval); + free(devname); pos = dot + 1; } @@ -330,12 +348,18 @@ static int env_param_complete(struct string_list *sl, char *instr, int eval) for_each_device(dev) { if (!strncmp(instr, dev_name(dev), len)) - device_param_complete(dev, sl, "", eval); + device_param_complete(dev, dev_name(dev), sl, "", eval); } return 0; } +int env_param_noeval_complete(struct string_list *sl, char *instr) +{ + return env_param_complete(sl, instr, 0); +} +EXPORT_SYMBOL(env_param_noeval_complete); + static int tab_pressed = 0; void complete_reset(void) @@ -343,21 +367,43 @@ void complete_reset(void) tab_pressed = 0; } +static char *skip_to_last_unescaped_space(char *instr) +{ + char *t; + + t = strrchr(instr, ' '); + if (t && (instr == t || t[-1] != '\\')) + return t + 1; + + return instr; +} + +static size_t strlen_escaped(char *instr) +{ + size_t count = 0; + + for (; *instr; instr++) { + if (is_valid_escape(instr)) + instr++; + + count++; + } + + return count; +} + static char* cmd_complete_lookup(struct string_list *sl, char *instr) { struct command *cmdtp; int len; int ret = COMPLETE_END; char *res = NULL; - char *t; for_each_command(cmdtp) { len = strlen(cmdtp->name); if (!strncmp(instr, cmdtp->name, len) && instr[len] == ' ') { instr += len + 1; - t = strrchr(instr, ' '); - if (t) - instr = t + 1; + instr = skip_to_last_unescaped_space(instr); if (cmdtp->complete) { ret = cmdtp->complete(sl, instr); @@ -402,11 +448,11 @@ int complete(char *instr, char **outstr) if (!instr) { instr = t; if (t && (t[0] == '/' || !strncmp(t, "./", 2))) { - file_complete(&sl, t, 1); + file_complete(&sl, t, NULL, 1); instr = t; } else if ((t = strrchr(t, ' '))) { t++; - file_complete(&sl, t, 0); + file_complete(&sl, t, NULL, 0); instr = t; } else { command_complete(&sl, instr); @@ -417,7 +463,7 @@ int complete(char *instr, char **outstr) env_param_complete(&sl, instr + 1, 1); } - pos = strlen(instr); + pos = strlen_escaped(instr); *outstr = ""; if (list_empty(&sl.list)) |