X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fastrxfrm.c;fp=lib%2Fastrxfrm.c;h=00315bbacc8bb5370afe1e71b3b8fd270a999c99;hb=4e48756921cae0aec44afad18bfaa881890e6142;hp=0000000000000000000000000000000000000000;hpb=e5cff5489f169c538233c7eadbf36a9a959e3661;p=gnulib.git diff --git a/lib/astrxfrm.c b/lib/astrxfrm.c new file mode 100644 index 000000000..00315bbac --- /dev/null +++ b/lib/astrxfrm.c @@ -0,0 +1,181 @@ +/* Locale dependent string transformation for comparison. + Copyright (C) 2010 Free Software Foundation, Inc. + Written by Bruno Haible , 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 . */ + +#include + +/* Specification. */ +#include "astrxfrm.h" + +#include +#include +#include + +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; +}