mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-05-28 02:46:03 +02:00
various posts: Implement assignment parsing for typeset.
Typeset assignments now work like raw assignments except for no "+=" and no GLOB_ASSIGN. Documented in typeset builtin doc and mentioned in release notes. Tests to ensure basic sanity. Enabled by default, can be turned off by "disable -r" with typeset family of commands.
This commit is contained in:
parent
a68d22eb00
commit
39b28980f3
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
||||||
|
2015-06-24 Peter Stephenson <p.stephenson@samsung.com>
|
||||||
|
|
||||||
|
* various culminating in 35586, c.f. commits on typeset-array
|
||||||
|
branch: Config/version.mk, Doc/Zsh/builtins.yo,
|
||||||
|
Doc/Zsh/grammar.yo, Doc/Zsh/options.yo, NEWS, README,
|
||||||
|
Src/builtin.c, Src/exec.c, Src/hashtable.c, Src/lex.c,
|
||||||
|
Src/options.c, Src/parse.c, Src/text.c, Src/zsh.h,
|
||||||
|
Test/B02typeset.ztst, Test/D01prompt.ztst, Test/E01options.ztst:
|
||||||
|
Implement assignment handling for typeset etc. when matched as
|
||||||
|
reserved words. Document and test.
|
||||||
|
|
||||||
2015-06-23 Peter Stephenson <p.stephenson@samsung.com>
|
2015-06-23 Peter Stephenson <p.stephenson@samsung.com>
|
||||||
|
|
||||||
* 35573: Completion/compinit: turn off POSIX_BUILTINS
|
* 35573: Completion/compinit: turn off POSIX_BUILTINS
|
||||||
|
|
|
@ -27,5 +27,5 @@
|
||||||
# This must also serve as a shell script, so do not add spaces around the
|
# This must also serve as a shell script, so do not add spaces around the
|
||||||
# `=' signs.
|
# `=' signs.
|
||||||
|
|
||||||
VERSION=5.0.8-dev-0
|
VERSION=5.0.8-dev-1
|
||||||
VERSION_DATE='June 1, 2015'
|
VERSION_DATE='June 19, 2015'
|
||||||
|
|
|
@ -1729,7 +1729,7 @@ xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmprtux) ] \
|
||||||
[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ])
|
[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ])
|
||||||
xitem(SPACES()[ tt(+) | var(name)[tt(=)var(value)] ... ])
|
xitem(SPACES()[ tt(+) | var(name)[tt(=)var(value)] ... ])
|
||||||
xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglprux) ] [ {tt(PLUS())|tt(-)}tt(LRZ) [ var(n) ] ])
|
xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglprux) ] [ {tt(PLUS())|tt(-)}tt(LRZ) [ var(n) ] ])
|
||||||
xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array) [ var(sep) ] ])
|
xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=)LPAR()var(value)RPAR()] [ var(sep) ] ])
|
||||||
item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) | var(name) ... ])(
|
item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) | var(name) ... ])(
|
||||||
Set or display attributes and values for shell parameters.
|
Set or display attributes and values for shell parameters.
|
||||||
|
|
||||||
|
@ -1743,22 +1743,72 @@ ifnzman(noderef(Local Parameters))\
|
||||||
retain their special attributes when made local.
|
retain their special attributes when made local.
|
||||||
|
|
||||||
For each var(name)tt(=)var(value) assignment, the parameter
|
For each var(name)tt(=)var(value) assignment, the parameter
|
||||||
var(name) is set to var(value). Note that arrays currently cannot be
|
var(name) is set to var(value). All forms of the command
|
||||||
assigned in tt(typeset) expressions, only scalars and integers. Unless
|
handle scalar assignment.
|
||||||
the option tt(KSH_TYPESET) is set, normal expansion rules apply to
|
|
||||||
assignment arguments, so var(value) may be split into separate words; if
|
If any of the reserved words tt(declare), tt(export), tt(float),
|
||||||
the option is set, assignments which can be recognised when expansion is
|
tt(integer), tt(local), tt(readonly) or tt(typeset) is matched when the
|
||||||
performed are treated as single words. For example the command
|
line is parsed (N.B. not when it is executed) the shell will try to parse
|
||||||
tt(typeset vbl=$(echo one two)) is treated as having one argument if
|
arguments as assignments, except that the `tt(+=)' syntax and the
|
||||||
tt(KSH_TYPESET) is set, but otherwise is treated as having the two arguments
|
tt(GLOB_ASSIGN) option are not supported. This has two major differences
|
||||||
tt(vbl=one) and tt(two).
|
from normal command line argument parsing: array assignment is possible,
|
||||||
|
and scalar values after tt(=) are not split further into words even if
|
||||||
|
expanded (regardless of the setting of the tt(KSH_TYPESET) option; this
|
||||||
|
option is obsolete). Here is an example:
|
||||||
|
|
||||||
|
example(# Reserved word parsing
|
||||||
|
typeset svar=$(echo one word) avar=(several words))
|
||||||
|
|
||||||
|
The above creates a scalar parameter tt(svar) and an array
|
||||||
|
parameter tt(var) as if the assignments had been
|
||||||
|
|
||||||
|
example(svar="one word"
|
||||||
|
avar=(several words))
|
||||||
|
|
||||||
|
On the other hand:
|
||||||
|
|
||||||
|
example(# Normal builtin interface
|
||||||
|
builtin typeset svar=$(echo two words))
|
||||||
|
|
||||||
|
The tt(builtin) keyword causes the above to use the standard builtin
|
||||||
|
interface to tt(typeset) in which argument parsing is perfomed in the same
|
||||||
|
way as for other commands. This example creates a scalar tt(svar)
|
||||||
|
containing the value tt(two) and another scalar parameter tt(words) with
|
||||||
|
no value. An array value in this case would either cause an error or be
|
||||||
|
treated as an obscure set of glob qualifiers.
|
||||||
|
|
||||||
|
Arbitrary arguments are allowed if they take the form of assignments
|
||||||
|
after command line expansion; however, these only perform scalar
|
||||||
|
assignment:
|
||||||
|
|
||||||
|
example(var='svar=val'
|
||||||
|
typeset $var)
|
||||||
|
|
||||||
|
The above sets the scalar parameter tt(svar) to the value tt(val).
|
||||||
|
Parentheses around the value within tt(var) would not cause array
|
||||||
|
assignment as they will be treated as ordinary characters when tt($var)
|
||||||
|
is substituted. Any non-trivial expansion in the name part of the
|
||||||
|
assignment causes the argument to be treated in this fashion:
|
||||||
|
|
||||||
|
example(typeset {var1,var2,var3}=name)
|
||||||
|
|
||||||
|
The above syntax is valid, and has the expected effect of setting the
|
||||||
|
three parameters to the same value, but the command line is parsed as
|
||||||
|
a set of three normal command line arguments to tt(typeset) after
|
||||||
|
expansion. Hence it is not possible to assign to multiple arrays by
|
||||||
|
this means.
|
||||||
|
|
||||||
|
Note that each interface to any of the commands my be disabled
|
||||||
|
separately. For example, `tt(disable -r typeset)' disables the reserved
|
||||||
|
word interface to tt(typeset), exposing the builtin interface, while
|
||||||
|
`tt(disable typeset)' disables the builtin.
|
||||||
|
|
||||||
If the shell option tt(TYPESET_SILENT) is not set, for each remaining
|
If the shell option tt(TYPESET_SILENT) is not set, for each remaining
|
||||||
var(name) that refers to a parameter that is set, the name and value of the
|
var(name) that refers to a parameter that is already set, the name and
|
||||||
parameter are printed in the form of an assignment. Nothing is printed for
|
value of the parameter are printed in the form of an assignment.
|
||||||
newly-created parameters, or when any attribute flags listed below are
|
Nothing is printed for newly-created parameters, or when any attribute
|
||||||
given along with the var(name). Using `tt(PLUS())' instead of minus to
|
flags listed below are given along with the var(name). Using
|
||||||
introduce an attribute turns it off.
|
`tt(PLUS())' instead of minus to introduce an attribute turns it off.
|
||||||
|
|
||||||
If no var(name) is present, the names and values of all parameters are
|
If no var(name) is present, the names and values of all parameters are
|
||||||
printed. In this case the attribute flags restrict the display to only
|
printed. In this case the attribute flags restrict the display to only
|
||||||
|
@ -1829,7 +1879,7 @@ the current state, readonly specials (whose values cannot be
|
||||||
changed) are not shown and assignments to arrays are shown before
|
changed) are not shown and assignments to arrays are shown before
|
||||||
the tt(typeset) rendering the array readonly.
|
the tt(typeset) rendering the array readonly.
|
||||||
)
|
)
|
||||||
item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array) [ var(sep) ] ])(
|
item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=)LPAR()var(value)...RPAR()] [ var(sep) ] ])(
|
||||||
This flag has a different meaning when used with tt(-f); see below.
|
This flag has a different meaning when used with tt(-f); see below.
|
||||||
Otherwise the tt(-T) option requires zero, two, or three arguments to be
|
Otherwise the tt(-T) option requires zero, two, or three arguments to be
|
||||||
present. With no arguments, the list of parameters created in this
|
present. With no arguments, the list of parameters created in this
|
||||||
|
@ -1839,10 +1889,13 @@ together in the manner of tt($PATH) and tt($path). The optional third
|
||||||
argument is a single-character separator which will be used to join the
|
argument is a single-character separator which will be used to join the
|
||||||
elements of the array to form the scalar; if absent, a colon is used, as
|
elements of the array to form the scalar; if absent, a colon is used, as
|
||||||
with tt($PATH). Only the first character of the separator is significant;
|
with tt($PATH). Only the first character of the separator is significant;
|
||||||
any remaining characters are ignored.
|
any remaining characters are ignored. Multibyte characters are not
|
||||||
|
yet supported.
|
||||||
|
|
||||||
Only the scalar parameter may be assigned an initial value. Both the
|
Only one of the scalar and array parameters may be assigned an initial
|
||||||
scalar and the array may otherwise be manipulated as normal. If one is
|
value (the restrictions on assignment forms described above also apply).
|
||||||
|
|
||||||
|
Both the scalar and the array may be manipulated as normal. If one is
|
||||||
unset, the other will automatically be unset too. There is no way of
|
unset, the other will automatically be unset too. There is no way of
|
||||||
untying the variables without unsetting them, nor of converting the type
|
untying the variables without unsetting them, nor of converting the type
|
||||||
of one of them with another tt(typeset) command; tt(+T) does not work,
|
of one of them with another tt(typeset) command; tt(+T) does not work,
|
||||||
|
|
|
@ -472,7 +472,8 @@ word of a command unless quoted or disabled using tt(disable -r):
|
||||||
|
|
||||||
tt(do done esac then elif else fi for case
|
tt(do done esac then elif else fi for case
|
||||||
if while function repeat time until
|
if while function repeat time until
|
||||||
select coproc nocorrect foreach end ! [[ { })
|
select coproc nocorrect foreach end ! [[ { }
|
||||||
|
declare export float integer local readonly typeset)
|
||||||
|
|
||||||
Additionally, `tt(})' is recognized in any position if neither the
|
Additionally, `tt(})' is recognized in any position if neither the
|
||||||
tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set.
|
tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set.
|
||||||
|
|
|
@ -1928,7 +1928,13 @@ pindex(KSHTYPESET)
|
||||||
pindex(NOKSHTYPESET)
|
pindex(NOKSHTYPESET)
|
||||||
cindex(argument splitting, in typeset etc.)
|
cindex(argument splitting, in typeset etc.)
|
||||||
cindex(ksh, argument splitting in typeset)
|
cindex(ksh, argument splitting in typeset)
|
||||||
item(tt(KSH_TYPESET) <K>)(
|
item(tt(KSH_TYPESET))(
|
||||||
|
This option is now obsolete: a better appropximation to the behaviour of
|
||||||
|
other shells is obtained with the reserved word interface to
|
||||||
|
tt(declare), tt(export), tt(float), tt(integer), tt(local), tt(readonly)
|
||||||
|
and tt(typeset). Note that the option is only applied when the reserved
|
||||||
|
word interface is em(not) in use.
|
||||||
|
|
||||||
Alters the way arguments to the tt(typeset) family of commands, including
|
Alters the way arguments to the tt(typeset) family of commands, including
|
||||||
tt(declare), tt(export), tt(float), tt(integer), tt(local) and
|
tt(declare), tt(export), tt(float), tt(integer), tt(local) and
|
||||||
tt(readonly), are processed. Without this option, zsh will perform normal
|
tt(readonly), are processed. Without this option, zsh will perform normal
|
||||||
|
|
19
NEWS
19
NEWS
|
@ -4,7 +4,21 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
|
||||||
|
|
||||||
Note also the list of incompatibilities in the README file.
|
Note also the list of incompatibilities in the README file.
|
||||||
|
|
||||||
Changes from 5.0.7 to 5.0.8
|
Changes from 5.0.8 to 5.0.9
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
The builtins declare, export, local, readonly and typeset
|
||||||
|
now have corresponding reserved words. When used in
|
||||||
|
this form, the builtin syntax is extended so that assignments
|
||||||
|
following the reserved word are treated similarly to
|
||||||
|
assignments that appear at the start of the command line.
|
||||||
|
For example,
|
||||||
|
local scalar=`echo one word` array=(several words)
|
||||||
|
creates a local "scalar" containing the text "one word"
|
||||||
|
and an array "array" containing the words "several"
|
||||||
|
"words".
|
||||||
|
|
||||||
|
Changes from 5.0.0 to 5.0.8
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
- Global aliases can be created for syntactic tokens such as command
|
- Global aliases can be created for syntactic tokens such as command
|
||||||
|
@ -47,9 +61,6 @@ Changes from 5.0.7 to 5.0.8
|
||||||
- Some rationalisations have been made to the zsh/db/gdbm module that
|
- Some rationalisations have been made to the zsh/db/gdbm module that
|
||||||
should make it more useful and predictable in operation.
|
should make it more useful and predictable in operation.
|
||||||
|
|
||||||
Changes from 5.0.0 to 5.0.7
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
- Numeric constants encountered in mathematical expressions (but not other
|
- Numeric constants encountered in mathematical expressions (but not other
|
||||||
contexts) can contain underscores as separators that will be ignored on
|
contexts) can contain underscores as separators that will be ignored on
|
||||||
evaluation, as allowed in other scripting languages. For example,
|
evaluation, as allowed in other scripting languages. For example,
|
||||||
|
|
17
README
17
README
|
@ -30,6 +30,23 @@ Zsh is a shell with lots of features. For a list of some of these, see the
|
||||||
file FEATURES, and for the latest changes see NEWS. For more
|
file FEATURES, and for the latest changes see NEWS. For more
|
||||||
details, see the documentation.
|
details, see the documentation.
|
||||||
|
|
||||||
|
Incompatibilites between 5.0.8 and 5.0.9
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
As noted in NEWS, the builtins declare, export, float, integer, local,
|
||||||
|
readonly and typeset now have corresponding reserved words that provide
|
||||||
|
true assignment semantics instead of an approximation by means of normal
|
||||||
|
command line arguments. It is hoped that this additional consistency
|
||||||
|
provides a more natural interface. However, compatbility with older
|
||||||
|
versions of zsh can be obtained by turning off the reserved word
|
||||||
|
interface, exposing the builtin interface:
|
||||||
|
|
||||||
|
disable -r declare export float integer local readonly typeset
|
||||||
|
|
||||||
|
This is also necessary in the unusual eventuality that the builtins are
|
||||||
|
to be overridden by shell functions, since reserved words take
|
||||||
|
precedence over functions.
|
||||||
|
|
||||||
Incompatibilites between 5.0.7 and 5.0.8
|
Incompatibilites between 5.0.7 and 5.0.8
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
|
309
Src/builtin.c
309
Src/builtin.c
|
@ -53,7 +53,7 @@ static struct builtin builtins[] =
|
||||||
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
||||||
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
||||||
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
|
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
|
||||||
BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
|
BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
|
||||||
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
|
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
|
||||||
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
|
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
|
||||||
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
|
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
|
||||||
|
@ -62,7 +62,7 @@ static struct builtin builtins[] =
|
||||||
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
|
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
|
||||||
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
|
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
|
||||||
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
|
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
|
||||||
BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
|
BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
|
||||||
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
|
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
|
||||||
/*
|
/*
|
||||||
* We used to behave as if the argument to -e was optional.
|
* We used to behave as if the argument to -e was optional.
|
||||||
|
@ -71,7 +71,7 @@ static struct builtin builtins[] =
|
||||||
*/
|
*/
|
||||||
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
|
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
|
||||||
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
|
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
|
||||||
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
|
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
|
||||||
BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
|
BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
|
||||||
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
|
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
|
||||||
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
|
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
|
||||||
|
@ -82,11 +82,11 @@ static struct builtin builtins[] =
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
|
BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
|
||||||
BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
|
BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
|
||||||
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
|
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
|
||||||
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
|
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
|
||||||
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
|
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
|
||||||
BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
|
BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
|
||||||
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
|
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
|
||||||
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
|
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ static struct builtin builtins[] =
|
||||||
BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
|
BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
|
||||||
BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
|
BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
|
||||||
BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
|
BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
|
||||||
BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
|
BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
|
||||||
BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
|
BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
|
||||||
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
|
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
|
||||||
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
|
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
|
||||||
|
@ -120,7 +120,7 @@ static struct builtin builtins[] =
|
||||||
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
|
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
|
||||||
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
|
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
|
||||||
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
|
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
|
||||||
BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
|
BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
|
||||||
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
|
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
|
||||||
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
|
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
|
||||||
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
|
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
|
||||||
|
@ -246,7 +246,7 @@ new_optarg(Options ops)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
int
|
int
|
||||||
execbuiltin(LinkList args, Builtin bn)
|
execbuiltin(LinkList args, LinkList assigns, Builtin bn)
|
||||||
{
|
{
|
||||||
char *pp, *name, *optstr;
|
char *pp, *name, *optstr;
|
||||||
int flags, sense, argc, execop, xtr = isset(XTRACE);
|
int flags, sense, argc, execop, xtr = isset(XTRACE);
|
||||||
|
@ -443,11 +443,44 @@ execbuiltin(LinkList args, Builtin bn)
|
||||||
fputc(' ', xtrerr);
|
fputc(' ', xtrerr);
|
||||||
quotedzputs(*fullargv++, xtrerr);
|
quotedzputs(*fullargv++, xtrerr);
|
||||||
}
|
}
|
||||||
|
if (assigns) {
|
||||||
|
LinkNode node;
|
||||||
|
for (node = firstnode(assigns); node; incnode(node)) {
|
||||||
|
Asgment asg = (Asgment)node;
|
||||||
|
fputc(' ', xtrerr);
|
||||||
|
quotedzputs(asg->name, xtrerr);
|
||||||
|
if (asg->is_array) {
|
||||||
|
LinkNode arrnode;
|
||||||
|
fprintf(xtrerr, "=(");
|
||||||
|
for (arrnode = firstnode(asg->value.array);
|
||||||
|
arrnode;
|
||||||
|
incnode(arrnode)) {
|
||||||
|
fputc(' ', xtrerr);
|
||||||
|
quotedzputs((char *)getdata(arrnode), xtrerr);
|
||||||
|
}
|
||||||
|
fprintf(xtrerr, " )");
|
||||||
|
} else if (asg->value.scalar) {
|
||||||
|
fputc('=', xtrerr);
|
||||||
|
quotedzputs(asg->value.scalar, xtrerr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fputc('\n', xtrerr);
|
fputc('\n', xtrerr);
|
||||||
fflush(xtrerr);
|
fflush(xtrerr);
|
||||||
}
|
}
|
||||||
/* call the handler function, and return its return value */
|
/* call the handler function, and return its return value */
|
||||||
return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
|
if (flags & BINF_ASSIGN)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Takes two sets of arguments.
|
||||||
|
*/
|
||||||
|
HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc;
|
||||||
|
return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1452,12 +1485,13 @@ bin_fc(char *nam, char **argv, Options ops, int func)
|
||||||
if (!asgf)
|
if (!asgf)
|
||||||
asgf = asgl = a;
|
asgf = asgl = a;
|
||||||
else {
|
else {
|
||||||
asgl->next = a;
|
asgl->node.next = &a->node;
|
||||||
asgl = a;
|
asgl = a;
|
||||||
}
|
}
|
||||||
a->name = *argv;
|
a->name = *argv;
|
||||||
a->value = s;
|
a->is_array = 0;
|
||||||
a->next = NULL;
|
a->value.scalar = s;
|
||||||
|
a->node.next = a->node.prev = NULL;
|
||||||
argv++;
|
argv++;
|
||||||
}
|
}
|
||||||
/* interpret and check first history line specifier */
|
/* interpret and check first history line specifier */
|
||||||
|
@ -1631,8 +1665,8 @@ fcsubs(char **sp, struct asgment *sub)
|
||||||
/* loop through the linked list */
|
/* loop through the linked list */
|
||||||
while (sub) {
|
while (sub) {
|
||||||
oldstr = sub->name;
|
oldstr = sub->name;
|
||||||
newstr = sub->value;
|
newstr = sub->value.scalar;
|
||||||
sub = sub->next;
|
sub = (Asgment)sub->node.next;
|
||||||
oldpos = s;
|
oldpos = s;
|
||||||
/* loop over occurences of oldstr in s, replacing them with newstr */
|
/* loop over occurences of oldstr in s, replacing them with newstr */
|
||||||
while ((newpos = (char *)strstr(oldpos, oldstr))) {
|
while ((newpos = (char *)strstr(oldpos, oldstr))) {
|
||||||
|
@ -1820,13 +1854,22 @@ fcedit(char *ename, char *fn)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static Asgment
|
static Asgment
|
||||||
getasg(char *s)
|
getasg(char ***argvp, LinkList assigns)
|
||||||
{
|
{
|
||||||
|
char *s = **argvp;
|
||||||
static struct asgment asg;
|
static struct asgment asg;
|
||||||
|
|
||||||
/* sanity check for valid argument */
|
/* sanity check for valid argument */
|
||||||
if (!s)
|
if (!s) {
|
||||||
|
if (assigns) {
|
||||||
|
Asgment asgp = (Asgment)firstnode(assigns);
|
||||||
|
if (!asgp)
|
||||||
|
return NULL;
|
||||||
|
(void)uremnode(assigns, &asgp->node);
|
||||||
|
return asgp;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if name is empty */
|
/* check if name is empty */
|
||||||
if (*s == '=') {
|
if (*s == '=') {
|
||||||
|
@ -1834,6 +1877,7 @@ getasg(char *s)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
asg.name = s;
|
asg.name = s;
|
||||||
|
asg.is_array = 0;
|
||||||
|
|
||||||
/* search for `=' */
|
/* search for `=' */
|
||||||
for (; *s && *s != '='; s++);
|
for (; *s && *s != '='; s++);
|
||||||
|
@ -1841,11 +1885,12 @@ getasg(char *s)
|
||||||
/* found `=', so return with a value */
|
/* found `=', so return with a value */
|
||||||
if (*s) {
|
if (*s) {
|
||||||
*s = '\0';
|
*s = '\0';
|
||||||
asg.value = s + 1;
|
asg.value.scalar = s + 1;
|
||||||
} else {
|
} else {
|
||||||
/* didn't find `=', so we only have a name */
|
/* didn't find `=', so we only have a name */
|
||||||
asg.value = NULL;
|
asg.value.scalar = NULL;
|
||||||
}
|
}
|
||||||
|
(*argvp)++;
|
||||||
return &asg;
|
return &asg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1927,7 +1972,7 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
|
||||||
/**/
|
/**/
|
||||||
static Param
|
static Param
|
||||||
typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
int on, int off, int roff, char *value, Param altpm,
|
int on, int off, int roff, Asgment asg, Param altpm,
|
||||||
Options ops, int joinchar)
|
Options ops, int joinchar)
|
||||||
{
|
{
|
||||||
int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
|
int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
|
||||||
|
@ -1975,7 +2020,24 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
|
|
||||||
/* attempting a type conversion, or making a tied colonarray? */
|
/* attempting a type conversion, or making a tied colonarray? */
|
||||||
tc = 0;
|
tc = 0;
|
||||||
if (usepm || newspecial != NS_NONE) {
|
if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR &&
|
||||||
|
!(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))))
|
||||||
|
on |= PM_ARRAY;
|
||||||
|
if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE &&
|
||||||
|
PM_TYPE(pm->node.flags) != PM_ARRAY &&
|
||||||
|
PM_TYPE(pm->node.flags) != PM_HASHED) {
|
||||||
|
if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) {
|
||||||
|
zerrnam(cname, "%s: can't assign array value to non-array", pname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pm->node.flags & PM_SPECIAL) {
|
||||||
|
zerrnam(cname, "%s: can't assign array value to non-array special", pname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tc = 1;
|
||||||
|
usepm = 0;
|
||||||
|
}
|
||||||
|
else if (usepm || newspecial != NS_NONE) {
|
||||||
int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
|
int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
|
||||||
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
|
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
|
||||||
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
|
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
|
||||||
|
@ -2023,7 +2085,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
tc = 0; /* but don't do a normal conversion */
|
tc = 0; /* but don't do a normal conversion */
|
||||||
}
|
}
|
||||||
} else if (!setsecondstype(pm, on, off)) {
|
} else if (!setsecondstype(pm, on, off)) {
|
||||||
if (value && !(pm = setsparam(pname, ztrdup(value))))
|
if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||||
return NULL;
|
return NULL;
|
||||||
usepm = 1;
|
usepm = 1;
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -2049,7 +2111,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
* Stricter rules about retaining readonly attribute in this case.
|
* Stricter rules about retaining readonly attribute in this case.
|
||||||
*/
|
*/
|
||||||
if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
|
if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
|
||||||
!value)
|
!ASG_VALUEP(asg))
|
||||||
on |= PM_UNSET;
|
on |= PM_UNSET;
|
||||||
else if (usepm && (pm->node.flags & PM_READONLY) &&
|
else if (usepm && (pm->node.flags & PM_READONLY) &&
|
||||||
!(on & PM_READONLY)) {
|
!(on & PM_READONLY)) {
|
||||||
|
@ -2068,8 +2130,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
* ii. we are creating a new local parameter
|
* ii. we are creating a new local parameter
|
||||||
*/
|
*/
|
||||||
if (usepm) {
|
if (usepm) {
|
||||||
|
if (asg->is_array ?
|
||||||
|
(asg->value.array && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) :
|
||||||
|
(asg->value.scalar && (PM_TYPE(pm->node.flags & (PM_ARRAY|PM_HASHED))))) {
|
||||||
|
zerrnam(cname, "%s: inconsistent type for assignment", pname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
on &= ~PM_LOCAL;
|
on &= ~PM_LOCAL;
|
||||||
if (!on && !roff && !value) {
|
if (!on && !roff && !ASG_VALUEP(asg)) {
|
||||||
if (OPT_ISSET(ops,'p'))
|
if (OPT_ISSET(ops,'p'))
|
||||||
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
||||||
else if (!OPT_ISSET(ops,'g') &&
|
else if (!OPT_ISSET(ops,'g') &&
|
||||||
|
@ -2123,15 +2191,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
}
|
}
|
||||||
if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
||||||
if (pm->node.flags & PM_EXPORTED) {
|
if (pm->node.flags & PM_EXPORTED) {
|
||||||
if (!(pm->node.flags & PM_UNSET) && !pm->env && !value)
|
if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg))
|
||||||
addenv(pm, getsparam(pname));
|
addenv(pm, getsparam(pname));
|
||||||
} else if (pm->env && !(pm->node.flags & PM_HASHELEM))
|
} else if (pm->env && !(pm->node.flags & PM_HASHELEM))
|
||||||
delenv(pm);
|
delenv(pm);
|
||||||
if (value && !(pm = setsparam(pname, ztrdup(value))))
|
DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected");
|
||||||
|
if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||||
|
return NULL;
|
||||||
|
} else if (asg->value.array) {
|
||||||
|
DPUTS(!ASG_ARRAYP(asg), "BUG: typeset got scalar value where array expected");
|
||||||
|
if (!(pm = setaparam(pname, zlinklist2array(asg->value.array))))
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (value) {
|
|
||||||
zwarnnam(cname, "can't assign new value for array %s", pname);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
pm->node.flags |= (on & PM_READONLY);
|
pm->node.flags |= (on & PM_READONLY);
|
||||||
if (OPT_ISSET(ops,'p'))
|
if (OPT_ISSET(ops,'p'))
|
||||||
|
@ -2139,6 +2209,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asg->is_array ?
|
||||||
|
(asg->value.array && !(on & (PM_ARRAY|PM_HASHED))) :
|
||||||
|
(asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) {
|
||||||
|
zerrnam(cname, "%s: inconsistent type for assignment", pname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're here either because we're creating a new parameter,
|
* We're here either because we're creating a new parameter,
|
||||||
* or we're adding a parameter at a different local level,
|
* or we're adding a parameter at a different local level,
|
||||||
|
@ -2158,9 +2235,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
/*
|
/*
|
||||||
* Try to carry over a value, but not when changing from,
|
* Try to carry over a value, but not when changing from,
|
||||||
* to, or between non-scalar types.
|
* to, or between non-scalar types.
|
||||||
|
*
|
||||||
|
* (We can do better now, but it does have user-visible
|
||||||
|
* implications.)
|
||||||
*/
|
*/
|
||||||
if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED)))
|
if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) {
|
||||||
value = dupstring(getsparam(pname));
|
asg->value.scalar = dupstring(getsparam(pname));
|
||||||
|
asg->is_array = 0;
|
||||||
|
}
|
||||||
/* pname may point to pm->nam which is about to disappear */
|
/* pname may point to pm->nam which is about to disappear */
|
||||||
pname = dupstring(pname);
|
pname = dupstring(pname);
|
||||||
unsetparam_pm(pm, 0, 1);
|
unsetparam_pm(pm, 0, 1);
|
||||||
|
@ -2251,16 +2333,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PM_TYPE(on) == PM_SCALAR) {
|
if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) {
|
||||||
/*
|
/*
|
||||||
* This will either complain about bad identifiers, or will set
|
* This will either complain about bad identifiers, or will set
|
||||||
* a hash element or array slice. This once worked by accident,
|
* a hash element or array slice. This once worked by accident,
|
||||||
* creating a stray parameter along the way via createparam(),
|
* creating a stray parameter along the way via createparam(),
|
||||||
* now called below in the isident() branch.
|
* now called below in the isident() branch.
|
||||||
*/
|
*/
|
||||||
if (!(pm = setsparam(pname, ztrdup(value ? value : ""))))
|
if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : ""))))
|
||||||
return NULL;
|
return NULL;
|
||||||
value = NULL;
|
asg->value.scalar = NULL;
|
||||||
|
asg->is_array = 0;
|
||||||
keeplocal = 0;
|
keeplocal = 0;
|
||||||
on = pm->node.flags;
|
on = pm->node.flags;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2331,10 +2414,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
pm->level = keeplocal;
|
pm->level = keeplocal;
|
||||||
else if (on & PM_LOCAL)
|
else if (on & PM_LOCAL)
|
||||||
pm->level = locallevel;
|
pm->level = locallevel;
|
||||||
if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
if (ASG_VALUEP(asg)) {
|
||||||
Param ipm = pm;
|
Param ipm = pm;
|
||||||
if (!(pm = setsparam(pname, ztrdup(value))))
|
if (pm->node.flags & (PM_ARRAY|PM_HASHED)) {
|
||||||
return NULL;
|
DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array");
|
||||||
|
if (!(pm=setaparam(pname, zlinklist2array(asg->value.array))))
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar");
|
||||||
|
if (!(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (pm != ipm) {
|
if (pm != ipm) {
|
||||||
DPUTS(ipm->node.flags != pm->node.flags,
|
DPUTS(ipm->node.flags != pm->node.flags,
|
||||||
"BUG: parameter recreated with wrong flags");
|
"BUG: parameter recreated with wrong flags");
|
||||||
|
@ -2371,12 +2461,6 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pm->node.flags |= (on & PM_READONLY);
|
pm->node.flags |= (on & PM_READONLY);
|
||||||
if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
|
||||||
zerrnam(cname, "%s: can't assign initial value for array", pname);
|
|
||||||
/* the only safe thing to do here seems to be unset the param */
|
|
||||||
unsetparam_pm(pm, 0, 1);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OPT_ISSET(ops,'p'))
|
if (OPT_ISSET(ops,'p'))
|
||||||
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
||||||
|
@ -2384,11 +2468,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* declare, export, integer, local, readonly, typeset */
|
/*
|
||||||
|
* declare, export, float, integer, local, readonly, typeset
|
||||||
|
*
|
||||||
|
* Note the difference in interface from most builtins, covered by the
|
||||||
|
* BINF_ASSIGN builtin flag. This is only made use of by builtins
|
||||||
|
* called by reserved word, which only covers declare, local, readonly
|
||||||
|
* and typeset. Otherwise assigns is NULL.
|
||||||
|
*/
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
int
|
int
|
||||||
bin_typeset(char *name, char **argv, Options ops, int func)
|
bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
|
||||||
{
|
{
|
||||||
Param pm;
|
Param pm;
|
||||||
Asgment asg;
|
Asgment asg;
|
||||||
|
@ -2397,6 +2488,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
int on = 0, off = 0, roff, bit = PM_ARRAY;
|
int on = 0, off = 0, roff, bit = PM_ARRAY;
|
||||||
int i;
|
int i;
|
||||||
int returnval = 0, printflags = 0;
|
int returnval = 0, printflags = 0;
|
||||||
|
int hasargs;
|
||||||
|
|
||||||
/* hash -f is really the builtin `functions' */
|
/* hash -f is really the builtin `functions' */
|
||||||
if (OPT_ISSET(ops,'f'))
|
if (OPT_ISSET(ops,'f'))
|
||||||
|
@ -2449,7 +2541,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
/* Given no arguments, list whatever the options specify. */
|
/* Given no arguments, list whatever the options specify. */
|
||||||
if (OPT_ISSET(ops,'p'))
|
if (OPT_ISSET(ops,'p'))
|
||||||
printflags |= PRINT_TYPESET;
|
printflags |= PRINT_TYPESET;
|
||||||
if (!*argv) {
|
hasargs = *argv != NULL || (assigns && firstnode(assigns));
|
||||||
|
if (!hasargs) {
|
||||||
if (!OPT_ISSET(ops,'p')) {
|
if (!OPT_ISSET(ops,'p')) {
|
||||||
if (!(on|roff))
|
if (!(on|roff))
|
||||||
printflags |= PRINT_TYPE;
|
printflags |= PRINT_TYPE;
|
||||||
|
@ -2468,9 +2561,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
|
|
||||||
if (on & PM_TIED) {
|
if (on & PM_TIED) {
|
||||||
Param apm;
|
Param apm;
|
||||||
struct asgment asg0;
|
struct asgment asg0, asg2;
|
||||||
char *oldval = NULL;
|
char *oldval = NULL, *joinstr;
|
||||||
int joinchar;
|
int joinchar, nargs;
|
||||||
|
|
||||||
if (OPT_ISSET(ops,'m')) {
|
if (OPT_ISSET(ops,'m')) {
|
||||||
zwarnnam(name, "incompatible options for -T");
|
zwarnnam(name, "incompatible options for -T");
|
||||||
|
@ -2478,34 +2571,41 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
on &= ~off;
|
on &= ~off;
|
||||||
if (!argv[1] || (argv[2] && argv[3])) {
|
nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0);
|
||||||
|
if (nargs < 2) {
|
||||||
zwarnnam(name, "-T requires names of scalar and array");
|
zwarnnam(name, "-T requires names of scalar and array");
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (nargs > 3) {
|
||||||
|
zwarnnam(name, "too many arguments for -T");
|
||||||
|
unqueue_signals();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
if (!(asg = getasg(&argv, assigns))) {
|
||||||
* Third argument, if given, is character used to join
|
|
||||||
* the elements of the array in the scalar.
|
|
||||||
*/
|
|
||||||
if (!argv[2])
|
|
||||||
joinchar = ':';
|
|
||||||
else if (!*argv[2])
|
|
||||||
joinchar = 0;
|
|
||||||
else if (*argv[2] == Meta)
|
|
||||||
joinchar = argv[2][1] ^ 32;
|
|
||||||
else
|
|
||||||
joinchar = *argv[2];
|
|
||||||
|
|
||||||
if (!(asg = getasg(argv[0]))) {
|
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
asg0 = *asg;
|
asg0 = *asg;
|
||||||
if (!(asg = getasg(argv[1]))) {
|
if (ASG_ARRAYP(&asg0)) {
|
||||||
|
unqueue_signals();
|
||||||
|
zwarnnam(name, "first argument of tie must be scalar: %s",
|
||||||
|
asg0.name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(asg = getasg(&argv, assigns))) {
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (!ASG_ARRAYP(asg) && asg->value.scalar) {
|
||||||
|
unqueue_signals();
|
||||||
|
zwarnnam(name, "second argument of tie must be array: %s",
|
||||||
|
asg->name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(asg0.name, asg->name)) {
|
if (!strcmp(asg0.name, asg->name)) {
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
|
zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
|
||||||
|
@ -2516,6 +2616,36 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
zerrnam(name, "can't tie array elements: %s", asg0.name);
|
zerrnam(name, "can't tie array elements: %s", asg0.name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) {
|
||||||
|
unqueue_signals();
|
||||||
|
zerrnam(name, "only one tied parameter can have value: %s", asg0.name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Third argument, if given, is character used to join
|
||||||
|
* the elements of the array in the scalar.
|
||||||
|
*/
|
||||||
|
if (*argv)
|
||||||
|
joinstr = *argv;
|
||||||
|
else if (assigns && firstnode(assigns)) {
|
||||||
|
Asgment nextasg = (Asgment)firstnode(assigns);
|
||||||
|
if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) {
|
||||||
|
zwarnnam(name, "third argument of tie must be join character");
|
||||||
|
unqueue_signals();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
joinstr = nextasg->name;
|
||||||
|
} else
|
||||||
|
joinstr = NULL;
|
||||||
|
if (!joinstr)
|
||||||
|
joinchar = ':';
|
||||||
|
else if (!*joinstr)
|
||||||
|
joinchar = 0;
|
||||||
|
else if (*joinstr == Meta)
|
||||||
|
joinchar = joinstr[1] ^ 32;
|
||||||
|
else
|
||||||
|
joinchar = *joinstr;
|
||||||
/*
|
/*
|
||||||
* Keep the old value of the scalar. We need to do this
|
* Keep the old value of the scalar. We need to do this
|
||||||
* here as if it is already tied to the same array it
|
* here as if it is already tied to the same array it
|
||||||
|
@ -2537,8 +2667,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
struct tieddata *tdp = (struct tieddata*)pm->u.data;
|
struct tieddata *tdp = (struct tieddata*)pm->u.data;
|
||||||
/* Update join character */
|
/* Update join character */
|
||||||
tdp->joinchar = joinchar;
|
tdp->joinchar = joinchar;
|
||||||
if (asg0.value)
|
if (asg0.value.scalar)
|
||||||
setsparam(asg0.name, ztrdup(asg0.value));
|
setsparam(asg0.name, ztrdup(asg0.value.scalar));
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
zwarnnam(name, "can't tie already tied scalar: %s",
|
zwarnnam(name, "can't tie already tied scalar: %s",
|
||||||
|
@ -2546,7 +2676,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
|
if (!asg0.value.scalar && !asg->value.array &&
|
||||||
|
!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
|
||||||
oldval = ztrdup(getsparam(asg0.name));
|
oldval = ztrdup(getsparam(asg0.name));
|
||||||
on |= (pm->node.flags & PM_EXPORTED);
|
on |= (pm->node.flags & PM_EXPORTED);
|
||||||
}
|
}
|
||||||
|
@ -2554,12 +2685,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
* Create the tied array; this is normal except that
|
* Create the tied array; this is normal except that
|
||||||
* it has the PM_TIED flag set. Do it first because
|
* it has the PM_TIED flag set. Do it first because
|
||||||
* we need the address.
|
* we need the address.
|
||||||
|
*
|
||||||
|
* Don't attempt to set it yet, it's too early
|
||||||
|
* to be exported properly.
|
||||||
*/
|
*/
|
||||||
|
asg2.name = asg->name;
|
||||||
|
asg2.is_array = 0;
|
||||||
|
asg2.value.array = (LinkList)0;
|
||||||
if (!(apm=typeset_single(name, asg->name,
|
if (!(apm=typeset_single(name, asg->name,
|
||||||
(Param)paramtab->getnode(paramtab,
|
(Param)paramtab->getnode(paramtab,
|
||||||
asg->name),
|
asg->name),
|
||||||
func, (on | PM_ARRAY) & ~PM_EXPORTED,
|
func, (on | PM_ARRAY) & ~PM_EXPORTED,
|
||||||
off, roff, asg->value, NULL, ops, 0))) {
|
off, roff, &asg2, NULL, ops, 0))) {
|
||||||
if (oldval)
|
if (oldval)
|
||||||
zsfree(oldval);
|
zsfree(oldval);
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
|
@ -2572,7 +2709,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
if (!(pm=typeset_single(name, asg0.name,
|
if (!(pm=typeset_single(name, asg0.name,
|
||||||
(Param)paramtab->getnode(paramtab,
|
(Param)paramtab->getnode(paramtab,
|
||||||
asg0.name),
|
asg0.name),
|
||||||
func, on, off, roff, asg0.value, apm,
|
func, on, off, roff, &asg0, apm,
|
||||||
ops, joinchar))) {
|
ops, joinchar))) {
|
||||||
if (oldval)
|
if (oldval)
|
||||||
zsfree(oldval);
|
zsfree(oldval);
|
||||||
|
@ -2591,7 +2728,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
if (apm->ename)
|
if (apm->ename)
|
||||||
zsfree(apm->ename);
|
zsfree(apm->ename);
|
||||||
apm->ename = ztrdup(asg0.name);
|
apm->ename = ztrdup(asg0.name);
|
||||||
if (oldval)
|
if (asg->value.array)
|
||||||
|
setaparam(asg->name, zlinklist2array(asg->value.array));
|
||||||
|
else if (oldval)
|
||||||
setsparam(asg0.name, oldval);
|
setsparam(asg0.name, oldval);
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
|
|
||||||
|
@ -2611,18 +2750,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
printflags |= PRINT_NAMEONLY;
|
printflags |= PRINT_NAMEONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((asg = getasg(*argv++))) {
|
while ((asg = getasg(&argv, assigns))) {
|
||||||
LinkList pmlist = newlinklist();
|
LinkList pmlist = newlinklist();
|
||||||
LinkNode pmnode;
|
LinkNode pmnode;
|
||||||
|
|
||||||
tokenize(asg->name); /* expand argument */
|
tokenize(asg->name); /* expand argument */
|
||||||
if (!(pprog = patcompile(asg->name, 0, NULL))) {
|
if (!(pprog = patcompile(asg->name, 0, NULL))) {
|
||||||
untokenize(asg->name);
|
untokenize(asg->name);
|
||||||
zwarnnam(name, "bad pattern : %s", argv[-1]);
|
zwarnnam(name, "bad pattern : %s", asg->name);
|
||||||
returnval = 1;
|
returnval = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (OPT_PLUS(ops,'m') && !asg->value) {
|
if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) {
|
||||||
scanmatchtable(paramtab, pprog, 1, on|roff, 0,
|
scanmatchtable(paramtab, pprog, 1, on|roff, 0,
|
||||||
paramtab->printnode, printflags);
|
paramtab->printnode, printflags);
|
||||||
continue;
|
continue;
|
||||||
|
@ -2648,7 +2787,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
|
for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
|
||||||
pm = (Param) getdata(pmnode);
|
pm = (Param) getdata(pmnode);
|
||||||
if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
|
if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
|
||||||
asg->value, NULL, ops, 0))
|
asg, NULL, ops, 0))
|
||||||
returnval = 1;
|
returnval = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2657,7 +2796,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Take arguments literally. Don't glob */
|
/* Take arguments literally. Don't glob */
|
||||||
while ((asg = getasg(*argv++))) {
|
while ((asg = getasg(&argv, assigns))) {
|
||||||
HashNode hn = (paramtab == realparamtab ?
|
HashNode hn = (paramtab == realparamtab ?
|
||||||
gethashnode2(paramtab, asg->name) :
|
gethashnode2(paramtab, asg->name) :
|
||||||
paramtab->getnode(paramtab, asg->name));
|
paramtab->getnode(paramtab, asg->name));
|
||||||
|
@ -2671,7 +2810,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!typeset_single(name, asg->name, (Param)hn,
|
if (!typeset_single(name, asg->name, (Param)hn,
|
||||||
func, on, off, roff, asg->value, NULL,
|
func, on, off, roff, asg, NULL,
|
||||||
ops, 0))
|
ops, 0))
|
||||||
returnval = 1;
|
returnval = 1;
|
||||||
}
|
}
|
||||||
|
@ -3514,7 +3653,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_signals();
|
queue_signals();
|
||||||
for (;*argv;++argv) {
|
while (*argv) {
|
||||||
void *hn;
|
void *hn;
|
||||||
if (OPT_ISSET(ops,'m')) {
|
if (OPT_ISSET(ops,'m')) {
|
||||||
/* with the -m option, treat the argument as a glob pattern */
|
/* with the -m option, treat the argument as a glob pattern */
|
||||||
|
@ -3529,12 +3668,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(asg = getasg(*argv))) {
|
if (!(asg = getasg(&argv, NULL))) {
|
||||||
zwarnnam(name, "bad assignment");
|
zwarnnam(name, "bad assignment");
|
||||||
returnval = 1;
|
returnval = 1;
|
||||||
} else if (asg->value) {
|
} else if (ASG_VALUEP(asg)) {
|
||||||
if(isset(RESTRICTED)) {
|
if(isset(RESTRICTED)) {
|
||||||
zwarnnam(name, "restricted: %s", asg->value);
|
zwarnnam(name, "restricted: %s", asg->value.scalar);
|
||||||
returnval = 1;
|
returnval = 1;
|
||||||
} else {
|
} else {
|
||||||
/* The argument is of the form foo=bar, *
|
/* The argument is of the form foo=bar, *
|
||||||
|
@ -3550,12 +3689,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
||||||
} else {
|
} else {
|
||||||
Nameddir nd = hn = zshcalloc(sizeof *nd);
|
Nameddir nd = hn = zshcalloc(sizeof *nd);
|
||||||
nd->node.flags = 0;
|
nd->node.flags = 0;
|
||||||
nd->dir = ztrdup(asg->value);
|
nd->dir = ztrdup(asg->value.scalar);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Cmdnam cn = hn = zshcalloc(sizeof *cn);
|
Cmdnam cn = hn = zshcalloc(sizeof *cn);
|
||||||
cn->node.flags = HASHED;
|
cn->node.flags = HASHED;
|
||||||
cn->u.cmd = ztrdup(asg->value);
|
cn->u.cmd = ztrdup(asg->value.scalar);
|
||||||
}
|
}
|
||||||
ht->addnode(ht, ztrdup(asg->name), hn);
|
ht->addnode(ht, ztrdup(asg->name), hn);
|
||||||
if(OPT_ISSET(ops,'v'))
|
if(OPT_ISSET(ops,'v'))
|
||||||
|
@ -3761,12 +3900,12 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
|
||||||
|
|
||||||
/* Take arguments literally. Don't glob */
|
/* Take arguments literally. Don't glob */
|
||||||
queue_signals();
|
queue_signals();
|
||||||
while ((asg = getasg(*argv++))) {
|
while ((asg = getasg(&argv, NULL))) {
|
||||||
if (asg->value && !OPT_ISSET(ops,'L')) {
|
if (asg->value.scalar && !OPT_ISSET(ops,'L')) {
|
||||||
/* The argument is of the form foo=bar and we are not *
|
/* The argument is of the form foo=bar and we are not *
|
||||||
* forcing a listing with -L, so define an alias */
|
* forcing a listing with -L, so define an alias */
|
||||||
ht->addnode(ht, ztrdup(asg->name),
|
ht->addnode(ht, ztrdup(asg->name),
|
||||||
createaliasnode(ztrdup(asg->value), flags1));
|
createaliasnode(ztrdup(asg->value.scalar), flags1));
|
||||||
} else if ((a = (Alias) ht->getnode(ht, asg->name))) {
|
} else if ((a = (Alias) ht->getnode(ht, asg->name))) {
|
||||||
/* display alias if appropriate */
|
/* display alias if appropriate */
|
||||||
if (!type_opts || ht == sufaliastab ||
|
if (!type_opts || ht == sufaliastab ||
|
||||||
|
|
176
Src/exec.c
176
Src/exec.c
|
@ -2437,13 +2437,13 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
char *text;
|
char *text;
|
||||||
int save[10];
|
int save[10];
|
||||||
int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0;
|
int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0;
|
||||||
int nullexec = 0, assign = 0, forked = 0;
|
int nullexec = 0, assign = 0, forked = 0, postassigns = 0;
|
||||||
int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
|
int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
|
||||||
/* Various flags to the command. */
|
/* Various flags to the command. */
|
||||||
int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
|
int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
|
||||||
LinkList redir;
|
LinkList redir;
|
||||||
wordcode code;
|
wordcode code;
|
||||||
Wordcode beg = state->pc, varspc;
|
Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0;
|
||||||
FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
|
FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
|
||||||
|
|
||||||
doneps4 = 0;
|
doneps4 = 0;
|
||||||
|
@ -2464,8 +2464,28 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
/* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
|
/* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
|
||||||
* But for that we would need to check/change all builtins so that
|
* But for that we would need to check/change all builtins so that
|
||||||
* they don't modify their argument strings. */
|
* they don't modify their argument strings. */
|
||||||
args = (type == WC_SIMPLE ?
|
switch (type) {
|
||||||
ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
|
case WC_SIMPLE:
|
||||||
|
args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WC_TYPESET:
|
||||||
|
args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok);
|
||||||
|
postassigns = *state->pc++;
|
||||||
|
assignspc = state->pc;
|
||||||
|
for (i = 0; i < postassigns; i++) {
|
||||||
|
code = *state->pc;
|
||||||
|
DPUTS(wc_code(code) != WC_ASSIGN,
|
||||||
|
"BUG: miscounted typeset assignments");
|
||||||
|
state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
|
||||||
|
3 : WC_ASSIGN_NUM(code) + 2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
args = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If assignment but no command get the status from variable
|
* If assignment but no command get the status from variable
|
||||||
* assignment.
|
* assignment.
|
||||||
|
@ -2488,7 +2508,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
|
|
||||||
/* If the command begins with `%', then assume it is a *
|
/* If the command begins with `%', then assume it is a *
|
||||||
* reference to a job in the job table. */
|
* reference to a job in the job table. */
|
||||||
if (type == WC_SIMPLE && args && nonempty(args) &&
|
if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) &&
|
||||||
*(char *)peekfirst(args) == '%') {
|
*(char *)peekfirst(args) == '%') {
|
||||||
if (how & Z_DISOWN) {
|
if (how & Z_DISOWN) {
|
||||||
oautocont = opts[AUTOCONTINUE];
|
oautocont = opts[AUTOCONTINUE];
|
||||||
|
@ -2517,20 +2537,32 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
|
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
|
||||||
* only works in simple cases. has_token() is called to make sure *
|
* only works in simple cases. has_token() is called to make sure *
|
||||||
* this really is a simple case. */
|
* this really is a simple case. */
|
||||||
if (type == WC_SIMPLE) {
|
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||||
while (args && nonempty(args)) {
|
while (args && nonempty(args)) {
|
||||||
char *cmdarg = (char *) peekfirst(args);
|
char *cmdarg = (char *) peekfirst(args);
|
||||||
checked = !has_token(cmdarg);
|
checked = !has_token(cmdarg);
|
||||||
if (!checked)
|
if (!checked)
|
||||||
break;
|
break;
|
||||||
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
|
if (type == WC_TYPESET &&
|
||||||
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
|
(hn = builtintab->getnode2(builtintab, cmdarg))) {
|
||||||
is_shfunc = 1;
|
/*
|
||||||
break;
|
* If reserved word for typeset command found (and so
|
||||||
}
|
* enabled), use regardless of whether builtin is
|
||||||
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
|
* enabled as we share the implementation.
|
||||||
checked = !(cflags & BINF_BUILTIN);
|
*
|
||||||
break;
|
* Reserved words take precedence over shell functions.
|
||||||
|
*/
|
||||||
|
checked = 1;
|
||||||
|
} else {
|
||||||
|
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
|
||||||
|
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
|
||||||
|
is_shfunc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
|
||||||
|
checked = !(cflags & BINF_BUILTIN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
orig_cflags |= cflags;
|
orig_cflags |= cflags;
|
||||||
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
|
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
|
||||||
|
@ -2661,7 +2693,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
if (args && htok)
|
if (args && htok)
|
||||||
prefork(args, esprefork);
|
prefork(args, esprefork);
|
||||||
|
|
||||||
if (type == WC_SIMPLE) {
|
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||||
int unglobbed = 0;
|
int unglobbed = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -2897,7 +2929,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == WC_SIMPLE && !nullexec) {
|
if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) {
|
||||||
char *s;
|
char *s;
|
||||||
char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
|
char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
|
||||||
(!redir || empty(redir)) && args && !empty(args) &&
|
(!redir || empty(redir)) && args && !empty(args) &&
|
||||||
|
@ -3457,9 +3489,117 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
execshfunc((Shfunc) hn, args);
|
execshfunc((Shfunc) hn, args);
|
||||||
} else {
|
} else {
|
||||||
/* It's a builtin */
|
/* It's a builtin */
|
||||||
|
LinkList assigns = (LinkList)0;
|
||||||
if (forked)
|
if (forked)
|
||||||
closem(FDT_INTERNAL);
|
closem(FDT_INTERNAL);
|
||||||
lastval = execbuiltin(args, (Builtin) hn);
|
if (postassigns) {
|
||||||
|
Wordcode opc = state->pc;
|
||||||
|
state->pc = assignspc;
|
||||||
|
assigns = newlinklist();
|
||||||
|
while (postassigns--) {
|
||||||
|
wordcode ac = *state->pc++;
|
||||||
|
char *name = ecgetstr(state, EC_DUPTOK, &htok);
|
||||||
|
Asgment asg;
|
||||||
|
local_list1(svl);
|
||||||
|
|
||||||
|
DPUTS(wc_code(ac) != WC_ASSIGN,
|
||||||
|
"BUG: bad assignment list for typeset");
|
||||||
|
if (htok) {
|
||||||
|
init_list1(svl, name);
|
||||||
|
if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR &&
|
||||||
|
WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
|
||||||
|
char *data;
|
||||||
|
/*
|
||||||
|
* Special case: this is a name only, so
|
||||||
|
* it's not required to be a single
|
||||||
|
* expansion. Furthermore, for
|
||||||
|
* consistency with the builtin
|
||||||
|
* interface, it may expand into
|
||||||
|
* scalar assignments:
|
||||||
|
* ass=(one=two three=four)
|
||||||
|
* typeset a=b $ass
|
||||||
|
*/
|
||||||
|
/* Unused dummy value for name */
|
||||||
|
(void)ecgetstr(state, EC_DUPTOK, &htok);
|
||||||
|
prefork(&svl, PREFORK_TYPESET);
|
||||||
|
if (errflag) {
|
||||||
|
state->pc = opc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
globlist(&svl, 0);
|
||||||
|
if (errflag) {
|
||||||
|
state->pc = opc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while ((data = ugetnode(&svl))) {
|
||||||
|
char *ptr;
|
||||||
|
asg = (Asgment)zhalloc(sizeof(struct asgment));
|
||||||
|
asg->is_array = 0;
|
||||||
|
if ((ptr = strchr(data, '='))) {
|
||||||
|
*ptr++ = '\0';
|
||||||
|
asg->name = data;
|
||||||
|
asg->value.scalar = ptr;
|
||||||
|
} else {
|
||||||
|
asg->name = data;
|
||||||
|
asg->value.scalar = NULL;
|
||||||
|
}
|
||||||
|
uaddlinknode(assigns, &asg->node);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prefork(&svl, PREFORK_SINGLE);
|
||||||
|
name = empty(&svl) ? "" :
|
||||||
|
(char *)getdata(firstnode(&svl));
|
||||||
|
}
|
||||||
|
untokenize(name);
|
||||||
|
asg = (Asgment)zhalloc(sizeof(struct asgment));
|
||||||
|
asg->name = name;
|
||||||
|
if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) {
|
||||||
|
char *val = ecgetstr(state, EC_DUPTOK, &htok);
|
||||||
|
asg->is_array = 0;
|
||||||
|
if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
|
||||||
|
/* Fake assignment, no value */
|
||||||
|
asg->value.scalar = NULL;
|
||||||
|
} else {
|
||||||
|
if (htok) {
|
||||||
|
init_list1(svl, val);
|
||||||
|
prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN);
|
||||||
|
if (errflag) {
|
||||||
|
state->pc = opc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* No globassign for typeset
|
||||||
|
* arguments, thank you
|
||||||
|
*/
|
||||||
|
val = empty(&svl) ? "" :
|
||||||
|
(char *)getdata(firstnode(&svl));
|
||||||
|
}
|
||||||
|
untokenize(val);
|
||||||
|
asg->value.scalar = val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asg->is_array = 1;
|
||||||
|
asg->value.array =
|
||||||
|
ecgetlist(state, WC_ASSIGN_NUM(ac),
|
||||||
|
EC_DUPTOK, &htok);
|
||||||
|
prefork(asg->value.array, PREFORK_ASSIGN);
|
||||||
|
if (errflag) {
|
||||||
|
state->pc = opc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
globlist(asg->value.array, 0);
|
||||||
|
if (errflag) {
|
||||||
|
state->pc = opc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uaddlinknode(assigns, &asg->node);
|
||||||
|
}
|
||||||
|
state->pc = opc;
|
||||||
|
}
|
||||||
|
lastval = execbuiltin(args, assigns, (Builtin) hn);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (save[1] == -2) {
|
if (save[1] == -2) {
|
||||||
if (ferror(stdout)) {
|
if (ferror(stdout)) {
|
||||||
|
@ -3501,7 +3641,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
||||||
if (!subsh && isset(RCS) && interact && !nohistsave)
|
if (!subsh && isset(RCS) && interact && !nohistsave)
|
||||||
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
|
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
|
||||||
}
|
}
|
||||||
if (type == WC_SIMPLE) {
|
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||||
if (varspc) {
|
if (varspc) {
|
||||||
int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT;
|
int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT;
|
||||||
if (forked)
|
if (forked)
|
||||||
|
|
|
@ -1050,22 +1050,29 @@ static struct reswd reswds[] = {
|
||||||
{{NULL, "}", 0}, OUTBRACE},
|
{{NULL, "}", 0}, OUTBRACE},
|
||||||
{{NULL, "case", 0}, CASE},
|
{{NULL, "case", 0}, CASE},
|
||||||
{{NULL, "coproc", 0}, COPROC},
|
{{NULL, "coproc", 0}, COPROC},
|
||||||
|
{{NULL, "declare", 0}, TYPESET},
|
||||||
{{NULL, "do", 0}, DOLOOP},
|
{{NULL, "do", 0}, DOLOOP},
|
||||||
{{NULL, "done", 0}, DONE},
|
{{NULL, "done", 0}, DONE},
|
||||||
{{NULL, "elif", 0}, ELIF},
|
{{NULL, "elif", 0}, ELIF},
|
||||||
{{NULL, "else", 0}, ELSE},
|
{{NULL, "else", 0}, ELSE},
|
||||||
{{NULL, "end", 0}, ZEND},
|
{{NULL, "end", 0}, ZEND},
|
||||||
{{NULL, "esac", 0}, ESAC},
|
{{NULL, "esac", 0}, ESAC},
|
||||||
|
{{NULL, "export", 0}, TYPESET},
|
||||||
{{NULL, "fi", 0}, FI},
|
{{NULL, "fi", 0}, FI},
|
||||||
|
{{NULL, "float", 0}, TYPESET},
|
||||||
{{NULL, "for", 0}, FOR},
|
{{NULL, "for", 0}, FOR},
|
||||||
{{NULL, "foreach", 0}, FOREACH},
|
{{NULL, "foreach", 0}, FOREACH},
|
||||||
{{NULL, "function", 0}, FUNC},
|
{{NULL, "function", 0}, FUNC},
|
||||||
{{NULL, "if", 0}, IF},
|
{{NULL, "if", 0}, IF},
|
||||||
|
{{NULL, "integer", 0}, TYPESET},
|
||||||
|
{{NULL, "local", 0}, TYPESET},
|
||||||
{{NULL, "nocorrect", 0}, NOCORRECT},
|
{{NULL, "nocorrect", 0}, NOCORRECT},
|
||||||
|
{{NULL, "readonly", 0}, TYPESET},
|
||||||
{{NULL, "repeat", 0}, REPEAT},
|
{{NULL, "repeat", 0}, REPEAT},
|
||||||
{{NULL, "select", 0}, SELECT},
|
{{NULL, "select", 0}, SELECT},
|
||||||
{{NULL, "then", 0}, THEN},
|
{{NULL, "then", 0}, THEN},
|
||||||
{{NULL, "time", 0}, TIME},
|
{{NULL, "time", 0}, TIME},
|
||||||
|
{{NULL, "typeset", 0}, TYPESET},
|
||||||
{{NULL, "until", 0}, UNTIL},
|
{{NULL, "until", 0}, UNTIL},
|
||||||
{{NULL, "while", 0}, WHILE},
|
{{NULL, "while", 0}, WHILE},
|
||||||
{{NULL, NULL, 0}, 0}
|
{{NULL, NULL, 0}, 0}
|
||||||
|
|
|
@ -1182,7 +1182,7 @@ gettokstr(int c, int sub)
|
||||||
c = Outpar;
|
c = Outpar;
|
||||||
}
|
}
|
||||||
} else if (peek != ENVSTRING &&
|
} else if (peek != ENVSTRING &&
|
||||||
incmdpos && !bct && !brct) {
|
(incmdpos || intypeset) && !bct && !brct) {
|
||||||
char *t = tokstr;
|
char *t = tokstr;
|
||||||
if (idigit(*t))
|
if (idigit(*t))
|
||||||
while (++t < lexbuf.ptr && idigit(*t));
|
while (++t < lexbuf.ptr && idigit(*t));
|
||||||
|
@ -1200,7 +1200,7 @@ gettokstr(int c, int sub)
|
||||||
t++;
|
t++;
|
||||||
if (t == lexbuf.ptr) {
|
if (t == lexbuf.ptr) {
|
||||||
e = hgetc();
|
e = hgetc();
|
||||||
if (e == '(' && incmdpos) {
|
if (e == '(') {
|
||||||
*lexbuf.ptr = '\0';
|
*lexbuf.ptr = '\0';
|
||||||
return ENVARRAY;
|
return ENVARRAY;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ static struct optname optns[] = {
|
||||||
{{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD},
|
{{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD},
|
||||||
{{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB},
|
{{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB},
|
||||||
{{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT},
|
{{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT},
|
||||||
{{NULL, "kshtypeset", OPT_EMULATE|OPT_KSH}, KSHTYPESET},
|
{{NULL, "kshtypeset", 0}, KSHTYPESET},
|
||||||
{{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT},
|
{{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT},
|
||||||
{{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS},
|
{{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS},
|
||||||
{{NULL, "listbeep", OPT_ALL}, LISTBEEP},
|
{{NULL, "listbeep", OPT_ALL}, LISTBEEP},
|
||||||
|
|
131
Src/parse.c
131
Src/parse.c
|
@ -63,6 +63,11 @@ int isnewlin;
|
||||||
/**/
|
/**/
|
||||||
int infor;
|
int infor;
|
||||||
|
|
||||||
|
/* != 0 if parsing arguments of typeset etc. */
|
||||||
|
|
||||||
|
/**/
|
||||||
|
int intypeset;
|
||||||
|
|
||||||
/* list of here-documents */
|
/* list of here-documents */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
@ -118,11 +123,20 @@ struct heredocs *hdocs;
|
||||||
* WC_ASSIGN
|
* WC_ASSIGN
|
||||||
* - data contains type (scalar, array) and number of array-elements
|
* - data contains type (scalar, array) and number of array-elements
|
||||||
* - followed by name and value
|
* - followed by name and value
|
||||||
|
* Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates
|
||||||
|
* a name with no equals, not an =+ which isn't valid here.
|
||||||
*
|
*
|
||||||
* WC_SIMPLE
|
* WC_SIMPLE
|
||||||
* - data contains the number of arguments (plus command)
|
* - data contains the number of arguments (plus command)
|
||||||
* - followed by strings
|
* - followed by strings
|
||||||
*
|
*
|
||||||
|
* WC_TYPESET
|
||||||
|
* Variant of WC_SIMPLE used when TYPESET reserved word found.
|
||||||
|
* - data contains the number of string arguments (plus command)
|
||||||
|
* - followed by strings
|
||||||
|
* - followed by number of assignments
|
||||||
|
* - followed by assignments if non-zero number.
|
||||||
|
*
|
||||||
* WC_SUBSH
|
* WC_SUBSH
|
||||||
* - data unused
|
* - data unused
|
||||||
* - followed by list
|
* - followed by list
|
||||||
|
@ -257,6 +271,7 @@ parse_context_save(struct parse_stack *ps, int toplevel)
|
||||||
ps->incasepat = incasepat;
|
ps->incasepat = incasepat;
|
||||||
ps->isnewlin = isnewlin;
|
ps->isnewlin = isnewlin;
|
||||||
ps->infor = infor;
|
ps->infor = infor;
|
||||||
|
ps->intypeset = intypeset;
|
||||||
|
|
||||||
ps->hdocs = hdocs;
|
ps->hdocs = hdocs;
|
||||||
ps->eclen = eclen;
|
ps->eclen = eclen;
|
||||||
|
@ -290,6 +305,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
|
||||||
incasepat = ps->incasepat;
|
incasepat = ps->incasepat;
|
||||||
isnewlin = ps->isnewlin;
|
isnewlin = ps->isnewlin;
|
||||||
infor = ps->infor;
|
infor = ps->infor;
|
||||||
|
intypeset = ps->intypeset;
|
||||||
|
|
||||||
hdocs = ps->hdocs;
|
hdocs = ps->hdocs;
|
||||||
eclen = ps->eclen;
|
eclen = ps->eclen;
|
||||||
|
@ -430,7 +446,7 @@ init_parse_status(void)
|
||||||
* between the two it's a bit ambiguous. We clear them when
|
* between the two it's a bit ambiguous. We clear them when
|
||||||
* using the lexical analyser for strings as well as here.
|
* using the lexical analyser for strings as well as here.
|
||||||
*/
|
*/
|
||||||
incasepat = incond = inredir = infor = 0;
|
incasepat = incond = inredir = infor = intypeset = 0;
|
||||||
incmdpos = 1;
|
incmdpos = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -992,6 +1008,7 @@ par_cmd(int *cmplx, int zsh_construct)
|
||||||
incmdpos = 1;
|
incmdpos = 1;
|
||||||
incasepat = 0;
|
incasepat = 0;
|
||||||
incond = 0;
|
incond = 0;
|
||||||
|
intypeset = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1709,7 +1726,8 @@ static int
|
||||||
par_simple(int *cmplx, int nr)
|
par_simple(int *cmplx, int nr)
|
||||||
{
|
{
|
||||||
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
|
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
|
||||||
int c = *cmplx, nrediradd, assignments = 0;
|
int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
|
||||||
|
wordcode postassigns = 0;
|
||||||
|
|
||||||
r = ecused;
|
r = ecused;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -1717,31 +1735,32 @@ par_simple(int *cmplx, int nr)
|
||||||
*cmplx = c = 1;
|
*cmplx = c = 1;
|
||||||
nocorrect = 1;
|
nocorrect = 1;
|
||||||
} else if (tok == ENVSTRING) {
|
} else if (tok == ENVSTRING) {
|
||||||
char *p, *name, *str;
|
char *ptr, *name, *str;
|
||||||
|
|
||||||
name = tokstr;
|
name = tokstr;
|
||||||
for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+';
|
for (ptr = tokstr;
|
||||||
p++);
|
*ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
|
||||||
if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p);
|
ptr++);
|
||||||
if (*p == '+') {
|
if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
|
||||||
*p++ = '\0';
|
if (*ptr == '+') {
|
||||||
|
*ptr++ = '\0';
|
||||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
|
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
|
||||||
} else
|
} else
|
||||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
|
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
|
||||||
|
|
||||||
if (*p == '=') {
|
if (*ptr == '=') {
|
||||||
*p = '\0';
|
*ptr = '\0';
|
||||||
str = p + 1;
|
str = ptr + 1;
|
||||||
} else
|
} else
|
||||||
equalsplit(tokstr, &str);
|
equalsplit(tokstr, &str);
|
||||||
for (p = str; *p; p++) {
|
for (ptr = str; *ptr; ptr++) {
|
||||||
/*
|
/*
|
||||||
* We can't treat this as "simple" if it contains
|
* We can't treat this as "simple" if it contains
|
||||||
* expansions that require process subsitution, since then
|
* expansions that require process subsitution, since then
|
||||||
* we need process handling.
|
* we need process handling.
|
||||||
*/
|
*/
|
||||||
if (p[1] == Inpar &&
|
if (ptr[1] == Inpar &&
|
||||||
(*p == Equals || *p == Inang || *p == OutangProc)) {
|
(*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) {
|
||||||
*cmplx = 1;
|
*cmplx = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1786,14 +1805,18 @@ par_simple(int *cmplx, int nr)
|
||||||
p = ecadd(WCB_SIMPLE(0));
|
p = ecadd(WCB_SIMPLE(0));
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (tok == STRING) {
|
if (tok == STRING || tok == TYPESET) {
|
||||||
int redir_var = 0;
|
int redir_var = 0;
|
||||||
|
|
||||||
*cmplx = 1;
|
*cmplx = 1;
|
||||||
incmdpos = 0;
|
incmdpos = 0;
|
||||||
|
|
||||||
|
if (tok == TYPESET)
|
||||||
|
intypeset = is_typeset = 1;
|
||||||
|
|
||||||
if (!isset(IGNOREBRACES) && *tokstr == Inbrace)
|
if (!isset(IGNOREBRACES) && *tokstr == Inbrace)
|
||||||
{
|
{
|
||||||
|
/* Look for redirs of the form {var}>file etc. */
|
||||||
char *eptr = tokstr + strlen(tokstr) - 1;
|
char *eptr = tokstr + strlen(tokstr) - 1;
|
||||||
char *ptr = eptr;
|
char *ptr = eptr;
|
||||||
|
|
||||||
|
@ -1824,8 +1847,21 @@ par_simple(int *cmplx, int nr)
|
||||||
|
|
||||||
if (!redir_var)
|
if (!redir_var)
|
||||||
{
|
{
|
||||||
ecstr(tokstr);
|
if (postassigns) {
|
||||||
argc++;
|
/*
|
||||||
|
* We're in the variable part of a typeset,
|
||||||
|
* but this doesn't have an assignment.
|
||||||
|
* We'll parse it as if it does, but mark
|
||||||
|
* it specially with WC_ASSIGN_INC.
|
||||||
|
*/
|
||||||
|
postassigns++;
|
||||||
|
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
|
||||||
|
ecstr(tokstr);
|
||||||
|
ecstr(""); /* TBD can possibly optimise out */
|
||||||
|
} else {
|
||||||
|
ecstr(tokstr);
|
||||||
|
argc++;
|
||||||
|
}
|
||||||
zshlex();
|
zshlex();
|
||||||
}
|
}
|
||||||
} else if (IS_REDIROP(tok)) {
|
} else if (IS_REDIROP(tok)) {
|
||||||
|
@ -1833,6 +1869,50 @@ par_simple(int *cmplx, int nr)
|
||||||
nrediradd = par_redir(&r, NULL);
|
nrediradd = par_redir(&r, NULL);
|
||||||
p += nrediradd;
|
p += nrediradd;
|
||||||
sr += nrediradd;
|
sr += nrediradd;
|
||||||
|
} else if (tok == ENVSTRING) {
|
||||||
|
char *ptr, *name, *str;
|
||||||
|
|
||||||
|
if (!postassigns++)
|
||||||
|
ppost = ecadd(0);
|
||||||
|
|
||||||
|
name = tokstr;
|
||||||
|
for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
|
||||||
|
ptr++);
|
||||||
|
if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
|
||||||
|
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
|
||||||
|
|
||||||
|
if (*ptr == '=') {
|
||||||
|
*ptr = '\0';
|
||||||
|
str = ptr + 1;
|
||||||
|
} else
|
||||||
|
equalsplit(tokstr, &str);
|
||||||
|
ecstr(name);
|
||||||
|
ecstr(str);
|
||||||
|
zshlex();
|
||||||
|
} else if (tok == ENVARRAY) {
|
||||||
|
int n, parr;
|
||||||
|
|
||||||
|
if (!postassigns++)
|
||||||
|
ppost = ecadd(0);
|
||||||
|
|
||||||
|
parr = ecadd(0);
|
||||||
|
ecstr(tokstr);
|
||||||
|
cmdpush(CS_ARRAY);
|
||||||
|
/*
|
||||||
|
* Careful here: this must be the typeset case,
|
||||||
|
* but we need to tell the lexer not to look
|
||||||
|
* for assignments until we've finished the
|
||||||
|
* present one.
|
||||||
|
*/
|
||||||
|
intypeset = 0;
|
||||||
|
zshlex();
|
||||||
|
n = par_nl_wordlist();
|
||||||
|
ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n);
|
||||||
|
cmdpop();
|
||||||
|
intypeset = 1;
|
||||||
|
if (tok != OUTPAR)
|
||||||
|
YYERROR(oecused);
|
||||||
|
zshlex();
|
||||||
} else if (tok == INOUTPAR) {
|
} else if (tok == INOUTPAR) {
|
||||||
zlong oldlineno = lineno;
|
zlong oldlineno = lineno;
|
||||||
int onp, so, oecssub = ecssub;
|
int onp, so, oecssub = ecssub;
|
||||||
|
@ -1841,7 +1921,7 @@ par_simple(int *cmplx, int nr)
|
||||||
if (!isset(MULTIFUNCDEF) && argc > 1)
|
if (!isset(MULTIFUNCDEF) && argc > 1)
|
||||||
YYERROR(oecused);
|
YYERROR(oecused);
|
||||||
/* Error if preceding assignments */
|
/* Error if preceding assignments */
|
||||||
if (assignments)
|
if (assignments || postassigns)
|
||||||
YYERROR(oecused);
|
YYERROR(oecused);
|
||||||
|
|
||||||
*cmplx = c;
|
*cmplx = c;
|
||||||
|
@ -1947,9 +2027,18 @@ par_simple(int *cmplx, int nr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
incmdpos = 1;
|
incmdpos = 1;
|
||||||
|
intypeset = 0;
|
||||||
|
|
||||||
if (!isfunc)
|
if (!isfunc) {
|
||||||
ecbuf[p] = WCB_SIMPLE(argc);
|
if (is_typeset) {
|
||||||
|
ecbuf[p] = WCB_TYPESET(argc);
|
||||||
|
if (postassigns)
|
||||||
|
ecbuf[ppost] = postassigns;
|
||||||
|
else
|
||||||
|
ecadd(0);
|
||||||
|
} else
|
||||||
|
ecbuf[p] = WCB_SIMPLE(argc);
|
||||||
|
}
|
||||||
|
|
||||||
return sr + 1;
|
return sr + 1;
|
||||||
}
|
}
|
||||||
|
|
57
Src/text.c
57
Src/text.c
|
@ -155,6 +155,46 @@ taddlist(Estate state, int num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* add an assignment */
|
||||||
|
|
||||||
|
static void
|
||||||
|
taddassign(wordcode code, Estate state, int typeset)
|
||||||
|
{
|
||||||
|
/* name */
|
||||||
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||||
|
/* value... maybe */
|
||||||
|
if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) {
|
||||||
|
if (typeset) {
|
||||||
|
/* dummy assignment --- just var name */
|
||||||
|
(void)ecgetstr(state, EC_NODUP, NULL);
|
||||||
|
taddchr(' ');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
taddchr('+');
|
||||||
|
}
|
||||||
|
taddchr('=');
|
||||||
|
if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
|
||||||
|
taddchr('(');
|
||||||
|
taddlist(state, WC_ASSIGN_NUM(code));
|
||||||
|
taddstr(") ");
|
||||||
|
} else {
|
||||||
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||||
|
taddchr(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add a number of assignments from typeset */
|
||||||
|
|
||||||
|
/**/
|
||||||
|
static void
|
||||||
|
taddassignlist(Estate state, wordcode count)
|
||||||
|
{
|
||||||
|
while (count--) {
|
||||||
|
wordcode code = *state->pc++;
|
||||||
|
taddassign(code, state, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* add a newline, or something equivalent, to the text buffer */
|
/* add a newline, or something equivalent, to the text buffer */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
@ -439,22 +479,17 @@ gettext2(Estate state)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WC_ASSIGN:
|
case WC_ASSIGN:
|
||||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
taddassign(code, state, 0);
|
||||||
if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+');
|
|
||||||
taddchr('=');
|
|
||||||
if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
|
|
||||||
taddchr('(');
|
|
||||||
taddlist(state, WC_ASSIGN_NUM(code));
|
|
||||||
taddstr(") ");
|
|
||||||
} else {
|
|
||||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
||||||
taddchr(' ');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case WC_SIMPLE:
|
case WC_SIMPLE:
|
||||||
taddlist(state, WC_SIMPLE_ARGC(code));
|
taddlist(state, WC_SIMPLE_ARGC(code));
|
||||||
stack = 1;
|
stack = 1;
|
||||||
break;
|
break;
|
||||||
|
case WC_TYPESET:
|
||||||
|
taddlist(state, WC_TYPESET_ARGC(code));
|
||||||
|
taddassignlist(state, *state->pc++);
|
||||||
|
stack = 1;
|
||||||
|
break;
|
||||||
case WC_SUBSH:
|
case WC_SUBSH:
|
||||||
if (!s) {
|
if (!s) {
|
||||||
taddstr("(");
|
taddstr("(");
|
||||||
|
|
87
Src/zsh.h
87
Src/zsh.h
|
@ -336,7 +336,8 @@ enum lextok {
|
||||||
THEN, /* then */
|
THEN, /* then */
|
||||||
TIME, /* time */ /* 60 */
|
TIME, /* time */ /* 60 */
|
||||||
UNTIL, /* until */
|
UNTIL, /* until */
|
||||||
WHILE /* while */
|
WHILE, /* while */
|
||||||
|
TYPESET /* typeset or similar */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Redirection types. If you modify this, you may also have to modify *
|
/* Redirection types. If you modify this, you may also have to modify *
|
||||||
|
@ -671,14 +672,6 @@ struct multio {
|
||||||
int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */
|
int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* structure for foo=bar assignments */
|
|
||||||
|
|
||||||
struct asgment {
|
|
||||||
struct asgment *next;
|
|
||||||
char *name;
|
|
||||||
char *value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* lvalue for variable assignment/expansion */
|
/* lvalue for variable assignment/expansion */
|
||||||
|
|
||||||
struct value {
|
struct value {
|
||||||
|
@ -789,23 +782,24 @@ struct eccstr {
|
||||||
#define WC_REDIR 4
|
#define WC_REDIR 4
|
||||||
#define WC_ASSIGN 5
|
#define WC_ASSIGN 5
|
||||||
#define WC_SIMPLE 6
|
#define WC_SIMPLE 6
|
||||||
#define WC_SUBSH 7
|
#define WC_TYPESET 7
|
||||||
#define WC_CURSH 8
|
#define WC_SUBSH 8
|
||||||
#define WC_TIMED 9
|
#define WC_CURSH 9
|
||||||
#define WC_FUNCDEF 10
|
#define WC_TIMED 10
|
||||||
#define WC_FOR 11
|
#define WC_FUNCDEF 11
|
||||||
#define WC_SELECT 12
|
#define WC_FOR 12
|
||||||
#define WC_WHILE 13
|
#define WC_SELECT 13
|
||||||
#define WC_REPEAT 14
|
#define WC_WHILE 14
|
||||||
#define WC_CASE 15
|
#define WC_REPEAT 15
|
||||||
#define WC_IF 16
|
#define WC_CASE 16
|
||||||
#define WC_COND 17
|
#define WC_IF 17
|
||||||
#define WC_ARITH 18
|
#define WC_COND 18
|
||||||
#define WC_AUTOFN 19
|
#define WC_ARITH 19
|
||||||
#define WC_TRY 20
|
#define WC_AUTOFN 20
|
||||||
|
#define WC_TRY 21
|
||||||
|
|
||||||
/* increment as necessary */
|
/* increment as necessary */
|
||||||
#define WC_COUNT 21
|
#define WC_COUNT 22
|
||||||
|
|
||||||
#define WCB_END() wc_bld(WC_END, 0)
|
#define WCB_END() wc_bld(WC_END, 0)
|
||||||
|
|
||||||
|
@ -849,6 +843,12 @@ struct eccstr {
|
||||||
#define WC_ASSIGN_SCALAR 0
|
#define WC_ASSIGN_SCALAR 0
|
||||||
#define WC_ASSIGN_ARRAY 1
|
#define WC_ASSIGN_ARRAY 1
|
||||||
#define WC_ASSIGN_NEW 0
|
#define WC_ASSIGN_NEW 0
|
||||||
|
/*
|
||||||
|
* In normal assignment, this indicate += to append.
|
||||||
|
* In assignment following a typeset, where that's not allowed,
|
||||||
|
* we overload this to indicate a variable without an
|
||||||
|
* assignment.
|
||||||
|
*/
|
||||||
#define WC_ASSIGN_INC 1
|
#define WC_ASSIGN_INC 1
|
||||||
#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2)
|
#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2)
|
||||||
#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2)))
|
#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2)))
|
||||||
|
@ -856,6 +856,9 @@ struct eccstr {
|
||||||
#define WC_SIMPLE_ARGC(C) wc_data(C)
|
#define WC_SIMPLE_ARGC(C) wc_data(C)
|
||||||
#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N))
|
#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N))
|
||||||
|
|
||||||
|
#define WC_TYPESET_ARGC(C) wc_data(C)
|
||||||
|
#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N))
|
||||||
|
|
||||||
#define WC_SUBSH_SKIP(C) wc_data(C)
|
#define WC_SUBSH_SKIP(C) wc_data(C)
|
||||||
#define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O))
|
#define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O))
|
||||||
|
|
||||||
|
@ -1140,6 +1143,34 @@ struct alias {
|
||||||
/* is this an alias for suffix handling? */
|
/* is this an alias for suffix handling? */
|
||||||
#define ALIAS_SUFFIX (1<<2)
|
#define ALIAS_SUFFIX (1<<2)
|
||||||
|
|
||||||
|
/* structure for foo=bar assignments */
|
||||||
|
|
||||||
|
struct asgment {
|
||||||
|
struct linknode node;
|
||||||
|
char *name;
|
||||||
|
int is_array;
|
||||||
|
union {
|
||||||
|
char *scalar;
|
||||||
|
LinkList array;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment is array?
|
||||||
|
*/
|
||||||
|
#define ASG_ARRAYP(asg) ((asg)->is_array)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment has value?
|
||||||
|
* We need to arrange for each of the values
|
||||||
|
* to be the same type or the compiler will
|
||||||
|
* get fed up.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ASG_VALUEP(asg) (ASG_ARRAYP(asg) ? \
|
||||||
|
((asg)->value.array != (LinkList)0) : \
|
||||||
|
((asg)->value.scalar != (char *)0))
|
||||||
|
|
||||||
/* node in command path hash table (cmdnamtab) */
|
/* node in command path hash table (cmdnamtab) */
|
||||||
|
|
||||||
struct cmdnam {
|
struct cmdnam {
|
||||||
|
@ -1268,6 +1299,7 @@ struct options {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef int (*HandlerFunc) _((char *, char **, Options, int));
|
typedef int (*HandlerFunc) _((char *, char **, Options, int));
|
||||||
|
typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int));
|
||||||
#define NULLBINCMD ((HandlerFunc) 0)
|
#define NULLBINCMD ((HandlerFunc) 0)
|
||||||
|
|
||||||
struct builtin {
|
struct builtin {
|
||||||
|
@ -1311,6 +1343,12 @@ struct builtin {
|
||||||
* does not terminate options.
|
* does not terminate options.
|
||||||
*/
|
*/
|
||||||
#define BINF_HANDLES_OPTS (1<<18)
|
#define BINF_HANDLES_OPTS (1<<18)
|
||||||
|
/*
|
||||||
|
* Handles the assignement interface. The argv list actually contains
|
||||||
|
* two nested litsts, the first of normal arguments, and the second of
|
||||||
|
* assignment structures.
|
||||||
|
*/
|
||||||
|
#define BINF_ASSIGN (1<<19)
|
||||||
|
|
||||||
struct module {
|
struct module {
|
||||||
struct hashnode node;
|
struct hashnode node;
|
||||||
|
@ -2779,6 +2817,7 @@ struct parse_stack {
|
||||||
int incasepat;
|
int incasepat;
|
||||||
int isnewlin;
|
int isnewlin;
|
||||||
int infor;
|
int infor;
|
||||||
|
int intypeset;
|
||||||
|
|
||||||
int eclen, ecused, ecnpats;
|
int eclen, ecused, ecnpats;
|
||||||
Wordcode ecbuf;
|
Wordcode ecbuf;
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
|
|
||||||
|
mkdir typeset.tmp && cd typeset.tmp
|
||||||
|
|
||||||
setopt noglob
|
setopt noglob
|
||||||
|
|
||||||
scalar=scalar
|
scalar=scalar
|
||||||
|
@ -231,7 +233,7 @@
|
||||||
|
|
||||||
typeset -T THIS will not work
|
typeset -T THIS will not work
|
||||||
1:Tied array syntax
|
1:Tied array syntax
|
||||||
?(eval):typeset:1: -T requires names of scalar and array
|
?(eval):typeset:1: too many arguments for -T
|
||||||
|
|
||||||
local array[2]=x
|
local array[2]=x
|
||||||
1:Illegal local array element assignment
|
1:Illegal local array element assignment
|
||||||
|
@ -508,3 +510,144 @@
|
||||||
>a2=(three four)
|
>a2=(three four)
|
||||||
>typeset -r r1=yes
|
>typeset -r r1=yes
|
||||||
>typeset -r r2=no
|
>typeset -r r2=no
|
||||||
|
|
||||||
|
one=hidden two=hidden three=hidden four=hidden five=hidden
|
||||||
|
fn() {
|
||||||
|
local bleugh="four=vier"
|
||||||
|
typeset -R10 one=eins two=(zwei dio) three $bleugh five=(cinq cinque)
|
||||||
|
three=drei
|
||||||
|
print -l $one $two $three $four $five
|
||||||
|
}
|
||||||
|
fn
|
||||||
|
print -l $one $two $three $four $five
|
||||||
|
0:typeset reserved word interface: basic
|
||||||
|
> eins
|
||||||
|
>zwei
|
||||||
|
>dio
|
||||||
|
> drei
|
||||||
|
> vier
|
||||||
|
>cinq
|
||||||
|
>cinque
|
||||||
|
>hidden
|
||||||
|
>hidden
|
||||||
|
>hidden
|
||||||
|
>hidden
|
||||||
|
>hidden
|
||||||
|
|
||||||
|
(
|
||||||
|
setopt glob
|
||||||
|
mkdir -p arrayglob
|
||||||
|
touch arrayglob/{one,two,three,four,five,six,seven}
|
||||||
|
fn() {
|
||||||
|
typeset array=(arrayglob/[tf]*)
|
||||||
|
print -l ${array:t}
|
||||||
|
#
|
||||||
|
typeset {first,second,third}=the_same_value array=(
|
||||||
|
extends
|
||||||
|
over
|
||||||
|
multiple
|
||||||
|
lines
|
||||||
|
)
|
||||||
|
print -l $first $second $third "$array"
|
||||||
|
#
|
||||||
|
integer i=$(echo 1 + 2 + 3 + 4)
|
||||||
|
print $i
|
||||||
|
#
|
||||||
|
# only noted by accident this was broken..
|
||||||
|
# we need to turn off special recognition
|
||||||
|
# of assignments within assignments...
|
||||||
|
typeset careful=( i=1 j=2 k=3 )
|
||||||
|
print -l $careful
|
||||||
|
}
|
||||||
|
fn
|
||||||
|
)
|
||||||
|
0:typeset reserved word, more complicated cases
|
||||||
|
>five
|
||||||
|
>four
|
||||||
|
>three
|
||||||
|
>two
|
||||||
|
>the_same_value
|
||||||
|
>the_same_value
|
||||||
|
>the_same_value
|
||||||
|
>extends over multiple lines
|
||||||
|
>10
|
||||||
|
>i=1
|
||||||
|
>j=2
|
||||||
|
>k=3
|
||||||
|
|
||||||
|
(
|
||||||
|
# reserved word is recognised at parsing.
|
||||||
|
# yes, this is documented.
|
||||||
|
# anyway, that means we need to
|
||||||
|
# re-eval the function...
|
||||||
|
fn='
|
||||||
|
fn() {
|
||||||
|
typeset foo=`echo one word=two`
|
||||||
|
print $foo
|
||||||
|
print $word
|
||||||
|
}
|
||||||
|
'
|
||||||
|
print reserved
|
||||||
|
eval $fn; fn
|
||||||
|
print builtin
|
||||||
|
disable -r typeset
|
||||||
|
eval $fn; fn
|
||||||
|
enable -r typeset
|
||||||
|
disable typeset
|
||||||
|
print reserved
|
||||||
|
eval $fn; fn
|
||||||
|
)
|
||||||
|
0:reserved word and builtin interfaces
|
||||||
|
>reserved
|
||||||
|
>one word=two
|
||||||
|
>
|
||||||
|
>builtin
|
||||||
|
>one
|
||||||
|
>two
|
||||||
|
>reserved
|
||||||
|
>one word=two
|
||||||
|
>
|
||||||
|
|
||||||
|
fn() {
|
||||||
|
emulate -L zsh
|
||||||
|
setopt typeset_silent
|
||||||
|
local k
|
||||||
|
typeset -A hash=(k1 v1 k2 v2)
|
||||||
|
typeset foo=word array=(more than one word)
|
||||||
|
for k in ${(ko)hash}; do
|
||||||
|
print $k $hash[$k]
|
||||||
|
done
|
||||||
|
print -l $foo $array
|
||||||
|
typeset -A hash
|
||||||
|
typeset foo array
|
||||||
|
for k in ${(ko)hash}; do
|
||||||
|
print $k $hash[$k]
|
||||||
|
done
|
||||||
|
print -l $foo $array
|
||||||
|
typeset hash=(k3 v3 k4 v4) array=(odd number here)
|
||||||
|
for k in ${(ko)hash}; do
|
||||||
|
print $k $hash[$k]
|
||||||
|
done
|
||||||
|
print -l $array
|
||||||
|
}
|
||||||
|
fn
|
||||||
|
0:typeset preserves existing variable types
|
||||||
|
>k1 v1
|
||||||
|
>k2 v2
|
||||||
|
>word
|
||||||
|
>more
|
||||||
|
>than
|
||||||
|
>one
|
||||||
|
>word
|
||||||
|
>k1 v1
|
||||||
|
>k2 v2
|
||||||
|
>word
|
||||||
|
>more
|
||||||
|
>than
|
||||||
|
>one
|
||||||
|
>word
|
||||||
|
>k3 v3
|
||||||
|
>k4 v4
|
||||||
|
>odd
|
||||||
|
>number
|
||||||
|
>here
|
||||||
|
|
|
@ -199,5 +199,5 @@
|
||||||
?+zsh_directory_name:4> [[ d == n ]]
|
?+zsh_directory_name:4> [[ d == n ]]
|
||||||
?+zsh_directory_name:12> [[ <parent>/very_long_directory_name == (#b)(*)/very_long_directory_name ]]
|
?+zsh_directory_name:12> [[ <parent>/very_long_directory_name == (#b)(*)/very_long_directory_name ]]
|
||||||
?+zsh_directory_name:14> return 0
|
?+zsh_directory_name:14> return 0
|
||||||
?+fn:7> local 'd=~[<parent>:l]'
|
?+fn:7> local d='~[<parent>:l]'
|
||||||
?+fn:8> print '~[<parent>:l]'
|
?+fn:8> print '~[<parent>:l]'
|
||||||
|
|
|
@ -570,6 +570,15 @@
|
||||||
>unset
|
>unset
|
||||||
>globassign
|
>globassign
|
||||||
|
|
||||||
|
# This test is now somewhat artificial as
|
||||||
|
# KSH_TYPESET only applies to the builtin
|
||||||
|
# interface. Tests to the more standard
|
||||||
|
# reserved word interface appear elsewhere.
|
||||||
|
(
|
||||||
|
# reserved words are handled during parsing,
|
||||||
|
# hence eval...
|
||||||
|
disable -r typeset
|
||||||
|
eval '
|
||||||
setopt kshtypeset
|
setopt kshtypeset
|
||||||
ktvars=(ktv1 ktv2)
|
ktvars=(ktv1 ktv2)
|
||||||
typeset ktfoo=`echo arg1 arg2` $ktvars
|
typeset ktfoo=`echo arg1 arg2` $ktvars
|
||||||
|
@ -580,6 +589,8 @@
|
||||||
print $noktfoo
|
print $noktfoo
|
||||||
print $+noktarg1 $+noktarg2
|
print $+noktarg1 $+noktarg2
|
||||||
unset ktfoo ktv1 ktv2 noktfoo noktarg2
|
unset ktfoo ktv1 ktv2 noktfoo noktarg2
|
||||||
|
'
|
||||||
|
)
|
||||||
0:KSH_TYPESET option
|
0:KSH_TYPESET option
|
||||||
>1 1 0
|
>1 1 0
|
||||||
>arg1 arg2
|
>arg1 arg2
|
||||||
|
|
Loading…
Reference in New Issue