- 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_;
+ iconv (cvtr->conv, NULL, 0, NULL, 0);
+
+ /* 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 (cvtr->conv, inp, inbytesp, &out, &outbytes) == -1)
+ switch (errno)
+ {
+ case EINVAL:
+ if (outbytes < null_bytes + 1)
+ return -E2BIG;
+ if (!fallbackchar)
+ return -EINVAL;
+ *out++ = fallbackchar;
+ for (j = 0 ; j < null_bytes ; ++j)
+ *out++ = '\0';
+ return out - 1 - 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 <= null_bytes - 1)
+ return -E2BIG;
+
+ for (i = 0 ; i < null_bytes ; ++i)
+ *out++ = '\0';
+
+ return out - 1 - out_;