--- /dev/null
+/* Locale dependent string transformation for comparison.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2010.
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "astrxfrm.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+astrxfrm (const char *s, char *resultbuf, size_t *lengthp)
+{
+ char tmpbuf[4000];
+ char *result; /* either == resultbuf or == tmpbuf or freshly allocated
+ or NULL. */
+ size_t allocated; /* number of bytes allocated at result */
+ size_t length;
+
+ if (resultbuf != NULL)
+ {
+ result = resultbuf;
+ allocated = *lengthp;
+ }
+ else
+ {
+ result = NULL;
+ allocated = 0;
+ }
+
+ {
+ size_t l = strlen (s);
+ size_t k;
+
+ /* A call to strxfrm costs about 20 times more than a call to strdup of
+ the result. Therefore it is worth to try to avoid calling strxfrm
+ more than once on a given string, by making enough room before calling
+ strxfrm. The size of the strxfrm result, k, is likely to be between
+ l and 3 * l. */
+ if (3 * l + 1 > allocated)
+ {
+ /* Grow the result buffer. */
+ if (3 * l + 1 <= sizeof (tmpbuf))
+ {
+ result = tmpbuf;
+ allocated = sizeof (tmpbuf);
+ }
+ else
+ {
+ size_t new_allocated;
+ char *new_result;
+
+ new_allocated = 3 * l + 1;
+ if (new_allocated < 2 * allocated)
+ new_allocated = 2 * allocated;
+ new_result = (char *) malloc (new_allocated);
+ if (new_result != NULL)
+ {
+ allocated = new_allocated;
+ result = new_result;
+ }
+ }
+ }
+
+ errno = 0;
+ k = strxfrm (result, s, allocated);
+ if (errno != 0)
+ goto fail;
+ if (k >= allocated)
+ {
+ /* Grow the result buffer. */
+ if (result != resultbuf && result != tmpbuf)
+ free (result);
+ if (k + 1 <= sizeof (tmpbuf))
+ {
+ result = tmpbuf;
+ allocated = sizeof (tmpbuf);
+ }
+ else
+ {
+ size_t new_allocated;
+ char *new_result;
+
+ new_allocated = k + 1;
+ new_result = (char *) malloc (new_allocated);
+ if (new_result == NULL)
+ goto out_of_memory;
+ allocated = new_allocated;
+ result = new_result;
+ }
+ /* Here k < allocated. */
+
+ /* Try again. */
+ errno = 0;
+ if (strxfrm (result, s, allocated) != k)
+ /* strxfrm() is not producing reproducible results. */
+ abort ();
+ if (errno != 0)
+ goto fail;
+ }
+
+ /* Verify that strxfrm() has NUL-terminated the result. */
+ if (result[k] != '\0')
+ abort ();
+ length = k + 1;
+ }
+
+ /* Here length > 0. */
+
+ if (result == tmpbuf)
+ {
+ if (resultbuf != NULL && length <= *lengthp)
+ {
+ memcpy (resultbuf, result, length);
+ result = resultbuf;
+ }
+ else
+ {
+ char *memory = (char *) malloc (length);
+
+ if (memory == NULL)
+ goto out_of_memory;
+ memcpy (memory, result, length);
+ result = memory;
+ }
+ }
+ else
+ {
+ /* Shrink the allocated memory if possible. */
+ if (result != resultbuf && length < allocated)
+ {
+ if (length <= *lengthp)
+ {
+ memcpy (resultbuf, result, length);
+ free (result);
+ result = resultbuf;
+ }
+ else
+ {
+ char *memory = (char *) realloc (result, length);
+ if (memory != NULL)
+ {
+ memcpy (memory, result, length);
+ result = memory;
+ }
+ }
+ }
+ }
+
+ *lengthp = length;
+ return result;
+
+ fail:
+ {
+ int saved_errno = errno;
+ if (result != resultbuf && result != tmpbuf)
+ free (result);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ out_of_memory:
+ errno = ENOMEM;
+ return NULL;
+}
--- /dev/null
+/* Locale dependent string transformation for comparison.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef ASTRXFRM_H
+#define ASTRXFRM_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Variant of strxfrm() with a calling convention that reduces the number
+ of strxfrm calls. */
+
+/* Transform the string starting at S to a string, in such a way that
+ comparing S1 and S2 with strcoll() is equivalent to comparing astrxfrm(S1)
+ and astrxfrm(S2) with strcmp().
+ The result of this function depends on the LC_COLLATE category of the
+ current locale.
+ If successful: If resultbuf is not NULL and the result fits into *lengthp
+ bytes, it is put in resultbuf, and resultbuf is returned. Otherwise, a
+ freshly allocated string is returned. In both cases, *lengthp is set to the
+ length of the returned string.
+ Upon failure, return NULL, with errno set. */
+extern char * astrxfrm (const char *s, char *resultbuf, size_t *lengthp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ASTRXFRM_H */