about summary refs log tree commit diff
path: root/posix
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1998-03-08 15:26:29 +0000
committerUlrich Drepper <drepper@redhat.com>1998-03-08 15:26:29 +0000
commit3db52d94e2dcd03925664df6a09e9676ad1f2baf (patch)
treec52e151c8bbe577afba5c1e6a0a084641853ce7f /posix
parenta78de796b7911443b7af40177ee9c33cf69a82e5 (diff)
downloadglibc-3db52d94e2dcd03925664df6a09e9676ad1f2baf.tar.gz
glibc-3db52d94e2dcd03925664df6a09e9676ad1f2baf.tar.xz
glibc-3db52d94e2dcd03925664df6a09e9676ad1f2baf.zip
Update.
1998-03-08 14:58  Ulrich Drepper  <drepper@cygnus.com>

	* Makeconfig (nisobjdir): Set to path to nis directory.
	(rpath-link): Add nisobjdir.
	Patch by Sven Verdoolaege <skimo@kotnet.org>.

	* elf/Makefile: Pretty print.
	* elf/dl-lookup.c: Include unistd.h.
	* elf/dl-runtime.c: Likewise.

	* localedata/Makefile (test-srcs): Add tst-rpmatch.
	(distribute): Add tst-rpmatch.sh.
	(tests): Add tst-rpmatch to dependency list and run tst-rpmatch.sh.

	New tests for rpmatch function by
	Jochen Hein <jochen.hein@delphi.central.de>.
	* localedata/tst-rpmatch.c: New file.
	* localedata/tst-rpmatch.sh: New file.

	* localedata/locales/de_DE: Correct yesexpr and noexpr.
	* localedata/locales/de_AT: Likewise.

	* posix/getopt.c: Update contact address.
	* posix/getopt1.c: Pretty print.

	* sysdeps/generic/libc-start.c: Do most of the initialization now
	here instead of in start.S.
	* sysdeps/unix/sysv/linux/libc-start.c: Likewise.
	* sysdeps/i386/elf/start.S: Remove most of the initialization code.

	* sysdeps/unix/sysv/linux/i386/profil-counter.h: No need for
	profil_counter to be public.

1998-03-08 13:06  Tim Waugh  <tim@cyberelk.demon.co.uk>

	* posix/wordexp.c (parse_arith): Now works for negative numbers too.
	(parse_param): Coded parameter length expansion (${#var}).
	(parse_param): Handling for "=", "+", "-", and the ":" versions added.
	(parse_param): Cleaned up (fixed) error handling.

	* posix/wordexp-test.c: IFS now includes non-whitespace character
	(comma).  Added more tests.
Diffstat (limited to 'posix')
-rw-r--r--posix/getopt.c14
-rw-r--r--posix/getopt1.c7
-rw-r--r--posix/wordexp-test.c49
-rw-r--r--posix/wordexp.c252
4 files changed, 252 insertions, 70 deletions
diff --git a/posix/getopt.c b/posix/getopt.c
index 3199925828..7afe6c4a96 100644
--- a/posix/getopt.c
+++ b/posix/getopt.c
@@ -1,9 +1,9 @@
 /* Getopt for GNU.
    NOTE: getopt is now part of the C library, so if you don't know what
-   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   "Keep this file name-space clean" means, talk to drepper@gnu.org
    before changing it!
 
-   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
    	Free Software Foundation, Inc.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -31,7 +31,7 @@
 #include <config.h>
 #endif
 
-#if !defined (__STDC__) || !__STDC__
+#if !defined __STDC__ || !__STDC__
 /* This is a separate conditional since some stdc systems
    reject `defined (const)'.  */
 #ifndef const
@@ -50,7 +50,7 @@
    it is simpler to just do this in the source for each such file.  */
 
 #define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
 #include <gnu-versions.h>
 #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
 #define ELIDE_CODE
@@ -222,7 +222,7 @@ my_index (str, chr)
 #ifdef __GNUC__
 /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
    That was relevant to code that was here before.  */
-#if !defined (__STDC__) || !__STDC__
+#if !defined __STDC__ || !__STDC__
 /* gcc with -traditional declares the built-in strlen to return int,
    and has done so at least since version 2.4.5. -- rms.  */
 extern int strlen (const char *);
@@ -289,7 +289,7 @@ text_set_element (__libc_subinit, store_args_and_env);
    `first_nonopt' and `last_nonopt' are relocated so that they describe
    the new indices of the non-options in ARGV after they are moved.  */
 
-#if defined (__STDC__) && __STDC__
+#if defined __STDC__ && __STDC__
 static void exchange (char **);
 #endif
 
@@ -375,7 +375,7 @@ exchange (argv)
 
 /* Initialize the internal data when the first call is made.  */
 
-#if defined (__STDC__) && __STDC__
+#if defined __STDC__ && __STDC__
 static const char *_getopt_initialize (int, char *const *, const char *);
 #endif
 static const char *
diff --git a/posix/getopt1.c b/posix/getopt1.c
index 4aa8de6f67..3d264f2db4 100644
--- a/posix/getopt1.c
+++ b/posix/getopt1.c
@@ -1,5 +1,6 @@
 /* getopt_long and getopt_long_only entry points for GNU getopt.
-   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+     Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -23,7 +24,7 @@
 
 #include "getopt.h"
 
-#if !defined (__STDC__) || !__STDC__
+#if !defined __STDC__ || !__STDC__
 /* This is a separate conditional since some stdc systems
    reject `defined (const)'.  */
 #ifndef const
@@ -42,7 +43,7 @@
    it is simpler to just do this in the source for each such file.  */
 
 #define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
 #include <gnu-versions.h>
 #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
 #define ELIDE_CODE
diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
index 604e685607..75cfe5f360 100644
--- a/posix/wordexp-test.c
+++ b/posix/wordexp-test.c
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 #include <wordexp.h>
 
+#define IFS ", \n\t"
+
 struct test_case_struct
 {
   int retval;
@@ -31,12 +33,57 @@ struct test_case_struct
   const char *wordv[10];
 } test_case[] =
   {
+    /* Simple word-splitting */
     { 0, NULL, "one", 0, 1, { "one", } },
     { 0, NULL, "one two", 0, 2, { "one", "two", } },
     { 0, NULL, "one two three", 0, 3, { "one", "two", "three", } },
+
+    /* Simple parameter expansion */
     { 0, "foo", "${var}", 0, 1, { "foo", } },
     { 0, "foo", "$var", 0, 1, { "foo", } },
+
+    /* Simple quote removal */
     { 0, NULL, "\"quoted\"", 0, 1, { "quoted", } },
+    { 0, "foo", "\"$var\"\"$var\"", 0, 1, { "foofoo", } },
+    { 0, NULL, "'singly-quoted'", 0, 1, { "singly-quoted", } },
+
+    /* Simple command substitution */
+    { 0, NULL, "$(echo hello)", 0, 1, { "hello", } },
+    { 0, NULL, "$( (echo hello) )", 0, 1, { "hello", } },
+
+    /* Simple arithmetic expansion */
+    { 0, NULL, "$((1 + 1))", 0, 1, { "2", } },
+    { 0, NULL, "$((2-3))", 0, 1, { "-1", } },
+    { 0, NULL, "$((-1))", 0, 1, { "-1", } },
+
+    /* Field splitting */
+    { 0, NULL, " \tfoo\t\tbar ", 0, 2, { "foo", "bar", } },
+    { 0, NULL, "  red  , white blue", 0, 3, { "red", "white", "blue", } },
+
+    /* Advanced parameter expansion */
+    { 0, NULL, "${var:-bar}", 0, 1, { "bar", } },
+    { 0, NULL, "${var-bar}", 0, 1, { "bar", } },
+    { 0, "", "${var:-bar}", 0, 1, { "bar", } },
+    { 0, "foo", "${var:-bar}", 0, 1, { "foo", } },
+    { 0, "", "${var-bar}", 0, 0, { NULL, } },
+    { 0, NULL, "${var:=bar}", 0, 1, { "bar", } },
+    { 0, NULL, "${var=bar}", 0, 1, { "bar", } },
+    { 0, "", "${var:=bar}", 0, 1, { "bar", } },
+    { 0, "foo", "${var:=bar}", 0, 1, { "foo", } },
+    { 0, "", "${var=bar}", 0, 0, { NULL, } },
+    { 0, "foo", "${var:?bar}", 0, 1, { "foo", } },
+    { 0, NULL, "${var:+bar}", 0, 0, { NULL, } },
+    { 0, NULL, "${var+bar}", 0, 0, { NULL, } },
+    { 0, "", "${var:+bar}", 0, 0, { NULL, } },
+    { 0, "foo", "${var:+bar}", 0, 1, { "bar", } },
+    { 0, "", "${var+bar}", 0, 1, { "bar", } },
+    { 0, "12345", "${#var}", 0, 1, { "5", } },
+
+    { 0, "banana", "${var%na*}", 0, 1, { "bana", } },
+    { 0, "banana", "${var%%na*}", 0, 1, { "ba", } },
+    { 0, "borabora-island", "${var#*bora}", 0, 1, { "bora-island", } },
+    { 0, "borabora-island", "${var##*bora}", 0, 1, {"-island", } },
+
     { -1, NULL, NULL, 0, 0, { NULL, } },
   };
 
@@ -49,7 +96,7 @@ main (int argc, char * argv[])
   int test;
   int fail = 0;
 
-  setenv ("IFS", " \t\n", 1);
+  setenv ("IFS", IFS, 1);
   for (test = 0; test_case[test].retval != -1; test++)
     if (testit (&test_case[test]))
       ++fail;
diff --git a/posix/wordexp.c b/posix/wordexp.c
index abd55a512c..3bc22b5717 100644
--- a/posix/wordexp.c
+++ b/posix/wordexp.c
@@ -59,7 +59,7 @@ static int parse_backtick (char **word, size_t *word_length,
 			   size_t *offset, int flags, wordexp_t *pwordexp,
 			   const char *ifs, const char *ifs_white)
      internal_function;
-static int eval_expr (char *expr, int *result) internal_function;
+static int eval_expr (char *expr, long int *result) internal_function;
 
 /* The w_*() functions manipulate word lists. */
 
@@ -93,16 +93,12 @@ w_addchar (char *buffer, size_t *actlen, size_t *maxlen, char ch)
 }
 
 static char *
-w_addstr (char *buffer, size_t *actlen, size_t *maxlen, const char *str)
-     /* (lengths exclude trailing zero) */
+internal_function
+w_addmem (char *buffer, size_t *actlen, size_t *maxlen, const char *str,
+	  size_t len)
 {
   /* Add a string to the buffer, allocating room for it if needed.
    */
-  size_t len;
-
-  assert (str != NULL); /* w_addstr only called from this file */
-  len = strlen (str);
-
   if (*actlen + len > *maxlen)
     {
       char *old_buffer = buffer;
@@ -123,7 +119,24 @@ w_addstr (char *buffer, size_t *actlen, size_t *maxlen, const char *str)
   return buffer;
 }
 
+
+static char *
+internal_function
+w_addstr (char *buffer, size_t *actlen, size_t *maxlen, const char *str)
+     /* (lengths exclude trailing zero) */
+{
+  /* Add a string to the buffer, allocating room for it if needed.
+   */
+  size_t len;
+
+  assert (str != NULL); /* w_addstr only called from this file */
+  len = strlen (str);
+
+  return w_addmem (buffer, actlen, maxlen, str, len);
+}
+
 static int
+internal_function
 w_addword (wordexp_t *pwordexp, char *word)
 {
   /* Add a word to the wordlist */
@@ -229,8 +242,8 @@ parse_tilde (char **word, size_t *word_length, size_t *max_length,
     {
       if (!((*word)[*word_length - 1] == '=' && wordc == 0))
 	{
-	  if (!((*word)[*word_length - 1] == ':' &&
-		strchr (*word, '=') && wordc == 0))
+	  if (!((*word)[*word_length - 1] == ':'
+		&& strchr (*word, '=') && wordc == 0))
 	    {
 	      *word = w_addchar (*word, word_length, max_length, '~');
 	      return *word ? 0 : WRDE_NOSPACE;
@@ -422,6 +435,7 @@ no_space:
 }
 
 static int
+internal_function
 parse_squote (char **word, size_t *word_length, size_t *max_length,
 	      const char *words, size_t *offset)
 {
@@ -444,7 +458,7 @@ parse_squote (char **word, size_t *word_length, size_t *max_length,
 /* Functions to evaluate an arithmetic expression */
 static int
 internal_function
-eval_expr_val (char **expr, int *result)
+eval_expr_val (char **expr, long int *result)
 {
   int sgn = +1;
   char *digit;
@@ -495,12 +509,12 @@ eval_expr_val (char **expr, int *result)
 
 static int
 internal_function
-eval_expr_multdiv (char **expr, int *result)
+eval_expr_multdiv (char **expr, long int *result)
 {
-  int arg;
+  long int arg;
 
   /* Read a Value */
-  if (eval_expr_val (expr, result))
+  if (eval_expr_val (expr, result) != 0)
     return WRDE_SYNTAX;
 
   while (**expr)
@@ -510,16 +524,16 @@ eval_expr_multdiv (char **expr, int *result)
 
       if (**expr == '*')
 	{
-	  (*expr)++;
-	  if ((eval_expr_val (expr, &arg)) != 0)
+	  ++(*expr);
+	  if (eval_expr_val (expr, &arg) != 0)
 	    return WRDE_SYNTAX;
 
 	  *result *= arg;
 	}
       else if (**expr == '/')
 	{
-	  (*expr)++;
-	  if ((eval_expr_val (expr, &arg)) != 0)
+	  ++(*expr);
+	  if (eval_expr_val (expr, &arg) != 0)
 	    return WRDE_SYNTAX;
 
 	  *result /= arg;
@@ -532,12 +546,12 @@ eval_expr_multdiv (char **expr, int *result)
 
 static int
 internal_function
-eval_expr (char *expr, int *result)
+eval_expr (char *expr, long int *result)
 {
-  int arg;
+  long int arg;
 
   /* Read a Multdiv */
-  if ((eval_expr_multdiv (&expr, result)) != 0)
+  if (eval_expr_multdiv (&expr, result) != 0)
     return WRDE_SYNTAX;
 
   while (*expr)
@@ -547,16 +561,16 @@ eval_expr (char *expr, int *result)
 
       if (*expr == '+')
 	{
-	  expr++;
-	  if ((eval_expr_multdiv (&expr, &arg)) != 0)
+	  ++expr;
+	  if (eval_expr_multdiv (&expr, &arg) != 0)
 	    return WRDE_SYNTAX;
 
 	  *result += arg;
 	}
       else if (*expr == '-')
 	{
-	  expr++;
-	  if ((eval_expr_multdiv (&expr, &arg)) != 0)
+	  ++expr;
+	  if (eval_expr_multdiv (&expr, &arg) != 0)
 	    return WRDE_SYNTAX;
 
 	  *result -= arg;
@@ -628,7 +642,8 @@ parse_arith (char **word, size_t *word_length, size_t *max_length,
 	  if (--paren_depth == 0)
 	    {
 	      char result[21];	/* 21 = ceil(log10(2^64)) + 1 */
-	      int numresult = 0;
+	      long int numresult = 0;
+	      long long int convertme;
 
 	      if (bracket || words[1 + *offset] != ')')
 		return WRDE_SYNTAX;
@@ -636,13 +651,25 @@ parse_arith (char **word, size_t *word_length, size_t *max_length,
 	      ++(*offset);
 
 	      /* Go - evaluate. */
-	      if (*expr &&
-		  eval_expr (expr, &numresult) != 0)
+	      if (*expr && eval_expr (expr, &numresult) != 0)
 		return WRDE_SYNTAX;
 
+	      if (numresult < 0)
+		{
+		  convertme = -numresult;
+		  *word = w_addchar (*word, word_length, max_length, '-');
+		  if (!*word)
+		    {
+		      free (expr);
+		      return WRDE_NOSPACE;
+		    }
+		}
+	      else
+		convertme = numresult;
+
 	      result[20] = '\0';
 	      *word = w_addstr (*word, word_length, max_length,
-				_itoa_word (numresult, &result[20], 10, 0));
+				_itoa (convertme, &result[20], 10, 0));
 	      free (expr);
 	      return *word ? 0 : WRDE_NOSPACE;
 	    }
@@ -656,7 +683,7 @@ parse_arith (char **word, size_t *word_length, size_t *max_length,
 	  if (bracket && paren_depth == 1)
 	    {
 	      char result[21];	/* 21 = ceil(log10(2^64)) + 1 */
-	      int numresult = 0;
+	      long int numresult = 0;
 
 	      /* Go - evaluate. */
 	      if (*expr && eval_expr (expr, &numresult) != 0)
@@ -758,14 +785,11 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
 		break;
 	    }
 
-	  for (i = 0; i < buflen; ++i)
+	  *word = w_addmem (*word, word_length, max_length, buffer, buflen);
+	  if (*word == NULL)
 	    {
-	      *word = w_addchar (*word, word_length, max_length, buffer[i]);
-	      if (*word == NULL)
-		{
-		  close (fildes[0]);
-		  return WRDE_NOSPACE;
-		}
+	      close (fildes[0]);
+	      return WRDE_NOSPACE;
 	    }
 	}
 
@@ -874,6 +898,7 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
 }
 
 static int
+internal_function
 parse_comm (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)
@@ -949,6 +974,7 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
   enum remove_pattern_enum remove = RP_NONE;
   int colon_seen = 0;
   int depth = 0;
+  int substitute_length = 0;
   int error;
 
   for (; words[*offset]; ++(*offset))
@@ -997,11 +1023,7 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	  goto envsubst;
 
 	case '#':
-	  /* At the start?  (ie. 'string length') */
-	  if (*offset == start + 1)
-	    /* FIXME: This isn't written yet! */
-	    break;
-
+	  /* '#' only has special meaning inside braces */
 	  if (words[start] != '{')
 	    {
 	      /* Evaluate */
@@ -1010,8 +1032,16 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	      goto envsubst;
 	    }
 
-	  /* Separating variable name from prefix pattern? */
+	  /* At the start? (i.e. 'string length') */
+	  if (*offset == start + 1)
+	    {
+	      substitute_length = 1;
+	      break;
+	    }
+	  else if (substitute_length)
+	    goto syntax;
 
+	  /* Separating variable name from prefix pattern? */
 	  if (remove == RP_NONE)
 	    {
 	      remove = RP_SHORT_LEFT;
@@ -1069,8 +1099,8 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	      break;
 	    }
 
-	  if ((words[1 + *offset] == '-') || (words[1 + *offset] == '=') ||
-	      (words[1 + *offset] == '?') || (words[1 + *offset] == '+'))
+	  if ((words[1 + *offset] == '-') || (words[1 + *offset] == '=')
+	      || (words[1 + *offset] == '?') || (words[1 + *offset] == '+'))
 	    {
 	      colon_seen = 1;
 	      break;
@@ -1085,6 +1115,9 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	  if (!*env)
 	    goto syntax;
 
+	  if (substitute_length)
+	    goto syntax;
+
 	  if (action != '\0' || remove != RP_NONE)
 	    {
 	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
@@ -1184,7 +1217,7 @@ envsubst:
 	    switch (remove)
 	      {
 	      case RP_SHORT_LEFT:
-		for (p = value; p <= end; p++)
+		for (p = value; p <= end; ++p)
 		  {
 		    c = *p;
 		    *p = '\0';
@@ -1200,7 +1233,7 @@ envsubst:
 		break;
 
 	      case RP_LONG_LEFT:
-		for (p = end; p >= value; p--)
+		for (p = end; p >= value; --p)
 		  {
 		    c = *p;
 		    *p = '\0';
@@ -1216,7 +1249,7 @@ envsubst:
 		break;
 
 	      case RP_SHORT_RIGHT:
-		for (p = end; p >= value; p--)
+		for (p = end; p >= value; --p)
 		  {
 		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
 		      {
@@ -1228,7 +1261,7 @@ envsubst:
 		break;
 
 	      case RP_LONG_RIGHT:
-		for (p = value; p <= end; p++)
+		for (p = value; p <= end; ++p)
 		  {
 		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
 		      {
@@ -1248,6 +1281,7 @@ envsubst:
 
 	case '?':
 	  if (value && *value)
+	    /* Substitute parameter */
 	    break;
 
 	  if (!colon_seen && value)
@@ -1292,8 +1326,94 @@ envsubst:
 	  free (pattern);
 	  return WRDE_BADVAL;
 
+	case '-':
+	  if (value && *value)
+	    /* Substitute parameter */
+	    break;
+
+	  if (!colon_seen && value)
+	    {
+	      /* Substitute NULL */
+	      free (env);
+	      free (pattern);
+	      return 0;
+	    }
+
+	subst_word:
+	  {
+	    /* Substitute word */
+	    wordexp_t we;
+	    char *expand_me = pattern;
+	    int i;
+
+	    if (pwordexp == NULL)
+	      {
+		/* No field-splitting is allowed, so imagine
+		   quotes around the word.  */
+		expand_me = alloca (strlen (pattern) + 2);
+		sprintf (expand_me, "\"%s\"", pattern);
+	      }
+
+	    error = wordexp (expand_me, &we, flags);
+	    if (error)
+	      {
+		free (env);
+		free (pattern);
+		return error;
+	      }
+
+	    /* Fingers crossed that the quotes worked.. */
+	    assert (pwordexp || we.we_wordc == 1);
+
+	    /* Substitute */
+	    for (i = 0; i < we.we_wordc; i++)
+	      if (w_addword (pwordexp, __strdup(we.we_wordv[i]))
+		  == WRDE_NOSPACE)
+		break;
+
+	    if (action == '=')
+	      /* Also assign */
+	      setenv (env, we.we_wordv[0], 1); /* need to strdup? */
+
+	    wordfree (&we);
+
+	    if (i < we.we_wordc)
+	      /* Ran out of space */
+	      goto no_space;
+
+	    return 0;
+	  }
+
+	case '+':
+	  if (value && *value)
+	    goto subst_word;
+
+	  if (!colon_seen && value)
+	    goto subst_word;
+
+	  /* Substitute NULL */
+	  free (env);
+	  free (pattern);
+	  return 0;
+
+	case '=':
+	  if (value && *value)
+	    /* Substitute parameter */
+	    break;
+
+	  if (!colon_seen && value)
+	    {
+	      /* Substitute NULL */
+	      free (env);
+	      free (pattern);
+	      return 0;
+	    }
+
+	  /* This checks for '=' so it knows to assign */
+	  goto subst_word;
+
 	default:
-	  printf ("warning: parameter substitution does not yet support \"%s%c\"\n", colon_seen?":":"", action);
+	  assert (! "Unrecognised action!");
 	}
     }
 
@@ -1309,9 +1429,22 @@ envsubst:
       return 0;
     }
 
+  if (substitute_length)
+    {
+      char param_length[21];
+      param_length[20] = '\0';
+      *word = w_addstr (*word, word_length, max_length,
+			_itoa_word (strlen (value), &param_length[20], 10, 0));
+      return *word ? 0 : WRDE_NOSPACE;
+    }
+
+
   if (pwordexp == NULL)
-    /* Quoted - no field split */
-    *word = w_addstr (*word, word_length, max_length, value);
+    {
+      /* Quoted - no field split */
+      *word = w_addstr (*word, word_length, max_length, value);
+      return *word ? 0 : WRDE_NOSPACE;
+    }
   else
     {
       /* Need to field-split */
@@ -1331,7 +1464,7 @@ envsubst:
 
 	  if (!seen_nonws_ifs && *field_begin == 0)
 	    /* Nothing but whitespace */
-	    return 0;
+	    break;
 
 	  /* Search for the end of the field */
 	  field_end = field_begin;
@@ -1358,22 +1491,23 @@ envsubst:
 	  *field_end = 0;
 	  field = __strdup (field_begin);
 	  if (field == NULL)
-	    goto no_space;
+	    return WRDE_NOSPACE;
 
 	  /* Tag the field onto the word list */
 	  if (w_addword (pwordexp, field) == WRDE_NOSPACE)
-	    return WRDE_NOSPACE;
+	    {
+	      free (field);
+	      return WRDE_NOSPACE;
+	    }
 
 	  *word = NULL;
 	  *word_length = *max_length = 0;
 
 	  field_begin = next_field;
 	} while (seen_nonws_ifs || (field_begin && *field_begin));
-
-      return 0;
     }
 
-  return *word ? 0 : WRDE_NOSPACE;
+  return 0;
 
 no_space:
   if (env)
@@ -1658,7 +1792,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
 		*whch++ = *ifsch;
 	    }
 
-	  ifsch++;
+	  ++ifsch;
 	}
       *whch = '\0';
     }