#define LPAREN '('
#define RPAREN ')'
+/* Evaluates to 1 if C is one of the shell's special parameters whose length
+ can be taken, but is also one of the special expansion characters. */
+#define VALID_SPECIAL_LENGTH_PARAM(c) \
+ ((c) == '-' || (c) == '?' || (c) == '#')
+
+/* 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) == '*')
+
+/* Evaluates to 1 if C is one of the OP characters that follows the parameter
+ in ${parameter[:]OPword}. */
+#define VALID_PARAM_EXPAND_CHAR(c) \
+ ((c) == '-' || (c) == '=' || (c) == '?' || (c) == '+')
+
/* Evaluates to 1 if this is one of the shell's special variables. */
#define SPECIAL_VAR(name, wi) \
- ((digit (*name) && all_digits (name)) || \
- (name[1] == '\0' && member (*name, "#-?$!@*")) || \
- (wi && name[2] == '\0' && member (name[1], "#?@*")))
+ ((isdigit (*name) && all_digits (name)) || \
+ (name[1] == '\0' && (sh_syntaxtab[*name] & CSPECVAR)) || \
+ (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1])))
/* Process ID of the last command executed within command substitution. */
pid_t last_command_subst_pid = NO_PID;
static WORD_LIST expand_word_error, expand_word_fatal;
static char expand_param_error, expand_param_fatal;
-static int doing_completion = 0;
-static int doing_prompt_expansion = 0;
+/* Tell the expansion functions to not longjmp back to top_level on fatal
+ errors. Enabled when doing completion and prompt string expansion. */
+static int no_longjmp_on_fatal_error = 0;
+
+/* Set by expand_word_unsplit; used to inhibit splitting and re-joining
+ $* on $IFS, primarily when doing assignment statements. */
+static int expand_no_split_dollar_star = 0;
/* Used to hold a list of variable assignments preceding a command. Global
so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a
SIGCHLD trap. */
WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL;
+
/* A WORD_LIST of words to be expanded by expand_word_list_internal,
without any leading variable assignments. */
static WORD_LIST *garglist = (WORD_LIST *)NULL;
{
if (*s == CTLESC)
{
- s++;
- continue;
+ s++;
+ continue;
}
l++;
if (*s == 0)
- break;
+ break;
}
r = result = xmalloc (2*len + 1); /* save room for quotes */
for (l = 0; l < len; s++)
{
if (*s == CTLESC)
- *r++ = *s++;
+ *r++ = *s++;
*r++ = *s;
l++;
if (*s == 0)
- break;
+ break;
}
*r = '\0';
return result;
The returned string will be run through expansion as if
it were double-quoted. */
if ((stripdq == 0 && c != '"') ||
- (stripdq && ((dquote && strchr (slashify_in_quotes, c)) || dquote == 0)))
+ (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0)))
temp[j++] = '\\';
temp[j++] = c;
pass_next = 0;
}
/* Add any character but a double quote to the quoted string we're
- accumulating. */
+ accumulating. */
if (c != '"')
{
temp[j++] = c;
char *string;
int sind;
{
- register int i;
+ register int c;
- for (i = sind; string[i] && string[i] != '\''; i++)
+ for (c = sind; string[c] && string[c] != '\''; c++)
;
- if (string[i])
- i++;
- return i;
+ if (string[c])
+ c++;
+ return c;
}
/* Just like string_extract, but doesn't hack backslashes or any of
return (extract_delimited_string (string, sindex, "$(", "(", ")"));
}
-/* Extract the $[ construct in STRING, and return a new string.
+/* Extract the $[ construct in STRING, and return a new string. (])
Start extracting at (SINDEX) as if we had just seen "$[".
Make (SINDEX) get the position of the matching "]". */
char *
char *string;
int *sindex;
{
- return (extract_delimited_string (string, sindex, "$[", "[", "]"));
+ return (extract_delimited_string (string, sindex, "$[", "[", "]")); /*]*/
}
#if defined (PROCESS_SUBSTITUTION)
c = string[i];
if (c == 0)
- break;
+ break;
if (pass_character) /* previous char was backslash */
{
continue;
}
-#if 0
- if (c == '\\' && delimiter == '"' &&
- (member (string[i], slashify_in_quotes)))
-#else
if (c == '\\')
-#endif
{
pass_character++;
i++;
/* Pass old-style command substitution through verbatim. */
if (c == '`')
- {
- si = i + 1;
- t = string_extract (string, &si, "`", 0);
- i = si + 1;
- FREE (t);
- continue;
- }
+ {
+ si = i + 1;
+ t = string_extract (string, &si, "`", 0);
+ i = si + 1;
+ FREE (t);
+ continue;
+ }
/* Pass single-quoted strings through verbatim. */
if (c == '\'')
- {
- si = i + 1;
- i = skip_single_quoted (string, si);
- continue;
- }
+ {
+ si = i + 1;
+ i = skip_single_quoted (string, si);
+ continue;
+ }
/* Pass embedded double-quoted strings through verbatim as well. */
if (c == '"')
- {
- si = i + 1;
- i = skip_double_quoted (string, si);
- continue;
- }
+ {
+ si = i + 1;
+ i = skip_double_quoted (string, si);
+ continue;
+ }
i++; /* move past this character, which was not special. */
}
#if 0
if (c == 0 && nesting_level)
#else
- if (c == 0 && nesting_level && doing_completion == 0)
+ if (c == 0 && nesting_level && no_longjmp_on_fatal_error == 0)
#endif
{
report_error ("bad substitution: no `%s' in %s", closer, string);
+ last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (DISCARD);
}
}
}
- if (c == 0 && nesting_level && doing_completion == 0)
+ if (c == 0 && nesting_level && no_longjmp_on_fatal_error == 0)
{
report_error ("bad substitution: no ending `}' in %s", string);
+ last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (DISCARD);
}
#if defined (READLINE)
/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is
an unclosed quoted string), or if the character at EINDEX is quoted
- by a backslash. DOING_COMPLETION is used to flag that the various
+ by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various
single and double-quoted string parsing functions should not return an
error if there are unclosed quotes or braces. */
-#define CQ_RETURN(x) do { doing_completion = 0; return (x); } while (0)
+#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0)
int
char_is_quoted (string, eindex)
{
int i, pass_next, quoted;
- doing_completion = 1;
+ no_longjmp_on_fatal_error = 1;
for (i = pass_next = quoted = 0; i <= eindex; i++)
{
if (pass_next)
int i, pass_next, backq, si;
char *temp;
- doing_completion = 1;
+ no_longjmp_on_fatal_error = 1;
for (i = start, pass_next = backq = 0; string[i]; i++)
{
if (pass_next)
continue;
}
else if (member (string[i], delims))
- break;
+ break;
}
CQ_RETURN(i);
}
ret = (WORD_LIST *)NULL;
- for (i = 0; member (string[i], d) && whitespace(string[i]); i++)
+ for (i = 0; member (string[i], d) && (whitespace(string[i]) || string[i] == '\n'); i++)
;
if (string[i] == '\0')
return (ret);
/* If we have a non-whitespace delimiter character, use it to make a
separate field. This is just about what $IFS splitting does and
is closer to the behavior of the shell parser. */
- if (ts == te && member(string[ts], d2))
+ if (ts == te && d2 && member (string[ts], d2))
{
te = ts + 1;
- while (member(string[te], d2))
+ while (member (string[te], d2))
te++;
}
cw = nw;
/* If the cursor is at whitespace just before word start, set the
- sentinel word to the current word. */
+ sentinel word to the current word. */
if (cwp && cw == -1 && sentinel == ts-1)
cw = nw;
/* If the cursor is at whitespace between two words, make a new, empty
- word, add it before (well, after, since the list is in reverse order)
- the word we just added, and set the current word to that one. */
+ word, add it before (well, after, since the list is in reverse order)
+ the word we just added, and set the current word to that one. */
if (cwp && cw == -1 && sentinel < ts)
- {
- tl = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
- tl->word = make_word ("");
- tl->next = ret->next;
- ret->next = tl;
- cw = nw;
- nw++;
- }
+ {
+ tl = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
+ tl->word = make_word ("");
+ tl->next = ret->next;
+ ret->next = tl;
+ cw = nw;
+ nw++;
+ }
if (string[te] == 0)
break;
- i = te + member(string[te], d);
+ i = te + member (string[te], d);
while (member (string[i], d) && whitespace(string[i]))
i++;
if (cwp && cw == -1 && sentinel >= slen)
{
if (whitespace (string[sentinel - 1]))
- {
- token = "";
- ret = add_string_to_list (token, ret);
- nw++;
- }
+ {
+ token = "";
+ ret = add_string_to_list (token, ret);
+ nw++;
+ }
cw = nw;
}
WORD_LIST *result;
WORD_DESC *t;
char *current_word, *s;
- int sindex, sh_style_split;
+ int sindex, sh_style_split, whitesep;
if (!string || !*string)
return ((WORD_LIST *)NULL);
free (current_word);
+ /* Note whether or not the separator is IFS whitespace, used later. */
+ whitesep = string[sindex] && spctabnl (string[sindex]);
+
/* Move past the current separator character. */
if (string[sindex])
sindex++;
in the list of separators. */
while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex]))
sindex++;
+
+ /* If the first separator was IFS whitespace and the current character is
+ a non-whitespace IFS character, it should be part of the current field
+ delimiter, not a separate delimiter that would result in an empty field.
+ Look at POSIX.2, 3.6.5, (3)(b). */
+ if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex]))
+ sindex++;
}
return (REVERSE_LIST (result, WORD_LIST *));
}
{
register char *s;
char *current_word;
- int sindex, sh_style_split;
+ int sindex, sh_style_split, whitesep;
if (!stringp || !*stringp || !**stringp)
return ((char *)NULL);
if (endptr)
*endptr = s + sindex;
+ /* Note whether or not the separator is IFS whitespace, used later. */
+ whitesep = s[sindex] && spctabnl (s[sindex]);
+
/* Move past the current separator character. */
if (s[sindex])
sindex++;
while (s[sindex] && spctabnl (s[sindex]) && issep (s[sindex]))
sindex++;
+ /* If the first separator was IFS whitespace and the current character is
+ a non-whitespace IFS character, it should be part of the current field
+ delimiter, not a separate delimiter that would result in an empty field.
+ Look at POSIX.2, 3.6.5, (3)(b). */
+ if (s[sindex] && whitesep && issep (s[sindex]) && !spctabnl (s[sindex]))
+ sindex++;
+
/* Update STRING to point to the next field. */
*stringp = s + sindex;
return (current_word);
i++;
}
else if (c == '\'')
- i = skip_single_quoted (s, ++i);
+ i = skip_single_quoted (s, ++i);
else if (c == '"')
i = skip_double_quoted (s, ++i);
else if (c == 0 || spctabnl (c))
ind = array_expand_index (t, ni - ind);
if (ind < 0)
{
- t[-1] = '['; /* restore original name */
+ t[-1] = '['; /* restore original name ] */
report_error ("%s: bad array subscript", name);
return ((SHELL_VAR *)NULL);
}
entry = bind_array_variable (name, ind, value);
- t[-1] = '['; /* restore original name */
+ t[-1] = '['; /* restore original name ] */
return (entry);
}
#endif /* ARRAY_VARS */
/* Perform tilde expansion. */
if (expand && temp[0])
- {
+ {
temp = (strchr (temp, '~') && unquoted_member ('~', temp))
? bash_tilde_expand (temp)
: savestring (temp);
#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0)
#if defined (ARRAY_VARS)
- if (t = strchr (name, '['))
+ if (t = strchr (name, '[')) /*]*/
{
if (assign_list)
{
}
entry = do_array_element_assignment (name, value);
if (entry == 0)
- ASSIGN_RETURN (0);
+ ASSIGN_RETURN (0);
}
else if (assign_list)
entry = assign_array_from_string (name, value);
VUNSETATTR (entry, att_invisible);
/* Return 1 if the assignment seems to have been performed correctly. */
- ASSIGN_RETURN (entry ? (readonly_p (entry) == 0) : 0);
+ ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0);
}
/* Perform the assignment statement in STRING, and expand the
{
ind -= 10;
for (p = rest_of_args; p && ind--; p = p->next)
- ;
+ ;
temp = p ? savestring (p->word->word) : (char *)NULL;
}
return (temp);
for (s = string; s && *s; )
{
if (*s == '\\')
- s++;
+ s++;
if (*s == 0)
- break;
+ break;
*r++ = *s++;
}
*r = '\0';
if (w->word == 0 || w->word[0] == '\0')
return ((char *)NULL);
+ if (strchr (w->word, '~') && unquoted_member ('~', w->word))
+ {
+ p = bash_tilde_expand (w->word);
+ free (w->word);
+ w->word = p;
+ }
+
l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0);
if (l)
{
r = string_list (l);
}
else
- {
- p = string_list (l);
- r = quote_string_for_globbing (p, QGLOB_CVTNULL);
- free (p);
- }
+ {
+ p = string_list (l);
+ r = quote_string_for_globbing (p, QGLOB_CVTNULL);
+ free (p);
+ }
dispose_words (l);
}
else
result = expand_word_internal (w, q, i, c, e);
if (result == &expand_word_error || result == &expand_word_fatal)
{
+ expand_no_split_dollar_star = 0; /* XXX */
/* By convention, each time this error is returned, w->word has
already been freed (it sometimes may not be in the fatal case,
but that doesn't result in a memory leak because we're going
to exit in most cases). */
w->word = (char *)NULL;
+ last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF);
/* NOTREACHED */
}
if (string == 0 || *string == 0)
return ((WORD_LIST *)NULL);
- bzero ((char *)&td, sizeof (td));
- td.word = string;
+ td.flags = 0;
+ td.word = savestring (string);
+
tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
+
+ FREE (td.word);
return (tresult);
}
{
WORD_LIST *value;
- if (!string || !*string)
+ if (string == 0 || *string == '\0')
return ((WORD_LIST *)NULL);
+ expand_no_split_dollar_star = 1;
value = expand_string_internal (string, quoted);
+ expand_no_split_dollar_star = 0;
+
if (value)
{
if (value->word)
if (string == 0 || *string == 0)
return ((WORD_LIST *)NULL);
- bzero ((char *)&td, sizeof (td));
+ td.flags = 0;
td.word = savestring (string);
- doing_prompt_expansion = 1;
+
+ no_longjmp_on_fatal_error = 1;
value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
- doing_prompt_expansion = 0;
+ no_longjmp_on_fatal_error = 0;
+
if (value == &expand_word_error || value == &expand_word_fatal)
{
value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL);
if (string == 0 || *string == '\0')
return (WORD_LIST *)NULL;
- bzero ((char *)&td, sizeof (td));
+ td.flags = 0;
td.word = string;
tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at);
return (tresult);
{
WORD_LIST *result;
- if (!string || !*string)
+ if (string == 0 || *string == '\0')
return ((WORD_LIST *)NULL);
result = expand_string_leave_quoted (string, quoted);
continue;
}
if (*s == CTLNUL)
- continue;
+ continue;
*p++ = *s;
}
*p = '\0';
case '\\':
return (*string == *pat);
case '?':
- return (*string == LPAREN ? 1 : (*string != '\0'));
+ return (*pat == LPAREN ? 1 : (*string != '\0'));
case '*':
return (1);
case '+':
case '!':
case '@':
- return (*string == LPAREN ? 1 : (*string == c));
+ return (*pat == LPAREN ? 1 : (*string == c));
case '[':
return (*string != '\0');
}
case MATCH_BEG:
if (match_pattern_char (pat, string) == 0)
- return (0);
+ return (0);
for (p = end; p >= string; p--)
{
c = *p; *p = '\0';
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
{
if (array_p (var) == 0)
- {
- report_error ("%s: bad array subscript", aspec);
- FREE (pattern);
- return ((char *)NULL);
- }
+ {
+ report_error ("%s: bad array subscript", aspec);
+ FREE (pattern);
+ return ((char *)NULL);
+ }
l = array_to_word_list (array_cell (var));
if (l == 0)
- return ((char *)NULL);
+ return ((char *)NULL);
ret = list_remove_pattern (l, pattern, patspec, t[0], quoted);
dispose_words (l);
}
does parameter expansion, command substitution, arithmetic expansion,
and quote removal. */
WORD_LIST *
-expand_word_no_split (word, quoted)
+expand_word_unsplit (word, quoted)
WORD_DESC *word;
int quoted;
{
WORD_LIST *result;
+ expand_no_split_dollar_star = 1;
result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
+ expand_no_split_dollar_star = 0;
+
return (result ? dequote_list (result) : result);
}
unlink all of them. add_fifo_list adds the name of an open FIFO to the
list. NFIFO is a count of the number of FIFOs in the list. */
#define FIFO_INCR 20
-extern char *mktemp ();
static char **fifo_list = (char **)NULL;
static int nfifo;
{
char *tname;
- tname = mktemp (savestring ("/tmp/sh-np-XXXXXX"));
+ tname = sh_mktmpname ("sh-np", MT_USERANDOM);
if (mkfifo (tname, 0600) < 0)
{
free (tname);
/* Cancel traps, in trap.c. */
restore_original_signals ();
setup_async_signals ();
- subshell_environment = SUBSHELL_COMSUB;
+ subshell_environment |= SUBSHELL_COMSUB;
}
#if defined (JOB_CONTROL)
}
if (open_for_read_in_child)
{
- if (unset_nodelay_mode (fd) < 0)
+ if (sh_unset_nodelay_mode (fd) < 0)
{
sys_error ("cannout reset nodelay mode for fd %d", fd);
exit (127);
istring = (char *)NULL;
istring_index = istring_size = bufn = 0;
+#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. */
while (1)
{
if (fd < 0)
- break;
+ break;
if (--bufn <= 0)
{
bufn = zread (fd, buf, sizeof (buf));
}
c = *bufp++;
+ if (c == 0)
+ {
+#if 0
+ internal_warning ("read_comsub: ignored null byte in input");
+#endif
+ continue;
+ }
+
/* Add the character to ISTRING, possibly after resizing it. */
RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE);
istring[istring_index++] = CTLESC;
istring[istring_index++] = c;
+
+#if 0
+#if defined (__CYGWIN__)
+ if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r')
+ {
+ istring_index--;
+ istring[istring_index - 1] = '\n';
+ }
+#endif
+#endif
}
if (istring)
old_pid = last_made_pid;
#if defined (JOB_CONTROL)
old_pipeline_pgrp = pipeline_pgrp;
- pipeline_pgrp = shell_pgrp;
+ /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */
+ if ((subshell_environment & SUBSHELL_PIPE) == 0)
+ pipeline_pgrp = shell_pgrp;
cleanup_the_pipeline ();
#endif
if (pid == 0)
{
set_sigint_handler (); /* XXX */
-#if 0
-#if defined (JOB_CONTROL)
- set_job_control (0);
-#endif
-#endif
+
if (dup2 (fildes[1], 1) < 0)
{
sys_error ("command_substitute: cannot duplicate pipe as fd 1");
interactive = 0;
/* This is a subshell environment. */
- subshell_environment = SUBSHELL_COMSUB;
+ subshell_environment |= SUBSHELL_COMSUB;
- /* Command substitution does not inherit the -e flag. */
- exit_immediately_on_error = 0;
+ /* When not in POSIX mode, command substitution does not inherit
+ the -e flag. */
+ if (posixly_correct == 0)
+ exit_immediately_on_error = 0;
remove_quoted_escapes (string);
#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)
+ if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0)
#endif
- give_terminal_to (pipeline_pgrp);
+ give_terminal_to (pipeline_pgrp, 0);
#endif /* JOB_CONTROL */
return (istring);
char *t;
int r, len;
- t = strchr (name, '[');
+ t = strchr (name, '['); /* ] */
if (t)
{
*t = '\0';
free (t);
free (exp);
if (expok == 0)
- jump_to_top_level (DISCARD);
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (DISCARD);
+ }
return val;
}
*t = '\0';
var = find_variable (s);
- *t++ = '[';
+ *t++ = '['; /* ] */
if (subp)
*subp = t;
if (var == 0)
return (char *)NULL;
+ /* [ */
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
{
- if (array_p (var) == 0 || allow_all == 0)
- {
- report_error ("%s: bad array subscript", s);
- return ((char *)NULL);
- }
- l = array_to_word_list (array_cell (var));
- if (l == (WORD_LIST *)NULL)
- return ((char *) NULL);
+ if (allow_all == 0)
+ {
+ report_error ("%s: bad array subscript", s);
+ return ((char *)NULL);
+ }
+ else if (array_p (var) == 0)
+ {
+ l = (WORD_LIST *)NULL;
+ l = add_string_to_list (value_cell (var), l);
+ }
+ else
+ {
+ l = array_to_word_list (array_cell (var));
+ if (l == (WORD_LIST *)NULL)
+ return ((char *) NULL);
+ }
-#if 0
- if (t[0] == '*') /* ${name[*]} */
- retval = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? string_list_dollar_star (l) : string_list (l);
- else /* ${name[@]} */
- retval = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (l) : l);
-#else
if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
{
temp = string_list_dollar_star (l);
}
else /* ${name[@]} or unquoted ${name[*]} */
retval = string_list_dollar_at (l, quoted);
-#endif
dispose_words (l);
}
return ((char *)NULL);
}
if (array_p (var) == 0)
- return (ind == 0 ? savestring (value_cell (var)) : (char *)NULL);
+ return (ind == 0 ? savestring (value_cell (var)) : (char *)NULL);
retval = array_reference (array_cell (var), ind);
if (retval)
retval = quote_escapes (retval);
}
else if (var == 0)
return 0;
- else if (array_p (var) == 0)
- return 1;
- array = array_cell (var);
+ /* We support a couple of expansions for variables that are not arrays.
+ We'll return the length of the value for v[0], and 1 for v[@] or
+ v[*]. Return 0 for everything else. */
+
+ array = array_p (var) ? array_cell (var) : (ARRAY *)NULL;
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
- return (array_num_elements (array));
+ return (array_p (var) ? array_num_elements (array) : 1);
ind = array_expand_index (t, len);
if (ind < 0)
report_error ("%s: bad array subscript", t);
return (-1);
}
- t = array_reference (array, ind);
- len = STRLEN (t);
+ if (array_p (var))
+ t = array_reference (array, ind);
+ else
+ t = (ind == 0) ? value_cell (var) : (char *)NULL;
+
+ len = STRLEN (t);
return (len);
}
#endif /* ARRAY_VARS */
char *name;
int var_is_special;
{
- if (digit (*name) && all_digits (name))
+ if (isdigit (*name) && all_digits (name))
return 1;
else if (var_is_special)
return 1;
WORD_LIST *l;
/* Handle multiple digit arguments, as in ${11}. */
- if (digit (*name))
+ if (isdigit (*name))
{
arg_index = atoi (name);
temp = get_dollar_var_value (arg_index);
else if (var = find_variable (name))
{
if (var && invisible_p (var) == 0)
- {
+ {
#if defined (ARRAY_VARS)
temp = array_p (var) ? array_reference (array_cell (var), 0) : value_cell (var);
#else
if (tempvar_p (var))
dispose_variable (var);
- }
+ }
else
temp = (char *)NULL;
}
int hasdol;
temp = (*value == '~' || (strchr (value, '~') && unquoted_substring ("=~", value)))
- ? bash_tilde_expand (value)
+ ? bash_tilde_expand (value)
: savestring (value);
/* If the entire expression is between double quotes, we want to treat
if (value && *value)
{
- l = expand_string (value, 0);
+ temp = (*value == '~' || (strchr (value, '~') && unquoted_substring ("=~", value)))
+ ? bash_tilde_expand (value)
+ : savestring (value);
+
+ l = expand_string (temp, 0);
+ FREE (temp);
temp = string_list (l);
report_error ("%s: %s", name, temp ? temp : ""); /* XXX was value not "" */
FREE (temp);
valid_length_expression (name)
char *name;
{
- return (!name[1] || /* ${#} */
- ((name[1] == '@' || name[1] == '*') && !name[2]) || /* ${#@}, ${#*} */
- (member (name[1], "-?$!#") && !name[2]) || /* ${#-}, etc. */
- (digit (name[1]) && all_digits (name + 1)) || /* ${#11} */
+ return (name[1] == '\0' || /* ${#} */
+ ((sh_syntaxtab[name[1]] & CSPECVAR) && name[2] == '\0') || /* special param */
+ (isdigit (name[1]) && all_digits (name + 1)) || /* ${#11} */
#if defined (ARRAY_VARS)
valid_array_reference (name + 1) || /* ${#a[7]} */
#endif
number = number_of_args ();
else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0') /* ${#@}, ${#*} */
number = number_of_args ();
- else if (member (name[1], "-?$!#") && name[2] == '\0')
+ else if ((sh_syntaxtab[name[1]] & CSPECVAR) && name[2] == '\0')
{
/* Take the lengths of some of the shell's special parameters. */
switch (name[1])
{
number = 0;
- if (digit (name[1])) /* ${#1} */
+ if (isdigit (name[1])) /* ${#1} */
{
t = get_dollar_var_value (atoi (name + 1));
number = STRLEN (t);
return (number);
}
+/* Skip characters in SUBSTR until DELIM. SUBSTR is an arithmetic expression,
+ so we do some ad-hoc parsing of an arithmetic expression to find
+ the first DELIM, instead of using strchr(3). Two rules:
+ 1. If the substring contains a `(', read until closing `)'.
+ 2. If the substring contains a `?', read past one `:' for each `?'.
+*/
+
+static char *
+skiparith (substr, delim)
+ char *substr;
+ int delim;
+{
+ int skipcol, pcount;
+ char *t;
+
+ for (skipcol = pcount = 0, t = substr; *t; t++)
+ {
+ /* Balance parens */
+ if (*t == '(')
+ {
+ pcount++;
+ continue;
+ }
+ if (*t == ')' && pcount)
+ {
+ pcount--;
+ continue;
+ }
+ if (pcount)
+ continue;
+
+ /* Skip one `:' for each `?' */
+ if (*t == ':' && skipcol)
+ {
+ skipcol--;
+ continue;
+ }
+ if (*t == delim)
+ break;
+ if (*t == '?')
+ {
+ skipcol++;
+ continue;
+ }
+ }
+ return t;
+}
+
/* Verify and limit the start and end of the desired substring. If
VTYPE == 0, a regular shell variable is being used; if it is 1,
then the positional parameters are being used; if it is 2, then
ARRAY *a;
#endif
+#if 1
+ /* duplicate behavior of strchr(3) */
+ t = skiparith (substr, ':');
+ if (*t && *t == ':')
+ *t = '\0';
+ else
+ t = (char *)0;
+#else
t = strchr (substr, ':');
if (t)
*t = '\0';
+#endif
temp1 = maybe_expand_string (substr, Q_DOUBLE_QUOTES, expand_string);
*e1p = evalexp (temp1, &expok);
free (temp1);
*e2p = evalexp (temp1, &expok);
free (temp1);
if (expok == 0)
- return (0);
+ return (0);
if (*e2p < 0)
- {
- internal_error ("%s: substring expression < 0", t);
+ {
+ internal_error ("%s: substring expression < 0", t);
return (0);
- }
+ }
*e2p += *e1p; /* want E2 chars starting at E1 */
if (*e2p > len)
- *e2p = len;
+ *e2p = len;
}
else
*e2p = len;
case VT_VARIABLE:
case VT_ARRAYMEMBER:
temp = quoted ? quoted_substring (value, e1, e2) : substring (value, e1, e2);
+ if (val && vtype == VT_ARRAYMEMBER)
+ free (val);
break;
case VT_POSPARMS:
temp = pos_params (varname, e1, e2, quoted);
/* OK, now copy the leading unmatched portion of the string (from
str to s) to ret starting at rptr (the current offset). Then copy
- the replacement string at ret + rptr + (s - str). Increment
- rptr (if necessary) and str and go on. */
+ the replacement string at ret + rptr + (s - str). Increment
+ rptr (if necessary) and str and go on. */
if (l)
{
strncpy (ret + rptr, str, l);
strncpy (ret + rptr, rep, replen);
rptr += replen;
}
+ if (s == e)
+ e++; /* avoid infinite recursion on zero-length match */
str = e; /* e == end of match */
if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY)
- break;
+ break;
}
/* Now copy the unmatched portion of the input string */
if ((mflags & MATCH_QUOTED) == 0)
rep = maybe_expand_string (rep, quoted, expand_string_unsplit);
else
- rep = expand_string_to_string (rep, quoted, expand_string_unsplit);
+ rep = expand_string_to_string (rep, quoted, expand_string_unsplit);
}
p = pat;
string[t_index] == '?' ||
string[t_index] == '#')) ||
(sindex == t_index - 1 && string[sindex] == '!' &&
- (string[t_index] == '#' ||
- string[t_index] == '?' ||
- string[t_index] == '@' ||
- string[t_index] == '*')))
+ (string[t_index] == '#' ||
+ string[t_index] == '?' ||
+ string[t_index] == '@' ||
+ string[t_index] == '*')))
{
t_index++;
free (name);
*name = string[sindex];
if (string[sindex] == '!')
{
- /* indirect reference of $#, $?, $@, or $* */
- name[1] = string[sindex + 1];
- strcpy (name + 2, temp1);
+ /* indirect reference of $#, $?, $@, or $* */
+ name[1] = string[sindex + 1];
+ strcpy (name + 2, temp1);
}
else
strcpy (name + 1, temp1);
characters, move past it as normal. If not, assume that
a substring specification is being given, and do not move
past it. */
- if (c == ':' && member (string[sindex], "-=?+"))
+ if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex]))
{
check_nullness++;
if (c = string[sindex])
/* ${#-} is a valid expansion and means to take the length of $-.
Similarly for ${#?} and ${##}... */
if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
- member (c, "-?#") && string[sindex] == RBRACE)
+ VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE)
{
name = xrealloc (name, 3);
name[1] = c;
either a variable name, one of the positional parameters or a special
variable that expands to one of the positional parameters. */
want_indir = *name == '!' &&
- (legal_variable_starter (name[1]) || digit (name[1]) || member (name[1], "#?@*"));
+ (legal_variable_starter (name[1]) || isdigit (name[1])
+ || VALID_INDIR_PARAM (name[1]));
/* Determine the value of this variable. */
if (*name == '#' && name[1])
{
/* If we are not pointing at the character just after the
- closing brace, then we haven't gotten all of the name.
- Since it begins with a special character, this is a bad
- substitution. Also check NAME for validity before trying
- to go on. */
+ closing brace, then we haven't gotten all of the name.
+ Since it begins with a special character, this is a bad
+ substitution. Also check NAME for validity before trying
+ to go on. */
if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0))
{
temp = (char *)NULL;
temp1[number - 1] = '\0';
x = all_variables_matching_prefix (temp1);
xlist = argv_to_word_list (x, 1, 0);
- temp = string_list_dollar_star (xlist, quoted);
+ if (string[sindex - 2] == '*')
+ temp = string_list_dollar_star (xlist);
+ else
+ {
+ temp = string_list_dollar_at (xlist, quoted);
+ if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+ *quoted_dollar_atp = 1;
+ if (contains_dollar_at)
+ *contains_dollar_at = 1;
+ }
free (x);
free (xlist);
free (temp1);
temp = parameter_brace_expand_word (name, var_is_special, quoted);
#if defined (ARRAY_VARS)
-#if 0
- if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && valid_array_reference (name))
-#else
if (valid_array_reference (name))
-#endif
{
temp1 = strchr (name, '[');
if (temp1 && temp1[1] == '@' && temp1[2] == ']')
- {
+ {
if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
*quoted_dollar_atp = 1;
if (contains_dollar_at)
if (c && c != RBRACE)
{
/* Extract the contents of the ${ ... } expansion
- according to the Posix.2 rules. */
+ according to the Posix.2 rules. */
value = extract_dollar_brace_string (string, &sindex, quoted);
if (string[sindex] == RBRACE)
- sindex++;
+ sindex++;
else
goto bad_substitution;
}
case RBRACE:
if (var_is_set == 0 && unbound_vars_is_error)
- {
+ {
report_error ("%s: unbound variable", name);
FREE (value);
FREE (temp);
case '#': /* ${param#[#]pattern} */
case '%': /* ${param%[%]pattern} */
if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0')
- {
- FREE (value);
+ {
+ FREE (value);
break;
- }
+ }
if ((name[0] == '@' || name[0] == '*') && name[1] == '\0')
temp1 = parameter_list_remove_pattern (value, name[0], c, quoted);
#if defined (ARRAY_VARS)
case '?':
case '+':
if (var_is_set && var_is_null == 0)
- {
- /* We don't want the value of the named variable for
- anything, just the value of the right hand side. */
-
- /* XXX -- if we're double-quoted and the named variable is "$@",
- we want to turn off any special handling of "$@" -- we're not
- using it, so whatever is on the rhs applies. */
- if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
- *quoted_dollar_atp = 0;
- if (contains_dollar_at)
- *contains_dollar_at = 0;
+ {
+ /* 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 "$@",
+ we want to turn off any special handling of "$@" --
+ we're not using it, so whatever is on the rhs applies. */
+ if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+ *quoted_dollar_atp = 0;
+ if (contains_dollar_at)
+ *contains_dollar_at = 0;
+
FREE (temp);
if (value)
- {
+ {
temp = parameter_brace_expand_rhs (name, value, c,
quoted,
quoted_dollar_atp,
free (value);
}
else
- temp = (char *)NULL;
+ temp = (char *)NULL;
}
else
{
/* Otherwise do nothing; just use the value in TEMP. */
}
else /* VAR not set or VAR is NULL. */
- {
+ {
FREE (temp);
temp = (char *)NULL;
if (c == '=' && var_is_special)
else if (c == '?')
{
parameter_brace_expand_error (name, value);
- return (interactive ? &expand_param_error : &expand_param_fatal);
+ return (interactive_shell ? &expand_param_error : &expand_param_fatal);
}
else if (c != '+')
- temp = parameter_brace_expand_rhs (name, value, c, quoted,
- quoted_dollar_atp,
- contains_dollar_at);
+ {
+ /* XXX -- if we're double-quoted and the named variable is "$@",
+ we want to turn off any special handling of "$@" --
+ we're not using it, so whatever is on the rhs applies. */
+ if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+ *quoted_dollar_atp = 0;
+ if (contains_dollar_at)
+ *contains_dollar_at = 0;
+
+ temp = parameter_brace_expand_rhs (name, value, c, quoted,
+ quoted_dollar_atp,
+ contains_dollar_at);
+ }
free (value);
}
+
break;
}
free (name);
temp = temp1;
}
else
- {
- /* If the $* is not quoted it is identical to $@ */
- temp = string_list_dollar_at (list, quoted);
- if (contains_dollar_at)
- *contains_dollar_at = 1;
- }
+ {
+ /* If the $* is not quoted it is identical to $@ */
+ temp = string_list_dollar_at (list, quoted);
+ if (expand_no_split_dollar_star == 0 && contains_dollar_at)
+ *contains_dollar_at = 1;
+ }
dispose_words (list);
break;
in the string. */
/* Note that we saw the quoted null so we can add one back at
the end of this function if there are no other characters
- in the string, discard TEMP, and go on. */
+ in the string, discard TEMP, and go on. The exception to
+ this is when we have "${@}" and $1 is '', since $@ needs
+ special handling. */
if (temp && QUOTED_NULL (temp))
{
if (had_quoted_null_p)
*had_quoted_null_p = 1;
- free (temp);
- temp = (char *)NULL;
+ if (*quoted_dollar_at_p == 0)
+ {
+ free (temp);
+ temp = (char *)NULL;
+ }
+
}
goto return0;
zindex = t_index;
/* For Posix.2-style `$(( ))' arithmetic substitution,
- extract the expression and pass it to the evaluator. */
+ extract the expression and pass it to the evaluator. */
if (temp && *temp == LPAREN)
{
char *temp2;
int had_quoted_null;
int has_dollar_at;
+ int tflag;
register int c; /* Current character. */
int number; /* Temporary number value. */
c = string[++sindex];
if (quoted & Q_HERE_DOCUMENT)
- temp1 = slashify_in_here_document;
+ tflag = CBSHDOC;
else if (quoted & Q_DOUBLE_QUOTES)
- temp1 = slashify_in_quotes;
+ tflag = CBSDQUOTE;
else
- temp1 = "";
+ tflag = 0;
+
- if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && member (c, temp1) == 0)
+ if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
{
twochars[0] = '\\';
twochars[1] = c;
if (word->flags & W_NOGLOB)
tword->flags |= W_NOGLOB; /* XXX */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
- tword->flags |= W_QUOTED;
+ tword->flags |= W_QUOTED;
}
else
{
{
case '\\':
c = string[++sindex];
- if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && member (c, slashify_in_quotes) == 0)
+ if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
*r++ = '\\';
/* FALLTHROUGH */
next = tlist->next;
/* If the word isn't an assignment and contains an unquoted
- pattern matching character, then glob it. */
-#if 0
- if ((tlist->word->flags & W_ASSIGNMENT) == 0 &&
-#else
+ pattern matching character, then glob it. */
if ((tlist->word->flags & W_NOGLOB) == 0 &&
-#endif
unquoted_glob_pattern_p (tlist->word->word))
{
glob_array = shell_glob_filename (tlist->word->word);
/* Dispose the new list we're building. */
dispose_words (new_list);
+ last_command_exit_value = EXECUTION_FAILURE;
if (expanded == &expand_word_error)
jump_to_top_level (DISCARD);
else
tint = do_assignment (temp_list->word->word);
/* Variable assignment errors in non-interactive shells
running in Posix.2 mode cause the shell to exit. */
- if (tint == 0 && interactive_shell == 0 && posixly_correct)
+ if (tint == 0)
{
last_command_exit_value = EXECUTION_FAILURE;
- jump_to_top_level (FORCE_EOF);
+ if (interactive_shell == 0 && posixly_correct)
+ jump_to_top_level (FORCE_EOF);
+ else
+ jump_to_top_level (DISCARD);
}
}
dispose_words (subst_assign_varlist);
tint = (*assign_func) (temp_list->word->word);
/* Variable assignment errors in non-interactive shells running
in Posix.2 mode cause the shell to exit. */
- if (tint == 0 && assign_func == do_assignment &&
- interactive_shell == 0 && posixly_correct)
+ if (tint == 0 && assign_func == do_assignment)
{
last_command_exit_value = EXECUTION_FAILURE;
- jump_to_top_level (FORCE_EOF);
+ if (interactive_shell == 0 && posixly_correct)
+ jump_to_top_level (FORCE_EOF);
+ else
+ jump_to_top_level (DISCARD);
}
}