summaryrefslogtreecommitdiffstats
path: root/common/complete.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/complete.c')
-rw-r--r--common/complete.c152
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))