X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fi18n.c;h=df7ae67da0d9695bfa511953a0a0e5e6f3c23e80;hb=815b194a23ee63977333c1e9d8fe11c2ccc7edd1;hp=cdcf57003bfbd608008314f0c468826e899b19a0;hpb=f68aeb1f6d16c5863d9bc7803f403ff6ded9abac;p=pspp diff --git a/src/libpspp/i18n.c b/src/libpspp/i18n.c index cdcf57003b..df7ae67da0 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, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. + Copyright (C) 2006, 2009, 2010, 2011, 2012, 2013, 2014 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 @@ -148,53 +148,75 @@ recode_string_len (const char *to, const char *from, 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) +try_recode (iconv_t conv, char fallbackchar, + const char *in, size_t inbytes, + char *out_, size_t outbytes) { - /* FIXME: Need to ensure that this char is valid in the target encoding */ - const char fallbackchar = '?'; - char *op = op_; + char *out = out_; + int i; /* 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; - } + /* Do two rounds of iconv() calls: + + - The first round does the bulk of the conversion using the + caller-supplied input data.. + + - The second round flushes any leftover output. This has a real effect + with input encodings that use combining diacritics, e.g. without the + second round the last character tends to gets dropped when converting + from windows-1258 to other encodings. + */ + for (i = 0; i < 2; i++) + { + ICONV_CONST char **inp = i ? NULL : (ICONV_CONST char **) ∈ + size_t *inbytesp = i ? NULL : &inbytes; + + while (iconv (conv, inp, inbytesp, &out, &outbytes) == -1) + switch (errno) + { + case EINVAL: + if (outbytes < 2) + return -E2BIG; + if (!fallbackchar) + return -EINVAL; + *out++ = fallbackchar; + *out = '\0'; + return out - out_; + + case EILSEQ: + if (outbytes == 0) + return -E2BIG; + if (!fallbackchar) + return -EILSEQ; + *out++ = fallbackchar; + outbytes--; + if (inp) + { + in++; + inbytes--; + } + break; + + case E2BIG: + return -E2BIG; + + default: + /* should never happen */ + fprintf (stderr, "Character conversion error: %s\n", + strerror (errno)); + NOT_REACHED (); + break; + } + } if (outbytes == 0) - return -1; + return -E2BIG; - *op = '\0'; - return op - op_; + *out = '\0'; + return out - out_; } /* Converts the string TEXT, which should be encoded in FROM-encoding, to a @@ -498,6 +520,57 @@ filename_to_utf8 (const char *filename) return recode_string ("UTF-8", filename_encoding (), filename, -1); } +static int +recode_substring_pool__ (const char *to, const char *from, + struct substring text, char fallbackchar, + struct pool *pool, struct substring *out) +{ + size_t bufsize; + iconv_t conv ; + + if (to == NULL) + to = default_encoding; + + if (from == NULL) + from = default_encoding; + + conv = create_iconv (to, from); + + if ( (iconv_t) -1 == conv ) + { + if (fallbackchar) + { + out->string = pool_malloc (pool, text.length + 1); + out->length = text.length; + memcpy (out->string, text.string, text.length); + out->string[out->length] = '\0'; + return 0; + } + else + return EPROTO; + } + + for (bufsize = text.length + 1; bufsize > text.length; bufsize *= 2) + { + char *output = pool_malloc (pool, bufsize); + ssize_t retval; + + retval = try_recode (conv, fallbackchar, text.string, text.length, + output, bufsize); + if (retval >= 0) + { + *out = ss_buffer (output, retval); + return 0; + } + pool_free (pool, output); + + if (retval != -E2BIG) + return -retval; + } + + NOT_REACHED (); +} + /* 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 '?'. @@ -513,42 +586,32 @@ struct substring recode_substring_pool (const char *to, const char *from, struct substring text, struct pool *pool) { - size_t outbufferlength; - iconv_t conv ; - - if (to == NULL) - to = default_encoding; - - if (from == NULL) - from = default_encoding; - - conv = create_iconv (to, from); + struct substring out; - if ( (iconv_t) -1 == conv ) - { - struct substring out; + recode_substring_pool__ (to, from, text, '?', pool, &out); + return out; +} - out.string = pool_malloc (pool, text.length + 1); - out.length = text.length; - memcpy (out.string, text.string, text.length); - out.string[out.length] = '\0'; - return out; - } +/* Converts the string TEXT, which should be encoded in FROM-encoding, to a + dynamically allocated string in TO-encoding. On success, returns 0, and the + converted null-terminated string, allocated from POOL with pool_malloc(), is + stored in *OUT. On failure, returns a positive errno value. - 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); - } + The function fails with an error if any part of the input string is not + valid in the declared input encoding. */ +int +recode_pedantically (const char *to, const char *from, + struct substring text, struct pool *pool, + struct substring *out) +{ + int error; - NOT_REACHED (); + error = recode_substring_pool__ (to, from, text, 0, pool, out); + if (error) + *out = ss_empty (); + return error; } - + void i18n_init (void) {