1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2009, 2010 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 3 of the License, or
7 (at your option) 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
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/i18n.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/hmapx.h"
33 #include "libpspp/hash-functions.h"
34 #include "libpspp/pool.h"
35 #include "libpspp/version.h"
37 #include "gl/localcharset.h"
38 #include "gl/xalloc.h"
39 #include "gl/relocatable.h"
40 #include "gl/xstrndup.h"
49 static char *default_encoding;
50 static struct hmapx map;
52 /* A wrapper around iconv_open */
54 create_iconv (const char* tocode, const char* fromcode)
57 struct hmapx_node *node;
58 struct converter *converter;
61 hash = hash_string (tocode, hash_string (fromcode, 0));
62 HMAPX_FOR_EACH_WITH_HASH (converter, node, hash, &map)
63 if (!strcmp (tocode, converter->tocode)
64 && !strcmp (fromcode, converter->fromcode))
65 return converter->conv;
67 converter = xmalloc (sizeof *converter);
68 converter->tocode = xstrdup (tocode);
69 converter->fromcode = xstrdup (fromcode);
70 converter->conv = iconv_open (tocode, fromcode);
71 hmapx_insert (&map, converter, hash);
73 /* I don't think it's safe to translate this string or to use messaging
74 as the converters have not yet been set up */
75 if ( (iconv_t) -1 == converter->conv && 0 != strcmp (tocode, fromcode))
77 const int err = errno;
80 "cannot create a converter for `%s' to `%s': %s\n",
81 fromcode, tocode, strerror (err));
84 return converter->conv;
88 /* Similar to recode_string_pool, but allocates the returned value on the heap
89 instead of in a pool. It is the caller's responsibility to free the
92 recode_string (const char *to, const char *from,
93 const char *text, int length)
95 return recode_string_pool (to, from, text, length, NULL);
99 /* Uses CONV to convert the INBYTES starting at IP into the OUTBYTES starting
100 at OP, and appends a null terminator to the output.
102 Returns true if successful, false if the output buffer is too small. */
104 try_recode (iconv_t conv,
105 const char *ip, size_t inbytes,
106 char *op, size_t outbytes)
108 /* FIXME: Need to ensure that this char is valid in the target encoding */
109 const char fallbackchar = '?';
111 /* Put the converter into the initial shift state, in case there was any
112 state information left over from its last usage. */
113 iconv (conv, NULL, 0, NULL, 0);
115 while (iconv (conv, (ICONV_CONST char **) &ip, &inbytes,
116 &op, &outbytes) == -1)
122 *op++ = fallbackchar;
129 *op++ = fallbackchar;
139 /* should never happen */
140 fprintf (stderr, "Character conversion error: %s\n", strerror (errno));
152 /* Converts the string TEXT, which should be encoded in FROM-encoding, to a
153 dynamically allocated string in TO-encoding. Any characters which cannot be
154 converted will be represented by '?'.
156 LENGTH should be the length of the string or -1, if null terminated.
158 The returned string will be allocated on POOL.
160 This function's behaviour differs from that of g_convert_with_fallback
161 provided by GLib. The GLib function will fail (returns NULL) if any part of
162 the input string is not valid in the declared input encoding. This function
163 however perseveres even in the presence of badly encoded input. */
165 recode_string_pool (const char *to, const char *from,
166 const char *text, int length, struct pool *pool)
168 size_t outbufferlength;
175 length = strlen(text);
178 to = default_encoding;
181 from = default_encoding;
183 conv = create_iconv (to, from);
185 if ( (iconv_t) -1 == conv )
186 return xstrdup (text);
188 for ( outbufferlength = 1 ; outbufferlength != 0; outbufferlength <<= 1 )
189 if ( outbufferlength > length)
191 char *output = pool_malloc (pool, outbufferlength);
192 if (try_recode (conv, text, length, output, outbufferlength))
194 pool_free (pool, output);
204 setlocale (LC_CTYPE, "");
206 setlocale (LC_MESSAGES, "");
209 setlocale (LC_PAPER, "");
211 bindtextdomain (PACKAGE, relocate(locale_dir));
212 textdomain (PACKAGE);
213 #endif /* ENABLE_NLS */
215 assert (default_encoding == NULL);
216 default_encoding = xstrdup (locale_charset ());
223 get_default_encoding (void)
225 return default_encoding;
229 set_default_encoding (const char *enc)
231 free (default_encoding);
232 default_encoding = xstrdup (enc);
236 /* Attempts to set the encoding from a locale name
237 returns true if successfull.
238 This function does not (should not!) alter the current locale.
241 set_encoding_from_locale (const char *loc)
246 char *tmp = xstrdup (setlocale (LC_CTYPE, NULL));
248 setlocale (LC_CTYPE, "C");
249 c_encoding = xstrdup (locale_charset ());
251 setlocale (LC_CTYPE, loc);
252 loc_encoding = xstrdup (locale_charset ());
255 if ( 0 == strcmp (loc_encoding, c_encoding))
261 setlocale (LC_CTYPE, tmp);
267 free (default_encoding);
268 default_encoding = loc_encoding;
281 struct hmapx_node *node;
282 struct converter *cvtr;
284 HMAPX_FOR_EACH (cvtr, node, &map)
287 free (cvtr->fromcode);
288 iconv_close (cvtr->conv);
292 hmapx_destroy (&map);
294 free (default_encoding);
295 default_encoding = NULL;
301 valid_encoding (const char *enc)
303 iconv_t conv = iconv_open ("UTF8", enc);
305 if ( conv == (iconv_t) -1)
314 /* Return the system local's idea of the
315 decimal seperator character */
317 get_system_decimal (void)
321 char *ol = xstrdup (setlocale (LC_NUMERIC, NULL));
322 setlocale (LC_NUMERIC, "");
325 radix_char = nl_langinfo (RADIXCHAR)[0];
329 snprintf (buf, sizeof buf, "%f", 2.5);
334 /* We MUST leave LC_NUMERIC untouched, since it would
335 otherwise interfere with data_{in,out} */
336 setlocale (LC_NUMERIC, ol);