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