aboutsummaryrefslogtreecommitdiffstats
path: root/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'subst.c')
-rw-r--r--subst.c784
1 files changed, 553 insertions, 231 deletions
diff --git a/subst.c b/subst.c
index 89c4d7b..145e937 100644
--- a/subst.c
+++ b/subst.c
@@ -4,7 +4,7 @@
/* ``Have a little faith, there's magic in the night. You ain't a
beauty, but, hey, you're alright.'' */
-/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -42,6 +42,7 @@
#include "bashintl.h"
#include "shell.h"
+#include "parser.h"
#include "flags.h"
#include "jobs.h"
#include "execute_cmd.h"
@@ -51,6 +52,7 @@
#include "mailcheck.h"
#include "shmbutil.h"
+#include "typemax.h"
#include "builtins/getopt.h"
#include "builtins/common.h"
@@ -107,7 +109,7 @@ extern int errno;
/* Evaluates to 1 if C is one of the shell's special parameters for which an
indirect variable reference may be made. */
#define VALID_INDIR_PARAM(c) \
- ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*')
+ ((posixly_correct == 0 && (c) == '#') || (posixly_correct == 0 && (c) == '?') || (c) == '@' || (c) == '*')
/* Evaluates to 1 if C is one of the OP characters that follows the parameter
in ${parameter[:]OPword}. */
@@ -244,10 +246,8 @@ static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int));
#endif
static char *remove_pattern __P((char *, char *, int));
-static int match_pattern_char __P((char *, char *));
static int match_upattern __P((char *, char *, int, char **, char **));
#if defined (HANDLE_MULTIBYTE)
-static int match_pattern_wchar __P((wchar_t *, wchar_t *));
static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **));
#endif
static int match_pattern __P((char *, char *, int, char **, char **));
@@ -259,7 +259,7 @@ static char *parameter_list_remove_pattern __P((int, char *, int, int));
#ifdef ARRAY_VARS
static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int));
#endif
-static char *parameter_brace_remove_pattern __P((char *, char *, char *, int, int));
+static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int));
static char *process_substitute __P((char *, int));
@@ -273,7 +273,7 @@ static int valid_brace_expansion_word __P((char *, int));
static int chk_atstar __P((char *, int, int *, int *));
static int chk_arithsub __P((const char *, int));
-static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int));
+static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *));
static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *));
static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *));
static void parameter_brace_expand_error __P((char *, char *));
@@ -283,16 +283,18 @@ static intmax_t parameter_brace_expand_length __P((char *));
static char *skiparith __P((char *, int));
static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *));
-static int get_var_and_type __P((char *, char *, int, SHELL_VAR **, char **));
+static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **));
static char *mb_substring __P((char *, int, int));
-static char *parameter_brace_substring __P((char *, char *, char *, int));
+static char *parameter_brace_substring __P((char *, char *, int, char *, int, int));
+
+static int shouldexp_replacement __P((char *));
static char *pos_params_pat_subst __P((char *, char *, char *, int));
-static char *parameter_brace_patsub __P((char *, char *, char *, int));
+static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int));
static char *pos_params_casemod __P((char *, char *, int, int));
-static char *parameter_brace_casemod __P((char *, char *, int, char *, int));
+static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int));
static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *));
static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int));
@@ -608,7 +610,6 @@ unquoted_substring (substr, string)
{
case '\\':
sindex++;
-
if (string[sindex])
ADVANCE_CHAR (string, slen, sindex);
break;
@@ -785,6 +786,7 @@ string_extract_double_quoted (string, sindex, stripdq)
/* Process a character that was quoted by a backslash. */
if (pass_next)
{
+ /* XXX - take another look at this in light of Interp 221 */
/* Posix.2 sez:
``The backslash shall retain its special meaning as an escape
@@ -858,7 +860,7 @@ add_one_character:
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, 0);
else
- ret = extract_dollar_brace_string (string, &si, 1, 0);
+ ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0);
temp[j++] = '$';
temp[j++] = string[i + 1];
@@ -960,7 +962,7 @@ skip_double_quoted (string, slen, sind)
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, SX_NOALLOC);
else
- ret = extract_dollar_brace_string (string, &si, 1, SX_NOALLOC);
+ ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
i = si + 1;
continue;
@@ -1264,17 +1266,15 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
continue;
}
-#if 0
- /* Process a nested command substitution, but only if we're parsing a
- command substitution. XXX - for bash-4.2 */
+ /* Process a nested command substitution, but only if we're parsing an
+ arithmetic substitution. */
if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
{
si = i + 2;
- t = extract_command_subst (string, &si, flags);
+ t = extract_command_subst (string, &si, flags|SX_NOALLOC);
i = si + 1;
continue;
}
-#endif
/* Process a nested OPENER. */
if (STREQN (string + i, opener, len_opener))
@@ -1370,7 +1370,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
{
register int i, c;
size_t slen;
- int pass_character, nesting_level, si;
+ int pass_character, nesting_level, si, dolbrace_state;
char *result, *t;
DECLARE_MBSTATE;
@@ -1378,6 +1378,12 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
nesting_level = 1;
slen = strlen (string + *sindex) + *sindex;
+ /* The handling of dolbrace_state needs to agree with the code in parse.y:
+ parse_matched_pair() */
+ dolbrace_state = 0;
+ if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+ dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM;
+
i = *sindex;
while (c = string[i])
{
@@ -1432,6 +1438,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
continue;
}
+#if 0
/* Pass the contents of single-quoted and double-quoted strings
through verbatim. */
if (c == '\'' || c == '"')
@@ -1442,9 +1449,50 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
/* skip_XXX_quoted leaves index one past close quote */
continue;
}
+#else /* XXX - bash-4.2 */
+ /* Pass the contents of double-quoted strings through verbatim. */
+ if (c == '"')
+ {
+ si = i + 1;
+ i = skip_double_quoted (string, slen, si);
+ /* skip_XXX_quoted leaves index one past close quote */
+ continue;
+ }
+
+ if (c == '\'')
+ {
+/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/
+ if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+ ADVANCE_CHAR (string, slen, i);
+ else
+ {
+ si = i + 1;
+ i = skip_single_quoted (string, slen, si);
+ }
+
+ continue;
+ }
+#endif
/* move past this character, which was not special. */
ADVANCE_CHAR (string, slen, i);
+
+ /* This logic must agree with parse.y:parse_matched_pair, since they
+ share the same defines. */
+ if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0)
+ dolbrace_state = DOLBRACE_OP;
+ else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0)
+ dolbrace_state = DOLBRACE_WORD;
}
if (c == 0 && nesting_level)
@@ -1645,7 +1693,7 @@ skip_to_delim (string, start, delims, flags)
{
int i, pass_next, backq, si, c, invert, skipquote, skipcmd;
size_t slen;
- char *temp;
+ char *temp, open[3];
DECLARE_MBSTATE;
slen = strlen (string + start) + start;
@@ -1728,6 +1776,25 @@ skip_to_delim (string, start, delims, flags)
continue;
}
#endif /* PROCESS_SUBSTITUTION */
+#if defined (EXTENDED_GLOB)
+ else if ((flags & SD_EXTGLOB) && extended_glob && string[i+1] == LPAREN && member (c, "?*+!@"))
+ {
+ si = i + 2;
+ if (string[si] == '\0')
+ CQ_RETURN(si);
+
+ open[0] = c;
+ open[1] = LPAREN;
+ open[2] = '\0';
+ temp = extract_delimited_string (string, &si, open, "(", ")", SX_NOALLOC); /* ) */
+
+ i = si;
+ if (string[i] == '\0') /* don't increment i past EOS in loop */
+ break;
+ i++;
+ continue;
+ }
+#endif
else if ((skipquote || invert) && (member (c, delims) == 0))
break;
else
@@ -2230,11 +2297,7 @@ string_list_dollar_at (list, quoted)
/* XXX -- why call quote_list if ifs == 0? we can get away without doing
it now that quote_escapes quotes spaces */
-#if 0
- tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0))
-#else
tlist = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE))
-#endif
? quote_list (list)
: list_quote_escapes (list);
@@ -2661,8 +2724,8 @@ do_assignment_internal (word, expand)
const WORD_DESC *word;
int expand;
{
- int offset, tlen, appendop, assign_list, aflags, retval;
- char *name, *value;
+ int offset, appendop, assign_list, aflags, retval;
+ char *name, *value, *temp;
SHELL_VAR *entry;
#if defined (ARRAY_VARS)
char *t;
@@ -2681,8 +2744,6 @@ do_assignment_internal (word, expand)
if (name[offset] == '=')
{
- char *temp;
-
if (name[offset - 1] == '+')
{
appendop = 1;
@@ -2691,7 +2752,6 @@ do_assignment_internal (word, expand)
name[offset] = 0; /* might need this set later */
temp = name + offset + 1;
- tlen = STRLEN (temp);
#if defined (ARRAY_VARS)
if (expand && (word->flags & W_COMPASSIGN))
@@ -2792,8 +2852,9 @@ do_assignment (string)
}
int
-do_word_assignment (word)
+do_word_assignment (word, flags)
WORD_DESC *word;
+ int flags;
{
return do_assignment_internal (word, 1);
}
@@ -3721,6 +3782,7 @@ mb_getcharlens (string, len)
#define RP_LONG_RIGHT 3
#define RP_SHORT_RIGHT 4
+/* Returns its first argument if nothing matched; new memory otherwise */
static char *
remove_upattern (param, pattern, op)
char *param, *pattern;
@@ -3789,10 +3851,11 @@ remove_upattern (param, pattern, op)
break;
}
- return (savestring (param)); /* no match, return original string */
+ return (param); /* no match, return original string */
}
#if defined (HANDLE_MULTIBYTE)
+/* Returns its first argument if nothing matched; new memory otherwise */
static wchar_t *
remove_wpattern (wparam, wstrlen, wpattern, op)
wchar_t *wparam;
@@ -3858,7 +3921,7 @@ remove_wpattern (wparam, wstrlen, wpattern, op)
break;
}
- return (wcsdup (wparam)); /* no match, return original string */
+ return (wparam); /* no match, return original string */
}
#endif /* HANDLE_MULTIBYTE */
@@ -3867,6 +3930,8 @@ remove_pattern (param, pattern, op)
char *param, *pattern;
int op;
{
+ char *xret;
+
if (param == NULL)
return (param);
if (*param == '\0' || pattern == NULL || *pattern == '\0') /* minor optimization */
@@ -3879,18 +3944,29 @@ remove_pattern (param, pattern, op)
size_t n;
wchar_t *wparam, *wpattern;
mbstate_t ps;
- char *xret;
n = xdupmbstowcs (&wpattern, NULL, pattern);
if (n == (size_t)-1)
- return (remove_upattern (param, pattern, op));
+ {
+ xret = remove_upattern (param, pattern, op);
+ return ((xret == param) ? savestring (param) : xret);
+ }
n = xdupmbstowcs (&wparam, NULL, param);
if (n == (size_t)-1)
{
free (wpattern);
- return (remove_upattern (param, pattern, op));
+ xret = remove_upattern (param, pattern, op);
+ return ((xret == param) ? savestring (param) : xret);
}
oret = ret = remove_wpattern (wparam, n, wpattern, op);
+ /* Don't bother to convert wparam back to multibyte string if nothing
+ matched; just return copy of original string */
+ if (ret == wparam)
+ {
+ free (wparam);
+ free (wpattern);
+ return (savestring (param));
+ }
free (wparam);
free (wpattern);
@@ -3905,36 +3981,9 @@ remove_pattern (param, pattern, op)
}
else
#endif
- return (remove_upattern (param, pattern, op));
-}
-
-/* Return 1 of the first character of STRING could match the first
- character of pattern PAT. Used to avoid n2 calls to strmatch(). */
-static int
-match_pattern_char (pat, string)
- char *pat, *string;
-{
- char c;
-
- if (*string == 0)
- return (0);
-
- switch (c = *pat++)
{
- default:
- return (*string == c);
- case '\\':
- return (*string == *pat);
- case '?':
- return (*pat == LPAREN ? 1 : (*string != '\0'));
- case '*':
- return (1);
- case '+':
- case '!':
- case '@':
- return (*pat == LPAREN ? 1 : (*string == c));
- case '[':
- return (*string != '\0');
+ xret = remove_upattern (param, pattern, op);
+ return ((xret == param) ? savestring (param) : xret);
}
}
@@ -3950,9 +3999,10 @@ match_upattern (string, pat, mtype, sp, ep)
int mtype;
char **sp, **ep;
{
- int c, len;
+ int c, len, mlen;
register char *p, *p1, *npat;
char *end;
+ int n1;
/* If the pattern doesn't match anywhere in the string, go ahead and
short-circuit right away. A minor optimization, saves a bunch of
@@ -3986,6 +4036,8 @@ match_upattern (string, pat, mtype, sp, ep)
len = STRLEN (string);
end = string + len;
+ mlen = umatchlen (pat, len);
+
switch (mtype)
{
case MATCH_ANY:
@@ -3993,7 +4045,22 @@ match_upattern (string, pat, mtype, sp, ep)
{
if (match_pattern_char (pat, p))
{
+#if 0
for (p1 = end; p1 >= p; p1--)
+#else
+ p1 = (mlen == -1) ? end : p + mlen;
+ /* p1 - p = length of portion of string to be considered
+ p = current position in string
+ mlen = number of characters consumed by match (-1 for entire string)
+ end = end of string
+ we want to break immediately if the potential match len
+ is greater than the number of characters remaining in the
+ string
+ */
+ if (p1 > end)
+ break;
+ for ( ; p1 >= p; p1--)
+#endif
{
c = *p1; *p1 = '\0';
if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
@@ -4004,6 +4071,11 @@ match_upattern (string, pat, mtype, sp, ep)
return 1;
}
*p1 = c;
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
}
}
@@ -4014,7 +4086,11 @@ match_upattern (string, pat, mtype, sp, ep)
if (match_pattern_char (pat, string) == 0)
return (0);
+#if 0
for (p = end; p >= string; p--)
+#else
+ for (p = (mlen == -1) ? end : string + mlen; p >= string; p--)
+#endif
{
c = *p; *p = '\0';
if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0)
@@ -4025,12 +4101,21 @@ match_upattern (string, pat, mtype, sp, ep)
return 1;
}
*p = c;
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
return (0);
case MATCH_END:
+#if 0
for (p = string; p <= end; p++)
+#else
+ for (p = end - ((mlen == -1) ? len : mlen); p <= end; p++)
+#endif
{
if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
{
@@ -4038,7 +4123,11 @@ match_upattern (string, pat, mtype, sp, ep)
*ep = end;
return 1;
}
-
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
return (0);
@@ -4048,36 +4137,6 @@ match_upattern (string, pat, mtype, sp, ep)
}
#if defined (HANDLE_MULTIBYTE)
-/* Return 1 of the first character of WSTRING could match the first
- character of pattern WPAT. Wide character version. */
-static int
-match_pattern_wchar (wpat, wstring)
- wchar_t *wpat, *wstring;
-{
- wchar_t wc;
-
- if (*wstring == 0)
- return (0);
-
- switch (wc = *wpat++)
- {
- default:
- return (*wstring == wc);
- case L'\\':
- return (*wstring == *wpat);
- case L'?':
- return (*wpat == LPAREN ? 1 : (*wstring != L'\0'));
- case L'*':
- return (1);
- case L'+':
- case L'!':
- case L'@':
- return (*wpat == LPAREN ? 1 : (*wstring == wc));
- case L'[':
- return (*wstring != L'\0');
- }
-}
-
/* Match WPAT anywhere in WSTRING and return the match boundaries.
This returns 1 in case of a successful match, 0 otherwise. Wide
character version. */
@@ -4091,11 +4150,14 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
char **sp, **ep;
{
wchar_t wc, *wp, *nwpat, *wp1;
- int len;
-#if 0
- size_t n, n1; /* Apple's gcc seems to miscompile this badly */
-#else
- int n, n1;
+ size_t len;
+ int mlen;
+ int n, n1, n2, simple;
+
+ simple = (wpat[0] != L'\\' && wpat[0] != L'*' && wpat[0] != L'?' && wpat[0] != L'[');
+#if defined (EXTENDED_GLOB)
+ if (extended_glob)
+ simple |= (wpat[1] != L'(' || (wpat[0] != L'*' && wpat[0] != L'?' && wpat[0] != L'+' && wpat[0] != L'!' && wpat[0] != L'@')); /*)*/
#endif
/* If the pattern doesn't match anywhere in the string, go ahead and
@@ -4104,8 +4166,6 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
characters) if the match is unsuccessful. To preserve the semantics
of the substring matches below, we make sure that the pattern has
`*' as first and last character, making a new pattern if necessary. */
- /* XXX - check this later if I ever implement `**' with special meaning,
- since this will potentially result in `**' at the beginning or end */
len = wcslen (wpat);
if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*')
{
@@ -4127,14 +4187,30 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
if (len == FNM_NOMATCH)
return (0);
+ mlen = wmatchlen (wpat, wstrlen);
+
+/* itrace("wmatchlen (%ls) -> %d", wpat, mlen); */
switch (mtype)
{
case MATCH_ANY:
for (n = 0; n <= wstrlen; n++)
{
- if (match_pattern_wchar (wpat, wstring + n))
+#if 1
+ n2 = simple ? (*wpat == wstring[n]) : match_pattern_wchar (wpat, wstring + n);
+#else
+ n2 = match_pattern_wchar (wpat, wstring + n);
+#endif
+ if (n2)
{
+#if 0
for (n1 = wstrlen; n1 >= n; n1--)
+#else
+ n1 = (mlen == -1) ? wstrlen : n + mlen;
+ if (n1 > wstrlen)
+ break;
+
+ for ( ; n1 >= n; n1--)
+#endif
{
wc = wstring[n1]; wstring[n1] = L'\0';
if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
@@ -4145,6 +4221,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
return 1;
}
wstring[n1] = wc;
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
}
}
@@ -4155,7 +4236,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
if (match_pattern_wchar (wpat, wstring) == 0)
return (0);
+#if 0
for (n = wstrlen; n >= 0; n--)
+#else
+ for (n = (mlen == -1) ? wstrlen : mlen; n >= 0; n--)
+#endif
{
wc = wstring[n]; wstring[n] = L'\0';
if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0)
@@ -4166,12 +4251,21 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
return 1;
}
wstring[n] = wc;
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
return (0);
case MATCH_END:
+#if 0
for (n = 0; n <= wstrlen; n++)
+#else
+ for (n = wstrlen - ((mlen == -1) ? wstrlen : mlen); n <= wstrlen; n++)
+#endif
{
if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
{
@@ -4179,6 +4273,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
*ep = indices[wstrlen];
return 1;
}
+#if 1
+ /* If MLEN != -1, we have a fixed length pattern. */
+ if (mlen != -1)
+ break;
+#endif
}
return (0);
@@ -4199,6 +4298,7 @@ match_pattern (string, pat, mtype, sp, ep)
size_t n;
wchar_t *wstring, *wpat;
char **indices;
+ size_t slen, plen, mslen, mplen;
#endif
if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
@@ -4207,6 +4307,17 @@ match_pattern (string, pat, mtype, sp, ep)
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1)
{
+#if 0
+ slen = STRLEN (string);
+ mslen = MBSLEN (string);
+ plen = STRLEN (pat);
+ mplen = MBSLEN (pat);
+ if (slen == mslen && plen == mplen)
+#else
+ if (mbsmbchar (string) == 0 && mbsmbchar (pat) == 0)
+#endif
+ return (match_upattern (string, pat, mtype, sp, ep));
+
n = xdupmbstowcs (&wpat, NULL, pat);
if (n == (size_t)-1)
return (match_upattern (string, pat, mtype, sp, ep));
@@ -4382,9 +4493,11 @@ array_remove_pattern (var, pattern, patspec, varname, quoted)
#endif /* ARRAY_VARS */
static char *
-parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted)
- char *varname, *value, *patstr;
- int rtype, quoted;
+parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags)
+ char *varname, *value;
+ int ind;
+ char *patstr;
+ int rtype, quoted, flags;
{
int vtype, patspec, starsub;
char *temp1, *val, *pattern;
@@ -4395,7 +4508,7 @@ parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted)
this_command_name = varname;
- vtype = get_var_and_type (varname, value, quoted, &v, &val);
+ vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
if (vtype == -1)
return ((char *)NULL);
@@ -4549,6 +4662,15 @@ static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL;
static int nfifo;
static int fifo_list_size;
+char *
+copy_fifo_list (sizep)
+ int *sizep;
+{
+ if (sizep)
+ *sizep = 0;
+ return (char *)NULL;
+}
+
static void
add_fifo_list (pathname)
char *pathname;
@@ -4565,6 +4687,19 @@ add_fifo_list (pathname)
}
void
+unlink_fifo (i)
+ int i;
+{
+ if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1))
+ {
+ unlink (fifo_list[i].file);
+ free (fifo_list[i].file);
+ fifo_list[i].file = (char *)NULL;
+ fifo_list[i].proc = -1;
+ }
+}
+
+void
unlink_fifo_list ()
{
int saved, i, j;
@@ -4601,12 +4736,44 @@ unlink_fifo_list ()
nfifo = 0;
}
+/* Take LIST, which is a bitmap denoting active FIFOs in fifo_list
+ from some point in the past, and close all open FIFOs in fifo_list
+ that are not marked as active in LIST. If LIST is NULL, close
+ everything in fifo_list. LSIZE is the number of elements in LIST, in
+ case it's larger than fifo_list_size (size of fifo_list). */
+void
+close_new_fifos (list, lsize)
+ char *list;
+ int lsize;
+{
+ int i;
+
+ if (list == 0)
+ {
+ unlink_fifo_list ();
+ return;
+ }
+
+ for (i = 0; i < lsize; i++)
+ if (list[i] == 0 && i < fifo_list_size && fifo_list[i].proc != -1)
+ unlink_fifo (i);
+
+ for (i = lsize; i < fifo_list_size; i++)
+ unlink_fifo (i);
+}
+
int
fifos_pending ()
{
return nfifo;
}
+int
+num_fifos ()
+{
+ return nfifo;
+}
+
static char *
make_named_pipe ()
{
@@ -4633,11 +4800,30 @@ static char *dev_fd_list = (char *)NULL;
static int nfds;
static int totfds; /* The highest possible number of open files. */
+char *
+copy_fifo_list (sizep)
+ int *sizep;
+{
+ char *ret;
+
+ if (nfds == 0 || totfds == 0)
+ {
+ if (sizep)
+ *sizep = 0;
+ return (char *)NULL;
+ }
+
+ if (sizep)
+ *sizep = totfds;
+ ret = (char *)xmalloc (totfds);
+ return (memcpy (ret, dev_fd_list, totfds));
+}
+
static void
add_fifo_list (fd)
int fd;
{
- if (!dev_fd_list || fd >= totfds)
+ if (dev_fd_list == 0 || fd >= totfds)
{
int ofds;
@@ -4662,6 +4848,24 @@ fifos_pending ()
return 0; /* used for cleanup; not needed with /dev/fd */
}
+int
+num_fifos ()
+{
+ return nfds;
+}
+
+void
+unlink_fifo (fd)
+ int fd;
+{
+ if (dev_fd_list[fd])
+ {
+ close (fd);
+ dev_fd_list[fd] = 0;
+ nfds--;
+ }
+}
+
void
unlink_fifo_list ()
{
@@ -4671,16 +4875,37 @@ unlink_fifo_list ()
return;
for (i = 0; nfds && i < totfds; i++)
- if (dev_fd_list[i])
- {
- close (i);
- dev_fd_list[i] = 0;
- nfds--;
- }
+ unlink_fifo (i);
nfds = 0;
}
+/* Take LIST, which is a snapshot copy of dev_fd_list from some point in
+ the past, and close all open fds in dev_fd_list that are not marked
+ as open in LIST. If LIST is NULL, close everything in dev_fd_list.
+ LSIZE is the number of elements in LIST, in case it's larger than
+ totfds (size of dev_fd_list). */
+void
+close_new_fifos (list, lsize)
+ char *list;
+ int lsize;
+{
+ int i;
+
+ if (list == 0)
+ {
+ unlink_fifo_list ();
+ return;
+ }
+
+ for (i = 0; i < lsize; i++)
+ if (list[i] == 0 && i < totfds && dev_fd_list[i])
+ unlink_fifo (i);
+
+ for (i = lsize; i < totfds; i++)
+ unlink_fifo (i);
+}
+
#if defined (NOTDEF)
print_dev_fd_list ()
{
@@ -4786,7 +5011,7 @@ process_substitute (string, open_for_read_in_child)
reset_terminating_signals (); /* XXX */
free_pushed_string_input ();
/* Cancel traps, in trap.c. */
- restore_original_signals ();
+ restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */
setup_async_signals ();
subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB;
}
@@ -4922,10 +5147,6 @@ read_comsub (fd, quoted, rflag)
for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++)
skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL;
-#ifdef __CYGWIN__
- setmode (fd, O_TEXT); /* we don't want CR/LF, we want Unix-style */
-#endif
-
/* Read the output of the command through the pipe. This may need to be
changed to understand multibyte characters in the future. */
while (1)
@@ -5079,9 +5300,13 @@ command_substitute (string, quoted)
last_asynchronous_pid = old_async_pid;
if (pid == 0)
- /* Reset the signal handlers in the child, but don't free the
- trap strings. */
- reset_signal_handlers ();
+ {
+ /* Reset the signal handlers in the child, but don't free the
+ trap strings. Set a flag noting that we have to free the
+ trap strings if we run trap to change a signal disposition. */
+ reset_signal_handlers ();
+ subshell_environment |= SUBSHELL_RESETTRAP;
+ }
#if defined (JOB_CONTROL)
/* XXX DO THIS ONLY IN PARENT ? XXX */
@@ -5132,6 +5357,13 @@ command_substitute (string, quoted)
(fildes[0] != fileno (stderr)))
close (fildes[0]);
+#ifdef __CYGWIN__
+ /* Let stdio know the fd may have changed from text to binary mode, and
+ make sure to preserve stdout line buffering. */
+ freopen (NULL, "w", stdout);
+ sh_setlinebuf (stdout);
+#endif /* __CYGWIN__ */
+
/* The currently executing shell is not interactive. */
interactive = 0;
@@ -5211,11 +5443,7 @@ command_substitute (string, quoted)
pipline, so what we are concerned about is whether or not that
pipeline was started in the background. A pipeline started in
the background should never get the tty back here. */
-#if 0
- if (interactive && pipeline_pgrp != (pid_t)0 && pipeline_pgrp != last_asynchronous_pid)
-#else
if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0)
-#endif
give_terminal_to (pipeline_pgrp, 0);
#endif /* JOB_CONTROL */
@@ -5244,6 +5472,7 @@ array_length_reference (s)
char *akey;
char *t, c;
ARRAY *array;
+ HASH_TABLE *h;
SHELL_VAR *var;
var = array_variable_part (s, &t, &len);
@@ -5267,15 +5496,16 @@ array_length_reference (s)
v[*]. Return 0 for everything else. */
array = array_p (var) ? array_cell (var) : (ARRAY *)NULL;
+ h = assoc_p (var) ? assoc_cell (var) : (HASH_TABLE *)NULL;
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
{
if (assoc_p (var))
- return (assoc_num_elements (assoc_cell (var)));
+ return (h ? assoc_num_elements (h) : 0);
else if (array_p (var))
- return (array_num_elements (array));
+ return (array ? array_num_elements (array) : 0);
else
- return 1;
+ return (var_isset (var) ? 1 : 0);
}
if (assoc_p (var))
@@ -5393,20 +5623,25 @@ chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at)
the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that
NAME was found inside of a double-quoted expression. */
static WORD_DESC *
-parameter_brace_expand_word (name, var_is_special, quoted, pflags)
+parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp)
char *name;
int var_is_special, quoted, pflags;
+ arrayind_t *indp;
{
WORD_DESC *ret;
char *temp, *tt;
intmax_t arg_index;
SHELL_VAR *var;
int atype, rflags;
+ arrayind_t ind;
ret = 0;
temp = 0;
rflags = 0;
+ if (indp)
+ *indp = INTMAX_MIN;
+
/* Handle multiple digit arguments, as in ${11}. */
if (legal_number (name, &arg_index))
{
@@ -5433,11 +5668,16 @@ parameter_brace_expand_word (name, var_is_special, quoted, pflags)
#if defined (ARRAY_VARS)
else if (valid_array_reference (name))
{
- temp = array_value (name, quoted, &atype);
+ temp = array_value (name, quoted, 0, &atype, &ind);
if (atype == 0 && temp)
- temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
- ? quote_string (temp)
- : quote_escapes (temp);
+ {
+ temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
+ ? quote_string (temp)
+ : quote_escapes (temp);
+ rflags |= W_ARRAYIND;
+ if (indp)
+ *indp = ind;
+ }
else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
rflags |= W_HASQUOTEDNULL;
}
@@ -5488,7 +5728,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c
char *temp, *t;
WORD_DESC *w;
- w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND);
+ w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0);
t = w->word;
/* Have to dequote here if necessary */
if (t)
@@ -5505,7 +5745,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c
if (t == 0)
return (WORD_DESC *)NULL;
- w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0);
+ w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0);
free (t);
return w;
@@ -5589,9 +5829,11 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
else
#endif /* ARRAY_VARS */
bind_variable (name, t1, 0);
- free (t1);
- w->word = temp;
+ /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */
+ free (temp);
+
+ w->word = t1;
return w;
}
@@ -5638,34 +5880,6 @@ valid_length_expression (name)
legal_identifier (name + 1)); /* ${#PS1} */
}
-#if defined (HANDLE_MULTIBYTE)
-size_t
-mbstrlen (s)
- const char *s;
-{
- size_t clen, nc;
- mbstate_t mbs, mbsbak;
-
- nc = 0;
- memset (&mbs, 0, sizeof (mbs));
- mbsbak = mbs;
- while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0)
- {
- if (MB_INVALIDCH(clen))
- {
- clen = 1; /* assume single byte */
- mbs = mbsbak;
- }
-
- s += clen;
- nc++;
- mbsbak = mbs;
- }
- return nc;
-}
-#endif
-
-
/* Handle the parameter brace expansion that requires us to return the
length of a parameter. */
static intmax_t
@@ -5699,7 +5913,7 @@ parameter_brace_expand_length (name)
break;
case '!':
if (last_asynchronous_pid == NO_PID)
- t = (char *)NULL;
+ t = (char *)NULL; /* XXX - error if set -u set? */
else
t = itos (last_asynchronous_pid);
break;
@@ -5721,6 +5935,8 @@ parameter_brace_expand_length (name)
if (legal_number (name + 1, &arg_index)) /* ${#1} */
{
t = get_dollar_var_value (arg_index);
+ if (t == 0 && unbound_vars_is_error)
+ return INTMAX_MIN;
number = MB_STRLEN (t);
FREE (t);
}
@@ -5731,6 +5947,8 @@ parameter_brace_expand_length (name)
t = assoc_reference (assoc_cell (var), "0");
else
t = array_reference (array_cell (var), 0);
+ if (t == 0 && unbound_vars_is_error)
+ return INTMAX_MIN;
number = MB_STRLEN (t);
}
#endif
@@ -5744,7 +5962,7 @@ parameter_brace_expand_length (name)
if (list)
dispose_words (list);
- number = MB_STRLEN (t);
+ number = t ? MB_STRLEN (t) : 0;
FREE (t);
}
}
@@ -5903,7 +6121,7 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p)
free (temp1);
if (expok == 0)
return (0);
- if (*e2p < 0)
+ if ((vtype == VT_ARRAYVAR || vtype == VT_POSPARMS) && *e2p < 0)
{
internal_error (_("%s: substring expression < 0"), t);
return (0);
@@ -5915,7 +6133,17 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p)
if (vtype != VT_ARRAYVAR)
#endif
{
- *e2p += *e1p; /* want E2 chars starting at E1 */
+ if (*e2p < 0)
+ {
+ *e2p += len;
+ if (*e2p < 0 || *e2p < *e1p)
+ {
+ internal_error (_("%s: substring expression < 0"), t);
+ return (0);
+ }
+ }
+ else
+ *e2p += *e1p; /* want E2 chars starting at E1 */
if (*e2p > len)
*e2p = len;
}
@@ -5929,13 +6157,18 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p)
/* Return the type of variable specified by VARNAME (simple variable,
positional param, or array variable). Also return the value specified
by VARNAME (value of a variable or a reference to an array element).
+ QUOTED is the standard description of quoting state, using Q_* defines.
+ FLAGS is currently a set of flags to pass to array_value. If IND is
+ non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is
+ passed to array_value so the array index is not computed again.
If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL
characters in the value are quoted with CTLESC and takes appropriate
steps. For convenience, *VALP is set to the dequoted VALUE. */
static int
-get_var_and_type (varname, value, quoted, varp, valp)
+get_var_and_type (varname, value, ind, quoted, flags, varp, valp)
char *varname, *value;
- int quoted;
+ arrayind_t ind;
+ int quoted, flags;
SHELL_VAR **varp;
char **valp;
{
@@ -5944,6 +6177,7 @@ get_var_and_type (varname, value, quoted, varp, valp)
#if defined (ARRAY_VARS)
SHELL_VAR *v;
#endif
+ arrayind_t lind;
/* This sets vtype to VT_VARIABLE or VT_POSPARMS */
vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0';
@@ -5955,6 +6189,9 @@ get_var_and_type (varname, value, quoted, varp, valp)
if (valid_array_reference (varname))
{
v = array_variable_part (varname, &temp, (int *)0);
+ /* If we want to signal array_value to use an already-computed index,
+ set LIND to that index */
+ lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0;
if (v && (array_p (v) || assoc_p (v)))
{ /* [ */
if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')
@@ -5968,7 +6205,7 @@ get_var_and_type (varname, value, quoted, varp, valp)
else
{
vtype = VT_ARRAYMEMBER;
- *valp = array_value (varname, 1, (int *)NULL);
+ *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind);
}
*varp = v;
}
@@ -5985,7 +6222,7 @@ get_var_and_type (varname, value, quoted, varp, valp)
{
vtype = VT_ARRAYMEMBER;
*varp = v;
- *valp = array_value (varname, 1, (int *)NULL);
+ *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind);
}
}
else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v)))
@@ -6052,9 +6289,11 @@ mb_substring (string, s, e)
VARNAME. If VARNAME is an array variable, use the array elements. */
static char *
-parameter_brace_substring (varname, value, substr, quoted)
- char *varname, *value, *substr;
- int quoted;
+parameter_brace_substring (varname, value, ind, substr, quoted, flags)
+ char *varname, *value;
+ int ind;
+ char *substr;
+ int quoted, flags;
{
intmax_t e1, e2;
int vtype, r, starsub;
@@ -6067,7 +6306,7 @@ parameter_brace_substring (varname, value, substr, quoted)
oname = this_command_name;
this_command_name = varname;
- vtype = get_var_and_type (varname, value, quoted, &v, &val);
+ vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
if (vtype == -1)
{
this_command_name = oname;
@@ -6139,26 +6378,52 @@ parameter_brace_substring (varname, value, substr, quoted)
/* */
/****************************************************************/
+static int
+shouldexp_replacement (s)
+ char *s;
+{
+ register char *p;
+
+ for (p = s; p && *p; p++)
+ {
+ if (*p == '\\')
+ p++;
+ else if (*p == '&')
+ return 1;
+ }
+ return 0;
+}
+
char *
pat_subst (string, pat, rep, mflags)
char *string, *pat, *rep;
int mflags;
{
- char *ret, *s, *e, *str;
- int rsize, rptr, l, replen, mtype;
+ char *ret, *s, *e, *str, *rstr, *mstr;
+ int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen;
+
+ if (string == 0)
+ return (savestring (""));
mtype = mflags & MATCH_TYPEMASK;
+#if 0 /* bash-4.2 ? */
+ rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0;
+#else
+ rxpand = 0;
+#endif
+
/* Special cases:
* 1. A null pattern with mtype == MATCH_BEG means to prefix STRING
* with REP and return the result.
* 2. A null pattern with mtype == MATCH_END means to append REP to
* STRING and return the result.
+ * These don't understand or process `&' in the replacement string.
*/
if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
{
replen = STRLEN (rep);
- l = strlen (string);
+ l = STRLEN (string);
ret = (char *)xmalloc (replen + l + 2);
if (replen == 0)
strcpy (ret, string);
@@ -6183,7 +6448,25 @@ pat_subst (string, pat, rep, mflags)
if (match_pattern (str, pat, mtype, &s, &e) == 0)
break;
l = s - str;
- RESIZE_MALLOCED_BUFFER (ret, rptr, (l + replen), rsize, 64);
+
+ if (rxpand)
+ {
+ int x;
+ mlen = e - s;
+ mstr = xmalloc (mlen + 1);
+ for (x = 0; x < mlen; x++)
+ mstr[x] = s[x];
+ mstr[mlen] = '\0';
+ rstr = strcreplace (rep, '&', mstr, 0);
+ rslen = strlen (rstr);
+ }
+ else
+ {
+ rstr = rep;
+ rslen = replen;
+ }
+
+ RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64);
/* OK, now copy the leading unmatched portion of the string (from
str to s) to ret starting at rptr (the current offset). Then copy
@@ -6196,11 +6479,14 @@ pat_subst (string, pat, rep, mflags)
}
if (replen)
{
- strncpy (ret + rptr, rep, replen);
- rptr += replen;
+ strncpy (ret + rptr, rstr, rslen);
+ rptr += rslen;
}
str = e; /* e == end of match */
+ if (rstr != rep)
+ free (rstr);
+
if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY)
break;
@@ -6215,7 +6501,7 @@ pat_subst (string, pat, rep, mflags)
}
/* Now copy the unmatched portion of the input string */
- if (*str)
+ if (str && *str)
{
RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64);
strcpy (ret + rptr, str);
@@ -6276,9 +6562,11 @@ pos_params_pat_subst (string, pat, rep, mflags)
and the string to substitute. QUOTED is a flags word containing
the type of quoting currently in effect. */
static char *
-parameter_brace_patsub (varname, value, patsub, quoted)
- char *varname, *value, *patsub;
- int quoted;
+parameter_brace_patsub (varname, value, ind, patsub, quoted, flags)
+ char *varname, *value;
+ int ind;
+ char *patsub;
+ int quoted, flags;
{
int vtype, mflags, starsub, delim;
char *val, *temp, *pat, *rep, *p, *lpatsub, *tt;
@@ -6289,7 +6577,7 @@ parameter_brace_patsub (varname, value, patsub, quoted)
this_command_name = varname;
- vtype = get_var_and_type (varname, value, quoted, &v, &val);
+ vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
if (vtype == -1)
return ((char *)NULL);
@@ -6464,11 +6752,11 @@ pos_params_modcase (string, pat, modop, mflags)
to perform. QUOTED is a flags word containing the type of quoting
currently in effect. */
static char *
-parameter_brace_casemod (varname, value, modspec, patspec, quoted)
+parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags)
char *varname, *value;
- int modspec;
+ int ind, modspec;
char *patspec;
- int quoted;
+ int quoted, flags;
{
int vtype, starsub, modop, mflags, x;
char *val, *temp, *pat, *p, *lpat, *tt;
@@ -6479,7 +6767,7 @@ parameter_brace_casemod (varname, value, modspec, patspec, quoted)
this_command_name = varname;
- vtype = get_var_and_type (varname, value, quoted, &v, &val);
+ vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
if (vtype == -1)
return ((char *)NULL);
@@ -6630,6 +6918,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
WORD_DESC *tdesc, *ret;
int t_index, sindex, c, tflag, modspec;
intmax_t number;
+ arrayind_t ind;
temp = temp1 = value = (char *)NULL;
var_is_set = var_is_null = var_is_special = check_nullness = 0;
@@ -6656,18 +6945,13 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
ret = 0;
tflag = 0;
+ ind = INTMAX_MIN;
+
/* If the name really consists of a special variable, then make sure
that we have the entire name. We don't allow indirect references
to special variables except `#', `?', `@' and `*'. */
- if ((sindex == t_index &&
- (string[t_index] == '-' ||
- string[t_index] == '?' ||
- string[t_index] == '#')) ||
- (sindex == t_index - 1 && string[sindex] == '!' &&
- (string[t_index] == '#' ||
- string[t_index] == '?' ||
- string[t_index] == '@' ||
- string[t_index] == '*')))
+ if ((sindex == t_index && VALID_SPECIAL_LENGTH_PARAM (string[t_index])) ||
+ (sindex == t_index - 1 && string[sindex] == '!' && VALID_INDIR_PARAM (string[t_index])))
{
t_index++;
free (name);
@@ -6762,6 +7046,13 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
}
number = parameter_brace_expand_length (name);
+ if (number == INTMAX_MIN && unbound_vars_is_error)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ err_unboundvar (name+1);
+ free (name);
+ return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
+ }
free (name);
*indexp = sindex;
@@ -6862,7 +7153,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (want_indir)
tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at);
else
- tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2));
+ tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind);
if (tdesc)
{
@@ -6886,7 +7177,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
{
/* Extract the contents of the ${ ... } expansion
according to the Posix.2 rules. */
- value = extract_dollar_brace_string (string, &sindex, quoted, 0);
+ value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0);
if (string[sindex] == RBRACE)
sindex++;
else
@@ -6897,10 +7188,25 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
*indexp = sindex;
+ /* All the cases where an expansion can possibly generate an unbound
+ variable error. */
+ if (want_substring || want_patsub || want_casemod || c == '#' || c == '%' || c == RBRACE)
+ {
+ if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1]))
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ err_unboundvar (name);
+ FREE (value);
+ FREE (temp);
+ free (name);
+ return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
+ }
+ }
+
/* If this is a substring spec, process it and add the result. */
if (want_substring)
{
- temp1 = parameter_brace_substring (name, temp, value, quoted);
+ temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
FREE (name);
FREE (value);
FREE (temp);
@@ -6918,7 +7224,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
}
else if (want_patsub)
{
- temp1 = parameter_brace_patsub (name, temp, value, quoted);
+ temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
FREE (name);
FREE (value);
FREE (temp);
@@ -6939,7 +7245,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
#if defined (CASEMOD_EXPANSIONS)
else if (want_casemod)
{
- temp1 = parameter_brace_casemod (name, temp, modspec, value, quoted);
+ temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
FREE (name);
FREE (value);
FREE (temp);
@@ -6970,15 +7276,6 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
return &expand_wdesc_error;
case RBRACE:
- if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1]))
- {
- last_command_exit_value = EXECUTION_FAILURE;
- err_unboundvar (name);
- FREE (value);
- FREE (temp);
- free (name);
- return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
- }
break;
case '#': /* ${param#[#]pattern} */
@@ -6988,9 +7285,10 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
FREE (value);
break;
}
- temp1 = parameter_brace_remove_pattern (name, temp, value, c, quoted);
+ temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
free (temp);
free (value);
+ free (name);
ret = alloc_word_desc ();
ret->word = temp1;
@@ -7006,7 +7304,6 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
{
/* If the operator is `+', we don't want the value of the named
variable for anything, just the value of the right hand side. */
-
if (c == '+')
{
/* XXX -- if we're double-quoted and the named variable is "$@",
@@ -7020,6 +7317,11 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
FREE (temp);
if (value)
{
+ /* From Posix discussion on austin-group list. Issue 221
+ requires that backslashes escaping `}' inside
+ double-quoted ${...} be removed. */
+ if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+ quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c,
quoted,
quoted_dollar_atp,
@@ -7063,6 +7365,11 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (contains_dollar_at)
*contains_dollar_at = 0;
+ /* From Posix discussion on austin-group list. Issue 221 requires
+ that backslashes escaping `}' inside double-quoted ${...} be
+ removed. */
+ if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+ quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c, quoted,
quoted_dollar_atp,
contains_dollar_at);
@@ -7226,11 +7533,14 @@ param_expand (string, sindex, quoted, expanded_something,
is unset, the parameters are separated by ' '; if $IFS is
null, the parameters are concatenated. */
temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list) : string_list (list);
- temp1 = quote_string (temp);
- if (*temp == 0)
- tflag |= W_HASQUOTEDNULL;
- free (temp);
- temp = temp1;
+ if (temp)
+ {
+ temp1 = quote_string (temp);
+ if (*temp == 0)
+ tflag |= W_HASQUOTEDNULL;
+ free (temp);
+ temp = temp1;
+ }
}
else
{
@@ -7879,7 +8189,13 @@ add_string:
else
tflag = 0;
- if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
+ /* From Posix discussion on austin-group list: Backslash escaping
+ a } in ${...} is removed. Issue 0000221 */
+ if ((quoted & Q_DOLBRACE) && c == RBRACE)
+ {
+ SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
+ }
+ else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
{
SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size);
}
@@ -7998,7 +8314,7 @@ add_twochars:
if (list->next)
{
#if 0
- if (quoted_dollar_at && word->flags & W_NOSPLIT2)
+ if (quoted_dollar_at && (word->flags & W_NOSPLIT2))
temp = string_list_internal (quote_list (list), " ");
else
#endif
@@ -8038,7 +8354,7 @@ add_twochars:
/* We do not want to add quoted nulls to strings that are only
partially quoted; we can throw them away. */
- if (temp == 0 && quoted_state == PARTIALLY_QUOTED)
+ if (temp == 0 && quoted_state == PARTIALLY_QUOTED && (word->flags & (W_NOSPLIT|W_NOSPLIT2)))
continue;
add_quoted_string:
@@ -8876,7 +9192,7 @@ shell_expand_word_list (tlist, eflags)
if (tlist->word->flags & W_ASSIGNASSOC)
make_internal_declare (tlist->word->word, "-A");
- t = do_word_assignment (tlist->word);
+ t = do_word_assignment (tlist->word, 0);
if (t == 0)
{
last_command_exit_value = EXECUTION_FAILURE;
@@ -8976,7 +9292,7 @@ expand_word_list_internal (list, eflags)
for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
{
this_command_name = (char *)NULL; /* no arithmetic errors */
- tint = do_word_assignment (temp_list->word);
+ tint = do_word_assignment (temp_list->word, 0);
/* Variable assignment errors in non-interactive shells
running in Posix.2 mode cause the shell to exit. */
if (tint == 0)
@@ -9025,6 +9341,7 @@ expand_word_list_internal (list, eflags)
if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist)
{
sh_wassign_func_t *assign_func;
+ int is_special_builtin, is_builtin_or_func;
/* If the remainder of the words expand to nothing, Posix.2 requires
that the variable and environment assignments affect the shell's
@@ -9032,11 +9349,16 @@ expand_word_list_internal (list, eflags)
assign_func = new_list ? assign_in_env : do_word_assignment;
tempenv_assign_error = 0;
+ is_builtin_or_func = (new_list && new_list->word && (find_shell_builtin (new_list->word->word) || find_function (new_list->word->word)));
+ /* Posix says that special builtins exit if a variable assignment error
+ occurs in an assignment preceding it. */
+ is_special_builtin = (posixly_correct && new_list && new_list->word && find_special_builtin (new_list->word->word));
+
for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
{
this_command_name = (char *)NULL;
assigning_in_environment = (assign_func == assign_in_env);
- tint = (*assign_func) (temp_list->word);
+ tint = (*assign_func) (temp_list->word, is_builtin_or_func);
assigning_in_environment = 0;
/* Variable assignment errors in non-interactive shells running
in Posix.2 mode cause the shell to exit. */
@@ -9045,7 +9367,7 @@ expand_word_list_internal (list, eflags)
if (assign_func == do_word_assignment)
{
last_command_exit_value = EXECUTION_FAILURE;
- if (interactive_shell == 0 && posixly_correct)
+ if (interactive_shell == 0 && posixly_correct && is_special_builtin)
exp_jump_to_top_level (FORCE_EOF);
else
exp_jump_to_top_level (DISCARD);