fix backslash-asterisk glob noise for nōn-existant targets and tilde

expansion completion; problem analysis and solution draft by Andrew
Kudryashov (thanks a lot!); this fix instead re-uses existing code
for solving the problem with multiple matches

debugged using “make d dr” targets, yay!
This commit is contained in:
tg 2012-10-21 18:33:46 +00:00
parent b843e91274
commit 1f43cc907d
1 changed files with 42 additions and 27 deletions

69
edit.c
View File

@ -28,7 +28,7 @@
#ifndef MKSH_NO_CMDLINE_EDITING
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.254 2012/10/03 17:24:17 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.255 2012/10/21 18:33:46 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@ -77,7 +77,7 @@ static void x_print_expansions(int, char *const *, bool);
static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
static size_t x_longest_prefix(int, char *const *);
static void x_glob_hlp_add_qchar(char *);
static void x_glob_hlp_rem_qchar(char *);
static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
static int x_basename(const char *, const char *);
static void x_free_words(int, char **);
static int x_escape(const char *, size_t, int (*)(const char *, size_t));
@ -323,12 +323,42 @@ x_glob_hlp_add_qchar(char *cp)
}
/*
* Unescape a QCHAR-escaped string
* Run tilde expansion on argument string, return the result
* after unescaping; if the flag is set, the original string
* is freed if changed and assumed backslash-escaped, if not
* it is assumed QCHAR-escaped
*/
static void
x_glob_hlp_rem_qchar(char *cp)
static char *
x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
{
char ch, *dp = cp;
char ch, *cp, *dp;
/*
* On the string, check whether we have a tilde expansion,
* and if so, discern "~foo/bar" and "~/baz" from "~blah";
* if we have a directory part (the former), try to expand
*/
if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
/* ok, so split into "~foo"/"bar" or "~"/"baz" */
*cp++ = 0;
/* try to expand the tilde */
if (!(dp = tilde(s + 1))) {
/* nope, revert damage */
*--cp = '/';
} else {
/* ok, expand and replace */
cp = shf_smprintf("%s/%s", dp, cp);
if (magic_flag)
afree(s, ATEMP);
s = cp;
}
}
/* ... convert it from backslash-escaped via QCHAR-escaped... */
if (magic_flag)
x_glob_hlp_add_qchar(s);
/* ... to unescaped, for comparison with the matches */
cp = dp = s;
while ((ch = *cp++)) {
if (ch == QCHAR && !(ch = *cp++))
@ -336,11 +366,12 @@ x_glob_hlp_rem_qchar(char *cp)
*dp++ = ch;
}
*dp = '\0';
return (s);
}
/**
* Do file globbing:
* - appends * to (copy of) str if no globbing chars found
* - does expansion, checks for no match, etc.
* - sets *wordsp to array of matching strings
* - returns number of matching strings
@ -390,8 +421,8 @@ x_file_glob(int *flagsp, char *toglob, char ***wordsp)
if (nwords == 1) {
struct stat statb;
/* Drop all QCHAR from toglob for strcmp below */
x_glob_hlp_rem_qchar(toglob);
/* Expand any tilde and drop all QCHAR for comparison */
toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
/*
* Check if globbing failed (returned glob pattern),
@ -2769,26 +2800,10 @@ do_complete(
/* make a copy of the original string part */
strndupx(unescaped, xbuf + start, olen, ATEMP);
if (*unescaped == '~') {
/*
* do some tilde expansion; we know at this
* point (by means of having nwords > 1) that
* the string looks like "~foo/bar" and that
* the tilde resolves
*/
char *cp;
cp = strchr(unescaped + 1, '/');
*cp++ = 0;
cp = shf_smprintf("%s/%s", tilde(unescaped + 1), cp);
afree(unescaped, ATEMP);
unescaped = cp;
}
/* expand any tilde and unescape the string for comparison */
unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true);
/* ... convert it from backslash-escaped via QCHAR-escaped... */
x_glob_hlp_add_qchar(unescaped);
/* ... to unescaped, for comparison with the matches */
x_glob_hlp_rem_qchar(unescaped);
/*
* match iff entire original string is part of the
* longest prefix, implying the latter is at least