Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102544073
D29361.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D29361.diff
View Options
diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
--- a/bin/sh/histedit.c
+++ b/bin/sh/histedit.c
@@ -39,6 +39,8 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <limits.h>
#include <paths.h>
#include <stdio.h>
@@ -72,6 +74,9 @@
static char *fc_replace(const char *, char *, char *);
static int not_fcnumber(const char *);
static int str_to_event(const char *, int);
+static int comparator(const void *, const void *, void *);
+static char **sh_matches(const char *, int, int);
+static unsigned char sh_complete(EditLine *, int);
/*
* Set history and editing status. Called whenever the status may
@@ -122,7 +127,7 @@
el_set(el, EL_PROMPT, getprompt);
el_set(el, EL_ADDFN, "sh-complete",
"Filename completion",
- _el_fn_complete);
+ sh_complete);
} else {
bad:
out2fmt_flush("sh: can't initialize editing\n");
@@ -519,3 +524,124 @@
return (0);
}
#endif
+
+/*
+ * Comparator function for qsort(). The use of curpos here is to skip
+ * characters that we already know to compare equal (common prefix).
+ */
+static int
+comparator(const void *a, const void *b, void *thunk)
+{
+ size_t curpos = (intptr_t)thunk;
+ return (strcmp(*(char *const *)a + curpos,
+ *(char *const *)b + curpos));
+}
+
+/*
+ * This function is passed to libedit's fn_complete2(). The library will
+ * use it instead of its standard function that finds matching files in
+ * current directory. If we're at the start of the line, we want to look
+ * for available commands from all paths in $PATH.
+ */
+static char
+**sh_matches(const char *text, int start, int end)
+{
+ char *free_path = NULL, *path;
+ const char *dirname;
+ char **matches = NULL;
+ size_t i = 0, size = 16, j, k;
+ size_t curpos = end - start;
+
+ if (start > 0 || memchr("/.~", text[0], 3) != NULL)
+ return (NULL);
+ if ((free_path = path = strdup(pathval())) == NULL)
+ goto out;
+ if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
+ goto out;
+ while ((dirname = strsep(&path, ":")) != NULL) {
+ struct dirent *entry;
+ DIR *dir;
+ int dfd;
+
+ dir = opendir(dirname[0] == '\0' ? "." : dirname);
+ if (dir == NULL)
+ continue;
+ if ((dfd = dirfd(dir)) == -1) {
+ closedir(dir);
+ continue;
+ }
+ while ((entry = readdir(dir)) != NULL) {
+ struct stat statb;
+ char **rmatches;
+
+ if (strncmp(entry->d_name, text, curpos) != 0)
+ continue;
+ if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) {
+ if (fstatat(dfd, entry->d_name, &statb, 0) == -1)
+ continue;
+ if (!S_ISREG(statb.st_mode))
+ continue;
+ } else if (entry->d_type != DT_REG)
+ continue;
+ matches[++i] = strdup(entry->d_name);
+ if (i < size - 1)
+ continue;
+ size *= 2;
+ rmatches = reallocarray(matches, size, sizeof(matches[0]));
+ if (rmatches == NULL) {
+ closedir(dir);
+ goto out;
+ }
+ matches = rmatches;
+ }
+ closedir(dir);
+ }
+out:
+ free(free_path);
+ /*
+ * matches[0] is special: it's not a real matching file name but a common
+ * prefix for all matching names. It can't be null, unlike any other
+ * element of the array. When strings matches[0] and matches[1] compare
+ * equal and matches[2] is null that means to libedit that there is only
+ * a single match. It will then replace user input with possibly escaped
+ * string in matches[0] which is the reason to copy the full name of the
+ * only match.
+ */
+ if (i == 0) {
+ free(matches);
+ return (NULL);
+ } else if (i == 1) {
+ matches[0] = strdup(matches[1]);
+ matches[2] = NULL;
+ if (matches[0] != NULL)
+ return (matches);
+ } else
+ matches[0] = strdup(text);
+ if (matches[0] == NULL) {
+ for (j = 1; j <= i; j++)
+ free(matches[j]);
+ free(matches);
+ return (NULL);
+ }
+ qsort_s(matches + 1, i, sizeof(matches[0]), comparator,
+ (void *)(intptr_t)curpos);
+ for (j = 1, k = 2; k <= i; k++)
+ if (strcmp(matches[j] + curpos, matches[k] + curpos) == 0)
+ free(matches[k]);
+ else
+ matches[++j] = matches[k];
+ matches[j + 1] = NULL;
+ return (matches);
+}
+
+/*
+ * This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
+ * bind a key (tab by default) to execute the function.
+ */
+unsigned char
+sh_complete(EditLine *sel, int ch __unused)
+{
+ return (unsigned char)fn_complete2(sel, NULL, sh_matches,
+ L" \t\n\"\\'`@$><=;|&{(", NULL, NULL, (size_t)100,
+ NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
+}
diff --git a/bin/sh/myhistedit.h b/bin/sh/myhistedit.h
--- a/bin/sh/myhistedit.h
+++ b/bin/sh/myhistedit.h
@@ -38,6 +38,8 @@
extern EditLine *el;
extern int displayhist;
+#include <filecomplete.h>
+
void histedit(void);
void sethistsize(const char *);
void setterm(const char *);
diff --git a/lib/libedit/Makefile b/lib/libedit/Makefile
--- a/lib/libedit/Makefile
+++ b/lib/libedit/Makefile
@@ -65,7 +65,7 @@
editline.3 tok_wreset.3 \
editline.3 tok_wstr.3
-INCS= histedit.h
+INCS= histedit.h filecomplete.h
SRCS+= common.h emacs.h fcns.h func.h help.h vi.h
CLEANFILES+= common.h emacs.h fcns.h func.h help.h vi.h
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 14, 8:42 PM (7 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14633514
Default Alt Text
D29361.diff (4 KB)
Attached To
Mode
D29361: sh(1): autocomplete commands
Attached
Detach File
Event Timeline
Log In to Comment