X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fi18n.c;h=4934617121c786f5b19c6db88cc20e6f2033f116;hb=97f9b8ad137e333af9b3c767556d28dfda93a461;hp=e08ba2804bbf4d6b0dbe7c6cf81b820c7a239462;hpb=8f04b0ced35a66cfdebefbcb53c81979add36ca3;p=pspp
diff --git a/src/libpspp/i18n.c b/src/libpspp/i18n.c
index e08ba2804b..4934617121 100644
--- a/src/libpspp/i18n.c
+++ b/src/libpspp/i18n.c
@@ -1,5 +1,5 @@
/* PSPP - a program for statistical analysis.
- Copyright (C) 2006, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,36 +15,35 @@
along with this program. If not, see . */
#include
-#include
+
+#include "libpspp/i18n.h"
+
#include
+#include
+#include
+#include
+#include
#include
-#include
#include
+#include
#include
-#include
-#include
-#include
-#include
-#include "assertion.h"
-#include "hmapx.h"
-#include "hash-functions.h"
-#include "pool.h"
-
-#include "i18n.h"
-
-#include "version.h"
-#include
-#include "xstrndup.h"
+#include "libpspp/assertion.h"
+#include "libpspp/hmapx.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+#include "libpspp/version.h"
-#if HAVE_NL_LANGINFO
-#include
-#endif
+#include "gl/localcharset.h"
+#include "gl/xalloc.h"
+#include "gl/relocatable.h"
+#include "gl/xstrndup.h"
struct converter
- {
- const char *tocode;
- const char *fromcode;
+ {
+ char *tocode;
+ char *fromcode;
iconv_t conv;
};
@@ -73,19 +72,23 @@ create_iconv (const char* tocode, const char* fromcode)
hmapx_insert (&map, converter, hash);
/* I don't think it's safe to translate this string or to use messaging
- as the convertors have not yet been set up */
+ as the converters have not yet been set up */
if ( (iconv_t) -1 == converter->conv && 0 != strcmp (tocode, fromcode))
{
const int err = errno;
fprintf (stderr,
"Warning: "
- "cannot create a convertor for \"%s\" to \"%s\": %s\n",
+ "cannot create a converter for `%s' to `%s': %s\n",
fromcode, tocode, strerror (err));
}
return converter->conv;
}
+
+/* Similar to recode_string_pool, but allocates the returned value on the heap
+ instead of in a pool. It is the caller's responsibility to free the
+ returned value. */
char *
recode_string (const char *to, const char *from,
const char *text, int length)
@@ -94,108 +97,135 @@ recode_string (const char *to, const char *from,
}
-/* Return a string based on TEXT which must be encoded using FROM.
- The returned string will be encoded in TO.
- If length is not -1, then it must be the number of bytes in TEXT.
- The returned string must be freed when no longer required.
-*/
-char *
-recode_string_pool (const char *to, const char *from,
- const char *text, int length, struct pool *pool)
-{
- char *outbuf = 0;
- size_t outbufferlength;
- size_t result;
- char *op ;
- size_t inbytes = 0;
- size_t outbytes ;
- iconv_t conv ;
+/* Uses CONV to convert the INBYTES starting at IP into the OUTBYTES starting
+ at OP, and appends a null terminator to the output.
+ Returns the output length if successful, -1 if the output buffer is too
+ small. */
+static ssize_t
+try_recode (iconv_t conv,
+ const char *ip, size_t inbytes,
+ char *op_, size_t outbytes)
+{
/* FIXME: Need to ensure that this char is valid in the target encoding */
const char fallbackchar = '?';
+ char *op = op_;
+
+ /* Put the converter into the initial shift state, in case there was any
+ state information left over from its last usage. */
+ iconv (conv, NULL, 0, NULL, 0);
+
+ while (iconv (conv, (ICONV_CONST char **) &ip, &inbytes,
+ &op, &outbytes) == -1)
+ switch (errno)
+ {
+ case EINVAL:
+ if (outbytes < 2)
+ return -1;
+ *op++ = fallbackchar;
+ *op = '\0';
+ return op - op_;
+
+ case EILSEQ:
+ if (outbytes == 0)
+ return -1;
+ *op++ = fallbackchar;
+ outbytes--;
+ ip++;
+ inbytes--;
+ break;
+
+ case E2BIG:
+ return -1;
+
+ default:
+ /* should never happen */
+ fprintf (stderr, "Character conversion error: %s\n", strerror (errno));
+ NOT_REACHED ();
+ break;
+ }
+
+ if (outbytes == 0)
+ return -1;
+
+ *op = '\0';
+ return op - op_;
+}
+
+/* Converts the string TEXT, which should be encoded in FROM-encoding, to a
+ dynamically allocated string in TO-encoding. Any characters which cannot be
+ converted will be represented by '?'.
+
+ LENGTH should be the length of the string or -1, if null terminated.
+
+ The returned string will be allocated on POOL.
+
+ This function's behaviour differs from that of g_convert_with_fallback
+ provided by GLib. The GLib function will fail (returns NULL) if any part of
+ the input string is not valid in the declared input encoding. This function
+ however perseveres even in the presence of badly encoded input. */
+char *
+recode_string_pool (const char *to, const char *from,
+ const char *text, int length, struct pool *pool)
+{
+ struct substring out;
if ( text == NULL )
return NULL;
if ( length == -1 )
- length = strlen(text);
+ length = strlen (text);
- if (to == NULL)
- to = default_encoding;
+ out = recode_substring_pool (to, from, ss_buffer (text, length), pool);
+ return out.string;
+}
- if (from == NULL)
- from = default_encoding;
+/* Converts the string TEXT, which should be encoded in FROM-encoding, to a
+ dynamically allocated string in TO-encoding. Any characters which cannot be
+ converted will be represented by '?'.
- for ( outbufferlength = 1 ; outbufferlength != 0; outbufferlength <<= 1 )
- if ( outbufferlength > length)
- break;
+ The returned string will be null-terminated and allocated on POOL.
- outbuf = pool_malloc (pool, outbufferlength);
- op = outbuf;
+ This function's behaviour differs from that of g_convert_with_fallback
+ provided by GLib. The GLib function will fail (returns NULL) if any part of
+ the input string is not valid in the declared input encoding. This function
+ however perseveres even in the presence of badly encoded input. */
+struct substring
+recode_substring_pool (const char *to, const char *from,
+ struct substring text, struct pool *pool)
+{
+ size_t outbufferlength;
+ iconv_t conv ;
- outbytes = outbufferlength;
- inbytes = length;
+ if (to == NULL)
+ to = default_encoding;
+ if (from == NULL)
+ from = default_encoding;
conv = create_iconv (to, from);
if ( (iconv_t) -1 == conv )
- return xstrdup (text);
-
- do {
- const char *ip = text;
- result = iconv (conv, (ICONV_CONST char **) &text, &inbytes,
- &op, &outbytes);
-
- if ( -1 == result )
- {
- int the_error = errno;
-
- switch (the_error)
- {
- case EILSEQ:
- case EINVAL:
- if ( outbytes > 0 )
- {
- *op++ = fallbackchar;
- outbytes--;
- text++;
- inbytes--;
- break;
- }
- /* Fall through */
- case E2BIG:
- free (outbuf);
- outbufferlength <<= 1;
- outbuf = pool_malloc (pool, outbufferlength);
- op = outbuf;
- outbytes = outbufferlength;
- inbytes = length;
- text = ip;
- break;
- default:
- /* should never happen */
- fprintf (stderr, "Character conversion error: %s\n", strerror (the_error));
- NOT_REACHED ();
- break;
- }
- }
- } while ( -1 == result );
-
- if (outbytes == 0 )
{
- char *const oldaddr = outbuf;
- outbuf = pool_realloc (pool, outbuf, outbufferlength + 1);
-
- op += (outbuf - oldaddr) ;
+ struct substring out;
+ ss_alloc_substring (&out, text);
+ return out;
}
- *op = '\0';
+ for ( outbufferlength = 1 ; outbufferlength != 0; outbufferlength <<= 1 )
+ if ( outbufferlength > text.length)
+ {
+ char *output = pool_malloc (pool, outbufferlength);
+ ssize_t output_len = try_recode (conv, text.string, text.length,
+ output, outbufferlength);
+ if (output_len >= 0)
+ return ss_buffer (output, output_len);
+ pool_free (pool, output);
+ }
- return outbuf;
+ NOT_REACHED ();
}
-
void
i18n_init (void)
{
@@ -279,8 +309,11 @@ i18n_done (void)
{
struct hmapx_node *node;
struct converter *cvtr;
+
HMAPX_FOR_EACH (cvtr, node, &map)
{
+ free (cvtr->tocode);
+ free (cvtr->fromcode);
iconv_close (cvtr->conv);
free (cvtr);
}
@@ -296,7 +329,7 @@ i18n_done (void)
bool
valid_encoding (const char *enc)
{
- iconv_t conv = iconv_open ("UTF8", enc);
+ iconv_t conv = iconv_open (UTF8, enc);
if ( conv == (iconv_t) -1)
return false;