Requires the following GNU-specific LibC functions: strchrnul(),
mbsnrtowcs().
Todo: The configuration step should check that they are available.
diff -Naur bash-4.1/lib/glob/xmbsrtowcs.c bash-4.1-fasterWideCharConversion/lib/glob/xmbsrtowcs.c
--- bash-4.1/lib/glob/xmbsrtowcs.c	2008-08-12 16:17:54.000000000 +0200
+++ bash-4.1-fasterWideCharConversion/lib/glob/xmbsrtowcs.c	2010-03-03 16:40:58.664811945 +0100
@@ -18,6 +18,14 @@
    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+/* Tell glibc's <string.h> and <wchar.h> to provide
+   prototypes for strchrnul() and mbsnrtowcs().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+#  define _GNU_SOURCE	1
+#endif
+
 #include <config.h>
 
 #include <bashansi.h>
@@ -131,6 +139,91 @@
    If conversion is failed, the return value is (size_t)-1 and the values
    of DESTP and INDICESP are NULL. */
 
+#ifndef _GNU_SOURCE
+# error "Optimized conversion to wide-character strings requires _GNU_SOURCE"
+#endif
+
+static size_t
+xdupmbstowcs__no_indices (destp, src)
+    wchar_t **destp;	/* Store the pointer to the wide character string */
+    const char *src;	/* Multibyte character string */
+{
+  const char *p;	/* Conversion start position of src */
+  wchar_t *wsbuf;	/* Buffer for wide characters. */
+  size_t wsbuf_size;	/* Size of WSBUF */
+  size_t wcnum;		/* Number of wide characters in WSBUF */
+  mbstate_t state;	/* Conversion State */
+
+  memset (&state, '\0', sizeof(mbstate_t));
+
+  wsbuf_size = 0;
+  wsbuf = NULL;
+
+  p = src;
+  wcnum = 0;
+  do
+    {
+      size_t wcslength;	/* Number of wide characters produced by the conversion. */
+      const char *end_or_backslash;
+      size_t nms;	/* Number of multibyte characters to convert at one time. */
+      mbstate_t tmp_state;
+      const char *tmp_p;
+
+      end_or_backslash = strchrnul(p, '\\');
+      nms = (end_or_backslash - p);
+      if(*end_or_backslash == '\0')
+	nms++;
+
+      /* Compute the number of produced wide-characters. */
+      tmp_p = p;
+      tmp_state = state;
+      wcslength = mbsnrtowcs(NULL, &tmp_p, nms, 0, &tmp_state);
+
+      /* Conversion failed. */
+      if (wcslength == (size_t)-1)
+	{
+	  free (wsbuf);
+	  *destp = NULL;
+	  return (size_t)-1;
+	}
+
+      /* Resize the buffer if it is not large enough. */
+      if (wsbuf_size < wcnum+wcslength+1)	/* 1 for the L'\0' or the potential L'\\' */
+	{
+	  wchar_t *wstmp;
+
+	  wsbuf_size = wcnum+wcslength+1;	/* 1 for the L'\0' or the potential L'\\' */
+
+	  wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
+	  if (wstmp == NULL)
+	    {
+	      free (wsbuf);
+	      *destp = NULL;
+	      return (size_t)-1;
+	    }
+	  wsbuf = wstmp;
+	}
+
+      /* Perform the conversion. This is assumed to return 'wcslength'.
+       * It may set 'p' to NULL. */
+      mbsnrtowcs(wsbuf+wcnum, &p, nms, wsbuf_size-wcnum, &state);
+
+      wcnum += wcslength;
+
+      if (mbsinit (&state) && (p != NULL) && (*p == '\\'))
+	{
+	  wsbuf[wcnum++] = L'\\';
+	  p++;
+	}
+    }
+  while (p != NULL);
+
+  *destp = wsbuf;
+
+  /* Return the length of the wide character string, not including `\0'. */
+  return wcnum;
+}
+
 #define WSBUF_INC 32
 
 size_t
@@ -155,6 +248,10 @@
       return (size_t)-1;
     }
 
+  /* Use faster version if 'indicesp' is NULL. */
+  if (indicesp == NULL)
+    return xdupmbstowcs__no_indices(destp, src);
+
   memset (&state, '\0', sizeof(mbstate_t));
   wsbuf_size = WSBUF_INC;
 

Reply via email to