From 3a94b076a02304c681288f2b07f8be31e7f4da9c Mon Sep 17 00:00:00 2001 From: tg Date: Fri, 22 Jun 2007 23:34:42 +0000 Subject: [PATCH] implement bash-style array initialisation, as requested by many still experimental --- check.t | 24 ++++++++++++++++++++++-- lex.c | 28 ++++++++++++++++++++++++++-- mksh.1 | 15 +++++++++++++-- sh.h | 6 ++++-- syn.c | 40 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 104 insertions(+), 9 deletions(-) diff --git a/check.t b/check.t index 4d87fa1..6459eff 100644 --- a/check.t +++ b/check.t @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.115 2007/06/21 16:04:45 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.116 2007/06/22 23:34:40 tg Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ @@ -7,7 +7,7 @@ # http://www.research.att.com/~gsf/public/ifs.sh expected-stdout: - @(#)MIRBSD KSH R29 2007/06/21 + @(#)MIRBSD KSH R29 2007/06/22 description: Check version of shell. category: pdksh @@ -4009,3 +4009,23 @@ expected-stdout: integer='typeset -i' local=typeset --- +name: arrays-1 +description: + Check if Korn Shell arrays work as expected +stdin: + v="c d" + set -A foo -- a \$v "$v" '$v' b + echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|" +expected-stdout: + 5|a|$v|c d|$v|b| +--- +name: arrays-2 +description: + Check if bash-style arrays work as expected +stdin: + v="c d" + foo=(a \$v "$v" '$v' b) + echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|" +expected-stdout: + 5|a|$v|c d|$v|b| +--- diff --git a/lex.c b/lex.c index b8926a9..2fcbc29 100644 --- a/lex.c +++ b/lex.c @@ -2,7 +2,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.35 2007/06/16 15:02:56 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.36 2007/06/22 23:34:40 tg Exp $"); /* Structure to keep track of the lexing state and the various pieces of info * needed for each particular state. */ @@ -37,6 +37,12 @@ struct lex_state { } u_sbquote; Lex_state *base; /* used to point to next state block */ + + /* =(...) */ + struct sletarray_info { + int nparen; /* count open parentheses */ +#define ls_sletarray ls_info.u_sletarray + } u_sletarray; } ls_info; }; @@ -121,6 +127,9 @@ yylex(int cf) *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ state = SLETPAREN; statep->ls_sletparen.nparen = 0; + } else if (cf&LETARRAY) { + state = SLETARRAY; + statep->ls_sletarray.nparen = 0; } else { /* normal lexing */ state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; while ((c = getsc()) == ' ' || c == '\t') @@ -535,6 +544,17 @@ yylex(int cf) ++statep->ls_sletparen.nparen; goto Sbase2; + case SLETARRAY: /* LETARRAY: =( ... ) */ + if (c == '('/*)*/) + ++statep->ls_sletarray.nparen; + else if (c == /*(*/')') + if (statep->ls_sletarray.nparen-- == 0) { + c = 0; + goto Done; + } + *wp++ = CHAR, *wp++ = c; + break; + case SHEREDELIM: /* <<,<<- delimiter */ /* XXX chuck this state (and the next) - use * the existing states ($ and \`..` should be @@ -607,6 +627,9 @@ yylex(int cf) /* XXX figure out what is missing */ yyerror("no closing quote\n"); + if (state == SLETARRAY && statep->ls_sletarray.nparen != -1) + yyerror("syntax error: ')' missing\n"); + /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; @@ -680,7 +703,8 @@ yylex(int cf) *wp++ = EOS; /* terminate word */ yylval.cp = Xclose(ws, wp); - if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ + if (state == SWORD || state == SLETPAREN || + state == SLETARRAY) /* ONEWORD? */ return LWORD; ungetsc(c); /* unget terminator */ diff --git a/mksh.1 b/mksh.1 index 1a0178e..450befa 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,7 +1,7 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.87 2007/06/17 00:50:08 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.88 2007/06/22 23:34:41 tg Exp $ .\" $OpenBSD: ksh.1,v 1.120 2007/05/31 20:47:44 otto Exp $ .\" -.Dd June 17, 2007 +.Dd June 22, 2007 .Dt MKSH 1 .Os MirBSD .Sh NAME @@ -3208,6 +3208,17 @@ is used, the array is reset (i.e. emptied) first; if .Ic +A is used, the first N elements are set (where N is the number of arguments); the rest are left untouched. +.Pp +An alternative syntax for the command +.Ic set -A foo -- a b c +which is compatible to +.Tn GNU +.Nm bash +and also supported by +.At +.Nm ksh93 +is: +.Ic foo=(a b c) .It Fl a \*(Ba Ic allexport All new parameters are created with the export attribute. .It Fl b \*(Ba Ic notify diff --git a/sh.h b/sh.h index 3d55d2e..dba80df 100644 --- a/sh.h +++ b/sh.h @@ -8,8 +8,8 @@ /* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */ /* $OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $ */ -#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.149 2007/06/21 16:04:46 tg Exp $" -#define MKSH_VERSION "R29 2007/06/21" +#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.150 2007/06/22 23:34:41 tg Exp $" +#define MKSH_VERSION "R29 2007/06/22" #if HAVE_SYS_PARAM_H #include @@ -1112,6 +1112,7 @@ struct source { #define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */ #define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */ #define STBRACE 12 /* parsing ${..[#%]..} */ +#define SLETARRAY 13 /* inside =( ), just copy */ typedef union { int i; @@ -1162,6 +1163,7 @@ typedef union { #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ #define LQCHAR BIT(10) /* source string contains QCHAR */ #define HEREDOC BIT(11) /* parsing a here document */ +#define LETARRAY BIT(12) /* copy expression inside =( ) */ #define HERES 10 /* max << in line */ diff --git a/syn.c b/syn.c index ad4e369..77cebd1 100644 --- a/syn.c +++ b/syn.c @@ -2,7 +2,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.14 2007/06/06 23:28:17 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.15 2007/06/22 23:34:42 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -248,6 +248,9 @@ get_command(int cf) ACCEPT; goto Subshell; } + if ((XPsize(args) == 0 || Flag(FKEYWORD)) && + XPsize(vars) == 1 && is_wdvarassign(yylval.cp)) + goto is_wdarrassign; /* Must be a function */ if (iopn != 0 || XPsize(args) != 1 || XPsize(vars) != 0) @@ -257,6 +260,41 @@ get_command(int cf) musthave(')', 0); t = function_body(XPptrv(args)[0], false); goto Leave; + is_wdarrassign: + { + static const char set_cmd0[] = { + CHAR, 'e', CHAR, 'v', + CHAR, 'a', CHAR, 'l', EOS + }; + static const char set_cmd1[] = { + CHAR, 's', CHAR, 'e', + CHAR, 't', CHAR, ' ', + CHAR, '-', CHAR, 'A', EOS + }; + static const char set_cmd2[] = { + CHAR, '-', CHAR, '-', EOS + }; + char *tcp; + XPfree(vars); + XPinit(vars, 16); + /* + * we know (or rather hope) that yylval.cp + * contains a string "varname=" + */ + tcp = wdcopy(yylval.cp, ATEMP); + tcp[wdscan(tcp, EOS) - tcp - 3] = EOS; + /* now make an array assignment command */ + t = newtp(TCOM); + t->lineno = source->line; + ACCEPT; + XPput(args, wdcopy(set_cmd0, ATEMP)); + XPput(args, wdcopy(set_cmd1, ATEMP)); + XPput(args, tcp); + XPput(args, wdcopy(set_cmd2, ATEMP)); + musthave(LWORD,LETARRAY); + XPput(args, yylval.cp); + break; + } default: goto Leave;