about summary refs log tree commit diff
path: root/wcsmbs/wcsrtombs.c
diff options
context:
space:
mode:
Diffstat (limited to 'wcsmbs/wcsrtombs.c')
-rw-r--r--wcsmbs/wcsrtombs.c95
1 files changed, 73 insertions, 22 deletions
diff --git a/wcsmbs/wcsrtombs.c b/wcsmbs/wcsrtombs.c
index 9f1000937b..99ca6acc5b 100644
--- a/wcsmbs/wcsrtombs.c
+++ b/wcsmbs/wcsrtombs.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
-Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>
+Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996.
 
 The GNU C Library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Library General Public License as
@@ -25,6 +25,18 @@ Boston, MA 02111-1307, USA.  */
 #endif
 
 
+static const wchar_t encoding_mask[] =
+{
+  ~0x7ff, ~0xffff, ~0x1fffff, ~0x3ffffff
+};
+
+static const unsigned char encoding_byte[] =
+{
+  0xc0, 0xe0, 0xf0, 0xf8, 0xfc
+};
+
+/* We don't need the state really because we don't have shift states
+   to maintain between calls to this function.  */
 static mbstate_t internal;
 
 size_t
@@ -34,40 +46,79 @@ wcsrtombs (dst, src, len, ps)
      size_t len;
      mbstate_t *ps;
 {
-  size_t result = 0;
+  size_t written = 0;
+  const wchar_t *run = *src;
 
   if (ps == NULL)
     ps = &internal;
 
-  /*************************************************************\
-  |* This is no complete implementation.  While the multi-byte *|
-  |* character handling is not finished this will do.	       *|
-  \*************************************************************/
+  if (dst == NULL)
+    /* The LEN parameter has to be ignored if we don't actually write
+       anything.  */
+    len = ~0;
 
-  while (len > 0 && **src != L'\0')
+  while (written < len)
     {
-      if ((**src & ~0xff) != 0)
+      wchar_t wc = *run++;
+
+      if (wc < 0 || wc > 0x7fffffff)
 	{
+	  /* This is no correct ISO 10646 character.  */
 	  errno = EILSEQ;
 	  return (size_t) -1;
 	}
 
-      if (dst != NULL)
-	dst[result] = (char) **src;
-      ++result;
-      ++(*src);
-      --len;
-    }
-
-  if (len > 0)
-    {
-      if (dst != NULL)
+      if (wc == L'\0')
+	{
+	  /* Found the end.  */
+	  if (dst != NULL)
+	    *dst = '\0';
+	  *src = NULL;
+	  return written;
+	}
+      else if (wc < 0x80)
 	{
-	  dst[result] = '\0';
-	  *ps = 0;
+	  /* It's an one byte sequence.  */
+	  if (dst != NULL)
+	    *dst++ = (char) wc;
+	  ++written;
+	}
+      else
+	{
+	  size_t step;
+
+	  for (step = 2; step < 6; ++step)
+	    if ((wc & encoding_mask[step - 2]) == 0)
+	      break;
+
+	  if (written + step >= len)
+	    /* Too long.  */
+	    break;
+
+	  if (dst != NULL)
+	    {
+	      size_t cnt = step;
+
+	      dst[0] = encoding_byte[cnt - 2];
+
+	      --cnt;
+	      do
+		{
+		  dst[cnt] = 0x80 | (wc & 0x3f);
+		  wc >>= 6;
+		}
+	      while (--cnt > 0);
+	      dst[0] |= wc;
+
+	      dst += step;
+	    }
+
+	  written += step;
 	}
-      *src = NULL;
     }
 
-  return result;
+  /* Store position of first unprocessed word.  */
+  *src = run;
+
+  return written;
 }