Doc fix.
[gsasl.git] / gl / iconvme.c
blob943380c03554bc90322eb9dd46a608df4e59e57c
1 /* Recode strings between character sets, using iconv.
2 Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
22 /* Get prototype. */
23 #include "iconvme.h"
25 /* Get malloc. */
26 #include <stdlib.h>
28 /* Get strcmp. */
29 #include <string.h>
31 /* Get errno. */
32 #include <errno.h>
34 #ifdef _LIBC
35 # define HAVE_ICONV 1
36 #else
37 /* Get strdup. */
38 # include "strdup.h"
39 #endif
41 #if HAVE_ICONV
42 /* Get iconv etc. */
43 # include <iconv.h>
44 /* Get MB_LEN_MAX, CHAR_BIT. */
45 # include <limits.h>
46 #endif
48 #ifndef SIZE_MAX
49 # define SIZE_MAX ((size_t) -1)
50 #endif
52 /* Convert a zero-terminated string STR from the FROM_CODSET code set
53 to the TO_CODESET code set. The returned string is allocated using
54 malloc, and must be dellocated by the caller using free. On
55 failure, NULL is returned and errno holds the error reason. Note
56 that if TO_CODESET uses \0 for anything but to terminate the
57 string, the caller of this function may have difficulties finding
58 out the length of the output string. */
59 char *
60 iconv_string (const char *str, const char *from_codeset,
61 const char *to_codeset)
63 char *dest = NULL;
64 #if HAVE_ICONV
65 iconv_t cd;
66 #endif
68 if (strcmp (to_codeset, from_codeset) == 0)
69 return strdup (str);
71 #if HAVE_ICONV
72 cd = iconv_open (to_codeset, from_codeset);
73 if (cd == (iconv_t) -1)
74 return NULL;
76 dest = iconv_alloc (cd, str);
79 int save_errno = errno;
81 if (iconv_close (cd) < 0 && dest)
83 int save_errno2 = errno;
84 /* If we didn't have a real error before, make sure we restore
85 the iconv_close error below. */
86 free (dest);
87 dest = NULL;
88 errno = save_errno2;
90 else
91 errno = save_errno;
93 #else
94 errno = ENOSYS;
95 #endif
97 return dest;
100 /* Convert a zero-terminated string STR using iconv descriptor CD.
101 The returned string is allocated using malloc, and must be
102 dellocated by the caller using free. On failure, NULL is returned
103 and errno holds the error reason. Note that if the target
104 character set uses \0 for anything but to terminate the string,
105 the caller of this function may have difficulties finding
106 out the length of the output string. */
107 #if HAVE_ICONV
108 char *
109 iconv_alloc (iconv_t cd, const char *str)
111 char *dest;
112 char *p = (char *) str;
113 char *outp;
114 size_t inbytes_remaining = strlen (p);
115 /* Guess the maximum length the output string can have. */
116 size_t outbuf_size = inbytes_remaining + 1;
117 size_t outbytes_remaining;
118 size_t err;
119 int have_error = 0;
121 /* Use a worst-case output size guess, so long as that wouldn't be
122 too large for comfort. It's OK if the guess is wrong so long as
123 it's nonzero. */
124 size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
125 if (outbuf_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
126 outbuf_size *= MB_LEN_MAX;
127 outbytes_remaining = outbuf_size - 1;
129 outp = dest = (char *) malloc (outbuf_size);
130 if (dest == NULL)
131 return NULL;
133 again:
134 err = iconv (cd, &p, &inbytes_remaining, &outp, &outbytes_remaining);
136 if (err == (size_t) -1)
138 switch (errno)
140 case EINVAL:
141 /* Incomplete text, do not report an error */
142 break;
144 case E2BIG:
146 size_t used = outp - dest;
147 size_t newsize = outbuf_size * 2;
148 char *newdest;
150 if (newsize <= outbuf_size)
152 errno = ENOMEM;
153 have_error = 1;
154 goto out;
156 newdest = (char *) realloc (dest, newsize);
157 if (newdest == NULL)
159 have_error = 1;
160 goto out;
162 dest = newdest;
163 outbuf_size = newsize;
165 outp = dest + used;
166 outbytes_remaining = outbuf_size - used - 1; /* -1 for NUL */
168 goto again;
170 break;
172 case EILSEQ:
173 have_error = 1;
174 break;
176 default:
177 have_error = 1;
178 break;
182 *outp = '\0';
184 out:
185 if (have_error && dest)
187 int save_errno = errno;
188 free (dest);
189 errno = save_errno;
190 dest = NULL;
193 return dest;
195 #endif