${foo@x} is now special-expansion for values of x, defined for now:
‘#’ = hash of $foo
This commit is contained in:
parent
846fbde3b8
commit
da9d0f3d97
14
check.t
14
check.t
@ -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
229
eval.c
@ -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
8
mksh.1
@ -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
4
sh.h
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user