aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--posix/wordexp-test.c9
-rw-r--r--posix/wordexp.c311
3 files changed, 218 insertions, 115 deletions
diff --git a/ChangeLog b/ChangeLog
index 8988cbb..cdef881 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+1999-04-11 Tim Waugh <tim@cyberelk.demon.co.uk>
+
+ * posix/wordexp.c (wordexp): Fix a leak when an invalid character
+ is seen, as well as fixing semantics. Don't reset the word count
+ to zero when an invalid character is seen, but leave it as it was
+ (this makes a difference with WRDE_APPEND).
+
+ * posix/wordexp-test.c: More test cases.
+
+ * posix/wordexp.c (parse_param): In words like ${var#pattern},
+ always expand pattern when it is needed. Also, handle quoting in
+ pattern properly.
+
1999-04-12 Philip Blundell <philb@gnu.org>
* elf/elf.h: Update ARM definitions to match current gas2.
diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
index 8e709c7..21bca96 100644
--- a/posix/wordexp-test.c
+++ b/posix/wordexp-test.c
@@ -81,6 +81,7 @@ struct test_case_struct
{ 0, NULL, "\"quoted\"", 0, 1, { "quoted", }, IFS },
{ 0, "foo", "\"$var\"\"$var\"", 0, 1, { "foofoo", }, IFS },
{ 0, NULL, "'singly-quoted'", 0, 1, { "singly-quoted", }, IFS },
+ { 0, NULL, "contin\\\nuation", 0, 1, { "continuation", }, IFS },
/* Simple command substitution */
{ 0, NULL, "$(echo hello)", 0, 1, { "hello", }, IFS },
@@ -118,10 +119,16 @@ struct test_case_struct
{ 0, NULL, "${var:-'}'}", 0, 1, { "}", }, IFS },
{ 0, NULL, "${var-}", 0, 0, { NULL }, IFS },
+ { 0, "pizza", "${var#${var}}", 0, 0, { NULL }, IFS },
+ { 0, "pepperoni", "${var%$(echo oni)}", 0, 1, { "pepper" }, IFS },
+ { 0, "6pack", "${var#$((6))}", 0, 1, { "pack" }, IFS },
+ { 0, "b*witched", "${var##b*}", 0, 0, { NULL }, IFS },
+ { 0, "b*witched", "${var##\"b*\"}", 0, 1, { "witched" }, IFS },
{ 0, "banana", "${var%na*}", 0, 1, { "bana", }, IFS },
{ 0, "banana", "${var%%na*}", 0, 1, { "ba", }, IFS },
{ 0, "borabora-island", "${var#*bora}", 0, 1, { "bora-island", }, IFS },
- { 0, "borabora-island", "${var##*bora}", 0, 1, {"-island", }, IFS },
+ { 0, "borabora-island", "${var##*bora}", 0, 1, { "-island", }, IFS },
+ { 0, "coconut", "${var##\\*co}", 0, 1, { "coconut", }, IFS },
{ 0, "100%", "${var%0%}", 0, 1, { "10" }, IFS },
/* Pathname expansion */
diff --git a/posix/wordexp.c b/posix/wordexp.c
index 4a6dd7c..aa5b94c 100644
--- a/posix/wordexp.c
+++ b/posix/wordexp.c
@@ -74,7 +74,7 @@ static int eval_expr (char *expr, long int *result) internal_function;
#define W_CHUNK (100)
-/* Result of w_newword will be ignored if it the last word. */
+/* Result of w_newword will be ignored if it's the last word. */
static inline char *
w_newword (size_t *actlen, size_t *maxlen)
{
@@ -1203,7 +1203,7 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
goto syntax;
}
- /* Now collect the pattern. */
+ /* Now collect the pattern, but don't expand it yet. */
++*offset;
for (; words[*offset]; ++(*offset))
{
@@ -1224,8 +1224,18 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
break;
case '\\':
- if (!pattern_is_quoted && words[++*offset] == '\0')
+ if (pattern_is_quoted)
+ /* Quoted; treat as normal character. */
+ break;
+
+ /* Otherwise, it's an escape: next character is literal. */
+ if (words[++*offset] == '\0')
goto syntax;
+
+ pattern = w_addchar (pattern, &pat_length, &pat_maxlen, '\\');
+ if (pattern == NULL)
+ goto no_space;
+
break;
case '\'':
@@ -1383,6 +1393,153 @@ envsubst:
if (action != ACT_NONE)
{
+ int expand_pattern = 0;
+
+ /* First, find out if we need to expand pattern (i.e. if we will
+ * use it). */
+ switch (action)
+ {
+ case ACT_RP_SHORT_LEFT:
+ case ACT_RP_LONG_LEFT:
+ case ACT_RP_SHORT_RIGHT:
+ case ACT_RP_LONG_RIGHT:
+ /* Always expand for these. */
+ expand_pattern = 1;
+ break;
+
+ case ACT_NULL_ERROR:
+ case ACT_NULL_SUBST:
+ case ACT_NULL_ASSIGN:
+ if (!value || (!*value && colon_seen))
+ /* If param is unset, or set but null and a colon has been seen,
+ the expansion of the pattern will be needed. */
+ expand_pattern = 1;
+
+ break;
+
+ case ACT_NONNULL_SUBST:
+ /* Expansion of word will be needed if parameter is set and not null,
+ or set null but no colon has been seen. */
+ if (value && (*value || !colon_seen))
+ expand_pattern = 1;
+
+ break;
+
+ default:
+ assert (! "Unrecognised action!");
+ }
+
+ if (expand_pattern)
+ {
+ /* We need to perform tilde expansion, parameter expansion,
+ command substitution, and arithmetic expansion. We also
+ have to be a bit careful with wildcard characters, as
+ pattern might be given to fnmatch soon. To do this, we
+ convert quotes to escapes. */
+
+ char *expanded;
+ size_t exp_len;
+ size_t exp_maxl;
+ char *p;
+ int quoted = 0; /* 1: single quotes; 2: double */
+
+ expanded = w_newword (&exp_len, &exp_maxl);
+ for (p = pattern; p && *p; p++)
+ {
+ int offset;
+
+ switch (*p)
+ {
+ case '"':
+ if (quoted == 2)
+ quoted = 0;
+ else if (quoted == 0)
+ quoted = 2;
+ else break;
+
+ continue;
+
+ case '\'':
+ if (quoted == 1)
+ quoted = 0;
+ else if (quoted == 0)
+ quoted = 1;
+ else break;
+
+ continue;
+
+ case '*':
+ case '?':
+ if (quoted)
+ {
+ /* Convert quoted wildchar to escaped wildchar. */
+ expanded = w_addchar (expanded, &exp_len,
+ &exp_maxl, '\\');
+
+ if (expanded == NULL)
+ goto no_space;
+ }
+ break;
+
+ case '$':
+ offset = 0;
+ error = parse_dollars (&expanded, &exp_len, &exp_maxl, p,
+ &offset, flags, NULL, NULL, NULL, 1);
+ if (error)
+ {
+ if (free_value)
+ free (value);
+
+ if (expanded)
+ free (expanded);
+
+ goto do_error;
+ }
+
+ p += offset;
+ continue;
+
+ case '~':
+ if (quoted || exp_len)
+ break;
+
+ offset = 0;
+ error = parse_tilde (&expanded, &exp_len, &exp_maxl, p,
+ &offset, 0);
+ if (error)
+ {
+ if (free_value)
+ free (value);
+
+ if (expanded)
+ free (expanded);
+
+ goto do_error;
+ }
+
+ p += offset;
+ continue;
+
+ case '\\':
+ expanded = w_addchar (expanded, &exp_len, &exp_maxl, '\\');
+ ++p;
+ assert (*p); /* checked when extracted initially */
+ if (expanded == NULL)
+ goto no_space;
+ }
+
+ expanded = w_addchar (expanded, &exp_len, &exp_maxl, *p);
+
+ if (expanded == NULL)
+ goto no_space;
+ }
+
+ if (pattern)
+ free (pattern);
+
+ pattern = expanded;
+ }
+
switch (action)
{
case ACT_RP_SHORT_LEFT:
@@ -1521,33 +1678,12 @@ envsubst:
/* Substitute parameter */
break;
+ error = 0;
if (!colon_seen && value)
/* Substitute NULL */
- error = 0;
+ ;
else if (*pattern)
- {
- /* Expand 'pattern' and write it to stderr */
- wordexp_t we;
-
- error = wordexp (pattern, &we, flags);
-
- if (error == 0)
- {
- int i;
-
- fprintf (stderr, "%s:", env);
-
- for (i = 0; i < we.we_wordc; ++i)
- {
- fprintf (stderr, " %s", we.we_wordv[i]);
- }
-
- fprintf (stderr, "\n");
- error = WRDE_BADVAL;
- }
-
- wordfree (&we);
- }
+ fprintf (stderr, "%s: %s\n", env, pattern);
else
{
fprintf (stderr, "%s: parameter null or not set\n", env);
@@ -1563,95 +1699,35 @@ envsubst:
/* Substitute parameter */
break;
- if (!colon_seen && value)
- {
- /* Substitute NULL */
- if (free_value)
- free (value);
- goto success;
- }
-
- subst_word:
- {
- /* Substitute word */
- wordexp_t we;
- int i;
-
- if (free_value)
- free (value);
-
- if (quoted)
- {
- /* No field-splitting is allowed, so imagine
- quotes around the word. */
- char *qtd_pattern = malloc (3 + strlen (pattern));
- if (qtd_pattern)
- sprintf (qtd_pattern, "\"%s\"", pattern);
- free (pattern);
- pattern = qtd_pattern;
- }
-
- if (pattern == NULL && (pattern = __strdup ("")) == NULL)
- goto no_space;
-
- error = wordexp (pattern, &we, flags);
- if (error)
- goto do_error;
-
- /* Fingers crossed that the quotes worked.. */
- assert (!quoted || we.we_wordc == 1);
-
- /* Substitute */
- for (i = 0; i < we.we_wordc; ++i)
- if ((error = w_addword (pwordexp, __strdup (we.we_wordv[i])))
- != 0)
- break;
-
- if (i < we.we_wordc)
- {
- /* Ran out of space */
- wordfree (&we);
- goto do_error;
- }
-
- if (action == ACT_NULL_ASSIGN)
- {
- char *words;
- char *cp;
- size_t words_size = 0;
+ if (free_value && value)
+ free (value);
- if (special)
- /* Cannot assign special parameters. */
- goto syntax;
+ if (!colon_seen && value)
+ /* Substitute NULL */
+ goto success;
- for (i = 0; i < we.we_wordc; i++)
- words_size += strlen (we.we_wordv[i]) + 1; /* for <space> */
- words_size++;
+ value = pattern ? __strdup (pattern) : pattern;
+ free_value = 1;
- cp = words = __alloca (words_size);
- *words = 0;
- for (i = 0; i < we.we_wordc - 1; i++)
- {
- cp = __stpcpy (cp, we.we_wordv[i]);
- *cp++ = ' ';
- }
+ if (pattern && !value)
+ goto no_space;
- strcpy (cp, we.we_wordv[i]);
+ break;
- /* Also assign */
- setenv (env, words, 1);
- }
+ case ACT_NONNULL_SUBST:
+ if (value && (*value || !colon_seen))
+ {
+ if (free_value && value)
+ free (value);
- wordfree (&we);
- goto success;
- }
+ value = pattern ? __strdup (pattern) : pattern;
+ free_value = 1;
- case ACT_NONNULL_SUBST:
- if (value && *value)
- goto subst_word;
+ if (pattern && !value)
+ goto no_space;
- if (!colon_seen && value)
- goto subst_word;
+ break;
+ }
/* Substitute NULL */
if (free_value)
@@ -1671,8 +1747,17 @@ envsubst:
goto success;
}
- /* This checks for '=' so it knows to assign */
- goto subst_word;
+ if (free_value && value)
+ free (value);
+
+ value = pattern ? __strdup (pattern) : pattern;
+ free_value = 1;
+
+ if (pattern && !value)
+ goto no_space;
+
+ setenv (env, value, 1);
+ break;
default:
assert (! "Unrecognised action!");
@@ -2190,10 +2275,8 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
if (strchr ("\n|&;<>(){}", ch))
{
/* Fail */
- wordfree (pwordexp);
- pwordexp->we_wordc = 0;
- pwordexp->we_wordv = old_wordv;
- return WRDE_BADCHAR;
+ error = WRDE_BADCHAR;
+ goto do_error;
}
/* "Ordinary" character -- add it to word */