${foo@x} is now special-expansion for values of x, defined for now:

‘#’ = hash of $foo
This commit is contained in:
tg 2011-01-21 22:25:34 +00:00
parent 846fbde3b8
commit da9d0f3d97
4 changed files with 175 additions and 80 deletions

14
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.399 2011/01/21 22:00:17 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.400 2011/01/21 22:25:31 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas 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: 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 $ # $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 # http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R39 2011/01/08 @(#)MIRBSD KSH R39 2011/01/21
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -5941,6 +5941,16 @@ expected-stdout:
c we c we
d we d we
--- ---
name: varexpand-special-hash
description:
Check special ${var@x} expansion for x=hash
stdin:
typeset -i8 foo=10
bar=baz
print ${bar@#} ${baz@#} .
expected-stdout:
57F1BA9A 04808901 .
---
name: varexpand-null-1 name: varexpand-null-1
description: description:
Ensure empty strings expand emptily Ensure empty strings expand emptily

229
eval.c
View File

@ -1,7 +1,7 @@
/* $OpenBSD: eval.c,v 1.35 2010/03/24 08:27:26 fgsch Exp $ */ /* $OpenBSD: eval.c,v 1.35 2010/03/24 08:27:26 fgsch Exp $ */
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org> * Thorsten Glaser <tg@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -22,7 +22,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.93 2010/09/14 21:26:10 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.94 2011/01/21 22:25:32 tg Exp $");
/* /*
* string expansion * string expansion
@ -129,7 +129,8 @@ eval(const char **ap, int f)
return (vap.rw); return (vap.rw);
} }
XPinit(w, 32); XPinit(w, 32);
XPput(w, NULL); /* space for shell name */ /* space for shell name */
XPput(w, NULL);
while (*ap != NULL) while (*ap != NULL)
expand(*ap++, &w, f); expand(*ap++, &w, f);
XPput(w, NULL); XPput(w, NULL);
@ -205,11 +206,13 @@ expand(const char *cp, /* input word */
const char *sp; /* source */ const char *sp; /* source */
int fdo, word; /* second pass flags; have word */ int fdo, word; /* second pass flags; have word */
int doblank; /* field splitting of parameter/command subst */ int doblank; /* field splitting of parameter/command subst */
Expand x = { /* expansion variables */ Expand x = {
/* expansion variables */
NULL, { NULL }, NULL, 0 NULL, { NULL }, NULL, 0
}; };
SubType st_head, *st; SubType st_head, *st;
int newlines = 0; /* For trailing newlines in COMSUB */ /* For trailing newlines in COMSUB */
int newlines = 0;
int saw_eq, tilde_ok; int saw_eq, tilde_ok;
int make_magic; int make_magic;
size_t len; size_t len;
@ -228,12 +231,14 @@ expand(const char *cp, /* input word */
if (Flag(FBRACEEXPAND) && (f & DOGLOB)) if (Flag(FBRACEEXPAND) && (f & DOGLOB))
f |= DOBRACE_; f |= DOBRACE_;
Xinit(ds, dp, 128, ATEMP); /* init dest. string */ /* init destination string */
Xinit(ds, dp, 128, ATEMP);
type = XBASE; type = XBASE;
sp = cp; sp = cp;
fdo = 0; fdo = 0;
saw_eq = 0; saw_eq = 0;
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ /* must be 1/0 */
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
doblank = 0; doblank = 0;
make_magic = 0; make_magic = 0;
word = (f&DOBLANK) ? IFS_WS : IFS_WORD; word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
@ -245,7 +250,8 @@ expand(const char *cp, /* input word */
Xcheck(ds, dp); Xcheck(ds, dp);
switch (type) { switch (type) {
case XBASE: /* original prefixed string */ case XBASE:
/* original prefixed string */
c = *sp++; c = *sp++;
switch (c) { switch (c) {
case EOS: case EOS:
@ -255,7 +261,8 @@ expand(const char *cp, /* input word */
c = *sp++; c = *sp++;
break; break;
case QCHAR: case QCHAR:
quote |= 2; /* temporary quote */ /* temporary quote */
quote |= 2;
c = *sp++; c = *sp++;
break; break;
case OQUOTE: case OQUOTE:
@ -299,7 +306,8 @@ expand(const char *cp, /* input word */
char *p; char *p;
v.flag = DEFINED|ISSET|INTEGER; v.flag = DEFINED|ISSET|INTEGER;
v.type = 10; /* not default */ /* not default */
v.type = 10;
v.name[0] = '\0'; v.name[0] = '\0';
v_evaluate(&v, substitute(sp, 0), v_evaluate(&v, substitute(sp, 0),
KSH_UNWIND_ERROR, true); KSH_UNWIND_ERROR, true);
@ -310,23 +318,27 @@ expand(const char *cp, /* input word */
} }
} }
continue; continue;
case OSUBST: { /* ${{#}var{:}[=+-?#%]word} */ case OSUBST: {
/* format is: /* ${{#}var{:}[=+-?#%]word} */
/*-
* format is:
* OSUBST [{x] plain-variable-part \0 * OSUBST [{x] plain-variable-part \0
* compiled-word-part CSUBST [}x] * compiled-word-part CSUBST [}x]
* This is where all syntax checking gets done... * This is where all syntax checking gets done...
*/ */
const char *varname = ++sp; /* skip the { or x (}) */ /* skip the { or x (}) */
const char *varname = ++sp;
int stype; int stype;
int slen = 0; int slen = 0;
sp = cstrchr(sp, '\0') + 1; /* skip variable */ /* skip variable */
sp = cstrchr(sp, '\0') + 1;
type = varsub(&x, varname, sp, &stype, &slen); type = varsub(&x, varname, sp, &stype, &slen);
if (type < 0) { if (type < 0) {
char *beg, *end, *str; char *beg, *end, *str;
unwind_substsyn: unwind_substsyn:
sp = varname - 2; /* restore sp */ /* restore sp */
sp = varname - 2;
end = (beg = wdcopy(sp, ATEMP)) + end = (beg = wdcopy(sp, ATEMP)) +
(wdscan(sp, CSUBST) - sp); (wdscan(sp, CSUBST) - sp);
/* ({) the } or x is already skipped */ /* ({) the } or x is already skipped */
@ -339,7 +351,8 @@ expand(const char *cp, /* input word */
if (f & DOBLANK) if (f & DOBLANK)
doblank++; doblank++;
tilde_ok = 0; tilde_ok = 0;
if (type == XBASE) { /* expand? */ if (type == XBASE) {
/* expand? */
if (!st->next) { if (!st->next) {
SubType *newst; SubType *newst;
@ -357,7 +370,11 @@ expand(const char *cp, /* input word */
/* skip qualifier(s) */ /* skip qualifier(s) */
if (stype) if (stype)
sp += slen; sp += slen;
switch (stype & 0x7f) { switch (stype & 0x17F) {
case 0x100 | '#':
x.str = shf_smprintf("%08X",
hash(str_val(st->var)));
break;
case '0': { case '0': {
char *beg, *mid, *end, *stg; char *beg, *mid, *end, *stg;
mksh_ari_t from = 0, num = -1, flen, finc = 0; mksh_ari_t from = 0, num = -1, flen, finc = 0;
@ -469,7 +486,8 @@ expand(const char *cp, /* input word */
tpat2 = tpat1 + 2; tpat2 = tpat1 + 2;
} }
again_repl: again_repl:
/* this would not be necessary if gmatchx would return /*
* this would not be necessary if gmatchx would return
* the start and end values of a match found, like re* * the start and end values of a match found, like re*
*/ */
if (!gmatchx(sbeg, tpat1, false)) if (!gmatchx(sbeg, tpat1, false))
@ -519,7 +537,8 @@ expand(const char *cp, /* input word */
f = DOPAT | (f&DONTRUNCOMMAND) | f = DOPAT | (f&DONTRUNCOMMAND) |
DOTEMP_; DOTEMP_;
st->quotew = quote = 0; st->quotew = quote = 0;
/* Prepend open pattern (so | /*
* Prepend open pattern (so |
* in a trim will work as * in a trim will work as
* expected) * expected)
*/ */
@ -527,7 +546,8 @@ expand(const char *cp, /* input word */
*dp++ = (char)('@' | 0x80); *dp++ = (char)('@' | 0x80);
break; break;
case '=': case '=':
/* Enabling tilde expansion /*
* Enabling tilde expansion
* after :s here is * after :s here is
* non-standard ksh, but is * non-standard ksh, but is
* consistent with rules for * consistent with rules for
@ -543,7 +563,8 @@ expand(const char *cp, /* input word */
if (!(x.var->flag & INTEGER)) if (!(x.var->flag & INTEGER))
f |= DOASNTILDE|DOTILDE; f |= DOASNTILDE|DOTILDE;
f |= DOTEMP_; f |= DOTEMP_;
/* These will be done after the /*
* These will be done after the
* value has been assigned. * value has been assigned.
*/ */
f &= ~(DOBLANK|DOGLOB|DOBRACE_); f &= ~(DOBLANK|DOGLOB|DOBRACE_);
@ -563,22 +584,26 @@ expand(const char *cp, /* input word */
sp += wdscan(sp, CSUBST) - sp; sp += wdscan(sp, CSUBST) - sp;
continue; continue;
} }
case CSUBST: /* only get here if expanding word */ case CSUBST:
/* only get here if expanding word */
do_CSUBST: do_CSUBST:
sp++; /* ({) skip the } or x */ /* ({) skip the } or x */
tilde_ok = 0; /* in case of ${unset:-} */ sp++;
/* in case of ${unset:-} */
tilde_ok = 0;
*dp = '\0'; *dp = '\0';
quote = st->quotep; quote = st->quotep;
f = st->f; f = st->f;
if (f&DOBLANK) if (f&DOBLANK)
doblank--; doblank--;
switch (st->stype&0x7f) { switch (st->stype & 0x17F) {
case '#': case '#':
case '%': case '%':
/* Append end-pattern */ /* Append end-pattern */
*dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
dp = Xrestpos(ds, dp, st->base); dp = Xrestpos(ds, dp, st->base);
/* Must use st->var since calling /*
* Must use st->var since calling
* global would break things * global would break things
* like x[i+=1]. * like x[i+=1].
*/ */
@ -593,20 +618,24 @@ expand(const char *cp, /* input word */
st = st->prev; st = st->prev;
continue; continue;
case '=': case '=':
/* Restore our position and substitute /*
* Restore our position and substitute
* the value of st->var (may not be * the value of st->var (may not be
* the assigned value in the presence * the assigned value in the presence
* of integer/right-adj/etc attributes). * of integer/right-adj/etc attributes).
*/ */
dp = Xrestpos(ds, dp, st->base); dp = Xrestpos(ds, dp, st->base);
/* Must use st->var since calling /*
* Must use st->var since calling
* global would cause with things * global would cause with things
* like x[i+=1] to be evaluated twice. * like x[i+=1] to be evaluated twice.
*/ */
/* Note: not exported by FEXPORT /*
* Note: not exported by FEXPORT
* in AT&T ksh. * in AT&T ksh.
*/ */
/* XXX POSIX says readonly is only /*
* XXX POSIX says readonly is only
* fatal for special builtins (setstr * fatal for special builtins (setstr
* does readonly check). * does readonly check).
*/ */
@ -630,6 +659,7 @@ expand(const char *cp, /* input word */
} }
case '0': case '0':
case '/': case '/':
case 0x100 | '#':
dp = Xrestpos(ds, dp, st->base); dp = Xrestpos(ds, dp, st->base);
type = XSUB; type = XSUB;
if (f&DOBLANK) if (f&DOBLANK)
@ -641,18 +671,21 @@ expand(const char *cp, /* input word */
type = XBASE; type = XBASE;
continue; continue;
case OPAT: /* open pattern: *(foo|bar) */ case OPAT:
/* open pattern: *(foo|bar) */
/* Next char is the type of pattern */ /* Next char is the type of pattern */
make_magic = 1; make_magic = 1;
c = *sp++ + 0x80; c = *sp++ + 0x80;
break; break;
case SPAT: /* pattern separator (|) */ case SPAT:
/* pattern separator (|) */
make_magic = 1; make_magic = 1;
c = '|'; c = '|';
break; break;
case CPAT: /* close pattern */ case CPAT:
/* close pattern */
make_magic = 1; make_magic = 1;
c = /*(*/ ')'; c = /*(*/ ')';
break; break;
@ -660,14 +693,16 @@ expand(const char *cp, /* input word */
break; break;
case XNULLSUB: case XNULLSUB:
/* Special case for "$@" (and "${foo[@]}") - no /*
* Special case for "$@" (and "${foo[@]}") - no
* word is generated if $# is 0 (unless there is * word is generated if $# is 0 (unless there is
* other stuff inside the quotes). * other stuff inside the quotes).
*/ */
type = XBASE; type = XBASE;
if (f&DOBLANK) { if (f&DOBLANK) {
doblank--; doblank--;
/* not really correct: x=; "$x$@" should /*
* not really correct: x=; "$x$@" should
* generate a null argument and * generate a null argument and
* set A; "${@:+}" shouldn't. * set A; "${@:+}" shouldn't.
*/ */
@ -691,7 +726,8 @@ expand(const char *cp, /* input word */
quote = 1; quote = 1;
case XARG: case XARG:
if ((c = *x.str++) == '\0') { if ((c = *x.str++) == '\0') {
/* force null words to be created so /*
* force null words to be created so
* set -- '' 2 ''; foo "$@" will do * set -- '' 2 ''; foo "$@" will do
* the right thing * the right thing
*/ */
@ -718,7 +754,8 @@ expand(const char *cp, /* input word */
break; break;
case XCOM: case XCOM:
if (newlines) { /* Spit out saved NLs */ if (newlines) {
/* Spit out saved NLs */
c = '\n'; c = '\n';
--newlines; --newlines;
} else { } else {
@ -748,7 +785,8 @@ expand(const char *cp, /* input word */
/* check for end of word or IFS separation */ /* check for end of word or IFS separation */
if (c == 0 || (!quote && (f & DOBLANK) && doblank && if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
!make_magic && ctype(c, C_IFS))) { !make_magic && ctype(c, C_IFS))) {
/* How words are broken up: /*-
* How words are broken up:
* | value of c * | value of c
* word | ws nws 0 * word | ws nws 0
* ----------------------------------- * -----------------------------------
@ -807,7 +845,8 @@ expand(const char *cp, /* input word */
case NOT: case NOT:
case '-': case '-':
case ']': case ']':
/* For character classes - doesn't hurt /*
* For character classes - doesn't hurt
* to have magic !,-,]s outside of * to have magic !,-,]s outside of
* [...] expressions. * [...] expressions.
*/ */
@ -843,13 +882,15 @@ expand(const char *cp, /* input word */
tilde_ok = 1; tilde_ok = 1;
} }
break; break;
case ':': /* : */ case ':':
/* : */
/* Note unquoted : for ~ */ /* Note unquoted : for ~ */
if (!(f & DOTEMP_) && (f & DOASNTILDE)) if (!(f & DOTEMP_) && (f & DOASNTILDE))
tilde_ok = 1; tilde_ok = 1;
break; break;
case '~': case '~':
/* tilde_ok is reset whenever /*
* tilde_ok is reset whenever
* any of ' " $( $(( ${ } are seen. * any of ' " $( $(( ${ } are seen.
* Note that tilde_ok must be preserved * Note that tilde_ok must be preserved
* through the sequence ${A=a=}~ * through the sequence ${A=a=}~
@ -875,7 +916,8 @@ expand(const char *cp, /* input word */
break; break;
} }
else else
quote &= ~2; /* undo temporary */ /* undo temporary */
quote &= ~2;
if (make_magic) { if (make_magic) {
make_magic = 0; make_magic = 0;
@ -885,7 +927,8 @@ expand(const char *cp, /* input word */
fdo |= DOMAGIC_; fdo |= DOMAGIC_;
*dp++ = MAGIC; *dp++ = MAGIC;
} }
*dp++ = c; /* save output char */ /* save output char */
*dp++ = c;
word = IFS_WORD; word = IFS_WORD;
} }
} }
@ -907,7 +950,8 @@ varsub(Expand *xp, const char *sp, const char *word,
struct tbl *vp; struct tbl *vp;
bool zero_ok = false; bool zero_ok = false;
if ((stype = sp[0]) == '\0') /* Bad variable name */ if ((stype = sp[0]) == '\0')
/* Bad variable name */
return (-1); return (-1);
xp->var = NULL; xp->var = NULL;
@ -975,7 +1019,8 @@ varsub(Expand *xp, const char *sp, const char *word,
} }
if (Flag(FNOUNSET) && c == 0 && !zero_ok) if (Flag(FNOUNSET) && c == 0 && !zero_ok)
errorf("%s: %s", sp, "parameter not set"); errorf("%s: %s", sp, "parameter not set");
*stypep = 0; /* unqualified variable/string substitution */ /* unqualified variable/string substitution */
*stypep = 0;
xp->str = shf_smprintf("%d", c); xp->str = shf_smprintf("%d", c);
return (XSUB); return (XSUB);
} }
@ -1000,14 +1045,24 @@ varsub(Expand *xp, const char *sp, const char *word,
} else if (ctype(c, C_SUBOP1)) { } else if (ctype(c, C_SUBOP1)) {
slen += 2; slen += 2;
stype |= c; stype |= c;
} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ } else if (ctype(c, C_SUBOP2)) {
/* Note: ksh88 allows :%, :%%, etc */
slen += 2; slen += 2;
stype = c; stype = c;
if (word[slen + 0] == CHAR && c == word[slen + 1]) { if (word[slen + 0] == CHAR && c == word[slen + 1]) {
stype |= 0x80; stype |= 0x80;
slen += 2; slen += 2;
} }
} else if (stype) /* : is not ok */ } else if (c == '@') {
/* @x where x is command char */
slen += 2;
stype |= 0x100;
if (word[slen] == CHAR) {
stype |= word[slen + 1];
slen += 2;
}
} else if (stype)
/* : is not ok */
return (-1); return (-1);
if (!stype && *word != CSUBST) if (!stype && *word != CSUBST)
return (-1); return (-1);
@ -1016,12 +1071,13 @@ varsub(Expand *xp, const char *sp, const char *word,
c = sp[0]; c = sp[0];
if (c == '*' || c == '@') { if (c == '*' || c == '@') {
switch (stype & 0x7f) { switch (stype & 0x17F) {
case '=': /* can't assign to a vector */ case '=': /* can't assign to a vector */
case '%': /* can't trim a vector (yet) */ case '%': /* can't trim a vector (yet) */
case '#': case '#':
case '0': case '0':
case '/': case '/':
case 0x100 | '#':
return (-1); return (-1);
} }
if (e->loc->argc == 0) { if (e->loc->argc == 0) {
@ -1034,19 +1090,21 @@ varsub(Expand *xp, const char *sp, const char *word,
xp->split = c == '@'; /* $@ */ xp->split = c == '@'; /* $@ */
state = XARG; state = XARG;
} }
zero_ok = true; /* POSIX 2009? */ /* POSIX 2009? */
zero_ok = true;
} else { } else {
if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') && if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
p[2] == ']') { p[2] == ']') {
XPtrV wv; XPtrV wv;
switch (stype & 0x7f) { switch (stype & 0x17F) {
case '=': /* can't assign to a vector */ case '=': /* can't assign to a vector */
case '%': /* can't trim a vector (yet) */ case '%': /* can't trim a vector (yet) */
case '#': case '#':
case '?': case '?':
case '0': case '0':
case '/': case '/':
case 0x100 | '#':
return (-1); return (-1);
} }
XPinit(wv, 32); XPinit(wv, 32);
@ -1073,7 +1131,7 @@ varsub(Expand *xp, const char *sp, const char *word,
} }
} else { } else {
/* Can't assign things like $! or $1 */ /* Can't assign things like $! or $1 */
if ((stype & 0x7f) == '=' && if ((stype & 0x17F) == '=' &&
ctype(*sp, C_VAR1 | C_DIGIT)) ctype(*sp, C_VAR1 | C_DIGIT))
return (-1); return (-1);
if (*sp == '!' && sp[1]) { if (*sp == '!' && sp[1]) {
@ -1088,7 +1146,8 @@ varsub(Expand *xp, const char *sp, const char *word,
} else if (xp->var->flag & ISSET) } else if (xp->var->flag & ISSET)
xp->str = xp->var->name; xp->str = xp->var->name;
else else
xp->str = "0"; /* ksh93 compat */ /* ksh93 compat */
xp->str = "0";
} else { } else {
xp->var = global(sp); xp->var = global(sp);
xp->str = str_val(xp->var); xp->str = str_val(xp->var);
@ -1097,12 +1156,14 @@ varsub(Expand *xp, const char *sp, const char *word,
} }
} }
c = stype&0x7f; c = stype & 0x7F;
/* test the compiler's code generator */ /* test the compiler's code generator */
if (ctype(c, C_SUBOP2) || stype == (0x80 | '0') || c == '/' || if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
c == '=' || c == '-' || c == '?' : c == '+')) c == '=' || c == '-' || c == '?' : c == '+'))) ||
state = XBASE; /* expand word instead of variable value */ stype == (0x80 | '0') || stype == (0x100 | '#'))
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok && if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
(ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
errorf("%s: %s", sp, "parameter not set"); errorf("%s: %s", sp, "parameter not set");
@ -1129,8 +1190,9 @@ comsub(Expand *xp, const char *cp)
if (t == NULL) if (t == NULL)
return (XBASE); return (XBASE);
if (t != NULL && t->type == TCOM && /* $(<file) */ if (t != NULL && t->type == TCOM &&
*t->args == NULL && *t->vars == NULL && t->ioact != NULL) { *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
/* $(<file) */
struct ioword *io = *t->ioact; struct ioword *io = *t->ioact;
char *name; char *name;
@ -1141,7 +1203,8 @@ comsub(Expand *xp, const char *cp)
SHF_MAPHI|SHF_CLEXEC); SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL) if (shf == NULL)
errorf("%s: %s %s", name, "can't open", "$() input"); errorf("%s: %s %s", name, "can't open", "$() input");
xp->split = 0; /* no waitlast() */ /* no waitlast() */
xp->split = 0;
} else { } else {
int ofd1, pv[2]; int ofd1, pv[2];
openpipe(pv); openpipe(pv);
@ -1154,7 +1217,8 @@ comsub(Expand *xp, const char *cp)
execute(t, XFORK|XXCOM|XPIPEO, NULL); execute(t, XFORK|XXCOM|XPIPEO, NULL);
restfd(1, ofd1); restfd(1, ofd1);
startlast(); startlast();
xp->split = 1; /* waitlast() */ /* waitlast() */
xp->split = 1;
} }
xp->u.shf = shf; xp->u.shf = shf;
@ -1172,7 +1236,8 @@ trimsub(char *str, char *pat, int how)
char *p, c; char *p, c;
switch (how & 0xFF) { switch (how & 0xFF) {
case '#': /* shortest at beginning */ case '#':
/* shortest match at beginning */
for (p = str; p <= end; p += utf_ptradj(p)) { for (p = str; p <= end; p += utf_ptradj(p)) {
c = *p; *p = '\0'; c = *p; *p = '\0';
if (gmatchx(str, pat, false)) { if (gmatchx(str, pat, false)) {
@ -1182,7 +1247,8 @@ trimsub(char *str, char *pat, int how)
*p = c; *p = c;
} }
break; break;
case '#'|0x80: /* longest match at beginning */ case '#'|0x80:
/* longest match at beginning */
for (p = end; p >= str; p--) { for (p = end; p >= str; p--) {
c = *p; *p = '\0'; c = *p; *p = '\0';
if (gmatchx(str, pat, false)) { if (gmatchx(str, pat, false)) {
@ -1192,7 +1258,8 @@ trimsub(char *str, char *pat, int how)
*p = c; *p = c;
} }
break; break;
case '%': /* shortest match at end */ case '%':
/* shortest match at end */
p = end; p = end;
while (p >= str) { while (p >= str) {
if (gmatchx(p, pat, false)) if (gmatchx(p, pat, false))
@ -1207,7 +1274,8 @@ trimsub(char *str, char *pat, int how)
--p; --p;
} }
break; break;
case '%'|0x80: /* longest match at end */ case '%'|0x80:
/* longest match at end */
for (p = str; p <= end; p++) for (p = str; p <= end; p++)
if (gmatchx(p, pat, false)) { if (gmatchx(p, pat, false)) {
trimsub_match: trimsub_match:
@ -1217,7 +1285,8 @@ trimsub(char *str, char *pat, int how)
break; break;
} }
return (str); /* no match, return string */ /* no match, return string */
return (str);
} }
/* /*
@ -1243,7 +1312,8 @@ glob(char *cp, XPtrV *wp, int markdirs)
#define GF_GLOBBED BIT(1) /* some globbing has been done */ #define GF_GLOBBED BIT(1) /* some globbing has been done */
#define GF_MARKDIR BIT(2) /* add trailing / to directories */ #define GF_MARKDIR BIT(2) /* add trailing / to directories */
/* Apply file globbing to cp and store the matching files in wp. Returns /*
* Apply file globbing to cp and store the matching files in wp. Returns
* the number of matches found. * the number of matches found.
*/ */
int int
@ -1275,8 +1345,10 @@ globit(XString *xs, /* dest string */
/* This to allow long expansions to be interrupted */ /* This to allow long expansions to be interrupted */
intrcheck(); intrcheck();
if (sp == NULL) { /* end of source path */ if (sp == NULL) {
/* We only need to check if the file exists if a pattern /* end of source path */
/*
* We only need to check if the file exists if a pattern
* is followed by a non-pattern (eg, foo*x/bar; no check * is followed by a non-pattern (eg, foo*x/bar; no check
* is needed for foo* since the match must exist) or if * is needed for foo* since the match must exist) or if
* any patterns were expanded and the markdirs option is set. * any patterns were expanded and the markdirs option is set.
@ -1292,7 +1364,8 @@ globit(XString *xs, /* dest string */
if (lstat(Xstring(*xs, xp), &lstatb) < 0) if (lstat(Xstring(*xs, xp), &lstatb) < 0)
return; return;
/* special case for systems which strip trailing /*
* special case for systems which strip trailing
* slashes from regular files (eg, /etc/passwd/). * slashes from regular files (eg, /etc/passwd/).
* SunOS 4.1.3 does this... * SunOS 4.1.3 does this...
*/ */
@ -1301,7 +1374,8 @@ globit(XString *xs, /* dest string */
(!S_ISLNK(lstatb.st_mode) || (!S_ISLNK(lstatb.st_mode) ||
stat_check() < 0 || !S_ISDIR(statb.st_mode))) stat_check() < 0 || !S_ISDIR(statb.st_mode)))
return; return;
/* Possibly tack on a trailing / if there isn't already /*
* Possibly tack on a trailing / if there isn't already
* one and if the file is a directory or a symlink to a * one and if the file is a directory or a symlink to a
* directory * directory
*/ */
@ -1328,7 +1402,8 @@ globit(XString *xs, /* dest string */
np = strchr(sp, '/'); np = strchr(sp, '/');
if (np != NULL) { if (np != NULL) {
se = np; se = np;
odirsep = *np; /* don't assume '/', can be multiple kinds */ /* don't assume '/', can be multiple kinds */
odirsep = *np;
*np++ = '\0'; *np++ = '\0';
} else { } else {
odirsep = '\0'; /* keep gcc quiet */ odirsep = '\0'; /* keep gcc quiet */
@ -1336,7 +1411,8 @@ globit(XString *xs, /* dest string */
} }
/* Check if sp needs globbing - done to avoid pattern checks for strings /*
* Check if sp needs globbing - done to avoid pattern checks for strings
* containing MAGIC characters, open [s without the matching close ], * containing MAGIC characters, open [s without the matching close ],
* etc. (otherwise opendir() will be called which may fail because the * etc. (otherwise opendir() will be called which may fail because the
* directory isn't readable - if no globbing is needed, only execute * directory isn't readable - if no globbing is needed, only execute
@ -1365,7 +1441,8 @@ globit(XString *xs, /* dest string */
name = d->d_name; name = d->d_name;
if (name[0] == '.' && if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0))) (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue; /* always ignore . and .. */ /* always ignore . and .. */
continue;
if ((*name == '.' && *sp != '.') || if ((*name == '.' && *sp != '.') ||
!gmatchx(name, sp, true)) !gmatchx(name, sp, true))
continue; continue;
@ -1416,7 +1493,8 @@ debunk(char *dp, const char *sp, size_t dlen)
return (dp); return (dp);
} }
/* Check if p is an unquoted name, possibly followed by a / or :. If so /*
* Check if p is an unquoted name, possibly followed by a / or :. If so
* puts the expanded version in *dcp,dp and returns a pointer in p just * puts the expanded version in *dcp,dp and returns a pointer in p just
* past the name, otherwise returns 0. * past the name, otherwise returns 0.
*/ */
@ -1534,7 +1612,8 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
} }
/* no valid expansions... */ /* no valid expansions... */
if (!p || count != 0) { if (!p || count != 0) {
/* Note that given a{{b,c} we do not expand anything (this is /*
* Note that given a{{b,c} we do not expand anything (this is
* what AT&T ksh does. This may be changed to do the {b,c} * what AT&T ksh does. This may be changed to do the {b,c}
* expansion. } * expansion. }
*/ */

8
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.246 2011/01/21 22:00:14 tg Exp $ .\" $MirOS: src/bin/mksh/mksh.1,v 1.247 2011/01/21 22:25:33 tg Exp $
.\" $OpenBSD: ksh.1,v 1.138 2010/09/20 07:41:17 jmc Exp $ .\" $OpenBSD: ksh.1,v 1.138 2010/09/20 07:41:17 jmc Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -1579,6 +1579,12 @@ are evaluated as arithmetic expressions.
Currently, Currently,
.Ar pos .Ar pos
must start with a space, opening parenthesis or digit to be recognised. must start with a space, opening parenthesis or digit to be recognised.
.It Pf ${ Ns Ar name Ns @#}
The internal hash of the expansion of
.Ar name .
At the moment, this is OAAT1 (Bob Jenkins' one-at-a-time hash with
an initialisation value of 0x00000100), but this is not set.
This is the hash the shell uses internally for its associative arrays.
.El .El
.Pp .Pp
Note that Note that

4
sh.h
View File

@ -154,9 +154,9 @@
#endif #endif
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.422 2011/01/21 21:04:47 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.423 2011/01/21 22:25:34 tg Exp $");
#endif #endif
#define MKSH_VERSION "R39 2011/01/08" #define MKSH_VERSION "R39 2011/01/21"
#ifndef MKSH_INCLUDES_ONLY #ifndef MKSH_INCLUDES_ONLY