Mercurial > gnulib
annotate lib/striconv.c @ 17363:5a51fb7777a9
sys_select, sys_time: port 2013-01-30 Solaris 2.6 fix to Cygwin
Problem reported by Marco Atzeri in
<http://lists.gnu.org/archive/html/bug-gnulib/2013-03/msg00000.html>.
* lib/sys_select.in.h [HAVE_SYS_SELECT_H && _CYGWIN_SYS_TIME_H]:
Simply delegate to the system <sys/select.h> in this case too.
Also, pay attention to _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H only
if OSF/1, since otherwise Cygwin breaks, and it doesn't seem to
be needed on Solaris either.
* lib/sys_time.in.h [_CYGWIN_SYS_TIME_H]:
Simply delgate to the system <sys/time.h> in this case.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Tue, 19 Mar 2013 09:08:47 -0700 |
parents | e542fd46ad6f |
children | 344018b6e5d7 |
rev | line source |
---|---|
7261 | 1 /* Charset conversion. |
17249
e542fd46ad6f
maint: update all copyright year number ranges
Eric Blake <eblake@redhat.com>
parents:
16366
diff
changeset
|
2 Copyright (C) 2001-2007, 2010-2013 Free Software Foundation, Inc. |
7261 | 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 | |
16366
bb182ee4a09d
maint: replace FSF snail-mail addresses with URLs
Paul Eggert <eggert@cs.ucla.edu>
parents:
16201
diff
changeset
|
16 along with this program; if not, see <http://www.gnu.org/licenses/>. */ |
7261 | 17 |
7304
1c4ed7637c24
Include <config.h> unconditionally.
Bruno Haible <bruno@clisp.org>
parents:
7261
diff
changeset
|
18 #include <config.h> |
7261 | 19 |
20 /* Specification. */ | |
21 #include "striconv.h" | |
22 | |
23 #include <errno.h> | |
24 #include <stdlib.h> | |
25 #include <string.h> | |
26 | |
27 #if HAVE_ICONV | |
28 # include <iconv.h> | |
29 /* Get MB_LEN_MAX, CHAR_BIT. */ | |
30 # include <limits.h> | |
31 #endif | |
32 | |
33 #include "c-strcase.h" | |
34 | |
35 #ifndef SIZE_MAX | |
36 # define SIZE_MAX ((size_t) -1) | |
37 #endif | |
38 | |
39 | |
40 #if HAVE_ICONV | |
41 | |
42 int | |
43 mem_cd_iconv (const char *src, size_t srclen, iconv_t cd, | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
44 char **resultp, size_t *lengthp) |
7261 | 45 { |
46 # define tmpbufsize 4096 | |
47 size_t length; | |
48 char *result; | |
49 | |
50 /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */ | |
51 # if defined _LIBICONV_VERSION \ | |
13885 | 52 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
53 || defined __sun) | |
7261 | 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; | |
7861
28688a13afbc
Align the temporary buffer.
Bruno Haible <bruno@clisp.org>
parents:
7586
diff
changeset
|
61 /* The alignment is needed when converting e.g. to glibc's WCHAR_T or |
28688a13afbc
Align the temporary buffer.
Bruno Haible <bruno@clisp.org>
parents:
7586
diff
changeset
|
62 libiconv's UCS-4-INTERNAL encoding. */ |
28688a13afbc
Align the temporary buffer.
Bruno Haible <bruno@clisp.org>
parents:
7586
diff
changeset
|
63 union { unsigned int align; char buf[tmpbufsize]; } tmp; |
28688a13afbc
Align the temporary buffer.
Bruno Haible <bruno@clisp.org>
parents:
7586
diff
changeset
|
64 # define tmpbuf tmp.buf |
7261 | 65 const char *inptr = src; |
66 size_t insize = srclen; | |
67 | |
68 while (insize > 0) | |
69 { | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
70 char *outptr = tmpbuf; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
71 size_t outsize = tmpbufsize; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
72 size_t res = iconv (cd, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
73 (ICONV_CONST char **) &inptr, &insize, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
74 &outptr, &outsize); |
7261 | 75 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
76 if (res == (size_t)(-1)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
77 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
78 if (errno == E2BIG) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
79 ; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
80 else if (errno == EINVAL) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
81 break; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
82 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
83 return -1; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
84 } |
13885 | 85 # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__) |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
86 /* Irix iconv() inserts a NUL byte if it cannot convert. |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
87 NetBSD iconv() inserts a question mark if it cannot convert. |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
88 Only GNU libiconv and GNU libc are known to prefer to fail rather |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
89 than doing a lossy conversion. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
90 else if (res > 0) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
91 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
92 errno = EILSEQ; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
93 return -1; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
94 } |
7261 | 95 # endif |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
96 count += outptr - tmpbuf; |
7261 | 97 } |
98 /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */ | |
99 # if defined _LIBICONV_VERSION \ | |
13885 | 100 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
101 || defined __sun) | |
7261 | 102 { |
103 char *outptr = tmpbuf; | |
104 size_t outsize = tmpbufsize; | |
105 size_t res = iconv (cd, NULL, NULL, &outptr, &outsize); | |
106 | |
107 if (res == (size_t)(-1)) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
108 return -1; |
7261 | 109 count += outptr - tmpbuf; |
110 } | |
111 # endif | |
112 length = count; | |
7861
28688a13afbc
Align the temporary buffer.
Bruno Haible <bruno@clisp.org>
parents:
7586
diff
changeset
|
113 # undef tmpbuf |
7261 | 114 } |
115 | |
116 if (length == 0) | |
117 { | |
118 *lengthp = 0; | |
119 return 0; | |
120 } | |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
121 if (*resultp != NULL && *lengthp >= length) |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
122 result = *resultp; |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
123 else |
7261 | 124 { |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
125 result = (char *) malloc (length); |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
126 if (result == NULL) |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
127 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
128 errno = ENOMEM; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
129 return -1; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
130 } |
7261 | 131 } |
132 | |
133 /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */ | |
134 # if defined _LIBICONV_VERSION \ | |
13885 | 135 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
136 || defined __sun) | |
7261 | 137 /* Return to the initial state. */ |
138 iconv (cd, NULL, NULL, NULL, NULL); | |
139 # endif | |
140 | |
141 /* Do the conversion for real. */ | |
142 { | |
143 const char *inptr = src; | |
144 size_t insize = srclen; | |
145 char *outptr = result; | |
146 size_t outsize = length; | |
147 | |
148 while (insize > 0) | |
149 { | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
150 size_t res = iconv (cd, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
151 (ICONV_CONST char **) &inptr, &insize, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
152 &outptr, &outsize); |
7261 | 153 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
154 if (res == (size_t)(-1)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
155 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
156 if (errno == EINVAL) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
157 break; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
158 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
159 goto fail; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
160 } |
13885 | 161 # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__) |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
162 /* Irix iconv() inserts a NUL byte if it cannot convert. |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
163 NetBSD iconv() inserts a question mark if it cannot convert. |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
164 Only GNU libiconv and GNU libc are known to prefer to fail rather |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
165 than doing a lossy conversion. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
166 else if (res > 0) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
167 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
168 errno = EILSEQ; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
169 goto fail; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
170 } |
7261 | 171 # endif |
172 } | |
173 /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */ | |
174 # if defined _LIBICONV_VERSION \ | |
13885 | 175 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
176 || defined __sun) | |
7261 | 177 { |
178 size_t res = iconv (cd, NULL, NULL, &outptr, &outsize); | |
179 | |
180 if (res == (size_t)(-1)) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
181 goto fail; |
7261 | 182 } |
183 # endif | |
184 if (outsize != 0) | |
185 abort (); | |
186 } | |
187 | |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
188 *resultp = result; |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
189 *lengthp = length; |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
190 |
7261 | 191 return 0; |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
192 |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
193 fail: |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
194 { |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
195 if (result != *resultp) |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
196 { |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
197 int saved_errno = errno; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
198 free (result); |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
199 errno = saved_errno; |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
200 } |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
201 return -1; |
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
202 } |
7261 | 203 # undef tmpbufsize |
204 } | |
205 | |
206 char * | |
207 str_cd_iconv (const char *src, iconv_t cd) | |
208 { | |
209 /* For most encodings, a trailing NUL byte in the input will be converted | |
210 to a trailing NUL byte in the output. But not for UTF-7. So that this | |
211 function is usable for UTF-7, we have to exclude the NUL byte from the | |
212 conversion and add it by hand afterwards. */ | |
13885 | 213 # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__) |
7881
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
214 /* Irix iconv() inserts a NUL byte if it cannot convert. |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
215 NetBSD iconv() inserts a question mark if it cannot convert. |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
216 Only GNU libiconv and GNU libc are known to prefer to fail rather |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
217 than doing a lossy conversion. For other iconv() implementations, |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
218 we have to look at the number of irreversible conversions returned; |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
219 but this information is lost when iconv() returns for an E2BIG reason. |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
220 Therefore we cannot use the second, faster algorithm. */ |
7261 | 221 |
222 char *result = NULL; | |
7912
57ae1482adc0
Make the API of mem_cd_iconv more useful.
Bruno Haible <bruno@clisp.org>
parents:
7881
diff
changeset
|
223 size_t length = 0; |
7261 | 224 int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length); |
225 char *final_result; | |
226 | |
227 if (retval < 0) | |
228 { | |
229 if (result != NULL) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
230 abort (); |
7261 | 231 return NULL; |
232 } | |
233 | |
234 /* Add the terminating NUL byte. */ | |
235 final_result = | |
236 (result != NULL ? realloc (result, length + 1) : malloc (length + 1)); | |
237 if (final_result == NULL) | |
238 { | |
12737
d49b651c4fd8
lib/striconv.c (str_cd_iconv): Avoid if before free.
Simon Josefsson <simon@josefsson.org>
parents:
12559
diff
changeset
|
239 free (result); |
7261 | 240 errno = ENOMEM; |
241 return NULL; | |
242 } | |
243 final_result[length] = '\0'; | |
244 | |
245 return final_result; | |
246 | |
247 # else | |
7881
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
248 /* This algorithm is likely faster than the one above. But it may produce |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
249 iconv() returns for an E2BIG reason, when the output size guess is too |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
250 small. Therefore it can only be used when we don't need the number of |
6b561fb10325
(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
Bruno Haible <bruno@clisp.org>
parents:
7862
diff
changeset
|
251 irreversible conversions performed. */ |
7261 | 252 char *result; |
253 size_t result_size; | |
254 size_t length; | |
255 const char *inptr = src; | |
256 size_t inbytes_remaining = strlen (src); | |
257 | |
258 /* Make a guess for the worst-case output size, in order to avoid a | |
259 realloc. It's OK if the guess is wrong as long as it is not zero and | |
260 doesn't lead to an integer overflow. */ | |
261 result_size = inbytes_remaining; | |
262 { | |
263 size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2); | |
264 if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX) | |
265 result_size *= MB_LEN_MAX; | |
266 } | |
267 result_size += 1; /* for the terminating NUL */ | |
268 | |
269 result = (char *) malloc (result_size); | |
270 if (result == NULL) | |
271 { | |
272 errno = ENOMEM; | |
273 return NULL; | |
274 } | |
275 | |
276 /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */ | |
277 # if defined _LIBICONV_VERSION \ | |
13885 | 278 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
279 || defined __sun) | |
7261 | 280 /* Set to the initial state. */ |
281 iconv (cd, NULL, NULL, NULL, NULL); | |
282 # endif | |
283 | |
284 /* Do the conversion. */ | |
285 { | |
286 char *outptr = result; | |
287 size_t outbytes_remaining = result_size - 1; | |
288 | |
289 for (;;) | |
290 { | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
291 /* Here inptr + inbytes_remaining = src + strlen (src), |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
292 outptr + outbytes_remaining = result + result_size - 1. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
293 size_t res = iconv (cd, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
294 (ICONV_CONST char **) &inptr, &inbytes_remaining, |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
295 &outptr, &outbytes_remaining); |
7261 | 296 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
297 if (res == (size_t)(-1)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
298 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
299 if (errno == EINVAL) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
300 break; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
301 else if (errno == E2BIG) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
302 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
303 size_t used = outptr - result; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
304 size_t newsize = result_size * 2; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
305 char *newresult; |
7261 | 306 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
307 if (!(newsize > result_size)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
308 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
309 errno = ENOMEM; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
310 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
311 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
312 newresult = (char *) realloc (result, newsize); |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
313 if (newresult == NULL) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
314 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
315 errno = ENOMEM; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
316 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
317 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
318 result = newresult; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
319 result_size = newsize; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
320 outptr = result + used; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
321 outbytes_remaining = result_size - 1 - used; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
322 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
323 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
324 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
325 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
326 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
327 break; |
7261 | 328 } |
329 /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */ | |
330 # if defined _LIBICONV_VERSION \ | |
13885 | 331 || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
332 || defined __sun) | |
7261 | 333 for (;;) |
334 { | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
335 /* Here outptr + outbytes_remaining = result + result_size - 1. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
336 size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining); |
7261 | 337 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
338 if (res == (size_t)(-1)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
339 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
340 if (errno == E2BIG) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
341 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
342 size_t used = outptr - result; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
343 size_t newsize = result_size * 2; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
344 char *newresult; |
7261 | 345 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
346 if (!(newsize > result_size)) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
347 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
348 errno = ENOMEM; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
349 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
350 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
351 newresult = (char *) realloc (result, newsize); |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
352 if (newresult == NULL) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
353 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
354 errno = ENOMEM; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
355 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
356 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
357 result = newresult; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
358 result_size = newsize; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
359 outptr = result + used; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
360 outbytes_remaining = result_size - 1 - used; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
361 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
362 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
363 goto failed; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
364 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
365 else |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
366 break; |
7261 | 367 } |
368 # endif | |
369 | |
370 /* Add the terminating NUL byte. */ | |
371 *outptr++ = '\0'; | |
372 | |
373 length = outptr - result; | |
374 } | |
375 | |
376 /* Give away unused memory. */ | |
377 if (length < result_size) | |
378 { | |
379 char *smaller_result = (char *) realloc (result, length); | |
380 | |
381 if (smaller_result != NULL) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
382 result = smaller_result; |
7261 | 383 } |
384 | |
385 return result; | |
386 | |
387 failed: | |
388 { | |
389 int saved_errno = errno; | |
390 free (result); | |
391 errno = saved_errno; | |
392 return NULL; | |
393 } | |
394 | |
395 # endif | |
396 } | |
397 | |
398 #endif | |
399 | |
400 char * | |
401 str_iconv (const char *src, const char *from_codeset, const char *to_codeset) | |
402 { | |
7919 | 403 if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0) |
7917
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
404 { |
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
405 char *result = strdup (src); |
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
406 |
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
407 if (result == NULL) |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
408 errno = ENOMEM; |
7917
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
409 return result; |
0b6b5a675452
Ensure errno when strdup fails.
Bruno Haible <bruno@clisp.org>
parents:
7912
diff
changeset
|
410 } |
7261 | 411 else |
412 { | |
413 #if HAVE_ICONV | |
414 iconv_t cd; | |
415 char *result; | |
416 | |
417 /* Avoid glibc-2.1 bug with EUC-KR. */ | |
13885 | 418 # if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \ |
419 && !defined _LIBICONV_VERSION | |
7261 | 420 if (c_strcasecmp (from_codeset, "EUC-KR") == 0 |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
421 || c_strcasecmp (to_codeset, "EUC-KR") == 0) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
422 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
423 errno = EINVAL; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
424 return NULL; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
425 } |
7261 | 426 # endif |
427 cd = iconv_open (to_codeset, from_codeset); | |
428 if (cd == (iconv_t) -1) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
429 return NULL; |
7261 | 430 |
431 result = str_cd_iconv (src, cd); | |
432 | |
433 if (result == NULL) | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
434 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
435 /* Close cd, but preserve the errno from str_cd_iconv. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
436 int saved_errno = errno; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
437 iconv_close (cd); |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
438 errno = saved_errno; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
439 } |
7261 | 440 else |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
441 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
442 if (iconv_close (cd) < 0) |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
443 { |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
444 /* Return NULL, but free the allocated memory, and while doing |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
445 that, preserve the errno from iconv_close. */ |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
446 int saved_errno = errno; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
447 free (result); |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
448 errno = saved_errno; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
449 return NULL; |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
450 } |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
451 } |
7261 | 452 return result; |
453 #else | |
454 /* This is a different error code than if iconv_open existed but didn't | |
12421
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
455 support from_codeset and to_codeset, so that the caller can emit |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
456 an error message such as |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
457 "iconv() is not supported. Installing GNU libiconv and |
e8d2c6fc33ad
Use spaces for indentation, not tabs.
Bruno Haible <bruno@clisp.org>
parents:
7944
diff
changeset
|
458 then reinstalling this package would fix this." */ |
7261 | 459 errno = ENOSYS; |
460 return NULL; | |
461 #endif | |
462 } | |
463 } |