Module Name: src Committed By: christos Date: Sun Aug 15 10:17:55 UTC 2021
Modified Files: src/bin/sh: histedit.c myhistedit.h Log Message: - Add command completion (from FreeBSD) - Use EL_SAFEREAD To generate a diff of this commit: cvs rdiff -u -r1.55 -r1.56 src/bin/sh/histedit.c cvs rdiff -u -r1.13 -r1.14 src/bin/sh/myhistedit.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/bin/sh/histedit.c diff -u src/bin/sh/histedit.c:1.55 src/bin/sh/histedit.c:1.56 --- src/bin/sh/histedit.c:1.55 Sun Feb 10 14:21:52 2019 +++ src/bin/sh/histedit.c Sun Aug 15 06:17:55 2021 @@ -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 @@ History *hist; /* history cookie */ 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 @@ histedit(void) 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 @@ histcmd(volatile int argc, char ** volat return 0; } -STATIC const char * +static const char * fc_replace(const char *s, char *p, char *r) { char *dest; @@ -512,20 +521,123 @@ fc_replace(const char *s, char *p, char STACKSTRNUL(dest); dest = grabstackstr(dest); - return (dest); + return dest; } -int -not_fcnumber(char *s) + +/* + * 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); +} + +/* + * 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 @@ str_to_event(const char *str, int last) if (retval == -1) error("history pattern not found: %s", str); } - return (he.num); + return he.num; } #else int Index: src/bin/sh/myhistedit.h diff -u src/bin/sh/myhistedit.h:1.13 src/bin/sh/myhistedit.h:1.14 --- src/bin/sh/myhistedit.h:1.13 Wed Jun 28 09:46:06 2017 +++ src/bin/sh/myhistedit.h Sun Aug 15 06:17:55 2021 @@ -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 History *hist; 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); -