diff options
Diffstat (limited to 'lib/amemxfrm.c')
| -rw-r--r-- | lib/amemxfrm.c | 180 | 
1 files changed, 180 insertions, 0 deletions
| diff --git a/lib/amemxfrm.c b/lib/amemxfrm.c new file mode 100644 index 00000000..d3525d98 --- /dev/null +++ b/lib/amemxfrm.c @@ -0,0 +1,180 @@ +/* Locale dependent memory area transformation for comparison. +   Copyright (C) 2009-2015 Free Software Foundation, Inc. +   Written by Bruno Haible <bruno@clisp.org>, 2009. + +   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 "amemxfrm.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +char * +amemxfrm (char *s, size_t n, char *resultbuf, size_t *lengthp) +{ +  /* Result accumulator.  */ +  char *result; +  size_t length; +  size_t allocated; + +  char orig_sentinel; + +  /* Initial memory allocation.  */ +  if (resultbuf != NULL && *lengthp > 0) +    { +      result = resultbuf; +      allocated = *lengthp; +    } +  else +    { +      allocated = (n > 0 ? n : 1); +      result = (char *) malloc (allocated); +      if (result == NULL) +        goto out_of_memory_2; +    } +  length = 0; + +  /* Add sentinel.byte.  */ +  orig_sentinel = s[n]; +  s[n] = '\0'; + +  /* Iterate through S, transforming each NUL terminated segment. +     Accumulate the resulting transformed segments in result, separated by +     NULs.  */ +  { +    const char *p_end = s + n + 1; +    const char *p; + +    p = s; +    for (;;) +      { +        /* Search next NUL byte.  */ +        size_t l = strlen (p); + +        for (;;) +          { +            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 >= allocated - length) +              { +                /* Grow the result buffer.  */ +                size_t new_allocated; +                char *new_result; + +                new_allocated = length + 3 * l + 1; +                if (new_allocated < 2 * allocated) +                  new_allocated = 2 * allocated; +                if (new_allocated < 64) +                  new_allocated = 64; +                if (result == resultbuf) +                  new_result = (char *) malloc (new_allocated); +                else +                  new_result = (char *) realloc (result, new_allocated); +                if (new_result != NULL) +                  { +                    allocated = new_allocated; +                    result = new_result; +                  } +              } + +            errno = 0; +            k = strxfrm (result + length, p, allocated - length); +            if (errno != 0) +              goto fail; +            if (k >= allocated - length) +              { +                /* Grow the result buffer.  */ +                size_t new_allocated; +                char *new_result; + +                new_allocated = length + k + 1; +                if (new_allocated < 2 * allocated) +                  new_allocated = 2 * allocated; +                if (new_allocated < 64) +                  new_allocated = 64; +                if (result == resultbuf) +                  new_result = (char *) malloc (new_allocated); +                else +                  new_result = (char *) realloc (result, new_allocated); +                if (new_result == NULL) +                  goto out_of_memory_1; +                allocated = new_allocated; +                result = new_result; +              } +            else +              { +                length += k; +                break; +              } +          } + +        p = p + l + 1; +        if (p == p_end) +          break; +        result[length] = '\0'; +        length++; +      } +  } + +  /* Shrink the allocated memory if possible. +     It is not worth calling realloc when length + 1 == allocated; it would +     save just one byte.  */ +  if (result != resultbuf && length + 1 < allocated) +    { +      if ((length > 0 ? length : 1) <= *lengthp) +        { +          memcpy (resultbuf, result, length); +          free (result); +          result = resultbuf; +        } +      else +        { +          char *memory = (char *) realloc (result, length > 0 ? length : 1); +          if (memory != NULL) +            result = memory; +        } +    } + +  s[n] = orig_sentinel; +  *lengthp = length; +  return result; + + fail: +  { +    int saved_errno = errno; +    if (result != resultbuf) +      free (result); +    s[n] = orig_sentinel; +    errno = saved_errno; +    return NULL; +  } + + out_of_memory_1: +  if (result != resultbuf) +    free (result); +  s[n] = orig_sentinel; + out_of_memory_2: +  errno = ENOMEM; +  return NULL; +} | 
