• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/gettext-0.17/gettext-runtime/gnulib-lib/
1/* Charset conversion.
2   Copyright (C) 2001-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible and Simon Josefsson.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19#include <config.h>
20
21/* Specification.  */
22#include "striconv.h"
23
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27
28#if HAVE_ICONV
29# include <iconv.h>
30/* Get MB_LEN_MAX, CHAR_BIT.  */
31# include <limits.h>
32#endif
33
34#include "c-strcase.h"
35
36#ifndef SIZE_MAX
37# define SIZE_MAX ((size_t) -1)
38#endif
39
40
41#if HAVE_ICONV
42
43int
44mem_cd_iconv (const char *src, size_t srclen, iconv_t cd,
45	      char **resultp, size_t *lengthp)
46{
47# define tmpbufsize 4096
48  size_t length;
49  char *result;
50
51  /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
52# if defined _LIBICONV_VERSION \
53     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
54  /* Set to the initial state.  */
55  iconv (cd, NULL, NULL, NULL, NULL);
56# endif
57
58  /* Determine the length we need.  */
59  {
60    size_t count = 0;
61    /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
62       libiconv's UCS-4-INTERNAL encoding.  */
63    union { unsigned int align; char buf[tmpbufsize]; } tmp;
64# define tmpbuf tmp.buf
65    const char *inptr = src;
66    size_t insize = srclen;
67
68    while (insize > 0)
69      {
70	char *outptr = tmpbuf;
71	size_t outsize = tmpbufsize;
72	size_t res = iconv (cd,
73			    (ICONV_CONST char **) &inptr, &insize,
74			    &outptr, &outsize);
75
76	if (res == (size_t)(-1))
77	  {
78	    if (errno == E2BIG)
79	      ;
80	    else if (errno == EINVAL)
81	      break;
82	    else
83	      return -1;
84	  }
85# if !defined _LIBICONV_VERSION && !defined __GLIBC__
86	/* Irix iconv() inserts a NUL byte if it cannot convert.
87	   NetBSD iconv() inserts a question mark if it cannot convert.
88	   Only GNU libiconv and GNU libc are known to prefer to fail rather
89	   than doing a lossy conversion.  */
90	else if (res > 0)
91	  {
92	    errno = EILSEQ;
93	    return -1;
94	  }
95# endif
96	count += outptr - tmpbuf;
97      }
98    /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
99# if defined _LIBICONV_VERSION \
100     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
101    {
102      char *outptr = tmpbuf;
103      size_t outsize = tmpbufsize;
104      size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
105
106      if (res == (size_t)(-1))
107	return -1;
108      count += outptr - tmpbuf;
109    }
110# endif
111    length = count;
112# undef tmpbuf
113  }
114
115  if (length == 0)
116    {
117      *lengthp = 0;
118      return 0;
119    }
120  if (*resultp != NULL && *lengthp >= length)
121    result = *resultp;
122  else
123    {
124      result = (char *) malloc (length);
125      if (result == NULL)
126	{
127	  errno = ENOMEM;
128	  return -1;
129	}
130    }
131
132  /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
133# if defined _LIBICONV_VERSION \
134     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
135  /* Return to the initial state.  */
136  iconv (cd, NULL, NULL, NULL, NULL);
137# endif
138
139  /* Do the conversion for real.  */
140  {
141    const char *inptr = src;
142    size_t insize = srclen;
143    char *outptr = result;
144    size_t outsize = length;
145
146    while (insize > 0)
147      {
148	size_t res = iconv (cd,
149			    (ICONV_CONST char **) &inptr, &insize,
150			    &outptr, &outsize);
151
152	if (res == (size_t)(-1))
153	  {
154	    if (errno == EINVAL)
155	      break;
156	    else
157	      goto fail;
158	  }
159# if !defined _LIBICONV_VERSION && !defined __GLIBC__
160	/* Irix iconv() inserts a NUL byte if it cannot convert.
161	   NetBSD iconv() inserts a question mark if it cannot convert.
162	   Only GNU libiconv and GNU libc are known to prefer to fail rather
163	   than doing a lossy conversion.  */
164	else if (res > 0)
165	  {
166	    errno = EILSEQ;
167	    goto fail;
168	  }
169# endif
170      }
171    /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
172# if defined _LIBICONV_VERSION \
173     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
174    {
175      size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
176
177      if (res == (size_t)(-1))
178	goto fail;
179    }
180# endif
181    if (outsize != 0)
182      abort ();
183  }
184
185  *resultp = result;
186  *lengthp = length;
187
188  return 0;
189
190 fail:
191  {
192    if (result != *resultp)
193      {
194	int saved_errno = errno;
195	free (result);
196	errno = saved_errno;
197      }
198    return -1;
199  }
200# undef tmpbufsize
201}
202
203char *
204str_cd_iconv (const char *src, iconv_t cd)
205{
206  /* For most encodings, a trailing NUL byte in the input will be converted
207     to a trailing NUL byte in the output.  But not for UTF-7.  So that this
208     function is usable for UTF-7, we have to exclude the NUL byte from the
209     conversion and add it by hand afterwards.  */
210# if !defined _LIBICONV_VERSION && !defined __GLIBC__
211  /* Irix iconv() inserts a NUL byte if it cannot convert.
212     NetBSD iconv() inserts a question mark if it cannot convert.
213     Only GNU libiconv and GNU libc are known to prefer to fail rather
214     than doing a lossy conversion.  For other iconv() implementations,
215     we have to look at the number of irreversible conversions returned;
216     but this information is lost when iconv() returns for an E2BIG reason.
217     Therefore we cannot use the second, faster algorithm.  */
218
219  char *result = NULL;
220  size_t length = 0;
221  int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length);
222  char *final_result;
223
224  if (retval < 0)
225    {
226      if (result != NULL)
227	abort ();
228      return NULL;
229    }
230
231  /* Add the terminating NUL byte.  */
232  final_result =
233    (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
234  if (final_result == NULL)
235    {
236      if (result != NULL)
237	free (result);
238      errno = ENOMEM;
239      return NULL;
240    }
241  final_result[length] = '\0';
242
243  return final_result;
244
245# else
246  /* This algorithm is likely faster than the one above.  But it may produce
247     iconv() returns for an E2BIG reason, when the output size guess is too
248     small.  Therefore it can only be used when we don't need the number of
249     irreversible conversions performed.  */
250  char *result;
251  size_t result_size;
252  size_t length;
253  const char *inptr = src;
254  size_t inbytes_remaining = strlen (src);
255
256  /* Make a guess for the worst-case output size, in order to avoid a
257     realloc.  It's OK if the guess is wrong as long as it is not zero and
258     doesn't lead to an integer overflow.  */
259  result_size = inbytes_remaining;
260  {
261    size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
262    if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
263      result_size *= MB_LEN_MAX;
264  }
265  result_size += 1; /* for the terminating NUL */
266
267  result = (char *) malloc (result_size);
268  if (result == NULL)
269    {
270      errno = ENOMEM;
271      return NULL;
272    }
273
274  /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
275# if defined _LIBICONV_VERSION \
276     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
277  /* Set to the initial state.  */
278  iconv (cd, NULL, NULL, NULL, NULL);
279# endif
280
281  /* Do the conversion.  */
282  {
283    char *outptr = result;
284    size_t outbytes_remaining = result_size - 1;
285
286    for (;;)
287      {
288	/* Here inptr + inbytes_remaining = src + strlen (src),
289		outptr + outbytes_remaining = result + result_size - 1.  */
290	size_t res = iconv (cd,
291			    (ICONV_CONST char **) &inptr, &inbytes_remaining,
292			    &outptr, &outbytes_remaining);
293
294	if (res == (size_t)(-1))
295	  {
296	    if (errno == EINVAL)
297	      break;
298	    else if (errno == E2BIG)
299	      {
300		size_t used = outptr - result;
301		size_t newsize = result_size * 2;
302		char *newresult;
303
304		if (!(newsize > result_size))
305		  {
306		    errno = ENOMEM;
307		    goto failed;
308		  }
309		newresult = (char *) realloc (result, newsize);
310		if (newresult == NULL)
311		  {
312		    errno = ENOMEM;
313		    goto failed;
314		  }
315		result = newresult;
316		result_size = newsize;
317		outptr = result + used;
318		outbytes_remaining = result_size - 1 - used;
319	      }
320	    else
321	      goto failed;
322	  }
323	else
324	  break;
325      }
326    /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
327# if defined _LIBICONV_VERSION \
328     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
329    for (;;)
330      {
331	/* Here outptr + outbytes_remaining = result + result_size - 1.  */
332	size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
333
334	if (res == (size_t)(-1))
335	  {
336	    if (errno == E2BIG)
337	      {
338		size_t used = outptr - result;
339		size_t newsize = result_size * 2;
340		char *newresult;
341
342		if (!(newsize > result_size))
343		  {
344		    errno = ENOMEM;
345		    goto failed;
346		  }
347		newresult = (char *) realloc (result, newsize);
348		if (newresult == NULL)
349		  {
350		    errno = ENOMEM;
351		    goto failed;
352		  }
353		result = newresult;
354		result_size = newsize;
355		outptr = result + used;
356		outbytes_remaining = result_size - 1 - used;
357	      }
358	    else
359	      goto failed;
360	  }
361	else
362	  break;
363      }
364# endif
365
366    /* Add the terminating NUL byte.  */
367    *outptr++ = '\0';
368
369    length = outptr - result;
370  }
371
372  /* Give away unused memory.  */
373  if (length < result_size)
374    {
375      char *smaller_result = (char *) realloc (result, length);
376
377      if (smaller_result != NULL)
378	result = smaller_result;
379    }
380
381  return result;
382
383 failed:
384  {
385    int saved_errno = errno;
386    free (result);
387    errno = saved_errno;
388    return NULL;
389  }
390
391# endif
392}
393
394#endif
395
396char *
397str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
398{
399  if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
400    {
401      char *result = strdup (src);
402
403      if (result == NULL)
404	errno = ENOMEM;
405      return result;
406    }
407  else
408    {
409#if HAVE_ICONV
410      iconv_t cd;
411      char *result;
412
413      /* Avoid glibc-2.1 bug with EUC-KR.  */
414# if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
415      if (c_strcasecmp (from_codeset, "EUC-KR") == 0
416	  || c_strcasecmp (to_codeset, "EUC-KR") == 0)
417	{
418	  errno = EINVAL;
419	  return NULL;
420	}
421# endif
422      cd = iconv_open (to_codeset, from_codeset);
423      if (cd == (iconv_t) -1)
424	return NULL;
425
426      result = str_cd_iconv (src, cd);
427
428      if (result == NULL)
429	{
430	  /* Close cd, but preserve the errno from str_cd_iconv.  */
431	  int saved_errno = errno;
432	  iconv_close (cd);
433	  errno = saved_errno;
434	}
435      else
436	{
437	  if (iconv_close (cd) < 0)
438	    {
439	      /* Return NULL, but free the allocated memory, and while doing
440		 that, preserve the errno from iconv_close.  */
441	      int saved_errno = errno;
442	      free (result);
443	      errno = saved_errno;
444	      return NULL;
445	    }
446	}
447      return result;
448#else
449      /* This is a different error code than if iconv_open existed but didn't
450	 support from_codeset and to_codeset, so that the caller can emit
451	 an error message such as
452	   "iconv() is not supported. Installing GNU libiconv and
453	    then reinstalling this package would fix this."  */
454      errno = ENOSYS;
455      return NULL;
456#endif
457    }
458}
459