refactor code to be able to track whether we have a parameter substitution

or a “proper” glob expansion; if there’s a dollar, but not a glob, refrain
from appending a space later (LP: #710539)
This commit is contained in:
tg 2011-02-03 15:57:52 +00:00
parent 1c4aab3a3e
commit d4658a569e
3 changed files with 260 additions and 188 deletions

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.404 2011/01/30 02:18:25 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.405 2011/02/03 15:57:49 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -25,7 +25,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout:
@(#)MIRBSD KSH R39 2011/01/29
@(#)MIRBSD KSH R39 2011/02/03
description:
Check version of shell.
stdin:

440
edit.c
View File

@ -25,7 +25,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.202 2011/01/21 22:43:17 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.203 2011/02/03 15:57:50 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@ -57,6 +57,9 @@ static X_chars edchars;
#define XCF_FILE BIT(1) /* Do file completion */
#define XCF_FULLPATH BIT(2) /* command completion: store full path */
#define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
#define XCF_IS_COMMAND BIT(3) /* return flag: is command */
#define XCF_IS_VARSUB BIT(4) /* return flag: is $FOO substitution */
#define XCF_IS_EXTGLOB BIT(5) /* return flag: is foo* expansion */
static char editmode;
static int xx_cols; /* for Emacs mode */
@ -68,8 +71,7 @@ static void x_putcf(int);
static bool x_mode(bool);
static int x_do_comment(char *, int, int *);
static void x_print_expansions(int, char *const *, bool);
static int x_cf_glob(int, const char *, int, int, int *, int *, char ***,
bool *);
static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
static int x_longest_prefix(int, char *const *);
static int x_basename(const char *, const char *);
static void x_free_words(int, char **);
@ -89,17 +91,10 @@ static int x_vi(char *, size_t);
#endif
static int path_order_cmp(const void *aa, const void *bb);
static char *add_glob(const char *, int)
MKSH_A_NONNULL((nonnull (1)))
MKSH_A_BOUNDED(string, 1, 2);
static void glob_table(const char *, XPtrV *, struct table *);
static void glob_path(int flags, const char *, XPtrV *, const char *);
static int x_file_glob(int, const char *, int, char ***)
MKSH_A_NONNULL((nonnull (2)))
MKSH_A_BOUNDED(string, 2, 3);
static int x_command_glob(int, const char *, int, char ***)
MKSH_A_NONNULL((nonnull (2)))
MKSH_A_BOUNDED(string, 2, 3);
static int x_file_glob(int, char *, char ***);
static int x_command_glob(int, char *, char ***);
static int x_locate_word(const char *, int, int, int *, bool *);
static int x_e_getmbc(char *);
@ -111,11 +106,14 @@ static int x_e_rebuildline(const char *);
void
x_init(void)
{
/* set to -2 to force initial binding */
/*
* Set edchars to -2 to force initial binding, except
* we need default values for some deficient systems
*/
edchars.erase = edchars.kill = edchars.intr = edchars.quit =
edchars.eof = -2;
/* default value for deficient systems */
edchars.werase = 027; /* ^W */
/* ^W */
edchars.werase = 027;
x_init_emacs();
}
@ -136,7 +134,8 @@ x_read(char *buf, size_t len)
i = x_vi(buf, len);
#endif
else
i = -1; /* internal error */
/* internal error */
i = -1;
editmode = 0;
x_mode(false);
return (i);
@ -179,7 +178,8 @@ x_putcf(int c)
* Misc common code for vi/emacs *
*********************************/
/* Handle the commenting/uncommenting of a line.
/*-
* Handle the commenting/uncommenting of a line.
* Returns:
* 1 if a carriage return is indicated (comment added)
* 0 if no return (comment removed)
@ -193,7 +193,8 @@ x_do_comment(char *buf, int bsize, int *lenp)
int i, j, len = *lenp;
if (len == 0)
return (1); /* somewhat arbitrary - it's what AT&T ksh does */
/* somewhat arbitrary - it's what AT&T ksh does */
return (1);
/* Already commented? */
if (buf[0] == '#') {
@ -238,7 +239,8 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
int prefix_len;
XPtrV l = { NULL, NULL, NULL };
/* Check if all matches are in the same directory (in this
/*
* Check if all matches are in the same directory (in this
* case, we want to omit the directory name)
*/
if (!is_command &&
@ -272,7 +274,8 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
pr_list(use_copy ? (char **)XPptrv(l) : words);
if (use_copy)
XPfree(l); /* not x_free_words() */
/* not x_free_words() */
XPfree(l);
}
/**
@ -283,19 +286,14 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
* - returns number of matching strings
*/
static int
x_file_glob(int flags MKSH_A_UNUSED, const char *str, int slen, char ***wordsp)
x_file_glob(int flags MKSH_A_UNUSED, char *toglob, char ***wordsp)
{
char *toglob, **words;
char **words;
int nwords, i, idx;
bool escaping;
XPtrV w;
struct source *s, *sold;
if (slen < 0)
return (0);
toglob = add_glob(str, slen);
/* remove all escaping backward slashes */
escaping = false;
for (i = 0, idx = 0; toglob[i]; i++) {
@ -338,8 +336,9 @@ x_file_glob(int flags MKSH_A_UNUSED, const char *str, int slen, char ***wordsp)
if (nwords == 1) {
struct stat statb;
/* Check if globbing failed (returned glob pattern),
* but be careful (E.g. toglob == "ab*" when the file
/*
* Check if globbing failed (returned glob pattern),
* but be careful (e.g. toglob == "ab*" when the file
* "ab*" exists is not an error).
* Also, check for empty result - happens if we tried
* to glob something which evaluated to an empty
@ -353,7 +352,6 @@ x_file_glob(int flags MKSH_A_UNUSED, const char *str, int slen, char ***wordsp)
nwords = 0;
}
}
afree(toglob, ATEMP);
if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
x_free_words(nwords, words);
@ -381,21 +379,15 @@ path_order_cmp(const void *aa, const void *bb)
}
static int
x_command_glob(int flags, const char *str, int slen, char ***wordsp)
x_command_glob(int flags, char *toglob, char ***wordsp)
{
char *toglob, *pat, *fpath;
char *pat, *fpath;
int nwords;
XPtrV w;
struct block *l;
if (slen < 0)
return (0);
toglob = add_glob(str, slen);
/* Convert "foo*" (toglob) to a pattern for future use */
pat = evalstr(toglob, DOPAT | DOTILDE);
afree(toglob, ATEMP);
XPinit(w, 32);
@ -481,7 +473,8 @@ x_locate_word(const char *buf, int buflen, int pos, int *startp,
/* The case where pos == buflen happens to take care of itself... */
start = pos;
/* Keep going backwards to start of word (has effect of allowing
/*
* Keep going backwards to start of word (has effect of allowing
* one blank after the end of a word)
*/
for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
@ -502,7 +495,8 @@ x_locate_word(const char *buf, int buflen, int pos, int *startp,
p--;
iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
if (iscmd) {
/* If command has a /, path, etc. is not searched;
/*
* If command has a /, path, etc. is not searched;
* only current directory is searched which is just
* like file globbing.
*/
@ -519,79 +513,96 @@ x_locate_word(const char *buf, int buflen, int pos, int *startp,
}
static int
x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
int *endp, char ***wordsp, bool *is_commandp)
x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
int *endp, char ***wordsp)
{
int len, nwords;
int len, nwords = 0;
char **words = NULL;
bool is_command;
len = x_locate_word(buf, buflen, pos, startp, &is_command);
if (!(flags & XCF_COMMAND))
if (!(*flagsp & XCF_COMMAND))
is_command = false;
/* Don't do command globing on zero length strings - it takes too
/*
* Don't do command globing on zero length strings - it takes too
* long and isn't very useful. File globs are more likely to be
* useful, so allow these.
*/
if (len == 0 && is_command)
return (0);
nwords = is_command ?
x_command_glob(flags, buf + *startp, len, &words) :
x_file_glob(flags, buf + *startp, len, &words);
if (len >= 0) {
char *toglob, *s;
bool saw_slash = false, saw_dollar = false, saw_glob = false;
/*
* Given a string, copy it and possibly add a '*' to the end.
*/
strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP);
toglob[len] = '\0';
/*
* If the pathname contains a wildcard (an unquoted '*',
* '?', or '[') or parameter expansion ('$'), or a ~username
* with no trailing slash, then it is globbed based on that
* value (i.e., without the appended '*').
*/
for (s = toglob; *s; s++) {
if (*s == '\\' && s[1])
s++;
else if (*s == '$') {
/*
* Do not append a space after the value
* if expanding a parameter substitution
* as in: cat $HOME/.ss (LP: #710539)
*/
saw_dollar = true;
} else if (*s == '?' || *s == '*' || *s == '[' ||
/* ?() *() +() @() !() but two already checked */
(s[1] == '(' /*)*/ &&
(*s == '+' || *s == '@' || *s == '!'))) {
/* just expand based on the extglob */
saw_glob = true;
} else if (*s == '/')
saw_slash = true;
}
if (saw_glob) {
/*
* do not append a glob, we already have a
* glob or extglob; it works even if this is
* a parameter expansion as we have a glob
*/
*flagsp |= XCF_IS_EXTGLOB;
} else if (saw_dollar) {
/* do not append a glob, nor later a space */
*flagsp |= XCF_IS_VARSUB;
} else if (*toglob != '~' || saw_slash) {
/* append a glob, this is not just a tilde */
toglob[len] = '*';
toglob[len + 1] = '\0';
}
/*
* Expand (glob) it now.
*/
nwords = is_command ?
x_command_glob(*flagsp, toglob, &words) :
x_file_glob(*flagsp, toglob, &words);
afree(toglob, ATEMP);
}
if (nwords == 0) {
*wordsp = NULL;
return (0);
}
if (is_commandp)
*is_commandp = is_command;
*flagsp |= XCF_IS_COMMAND;
*wordsp = words;
*endp = *startp + len;
return (nwords);
}
/* Given a string, copy it and possibly add a '*' to the end.
* The new string is returned.
*/
static char *
add_glob(const char *str, int slen)
{
char *toglob, *s;
bool saw_slash = false;
if (slen < 0)
return (NULL);
/* for clang's static analyser, the nonnull attribute isn't enough */
mkssert(str != NULL);
strndupx(toglob, str, slen + 1, ATEMP); /* + 1 for "*" */
toglob[slen] = '\0';
/*
* If the pathname contains a wildcard (an unquoted '*',
* '?', or '[') or parameter expansion ('$'), or a ~username
* with no trailing slash, then it is globbed based on that
* value (i.e., without the appended '*').
*/
for (s = toglob; *s; s++) {
if (*s == '\\' && s[1])
s++;
else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' ||
(s[1] == '(' /*)*/ && /* *s in '*','?' already checked */
(*s == '+' || *s == '@' || *s == '!')))
break;
else if (*s == '/')
saw_slash = true;
}
if (!*s && (*toglob != '~' || saw_slash)) {
toglob[slen] = '*';
toglob[slen + 1] = '\0';
}
return (toglob);
}
/*
* Find longest common prefix
*/
@ -622,7 +633,8 @@ x_free_words(int nwords, char **words)
afree(words, ATEMP);
}
/* Return the offset of the basename of string s (which ends at se - need not
/*-
* Return the offset of the basename of string s (which ends at se - need not
* be null terminated). Trailing slashes are ignored. If s is just a slash,
* then the offset is 0 (actually, length - 1).
* s Return
@ -694,7 +706,8 @@ glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
p = sp + strlen(sp);
pathlen = p - sp;
if (pathlen) {
/* Copy sp into xp, stuffing any MAGIC characters
/*
* Copy sp into xp, stuffing any MAGIC characters
* on the way
*/
const char *s = sp;
@ -713,7 +726,8 @@ glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
memcpy(xp, pat, patlen);
oldsize = XPsize(*wp);
glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
/* mark dirs */
glob_str(Xstring(xs, xp), wp, 1);
newsize = XPsize(*wp);
/* Check that each match is executable... */
@ -809,7 +823,8 @@ struct x_defbindings {
#define X_NTABS 3 /* normal, meta1, meta2 */
#define X_TABSZ 256 /* size of keydef tables etc */
/* Arguments for do_complete()
/*-
* Arguments for do_complete()
* 0 = enumerate M-= complete as much as possible and then list
* 1 = complete M-Esc
* 2 = list M-?
@ -1000,7 +1015,8 @@ static struct x_defbindings const x_defbindings[] = {
{ XFUNC_fold_capitalise, 1, 'C' },
{ XFUNC_fold_capitalise, 1, 'c' },
#endif
/* These for ansi arrow keys: arguablely shouldn't be here by
/*
* These for ANSI arrow keys: arguablely shouldn't be here by
* default, but its simpler/faster/smaller than using termcap
* entries.
*/
@ -1156,7 +1172,8 @@ x_emacs(char *buf, size_t len)
case KEOL:
i = xep - xbuf;
return (i);
case KINTR: /* special case for interrupt */
case KINTR:
/* special case for interrupt */
trapsig(SIGINT);
x_mode(false);
unwind(LSHELL);
@ -1267,7 +1284,8 @@ x_ins(const char *s)
x_lastcp();
x_adj_ok = (xcp >= xlp);
x_zots(cp);
if (adj == x_adj_done) { /* has x_adjust() been called? */
/* has x_adjust() been called? */
if (adj == x_adj_done) {
/* no */
cp = xlp;
while (cp > xcp)
@ -1356,13 +1374,15 @@ x_delete(int nc, int push)
x_push(nb);
xep -= nb;
memmove(xcp, xcp + nb, xep - xcp + 1); /* Copies the NUL */
x_adj_ok = 0; /* don't redraw */
/* Copies the NUL */
memmove(xcp, xcp + nb, xep - xcp + 1);
/* don't redraw */
x_adj_ok = 0;
xlp_valid = false;
x_zots(xcp);
/*
* if we are already filling the line,
* there is no need to ' ','\b'.
* there is no need to ' ', '\b'.
* But if we must, make sure we do the minimum.
*/
if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
@ -1474,10 +1494,12 @@ x_goto(char *cp)
/* we are heading off screen */
xcp = cp;
x_adjust();
} else if (cp < xcp) { /* move back */
} else if (cp < xcp) {
/* move back */
while (cp < xcp)
x_bs3(&xcp);
} else if (cp > xcp) { /* move forward */
} else if (cp > xcp) {
/* move forward */
while (cp > xcp)
x_zotc3(&xcp);
}
@ -1517,9 +1539,11 @@ x_size2(char *cp, char **dcp)
if (dcp)
*dcp = cp + 1;
if (c == '\t')
return (4); /* Kludge, tabs are always four spaces. */
/* Kludge, tabs are always four spaces. */
return (4);
if (c < ' ' || c == 0x7f)
return (2); /* control unsigned char */
/* control unsigned char */
return (2);
return (1);
}
@ -1702,7 +1726,8 @@ x_next_com(int c MKSH_A_UNUSED)
return (KSTD);
}
/* Goto a particular history number obtained from argument.
/*
* Goto a particular history number obtained from argument.
* If no argument is given history 1 is probably not what you
* want so we'll simply go to the oldest one.
*/
@ -1835,7 +1860,8 @@ x_search_hist(int c)
if (offset >= 0)
x_load_hist(histptr + 1);
break;
} else { /* other command */
} else {
/* other command */
x_e_ungetc(c);
break;
}
@ -1967,7 +1993,8 @@ x_cls(int c MKSH_A_UNUSED)
return (x_e_rebuildline(MKSH_CLS_STRING));
}
/* Redraw (part of) the line. If limit is < 0, the everything is redrawn
/*
* Redraw (part of) the line. If limit is < 0, the everything is redrawn
* on a NEW line, otherwise limit is the screen column up to which needs
* redrawing.
*/
@ -2004,7 +2031,8 @@ x_redraw(int limit)
limit = xx_cols;
if (limit >= 0) {
if (xep > xlp)
i = 0; /* we fill the line */
/* we fill the line */
i = 0;
else {
char *cpl = xbp;
@ -2021,7 +2049,8 @@ x_redraw(int limit)
j++;
}
i = ' ';
if (xep > xlp) { /* more off screen */
if (xep > xlp) {
/* more off screen */
if (xbp > xbuf)
i = '*';
else
@ -2045,7 +2074,8 @@ x_transpose(int c MKSH_A_UNUSED)
{
unsigned int tmpa, tmpb;
/* What transpose is meant to do seems to be up for debate. This
/*-
* What transpose is meant to do seems to be up for debate. This
* is a general summary of the options; the text is abcd with the
* upper case character or underscore indicating the cursor position:
* Who Before After Before After
@ -2066,8 +2096,9 @@ x_transpose(int c MKSH_A_UNUSED)
x_e_putc2(7);
return (KSTD);
}
/* Gosling/Unipress emacs style: Swap two characters before the
* cursor, do not change cursor position
/*
* Gosling/Unipress emacs style: Swap two characters before
* the cursor, do not change cursor position
*/
x_bs3(&xcp);
if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
@ -2084,7 +2115,8 @@ x_transpose(int c MKSH_A_UNUSED)
utf_wctomb(xcp, tmpb);
x_zotc3(&xcp);
} else {
/* GNU emacs style: Swap the characters before and under the
/*
* GNU emacs style: Swap the characters before and under the
* cursor, move cursor position along one.
*/
if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
@ -2281,7 +2313,8 @@ x_mapin(const char *cp, Area *ap)
/* XXX -- should handle \^ escape? */
if (*cp == '^') {
cp++;
if (*cp >= '?') /* includes '?'; ASCII */
if (*cp >= '?')
/* includes '?'; ASCII */
*op++ = CTRL(*cp);
else {
*op++ = '^';
@ -2345,9 +2378,11 @@ x_print(int prefix, int key)
int
x_bind(const char *a1, const char *a2,
#ifndef MKSH_SMALL
bool macro, /* bind -m */
/* bind -m */
bool macro,
#endif
bool list) /* bind -l */
/* bind -l */
bool list)
{
unsigned char f;
int prefix, key;
@ -2605,10 +2640,10 @@ x_expand(int c MKSH_A_UNUSED)
{
char **words;
int start, end, nwords, i;
bool is_command;
nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
&start, &end, &words, &is_command);
i = XCF_FILE;
nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf,
&start, &end, &words);
if (nwords == 0) {
x_e_putc2(7);
@ -2616,7 +2651,9 @@ x_expand(int c MKSH_A_UNUSED)
}
x_goto(xbuf + start);
x_delete(end - start, false);
for (i = 0; i < nwords;) {
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
(++i < nwords && x_ins(" ") < 0)) {
x_e_putc2(7);
@ -2628,24 +2665,26 @@ x_expand(int c MKSH_A_UNUSED)
return (KSTD);
}
/* type == 0 for list, 1 for complete and 2 for complete-list */
static void
do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
do_complete(
/* XCF_{COMMAND,FILE,COMMAND_FILE} */
int flags,
/* 0 for list, 1 for complete and 2 for complete-list */
Comp_type type)
{
char **words;
int start, end, nlen, olen, nwords;
bool is_command, completed = false;
bool completed = false;
nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
&start, &end, &words, &is_command);
nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf,
&start, &end, &words);
/* no match */
if (nwords == 0) {
x_e_putc2(7);
return;
}
if (type == CT_LIST) {
x_print_expansions(nwords, words, is_command);
x_print_expansions(nwords, words, flags & XCF_IS_COMMAND);
x_redraw(0);
x_free_words(nwords, words);
return;
@ -2660,13 +2699,14 @@ do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
x_adjust();
completed = true;
}
/* add space if single non-dir match */
if (nwords == 1 && words[0][nlen - 1] != '/') {
/* add space if single non-dir match and not parameter substitution */
if (nwords == 1 && words[0][nlen - 1] != '/' &&
!(flags & XCF_IS_VARSUB)) {
x_ins(" ");
completed = true;
}
if (type == CT_COMPLIST && !completed) {
x_print_expansions(nwords, words, is_command);
x_print_expansions(nwords, words, flags & XCF_IS_COMMAND);
completed = true;
}
if (completed)
@ -2675,7 +2715,8 @@ do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
x_free_words(nwords, words);
}
/* NAME:
/*-
* NAME:
* x_adjust - redraw the line adjusting starting point etc.
*
* DESCRIPTION:
@ -2691,7 +2732,8 @@ do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
static void
x_adjust(void)
{
x_adj_done++; /* flag the fact that we were called. */
/* flag the fact that we were called. */
x_adj_done++;
/*
* we had a problem if the prompt length > xx_cols / 2
*/
@ -2819,7 +2861,8 @@ x_e_puts(const char *s)
x_e_putc3(&s);
}
/* NAME:
/*-
* NAME:
* x_set_arg - set an arg value for next function
*
* DESCRIPTION:
@ -2833,7 +2876,8 @@ x_set_arg(int c)
{
int n = 0, first = 1;
c &= 255; /* strip command prefix */
/* strip command prefix */
c &= 255;
for (; c >= 0 && ksh_isdigit(c); c = x_e_getc(), first = 0)
n = n * 10 + (c - '0');
if (c < 0 || first) {
@ -2929,7 +2973,8 @@ x_edit_line(int c MKSH_A_UNUSED)
}
#endif
/* NAME:
/*-
* NAME:
* x_prev_histword - recover word from prev command
*
* DESCRIPTION:
@ -3026,7 +3071,8 @@ x_fold_capitalise(int c MKSH_A_UNUSED)
return (x_fold_case('C'));
}
/* NAME:
/*-
* NAME:
* x_fold_case - convert word to UPPER/lower/Capital case
*
* DESCRIPTION:
@ -3056,9 +3102,11 @@ x_fold_case(int c)
* a different action than for the rest.
*/
if (cp != xep) {
if (c == 'L') /* lowercase */
if (c == 'L')
/* lowercase */
*cp = ksh_tolower(*cp);
else /* uppercase, capitalise */
else
/* uppercase, capitalise */
*cp = ksh_toupper(*cp);
cp++;
}
@ -3066,9 +3114,11 @@ x_fold_case(int c)
* now for the rest of the word
*/
while (cp != xep && !is_mfs(*cp)) {
if (c == 'U') /* uppercase */
if (c == 'U')
/* uppercase */
*cp = ksh_toupper(*cp);
else /* lowercase, capitalise */
else
/* lowercase, capitalise */
*cp = ksh_tolower(*cp);
cp++;
}
@ -3079,7 +3129,8 @@ x_fold_case(int c)
}
#endif
/* NAME:
/*-
* NAME:
* x_lastcp - last visible char
*
* SYNOPSIS:
@ -3146,10 +3197,10 @@ x_mode(bool onoff)
cb.c_iflag &= ~(INLCR | ICRNL);
cb.c_lflag &= ~(ISIG | ICANON | ECHO);
#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
/* osf/1 processes lnext when ~icanon */
/* OSF/1 processes lnext when ~icanon */
cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
/* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
#endif
@ -3345,7 +3396,8 @@ static int ohnum; /* history line copied (after mod) */
static int hlast; /* 1 past last position in history */
static int state;
/* Information for keeping track of macros that are being expanded.
/*
* Information for keeping track of macros that are being expanded.
* The format of buf is the alias contents followed by a NUL byte followed
* by the name (letter) of the alias. The end of the buffer is marked by
* a double NUL. The name of the alias is stored so recursive macros can
@ -3809,7 +3861,8 @@ vi_insert(int ch)
expanded = NONE;
return (0);
}
/* If any chars are entered before escape, trash the saved insert
/*
* If any chars are entered before escape, trash the saved insert
* buffer (if user inserts & deletes char, ibuf gets trashed and
* we don't want to use it)
*/
@ -4332,29 +4385,37 @@ vi_cmd(int argcnt, const char *cmd)
return (ret);
}
case '=': /* AT&T ksh */
case Ctrl('e'): /* Nonstandard vi/ksh */
/* AT&T ksh */
case '=':
/* Nonstandard vi/ksh */
case Ctrl('e'):
print_expansions(es, 1);
break;
case Ctrl('i'): /* Nonstandard vi/ksh */
/* Nonstandard vi/ksh */
case Ctrl('i'):
if (!Flag(FVITABCOMPLETE))
return (-1);
complete_word(1, argcnt);
break;
case Ctrl('['): /* some annoying AT&T kshs */
/* some annoying AT&T kshs */
case Ctrl('['):
if (!Flag(FVIESCCOMPLETE))
return (-1);
case '\\': /* AT&T ksh */
case Ctrl('f'): /* Nonstandard vi/ksh */
/* AT&T ksh */
case '\\':
/* Nonstandard vi/ksh */
case Ctrl('f'):
complete_word(1, argcnt);
break;
case '*': /* AT&T ksh */
case Ctrl('x'): /* Nonstandard vi/ksh */
/* AT&T ksh */
case '*':
/* Nonstandard vi/ksh */
case Ctrl('x'):
expand_word(1);
break;
}
@ -5005,7 +5066,8 @@ display(char *wb1, char *wb2, int leftside)
col++;
}
if (es->winleft > 0 && moreright)
/* POSIX says to use * for this but that is a globbing
/*
* POSIX says to use * for this but that is a globbing
* character and may confuse people; + is more innocuous
*/
mc = '+';
@ -5054,11 +5116,8 @@ static int
expand_word(int cmd)
{
static struct edstate *buf;
int rval = 0;
int nwords;
int start, end;
int rval = 0, nwords, start, end, i;
char **words;
int i;
/* Undo previous expansion */
if (cmd == 0 && expanded == EXPAND && buf) {
@ -5072,9 +5131,9 @@ expand_word(int cmd)
buf = 0;
}
nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
es->cbuf, es->linelen, es->cursor,
&start, &end, &words, NULL);
i = XCF_COMMAND_FILE | XCF_FULLPATH;
nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
return (-1);
@ -5084,7 +5143,8 @@ expand_word(int cmd)
expanded = EXPAND;
del_range(start, end);
es->cursor = start;
for (i = 0; i < nwords; ) {
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
rval = -1;
break;
@ -5109,10 +5169,10 @@ static int
complete_word(int cmd, int count)
{
static struct edstate *buf;
int rval, nwords, start, end, match_len;
int rval, nwords, start, end, match_len, flags;
char **words;
char *match;
bool is_command, is_unique;
bool is_unique;
/* Undo previous completion */
if (cmd == 0 && expanded == COMPLETE && buf) {
@ -5131,12 +5191,15 @@ complete_word(int cmd, int count)
buf = 0;
}
/* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
* was done this way.
/*
* XCF_FULLPATH for count 'cause the menu printed by
* print_expansions() was done this way.
*/
nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
es->cbuf, es->linelen, es->cursor,
&start, &end, &words, &is_command);
flags = XCF_COMMAND_FILE;
if (count)
flags |= XCF_FULLPATH;
nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
return (-1);
@ -5147,7 +5210,8 @@ complete_word(int cmd, int count)
count--;
if (count >= nwords) {
vi_error();
x_print_expansions(nwords, words, is_command);
x_print_expansions(nwords, words,
flags & XCF_IS_COMMAND);
x_free_words(nwords, words);
redraw_line(0);
return (-1);
@ -5155,7 +5219,7 @@ complete_word(int cmd, int count)
/*
* Expand the count'th word to its basename
*/
if (is_command) {
if (flags & XCF_IS_COMMAND) {
match = words[count] +
x_basename(words[count], NULL);
/* If more than one possible match, use full path */
@ -5174,7 +5238,8 @@ complete_word(int cmd, int count)
} else {
match = words[0];
match_len = x_longest_prefix(nwords, words);
expanded = COMPLETE; /* next call will list completions */
/* next call will list completions */
expanded = COMPLETE;
is_unique = nwords == 1;
}
@ -5182,18 +5247,25 @@ complete_word(int cmd, int count)
del_range(start, end);
es->cursor = start;
/* escape all shell-sensitive characters and put the result into
* command buffer */
/*
* escape all shell-sensitive characters and put the result into
* command buffer
*/
rval = x_escape(match, match_len, x_vi_putbuf);
if (rval == 0 && is_unique) {
/* If exact match, don't undo. Allows directory completions
/*
* If exact match, don't undo. Allows directory completions
* to be used (ie, complete the next portion of the path).
*/
expanded = NONE;
/* If not a directory, add a space to the end... */
if (match_len > 0 && match[match_len - 1] != '/')
/*
* append a space if this is not a directory or the
* result of a parameter substitution
*/
if (match_len > 0 && match[match_len - 1] != '/' &&
!(flags & XCF_IS_VARSUB))
rval = putbuf(" ", 1, 0);
}
x_free_words(nwords, words);
@ -5201,7 +5273,8 @@ complete_word(int cmd, int count)
modified = 1;
hnum = hlast;
insert = INSERT;
lastac = 0; /* prevent this from being redone... */
/* prevent this from being redone... */
lastac = 0;
refresh(0);
return (rval);
@ -5210,18 +5283,17 @@ complete_word(int cmd, int count)
static int
print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
{
int start, end, nwords;
int start, end, nwords, i;
char **words;
bool is_command;
nwords = x_cf_glob(XCF_COMMAND_FILE | XCF_FULLPATH,
est->cbuf, est->linelen, est->cursor,
&start, &end, &words, &is_command);
i = XCF_COMMAND_FILE | XCF_FULLPATH;
nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
return (-1);
}
x_print_expansions(nwords, words, is_command);
x_print_expansions(nwords, words, i & XCF_IS_COMMAND);
x_free_words(nwords, words);
redraw_line(0);
return (0);

4
sh.h
View File

@ -154,9 +154,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.426 2011/01/30 01:36:00 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.427 2011/02/03 15:57:52 tg Exp $");
#endif
#define MKSH_VERSION "R39 2011/01/29"
#define MKSH_VERSION "R39 2011/02/03"
#ifndef MKSH_INCLUDES_ONLY