AnMaster wrote: > Configuration Information [Automatically generated, do not change]: > Machine: x86_64 > OS: linux-gnu > Compiler: gcc > Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' > -DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-unknown-linux-gnu' > -DCONF_VENDOR='unknown' > -DLOCALEDIR='/home/arvid/local/shells/bash/4.0/share/locale' -DPACKAGE='bash' > -DSHELL -DHAVE_CONFIG_H -I. -I. -I./include -I./lib > -DSYS_BASHRC=/etc/bash/bashrc -DSYS_BASH_LOGOUT=/etc/bash/bash_logout > -DDEFAULT_PATH_VALUE=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin > -pipe -O1 -g > uname output: Linux tux.lan 2.6.27-gentoo-r8-1 #1 Sat Jan 31 04:55:36 CET 2009 > x86_64 AMD Sempron(tm) Processor 3300+ AuthenticAMD GNU/Linux > Machine Type: x86_64-unknown-linux-gnu > > Bash Version: 4.0 > Patch Level: 0 > Release Status: release > > Description: > It seems that associative array indexes are not treated in a consistent way in > bash 4.0. I was testing if associative arrays would work for what I needed > (the > strings I plan to use for indexes are be untrusted and can legally contain > anything except null-bytes, thus I was testing if it was robust with "strange" > data). See "Repeat-By:" below. > > So the issues are: > 1. How do you escape the ] in myarray["a]a"] so that bash handles it the same > way as foo="a]a"; myarray["$foo"]. > 2. While myarray["a]a"] errors out when assigning, reading with > ${myarray["a]a"]} doesn't error out, but doesn't return the correct result > either. > 3. I'm not sure what happened in myarray['a]=test2;#a']="def". Did it eval the > single-quoted string? Seems buggy anyway.
This patch should correct all of these associative array subscript parsing problems. Let me know how it works for you. Chet -- ``The lyf so short, the craft so long to lerne.'' - Chaucer Chet Ramey, ITS, CWRU c...@case.edu http://cnswww.cns.cwru.edu/~chet/
*** ../bash-4.0/parse.y 2009-01-08 08:29:12.000000000 -0500 --- parse.y 2009-02-25 17:25:56.000000000 -0500 *************** *** 2916,2919 **** --- 2919,2923 ---- #define P_COMMAND 0x08 /* parsing a command, so look for comments */ #define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */ + #define P_ARRAYSUB 0x20 /* parsing a [...] array subscript for assignment */ /* Lexical state while parsing a grouping construct or $(...). */ *************** *** 3130,3133 **** --- 3134,3139 ---- FREE (nestret); } + else if ((flags & P_ARRAYSUB) && (tflags & LEX_WASDOL) && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */ + goto parse_dollar_word; } /* Parse an old-style command substitution within double quotes as a *************** *** 3146,3149 **** --- 3152,3156 ---- /* check for $(), $[], or ${} inside quoted string. */ { + parse_dollar_word: if (open == ch) /* undo previous increment */ count--; *************** *** 4249,4253 **** (token_index == 0 && (parser_state&PST_COMPASSIGN)))) { ! ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0); if (ttok == &matched_pair_error) return -1; /* Bail immediately. */ --- 4256,4260 ---- (token_index == 0 && (parser_state&PST_COMPASSIGN)))) { ! ttok = parse_matched_pair (cd, '[', ']', &ttoklen, P_ARRAYSUB); if (ttok == &matched_pair_error) return -1; /* Bail immediately. */ *** ../bash-4.0/arrayfunc.c 2009-01-04 14:32:21.000000000 -0500 --- arrayfunc.c 2009-02-25 07:58:54.000000000 -0500 *************** *** 605,666 **** } ! /* This function assumes s[i] == '['; returns with s[ret] == ']' if ! an array subscript is correctly parsed. */ ! int ! skipsubscript (s, i) ! const char *s; ! int i; ! { ! int count, c; ! #if defined (HANDLE_MULTIBYTE) ! mbstate_t state, state_bak; ! size_t slength, mblength; ! #endif ! ! #if defined (HANDLE_MULTIBYTE) ! memset (&state, '\0', sizeof (mbstate_t)); ! slength = strlen (s + i); ! #endif ! ! count = 1; ! while (count) ! { ! /* Advance one (possibly multibyte) character in S starting at I. */ ! #if defined (HANDLE_MULTIBYTE) ! if (MB_CUR_MAX > 1) ! { ! state_bak = state; ! mblength = mbrlen (s + i, slength, &state); ! ! if (MB_INVALIDCH (mblength)) ! { ! state = state_bak; ! i++; ! slength--; ! } ! else if (MB_NULLWCH (mblength)) ! return i; ! else ! { ! i += mblength; ! slength -= mblength; ! } ! } ! else ! #endif ! ++i; ! ! c = s[i]; ! ! if (c == 0) ! break; ! else if (c == '[') ! count++; ! else if (c == ']') ! count--; ! } ! ! return i; ! } /* This function is called with SUB pointing to just after the beginning --- 605,609 ---- } ! /* skipsubscript moved to subst.c to use private functions. 2009/02/24. */ /* This function is called with SUB pointing to just after the beginning *** ../bash-4.0/subst.c 2009-01-28 14:34:12.000000000 -0500 --- subst.c 2009-02-25 09:18:33.000000000 -0500 *************** *** 223,226 **** --- 223,227 ---- static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int)); static char *extract_dollar_brace_string __P((char *, int *, int, int)); + static int skip_matched_pair __P((const char *, int, int, int, int)); static char *pos_params __P((char *, int, int, int)); *************** *** 1375,1378 **** --- 1376,1480 ---- #define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0) + /* This function assumes s[i] == open; returns with s[ret] == close; used to + parse array subscripts. FLAGS currently unused. */ + static int + skip_matched_pair (string, start, open, close, flags) + const char *string; + int start, open, close, flags; + { + int i, pass_next, backq, si, c, count; + size_t slen; + char *temp, *ss; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + no_longjmp_on_fatal_error = 1; + + i = start + 1; /* skip over leading bracket */ + count = 1; + pass_next = backq = 0; + ss = (char *)string; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backq = 1; + i++; + continue; + } + else if (c == open) + { + count++; + i++; + continue; + } + else if (c == close) + { + count--; + if (count == 0) + break; + i++; + continue; + } + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (ss, slen, ++i) + : skip_double_quoted (ss, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if (c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); + } + + #if defined (ARRAY_VARS) + int + skipsubscript (string, start) + const char *string; + int start; + { + return (skip_matched_pair (string, start, '[', ']', 0)); + } + #endif + /* Skip characters in STRING until we find a character in DELIMS, and return the index of that character. START is the index into string at which we