diff options
Diffstat (limited to 'lib/memxfrm.c')
-rw-r--r-- | lib/memxfrm.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/memxfrm.c b/lib/memxfrm.c new file mode 100644 index 000000000..79db94b8f --- /dev/null +++ b/lib/memxfrm.c @@ -0,0 +1,108 @@ +/* Locale-specific memory transformation + + Copyright (C) 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert <eggert@cs.ucla.edu>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "memxfrm.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +/* Store into DEST (of size DESTSIZE) the text in SRC (of size SRCSIZE) + transformed so that the result of memcmp on two transformed texts + (with ties going to the longer text) is the same as the result of + memcoll on the two texts before their transformation. Perhaps + temporarily modify the byte after SRC, but restore its original + contents before returning. + + Return the size of the resulting text, or an indeterminate value if + there is an error. Set errno to an error number if there is an + error, and to zero otherwise. DEST contains an indeterminate value + if there is an error or if the resulting size is greater than + DESTSIZE. */ + +size_t +memxfrm (char *restrict dest, size_t destsize, + char *restrict src, size_t srcsize) +{ +#if HAVE_STRXFRM + + size_t di = 0; + size_t si = 0; + size_t result = 0; + + char ch = src[srcsize]; + src[srcsize] = '\0'; + + while (si < srcsize) + { + size_t slen = strlen (src + si); + + size_t result0 = result; + errno = 0; + result += strxfrm (dest + di, src + si, destsize - di) + 1; + if (errno != 0) + break; + if (result <= result0) + { + errno = ERANGE; + break; + } + + if (result == destsize + 1 && si + slen == srcsize) + { + /* The destination is exactly the right size, but strxfrm wants + room for a trailing null. Work around the problem with a + temporary buffer. */ + size_t bufsize = destsize - di + 1; + char stackbuf[4000]; + char *buf = stackbuf; + if (sizeof stackbuf < bufsize) + { + buf = malloc (bufsize); + if (! buf) + break; + } + strxfrm (buf, src + si, bufsize); + memcpy (dest + di, buf, destsize - di); + if (sizeof stackbuf < bufsize) + free (buf); + errno = 0; + } + + di = (result < destsize ? result : destsize); + si += slen + 1; + } + + src[srcsize] = ch; + return result - (si != srcsize); + +#else + + if (srcsize < destsize) + memcpy (dest, src, srcsize); + errno = 0; + return srcsize; + +#endif +} |