Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/bin/sh - Add command completion (from FreeBSD)
details: https://anonhg.NetBSD.org/src/rev/153325d254b5
branches: trunk
changeset: 1022935:153325d254b5
user: christos <christos%NetBSD.org@localhost>
date: Sun Aug 15 10:17:55 2021 +0000
description:
- Add command completion (from FreeBSD)
- Use EL_SAFEREAD
diffstat:
bin/sh/histedit.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++----
bin/sh/myhistedit.h | 7 +-
2 files changed, 127 insertions(+), 16 deletions(-)
diffs (230 lines):
diff -r c442ad58d10d -r 153325d254b5 bin/sh/histedit.c
--- a/bin/sh/histedit.c Sun Aug 15 10:12:54 2021 +0000
+++ b/bin/sh/histedit.c Sun Aug 15 10:17:55 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: histedit.c,v 1.55 2019/02/10 19:21:52 kre Exp $ */
+/* $NetBSD: histedit.c,v 1.56 2021/08/15 10:17:55 christos Exp $ */
/*-
* Copyright (c) 1993
@@ -37,11 +37,13 @@
#if 0
static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
#else
-__RCSID("$NetBSD: histedit.c,v 1.55 2019/02/10 19:21:52 kre Exp $");
+__RCSID("$NetBSD: histedit.c,v 1.56 2021/08/15 10:17:55 christos Exp $");
#endif
#endif /* not lint */
#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
@@ -71,13 +73,19 @@
EditLine *el; /* editline cookie */
int displayhist;
static FILE *el_in, *el_out;
-
-STATIC const char *fc_replace(const char *, char *, char *);
+static int curpos;
#ifdef DEBUG
extern FILE *tracefile;
#endif
+static const 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 *);
+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
* have changed (figures out what to do).
@@ -136,10 +144,11 @@
set_prompt_lit(lookupvar("PSlit"));
el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_SAFEREAD, 1);
el_set(el, EL_ALIAS_TEXT, alias_text, NULL);
el_set(el, EL_ADDFN, "rl-complete",
"ReadLine compatible completion function",
- _el_fn_complete);
+ sh_complete);
} else {
bad:
out2str("sh: can't initialize editing\n");
@@ -493,7 +502,7 @@
return 0;
}
-STATIC const char *
+static const char *
fc_replace(const char *s, char *p, char *r)
{
char *dest;
@@ -512,20 +521,123 @@
STACKSTRNUL(dest);
dest = grabstackstr(dest);
- return (dest);
+ return dest;
+}
+
+
+/*
+ * 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)
+{
+ return strcmp(*(char *const *)a + curpos,
+ *(char *const *)b + curpos);
}
-int
-not_fcnumber(char *s)
+/*
+ * This function is passed to libedit's fn_complete(). The library will
+ * use it instead of its standard function to find matches, which
+ * searches for 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, *dirname, *path;
+ char **matches = NULL;
+ size_t i = 0, size = 16;
+
+ if (start > 0)
+ return NULL;
+ curpos = end - start;
+ 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;
+
+ if ((dir = opendir(dirname)) == NULL)
+ continue;
+ if ((dfd = dirfd(dir)) == -1)
+ continue;
+ while ((entry = readdir(dir)) != NULL) {
+ struct stat statb;
+
+ 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;
+ if (++i >= size - 1) {
+ size *= 2;
+ if (reallocarr(&matches, size,
+ sizeof(*matches)))
+ {
+ closedir(dir);
+ goto out;
+ }
+ }
+ matches[i] = strdup(entry->d_name);
+ }
+ closedir(dir);
+ }
+out:
+ free(free_path);
+ if (i == 0) {
+ free(matches);
+ return NULL;
+ }
+ if (i == 1) {
+ matches[0] = strdup(matches[1]);
+ matches[i + 1] = NULL;
+ } else {
+ size_t j, k;
+
+ qsort(matches + 1, i, sizeof(matches[0]), comparator);
+ 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[0] = strdup(text);
+ 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_complete(sel, NULL, sh_matches,
+ L" \t\n\"\\'`@$><=;|&{(", NULL, NULL, (size_t)100,
+ NULL, &((int) {0}), NULL, NULL);
+}
+
+static int
+not_fcnumber(const char *s)
{
if (s == NULL)
return 0;
if (*s == '-')
s++;
- return (!is_number(s));
+ return !is_number(s);
}
-int
+static int
str_to_event(const char *str, int last)
{
HistEvent he;
@@ -571,7 +683,7 @@
if (retval == -1)
error("history pattern not found: %s", str);
}
- return (he.num);
+ return he.num;
}
#else
int
diff -r c442ad58d10d -r 153325d254b5 bin/sh/myhistedit.h
--- a/bin/sh/myhistedit.h Sun Aug 15 10:12:54 2021 +0000
+++ b/bin/sh/myhistedit.h Sun Aug 15 10:17:55 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: myhistedit.h,v 1.13 2017/06/28 13:46:06 kre Exp $ */
+/* $NetBSD: myhistedit.h,v 1.14 2021/08/15 10:17:55 christos Exp $ */
/*-
* Copyright (c) 1993
@@ -37,12 +37,11 @@
extern EditLine *el;
extern int displayhist;
+#include <filecomplete.h>
+
void histedit(void);
void sethistsize(const char *);
void setterm(const char *);
int inputrc(int, char **);
void set_editrc(const char *);
void set_prompt_lit(const char *);
-int not_fcnumber(char *);
-int str_to_event(const char *, int);
-
Home |
Main Index |
Thread Index |
Old Index