about summary refs log tree commit diff
path: root/string/strxfrm.c
diff options
context:
space:
mode:
Diffstat (limited to 'string/strxfrm.c')
-rw-r--r--string/strxfrm.c175
1 files changed, 166 insertions, 9 deletions
diff --git a/string/strxfrm.c b/string/strxfrm.c
index e40ae1c433..7dce9c117f 100644
--- a/string/strxfrm.c
+++ b/string/strxfrm.c
@@ -1,5 +1,6 @@
-/* Copyright (C) 1995 Free Software Foundation, Inc.
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
+Written by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
 
 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
@@ -13,12 +14,87 @@ Library General Public License for more details.
 
 You should have received a copy of the GNU Library General Public
 License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include "localeinfo.h"
+
+#ifndef STRING_TYPE
+# define STRING_TYPE char
+# define USTRING_TYPE unsigned char
+# define STRXFRM strxfrm
+# define STRLEN strlen
+# define STPNCPY __stpncpy
+#endif
+
+/* Include the shared helper functions.  `strxfrm'/`wcsxfrm' also use
+   these functions.  */
+#include "weight.h"
+
+
+/* Write 32 bit value UTF-8 encoded but only if enough space is left.  */
+static __inline size_t
+print_val (value, dest, max, act)
+     u32_t value;
+     STRING_TYPE *dest;
+     size_t max;
+     size_t act;
+{
+  char tmp[6];
+  int idx = 0;
+
+  if (value < 0x80)
+    tmp[idx++] = (char) value;
+  else
+    {
+      tmp[idx++] = '\x80' + (char) (value & 0x3f);
+      value >>= 6;
+
+      if (value < 0x20)
+	tmp[idx++] = '\xc0' + (char) value;
+      else
+	{
+	  tmp[idx++] = '\x80' + (char) (value & 0x3f);
+	  value >>= 6;
+
+	  if (value < 0x10)
+	    tmp[idx++] = '\xe0' + (char) value;
+	  else
+	    {
+	      tmp[idx++] = '\x80' + (char) (value & 0x3f);
+	      value >>= 6;
+
+	      if (value < 0x08)
+		tmp[idx++] = '\xf0' + (char) value;
+	      else
+		{
+		  tmp[idx++] = '\x80' + (char) (value & 0x3f);
+		  value >>= 6;
+
+		  if (value < 0x04)
+		    tmp[idx++] = '\xf8' + (char) value;
+		  else
+		    {
+		      tmp[idx++] = '\x80' + (char) (value & 0x3f);
+		      tmp[idx++] = '\xfc' + (char) (value >> 6);
+		    }
+		}
+	    }
+	}
+    }
+
+  while (idx-- > 0)
+    {
+      if (act < max)
+	dest[act] = tmp[idx];
+      ++act;
+    }
+
+  return act;
+}
 
 
 /* Transform SRC into a form such that the result of strcmp
@@ -27,13 +103,94 @@ Cambridge, MA 02139, USA.  */
    their transformation.  The transformed string is put in at
    most N characters of DEST and its length is returned.  */
 size_t
-strxfrm (dest, src, n)
-     char *dest;
-     const char *src;
+STRXFRM (dest, src, n)
+     STRING_TYPE *dest;
+     const STRING_TYPE *src;
      size_t n;
 {
-  if (n == 0)
-    return strlen (src);
+  weight_t *forw = NULL;
+  weight_t *backw = NULL;
+  size_t pass;
+  size_t written;
+
+  /* If the current locale does not specify locale data we use normal
+     8-bit string comparison.  */
+  if (collate_nrules == 0)
+    {
+      if (n != 0)
+	STPNCPY (dest, src, n);
+
+      return STRLEN (src);
+    }
+
+  /* Get full information about the string.  This means we get
+     information for all passes in a special data structure.  */
+  get_string (src, forw, backw);
+
+  /* Now we have all the information.  In at most the given number of
+     passes we can finally decide about the order.  */
+  written = 0;
+  for (pass = 0; pass < collate_nrules; ++pass)
+    {
+      int forward = (collate_rules[pass] & sort_forward) != 0;
+      const weight_t *run = forward ? forw : backw;
+      int idx = forward ? 0 : run->data[pass].number - 1;
+
+      do
+	{
+	  int ignore = 0;
+	  u32_t w;
+
+	  /* Here we have to check for IGNORE entries.  If these are
+	     found we count them and go on witht he next value.  */
+	  while ((w = run->data[pass].value[idx]) == IGNORE_CHAR)
+	    {
+	      ++ignore;
+	      if ((forward && ++idx >= run->data[pass].number)
+		  || (!forward && --idx < 0))
+		{
+		  weight_t *nextp = forward ? run->next : run->prev;
+		  if (nextp == NULL)
+		    {
+		      w = 0;
+		      break;
+		    }
+		  run = nextp;
+		  idx = forward ? 0 : run->data[pass].number - 1;
+		}
+	    }
+
+	  /* Now we have information of the number of ignored weights
+	     and the value of the next weight.  We have to add 2
+	     because 0 means EOS and 1 is the intermediate string end.  */
+	  if ((collate_rules[pass] & sort_position) != 0)
+	    written = print_val (ignore + 2, dest, n, written);
+
+	  if (w != 0)
+	    written = print_val (w, dest, n, written);
+
+	  /* We have to increment the index counters.  */
+	  if ((forward && ++idx >= run->data[pass].number)
+	      || (!forward && --idx < 0))
+	    if (forward)
+	      {
+		run = run->next;
+		idx = 0;
+	      }
+	    else
+	      {
+		run = run->prev;
+		if (run != NULL)
+		  idx = run->data[pass].number - 1;
+	      }
+	}
+      while (run != NULL);
+
+      /* Write marker for end of word.  */
+      if (pass + 1 < collate_nrules)
+	written = print_val (1, dest, n, written);
+    }
 
-  return __stpncpy (dest, src, n) - dest;
+  /* Terminate string.  */
+  return print_val (0, dest, n, written);
 }