]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Update.
authorUlrich Drepper <drepper@redhat.com>
Wed, 11 Mar 1998 15:33:24 +0000 (15:33 +0000)
committerUlrich Drepper <drepper@redhat.com>
Wed, 11 Mar 1998 15:33:24 +0000 (15:33 +0000)
1998-03-11 15:27  Ulrich Drepper  <drepper@cygnus.com>

* elf/rtld.c: Update help message.
Install link maps for preloaded objects using main_map as loader.

* elf/dl-misc.c: Use __libc_write instead of __write for debugging.
* elf/dl-profile.c: Likewise.

* elf/dlsym.c: Little optimization.
* elf/dlvsym.c: Likewise.

1998-03-11 14:56  Ulrich Drepper  <drepper@cygnus.com>

* posix/wordexp-test.c: Move test for parameter list at the very
beginning.

1998-03-11 00:16  Tim Waugh  <tim@cyberelk.demon.co.uk>

* posix/wordexp.c (wordexp): Set we_wordc to zero initially unless
WRDE_REUSE flag is set.
(parse_param): Allow `*', `@', and numbers in parameter names.
(parse_dollars): Differentiate between arithmetic expansion and a
command substitution that starts immediately with a sub-shell
(like ``$((1+3))'' as opposed to ``$((echo);(ls))'').
(parse_param): Memory allocated with __alloca in a block was
referenced outside that block.  Adjusted to use malloc/free.
(parse_param): Adjusted field-splitting algorithm so that there is
not necessarily a field split at the end of a parameter expansion.

1998-03-10 19:52  Tim Waugh  <tim@cyberelk.demon.co.uk>

* posix/wordexp.c (wordexp): If about to return WRDE_NOSPACE,
don't free words that have already been allocated.
(parse_param): A dollar sign on its own will never have a pattern
associated with it (like "${HOME%%/}" has), so don't try to free it.
(parse_glob): Attempt to glob when an unquoted `[' is found
(rather than an unquoted '{' (!)).  Also for unquoted '?'.
(parse_glob): Sorted out quoting in a glob-able word.
(parse_param): Added $* and $@ handling.

ChangeLog
configure
elf/dl-misc.c
elf/dl-profile.c
elf/dlsym.c
elf/dlvsym.c
elf/rtld.c
posix/wordexp-test.c
posix/wordexp.c

index 9b63b7ca5f85e534c5c58f85210cda421f3d9135..d549f8ab86166b4dbecfa026482f170dd40aeabc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+1998-03-11 15:27  Ulrich Drepper  <drepper@cygnus.com>
+
+       * elf/rtld.c: Update help message.
+       Install link maps for preloaded objects using main_map as loader.
+
+       * elf/dl-misc.c: Use __libc_write instead of __write for debugging.
+       * elf/dl-profile.c: Likewise.
+
+       * elf/dlsym.c: Little optimization.
+       * elf/dlvsym.c: Likewise.
+
+1998-03-11 14:56  Ulrich Drepper  <drepper@cygnus.com>
+
+       * posix/wordexp-test.c: Move test for parameter list at the very
+       beginning.
+
+1998-03-11 00:16  Tim Waugh  <tim@cyberelk.demon.co.uk>
+
+       * posix/wordexp.c (wordexp): Set we_wordc to zero initially unless
+       WRDE_REUSE flag is set.
+       (parse_param): Allow `*', `@', and numbers in parameter names.
+       (parse_dollars): Differentiate between arithmetic expansion and a
+       command substitution that starts immediately with a sub-shell
+       (like ``$((1+3))'' as opposed to ``$((echo);(ls))'').
+       (parse_param): Memory allocated with __alloca in a block was
+       referenced outside that block.  Adjusted to use malloc/free.
+       (parse_param): Adjusted field-splitting algorithm so that there is
+       not necessarily a field split at the end of a parameter expansion.
+
+1998-03-10 19:52  Tim Waugh  <tim@cyberelk.demon.co.uk>
+
+       * posix/wordexp.c (wordexp): If about to return WRDE_NOSPACE,
+       don't free words that have already been allocated.
+       (parse_param): A dollar sign on its own will never have a pattern
+       associated with it (like "${HOME%%/}" has), so don't try to free it.
+       (parse_glob): Attempt to glob when an unquoted `[' is found
+       (rather than an unquoted '{' (!)).  Also for unquoted '?'.
+       (parse_glob): Sorted out quoting in a glob-able word.
+       (parse_param): Added $* and $@ handling.
+
 1998-03-11  Ulrich Drepper  <drepper@cygnus.com>
 
        * nss/nss_db/db-XXX.c (lookup): Copy data to safe place before
index ef9431df25ca3f1ce391d979df7d2e8abe707dec..959d72734c74b75e44ea178428dc3e69e7336d3d 100755 (executable)
--- a/configure
+++ b/configure
@@ -1545,7 +1545,7 @@ if eval "test \"`echo '$''{'libc_cv_prog_as_gnu'+set}'`\" = set"; then
 else
   # Most GNU programs take a -v and spit out some text including
 # the word 'GNU'.  Some try to read stdin, so give them /dev/null.
-if $AS -v </dev/null 2>&1 | grep GNU 2>&1 > /dev/null; then
+if $AS -v </dev/null 2>&1 | grep GNU > /dev/null 2>&1; then
   libc_cv_prog_as_gnu=yes
 else
   libc_cv_prog_as_gnu=no
@@ -1563,7 +1563,7 @@ if eval "test \"`echo '$''{'libc_cv_prog_ld_gnu'+set}'`\" = set"; then
 else
   # Most GNU programs take a -v and spit out some text including
 # the word 'GNU'.  Some try to read stdin, so give them /dev/null.
-if $LD -v </dev/null 2>&1 | grep GNU 2>&1 > /dev/null; then
+if $LD -v </dev/null 2>&1 | grep GNU > /dev/null 2>&1; then
   libc_cv_prog_ld_gnu=yes
 else
   libc_cv_prog_ld_gnu=no
index 3d796360b325da740cd813796d5dc2b74480fd36..d3b0f340c2a57b79e6569e39bfe5747333ff2e9a 100644 (file)
 #include <sys/stat.h>
 #include <stdio-common/_itoa.h>
 
+/* We have prototype anywhere.  */
+extern ssize_t __libc_write __P ((int __fd, __const __ptr_t __buf,
+                                 size_t __n));
+
 #ifndef MAP_ANON
 /* This is the only dl-sysdep.c function that is actually needed at run-time
    by _dl_map_object.  */
@@ -89,7 +93,7 @@ _dl_sysdep_output (int fd, const char *msg, ...)
   do
     {
       size_t len = strlen (msg);
-      __write (fd, msg, len);
+      __libc_write (fd, msg, len);
       msg = va_arg (ap, const char *);
     }
   while (msg != NULL);
@@ -124,19 +128,19 @@ _dl_debug_message (int new_line, const char *msg, ...)
            char buf[7] = "00000:\t";
            assert (pid >= 0 && pid < 100000);
            _itoa_word (pid, &buf[5], 10, 0);
-           __write (_dl_debug_fd, buf, 7);
+           __libc_write (_dl_debug_fd, buf, 7);
            new_line = 0;
          }
 
        endp = strchr (msg, '\n');
        if (endp == NULL)
          {
-           __write (_dl_debug_fd, msg, strlen (msg));
+           __libc_write (_dl_debug_fd, msg, strlen (msg));
            msg = va_arg (ap, const char *);
          }
        else
          {
-           __write (_dl_debug_fd, msg, endp - msg + 1);
+           __libc_write (_dl_debug_fd, msg, endp - msg + 1);
            msg = endp + 1;
            new_line = 1;
          }
index 91626f6fa3dcb39d210d8c9d22516949bfe269c4..cd3f07eb3e1d9e21ed555e4e18a2b555813e1427 100644 (file)
@@ -1,5 +1,5 @@
 /* Profiling of shared libraries.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
    Based on the BSD mcount implementation.
 #include <sys/stat.h>
 #include <atomicity.h>
 
+/* We have prototype anywhere.  */
+extern ssize_t __libc_write __P ((int __fd, __const __ptr_t __buf,
+                                 size_t __n));
+
 /* The LD_PROFILE feature has to be implemented different to the
    normal profiling using the gmon/ functions.  The problem is that an
    arbitrary amount of processes simulataneously can be run using
@@ -305,8 +309,9 @@ _dl_start_profile (struct link_map *map, const char *output_dir)
          return;
        }
 
-      if (TEMP_FAILURE_RETRY (__write (fd, buf, (expected_size
-                                                & (_dl_pagesize - 1)))) < 0)
+      if (TEMP_FAILURE_RETRY (__libc_write (fd, buf, (expected_size
+                                                     & (_dl_pagesize - 1))))
+         < 0)
        goto cannot_create;
     }
   else if (st.st_size != expected_size)
index 3a4bd4ae6968e484a40c3eddeb5c6e4b9a60c234..0da19021787cb72d1bd32daa5e1cd4c0f1a1050c 100644 (file)
@@ -66,8 +66,7 @@ RTLD_NEXT used in code not dynamically loaded"));
        l = l->l_loader;
 
       {
-       struct link_map *map = l;
-       struct link_map *mapscope[2] = { map, NULL };
+       struct link_map *mapscope[2] = { l, NULL };
        args->loadbase = _dl_lookup_symbol_skip (args->name, &args->ref,
                                                 mapscope, NULL, match);
       }
index a332c590f2aa98d008c0997bb01f44b07563dfe4..c42b0d768f1ed205d29cf82a374b4cf32e7ef042 100644 (file)
@@ -68,8 +68,7 @@ RTLD_NEXT used in code not dynamically loaded"));
        l = l->l_loader;
 
       {
-       struct link_map *map = l;
-       struct link_map *mapscope[2] = { map, NULL };
+       struct link_map *mapscope[2] = { l, NULL };
        args->loadbase = _dl_lookup_versioned_symbol_skip (args->name,
                                                           &args->ref,
                                                           mapscope,
index 8b72d07883a0ef3644021b1e3a943074fb830268..a7e89e76ac30425840c8b995c93b88584846cee4 100644 (file)
@@ -349,7 +349,7 @@ dl_main (const ElfW(Phdr) *phdr,
         Grant the user some education.  */
       if (_dl_argc < 2)
        _dl_sysdep_fatal ("\
-Usage: ld.so [--list|--verify] EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
 You have invoked `ld.so', the helper program for shared library executables.\n\
 This program usually lives in the file `/lib/ld.so', and special directives\n\
 in executable files using ELF shared libraries tell the system's program\n\
@@ -360,7 +360,13 @@ command line to load and run an ELF executable file; this is like executing\n\
 that file itself, but always uses this helper program from the file you\n\
 specified, instead of the helper program file specified in the executable\n\
 file you run.  This is mostly of use for maintainers to test new versions\n\
-of this helper program; chances are you did not intend to run this program.\n",
+of this helper program; chances are you did not intend to run this program.\n\
+\n\
+  --list               list all dependencies and how they are resolved\n\
+  --verify             verify that given object really is a dynamically linked\n\
+                       object we get handle\n\
+  --library-path PATH  use given PATH instead of content of the environment\n\
+                       variable LD_LIBRARY_PATH\n",
                          NULL);
 
       ++_dl_skip_args;
@@ -509,7 +515,7 @@ of this helper program; chances are you did not intend to run this program.\n",
       while ((p = strsep (&list, " :")) != NULL)
        if (! __libc_enable_secure || strchr (p, '/') == NULL)
          {
-           struct link_map *new_map = _dl_map_object (NULL, p, 1,
+           struct link_map *new_map = _dl_map_object (main_map, p, 1,
                                                       lt_library, 0);
            if (new_map->l_opencount == 1)
              /* It is no duplicate.  */
@@ -570,7 +576,7 @@ of this helper program; chances are you did not intend to run this program.\n",
          runp = file + strspn (file, ": \t\n");
          while ((p = strsep (&runp, ": \t\n")) != NULL)
            {
-             struct link_map *new_map = _dl_map_object (NULL, p, 1,
+             struct link_map *new_map = _dl_map_object (main_map, p, 1,
                                                         lt_library, 0);
              if (new_map->l_opencount == 1)
                /* It is no duplicate.  */
@@ -584,7 +590,7 @@ of this helper program; chances are you did not intend to run this program.\n",
       if (problem != NULL)
        {
          char *p = strndupa (problem, file_size - (problem - file));
-         struct link_map *new_map = _dl_map_object (NULL, p, 1,
+         struct link_map *new_map = _dl_map_object (main_map, p, 1,
                                                     lt_library, 0);
          if (new_map->l_opencount == 1)
            /* It is no duplicate.  */
index 2a94caa3675e95f0fb819b7a114ec6339601eece..cb15fe036b70dfc9c37bdb4c89106f678750dc6d 100644 (file)
@@ -107,17 +107,17 @@ main (int argc, char *argv[])
   int test;
   int fail = 0;
 
-  setenv ("IFS", IFS, 1);
-  for (test = 0; test_case[test].retval != -1; test++)
-    if (testit (&test_case[test]))
-      ++fail;
-
   if (argc > 1)
     {
       command_line_test (argv[1]);
       return 0;
     }
 
+  setenv ("IFS", IFS, 1);
+  for (test = 0; test_case[test].retval != -1; test++)
+    if (testit (&test_case[test]))
+      ++fail;
+
   pw = getpwnam ("root");
   if (pw != NULL)
     {
index 3bc22b57177da00213ae148b2e16edeb3c231c13..869598ade83049dc46fc90f0de2b4aa8d7dd6ff6 100644 (file)
  * This is a recursive-descent-style word expansion routine.
  */
 
+/* This variable is defined and initialized in the startup code.  */
+extern char **__libc_argv;
+
 /* Some forward declarations */
 static int parse_dollars (char **word, size_t *word_length, size_t *max_length,
                          const char *words, size_t *offset, int flags,
                          wordexp_t *pwordexp, const char *ifs,
-                         const char *ifs_white)
+                         const char *ifs_white, int quoted)
      internal_function;
 static int parse_backtick (char **word, size_t *word_length,
                           size_t *max_length, const char *words,
@@ -332,39 +335,73 @@ parse_glob (char **word, size_t *word_length, size_t *max_length,
            const char *words, size_t *offset, int flags,
            wordexp_t *pwordexp, const char *ifs, const char *ifs_white)
 {
-  /* We are poised just after a '*' or a '{'. */
+  /* We are poised just after a '*', a '[' or a '?'. */
   int error;
   glob_t globbuf;
   int match;
   char *matching_word;
+  int quoted = 0; /* 1 if singly-quoted, 2 if doubly */
 
   for (; words[*offset]; (*offset)++)
-    switch (words[*offset])
-      {
-      case ' ':
-      case '\t':
+    {
+      if ((ifs && strchr (ifs, words[*offset])) ||
+         (!ifs && strchr (" \t\n", words[*offset])))
+       /* Reached IFS */
        break;
 
-      case '$':
-       error = parse_dollars (word, word_length, max_length, words, offset,
-                              flags, pwordexp, ifs, ifs_white);
-       if (error)
-         return error;
-
-       continue;
-
-      default:
-       if (ifs == NULL || strchr (ifs, words[*offset]) == NULL)
+      /* Sort out quoting */
+      if (words[*offset] == '\'')
+         if (quoted == 0)
+           {
+             quoted = 1;
+             continue;
+           }
+         else if (quoted == 1)
+           {
+             quoted = 0;
+             continue;
+           }
+      else if (words[*offset] == '"')
+       if (quoted == 0)
          {
-           *word = w_addchar (*word, word_length, max_length, words[*offset]);
-           if (*word == NULL)
-             return WRDE_NOSPACE;
-
+           quoted = 2;
+           continue;
+         }
+       else if (quoted == 2)
+         {
+           quoted = 0;
            continue;
          }
 
-       break;
-      }
+      /* Sort out other special characters */
+      if (quoted != 1 && words[*offset] == '$')
+       {
+         error = parse_dollars (word, word_length, max_length, words, offset,
+                                flags, pwordexp, ifs, ifs_white, quoted == 2);
+         if (error)
+           return error;
+
+         continue;
+       }
+      else if (words[*offset] == '\\')
+       {
+         if (quoted)
+           error = parse_qtd_backslash (word, word_length, max_length, words,
+                                        offset);
+         else
+           error = parse_backslash (word, word_length, max_length, words,
+                                    offset);
+
+         if (error)
+           return error;
+
+         continue;
+       }
+
+      *word = w_addchar (*word, word_length, max_length, words[*offset]);
+      if (*word == NULL)
+       return WRDE_NOSPACE;
+    }
 
   error = glob (*word, GLOB_NOCHECK, NULL, &globbuf);
 
@@ -599,8 +636,8 @@ parse_arith (char **word, size_t *word_length, size_t *max_length,
        {
        case '$':
          error = parse_dollars (&expr, &expr_length, &expr_maxlen,
-                                words, offset, flags, NULL, NULL, NULL);
-         /* The first NULL here is to tell parse_dollars not to
+                                words, offset, flags, NULL, NULL, NULL, 1);
+         /* The ``1'' here is to tell parse_dollars not to
           * split the fields.
           */
          if (error)
@@ -951,7 +988,7 @@ static int
 internal_function
 parse_param (char **word, size_t *word_length, size_t *max_length,
             const char *words, size_t *offset, int flags, wordexp_t *pwordexp,
-            const char *ifs, const char *ifs_white)
+            const char *ifs, const char *ifs_white, int quoted)
 {
   /* We are poised just after "$" */
   enum remove_pattern_enum
@@ -969,13 +1006,14 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
   size_t pat_maxlen = 0;
   char *env = NULL;
   char *pattern = NULL;
-  char *value;
+  char *value = NULL;
   char action = '\0';
   enum remove_pattern_enum remove = RP_NONE;
   int colon_seen = 0;
   int depth = 0;
   int substitute_length = 0;
   int error;
+  int star = 0;
 
   for (; words[*offset]; ++(*offset))
     {
@@ -1164,12 +1202,16 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
              break;
            }
 
-         if ((words[start] == '{') || isalpha (words[*offset]))
+         star = strchr ("*@", words[*offset]) != NULL;
+         if (isalnum (words[*offset]) || star)
            {
              env = w_addchar (env, &env_length, &env_maxlen, words[*offset]);
              if (env == NULL)
                goto no_space;
 
+             if (star)
+               goto envsubst;
+
              break;
            }
 
@@ -1178,7 +1220,8 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
        }
     }
 
-  /* End of input string */
+  /* End of input string -- remember to reparse the character that we stopped
+   * at.  */
   --(*offset);
 
 envsubst:
@@ -1190,10 +1233,92 @@ envsubst:
       *offset = start - 1;
       *word = w_addchar (*word, word_length, max_length, '$');
       free (env);
-      free (pattern);
       return *word ? 0 : WRDE_NOSPACE;
     }
 
+  /* Is it `$*' or `$@' ? */
+  if (strpbrk (env, "*@") != NULL)
+    {
+      size_t plist_len = 1;
+      int p;
+
+      if (env[1] != '\0')
+       {
+         /* Bad substitution if there is more than one character */
+         fprintf (stderr, "${%s}: bad substitution\n", env);
+         return WRDE_SYNTAX;
+       }
+
+      if (!quoted || *env == '*')
+       {
+         /* Build up value parameter by parameter (copy them) */
+         for (p = 1; __libc_argv[p]; p++)
+           {
+             char * old_pointer = value;
+
+             if (value)
+               value[plist_len - 1] = 0;
+
+             plist_len += 1 + strlen (__libc_argv[p]);
+
+             /* First realloc will act as malloc because value is
+              * initialised to NULL. */
+             value = realloc (value, plist_len);
+             if (value == NULL)
+               {
+                 free (old_pointer);
+                 return WRDE_NOSPACE;
+               }
+
+             strcat (value, __libc_argv[p]);
+             if (__libc_argv[p + 1])
+               {
+                 value[plist_len - 1] = '\0';
+                 value[plist_len - 2] = ' ';
+               }
+           }
+
+         if (value)
+           goto maybe_fieldsplit;
+       }
+
+      /* Each parameter is a separate word ("$@") */
+      if (__libc_argv[0] == NULL)
+       {
+         /* This can happen if the application is started without any
+            parameter, not even a name.  This is legal according to
+            POSIX since the giving parameters is only a "should" rule.  */
+         *word = __strdup ("");
+         *max_length = *word_length = 0;
+       }
+      else
+       {
+         for (p = 1; __libc_argv[p + 1]; p++)
+           {
+             char *copy = __strdup (__libc_argv[p]);
+             if (copy == NULL)
+               return WRDE_NOSPACE;
+
+             strcpy (copy, __libc_argv[p]);
+             error = w_addword (pwordexp, copy);
+             if (error)
+               {
+                 free (copy);
+                 return error;
+               }
+           }
+
+         /* Last parameter becomes current word */
+         if (__libc_argv[p])
+           {
+             *word = __strdup (__libc_argv[p]);
+             *max_length = *word_length = strlen (*word);
+           }
+       }
+
+      return 0;
+    }
+
   value = getenv (env);
 
   if (action != '\0' || remove != RP_NONE)
@@ -1343,18 +1468,19 @@ envsubst:
          {
            /* Substitute word */
            wordexp_t we;
-           char *expand_me = pattern;
            int i;
 
-           if (pwordexp == NULL)
+           if (quoted)
              {
                /* No field-splitting is allowed, so imagine
                   quotes around the word.  */
-               expand_me = alloca (strlen (pattern) + 2);
-               sprintf (expand_me, "\"%s\"", pattern);
+               char *qtd_pattern = malloc (3 + strlen (pattern));
+               sprintf (qtd_pattern, "\"%s\"", pattern);
+               free (pattern);
+               pattern = qtd_pattern;
              }
 
-           error = wordexp (expand_me, &we, flags);
+           error = wordexp (pattern, &we, flags);
            if (error)
              {
                free (env);
@@ -1363,7 +1489,7 @@ envsubst:
              }
 
            /* Fingers crossed that the quotes worked.. */
-           assert (pwordexp || we.we_wordc == 1);
+           assert (!quoted || we.we_wordc == 1);
 
            /* Substitute */
            for (i = 0; i < we.we_wordc; i++)
@@ -1439,7 +1565,8 @@ envsubst:
     }
 
 
-  if (pwordexp == NULL)
+ maybe_fieldsplit:
+  if (quoted || !pwordexp)
     {
       /* Quoted - no field split */
       *word = w_addstr (*word, word_length, max_length, value);
@@ -1454,10 +1581,19 @@ envsubst:
       do
        {
          char *field_end = field_begin;
-         char *field;
          char *next_field;
          char ch;
 
+         /* If this isn't the first field, start a new word */
+         if (field_begin != value)
+           {
+             if (w_addword (pwordexp, *word) == WRDE_NOSPACE)
+               return WRDE_NOSPACE;
+
+             *word = NULL;
+             *word_length = *max_length = 0;
+           }
+
          /* Skip IFS whitespace before the field */
          while (*field_begin && strchr (ifs_white, *field_begin) != NULL)
            field_begin++;
@@ -1487,21 +1623,14 @@ envsubst:
              next_field++;
            }
 
-         /* Null-terminate it and make a copy */
+         /* Null-terminate it */
          *field_end = 0;
-         field = __strdup (field_begin);
-         if (field == NULL)
-           return WRDE_NOSPACE;
 
-         /* Tag the field onto the word list */
-         if (w_addword (pwordexp, field) == WRDE_NOSPACE)
-           {
-             free (field);
-             return WRDE_NOSPACE;
-           }
-
-         *word = NULL;
-         *word_length = *max_length = 0;
+         /* Tag a copy onto the current word */
+         *word = w_addstr (*word, word_length, max_length,
+                           __strdup (field_begin));
+         if (*word == NULL)
+           return WRDE_NOSPACE;
 
          field_begin = next_field;
        } while (seen_nonws_ifs || (field_begin && *field_begin));
@@ -1532,7 +1661,8 @@ static int
 internal_function
 parse_dollars (char **word, size_t *word_length, size_t *max_length,
               const char *words, size_t *offset, int flags,
-              wordexp_t *pwordexp, const char *ifs, const char *ifs_white)
+              wordexp_t *pwordexp, const char *ifs, const char *ifs_white,
+              int quoted)
 {
   /* We are poised _at_ "$" */
   switch (words[1 + *offset])
@@ -1546,10 +1676,17 @@ parse_dollars (char **word, size_t *word_length, size_t *max_length,
     case '(':
       if (words[2 + *offset] == '(')
        {
-         (*offset) += 3;
-         /* Call parse_arith -- 0 is for "no brackets" */
-         return parse_arith (word, word_length, max_length, words, offset,
-                             flags, 0);
+         /* Differentiate between $((1+3)) and $((echo);(ls)) */
+         int i = 3 + *offset;
+         while (words[i] && words[i] != ')')
+           ++i;
+         if (words[i] == ')' && words[i + 1] == ')')
+           {
+             (*offset) += 3;
+             /* Call parse_arith -- 0 is for "no brackets" */
+             return parse_arith (word, word_length, max_length, words, offset,
+                                 flags, 0);
+           }
        }
 
       if (flags & WRDE_NOCMD)
@@ -1569,7 +1706,7 @@ parse_dollars (char **word, size_t *word_length, size_t *max_length,
     default:
       ++(*offset);     /* parse_param needs to know if "{" is there */
       return parse_param (word, word_length, max_length, words, offset, flags,
-                         pwordexp, ifs, ifs_white);
+                          pwordexp, ifs, ifs_white, quoted);
     }
 }
 
@@ -1640,7 +1777,8 @@ parse_backtick (char **word, size_t *word_length, size_t *max_length,
 static int
 internal_function
 parse_dquote (char **word, size_t *word_length, size_t *max_length,
-             const char *words, size_t *offset, int flags)
+             const char *words, size_t *offset, int flags,
+             wordexp_t *pwordexp, const char * ifs, const char * ifs_white)
 {
   /* We are poised just after a double-quote */
   int error;
@@ -1654,9 +1792,9 @@ parse_dquote (char **word, size_t *word_length, size_t *max_length,
 
        case '$':
          error = parse_dollars (word, word_length, max_length, words, offset,
-                                flags, NULL, NULL, NULL);
-         /* The first NULL here is to tell parse_dollars not to
-          * split the fields.
+                                flags, pwordexp, ifs, ifs_white, 1);
+         /* The ``1'' here is to tell parse_dollars not to
+          * split the fields.  It may need to, however ("$@").
           */
          if (error)
            return error;
@@ -1735,7 +1873,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
   char *ifs;
   char ifs_white[4];
   char **old_wordv = pwordexp->we_wordv;
-  size_t old_wordc = pwordexp->we_wordc;
+  size_t old_wordc = (flags & WRDE_REUSE) ? pwordexp->we_wordc : 0;
 
   if (flags & WRDE_REUSE)
     /* Minimal implementation of WRDE_REUSE for now */
@@ -1826,7 +1964,8 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
 
       case '$':
        error = parse_dollars (&word, &word_length, &max_length, words,
-                              &words_offset, flags, pwordexp, ifs, ifs_white);
+                              &words_offset, flags, pwordexp, ifs, ifs_white,
+                              0);
 
        if (error)
          goto do_error;
@@ -1850,7 +1989,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
       case '"':
        ++words_offset;
        error = parse_dquote (&word, &word_length, &max_length, words,
-                             &words_offset, flags);
+                             &words_offset, flags, pwordexp, ifs, ifs_white);
 
        if (error)
          goto do_error;
@@ -1877,7 +2016,8 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
        break;
 
       case '*':
-      case '{':
+      case '[':
+      case '?':
        error = parse_glob (&word, &word_length, &max_length, words,
                            &words_offset, flags, pwordexp, ifs, ifs_white);
 
@@ -1953,8 +2093,13 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
 
 do_error:
   /* Error:
-       free memory used, set we_wordc and wd_wordv back to what they were.
+   *   free memory used (unless error is WRDE_NOSPACE), and
+   *   set we_wordc and wd_wordv back to what they were.
    */
+
+  if (error == WRDE_NOSPACE)
+    return WRDE_NOSPACE;
+
   if (word != NULL)
     free (word);