diff options
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | posix/wordexp-test.c | 9 | ||||
-rw-r--r-- | posix/wordexp.c | 311 |
3 files changed, 218 insertions, 115 deletions
@@ -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 8e709c7569..21bca96681 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 4a6dd7cbd0..aa5b94c3c0 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 */ |