#include <errno.h>
#include <iconv.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
/* The various invalid sequences can be inferred from the table of
#define SUCCESS 0
#define FAILURE 1
+#define UNSUPPORTED 2 /* also success if the standard allows it */
/* Open a conversion descriptor by reference. Wraps the boilerplate
* error reporting when iconv_open() fails.
return SUCCESS;
}
-/*
- * Open and close a conversion descriptor (basic sanity test).
- */
-int test_open_close() {
- iconv_t cd;
- const char* tocode = "ASCII";
- const char* fromcode = "ASCII";
-
- printf("can iconv_open() and iconv_close() a conversion descriptor...");
- if (_my_iconv_open(&cd, tocode, fromcode) == FAILURE) {
- printf("FAIL\n");
- return FAILURE;
- }
-
- if (_my_iconv_close(&cd) == SUCCESS) {
- printf("OK\n");
- }
- else {
- printf("FAIL\n");
- return FAILURE;
- }
-
- return SUCCESS;
-}
-
-
/*
* Ensure that iconv_open() supports the given indicator suffix.
*/
switch (errno) {
case EINVAL:
printf("OK (EINVAL, not supported)\n");
- return SUCCESS;
+ return UNSUPPORTED;
default:
printf("FAIL (errno=%d, unexpected error)\n", errno);
return FAILURE;
}
size_t insize = 1, outsize = 1;
- char* inbuf = (char*)malloc(insize);
- char* outbuf = (char*)malloc(outsize);
+ char inbuf[1], outbuf[1];
+ char* inptr = inbuf;
+ char* outptr = outbuf;
inbuf[0] = ASCII_MAX + 1; // not ascii
- size_t result = iconv(cd, &inbuf, &insize, &outbuf, &outsize);
+ size_t result = iconv(cd, &inptr, &insize, &outptr, &outsize);
if (result == (size_t)(-1)) {
int saved_errno = errno; // iconv_close() clobbers it
}
size_t insize = 1, outsize = 1;
- char* inbuf = (char*)malloc(insize);
- char* outbuf = (char*)malloc(outsize);
+ char inbuf[1], outbuf[1];
+ char* inptr = inbuf;
+ char* outptr = outbuf;
// a single byte in the input buffer that demands
// a (nonexistent) subsequent byte
inbuf[0] = MULTIBYTE_MIN;
- size_t result = iconv(cd, &inbuf, &insize, &outbuf, &outsize);
+ size_t result = iconv(cd, &inptr, &insize, &outptr, &outsize);
if (result == (size_t)(-1)) {
int saved_errno = errno; // iconv_close() clobbers it
}
size_t insize = 2, outsize = 1;
- char* inbuf = (char*)malloc(insize);
- char* outbuf = (char*)malloc(outsize);
+ char inbuf[2], outbuf[1];
+ char* inptr = inbuf;
+ char* outptr = outbuf;
// a single byte in the input buffer that demands
// a (nonexistent) subsequent byte
inbuf[0] = 'h';
inbuf[1] = 'i';
- size_t result = iconv(cd, &inbuf, &insize, &outbuf, &outsize);
+ size_t result = iconv(cd, &inptr, &insize, &outptr, &outsize);
if (result == (size_t)(-1)) {
int saved_errno = errno; // iconv_close() clobbers it
return SUCCESS;
}
else {
- printf("FAIL (errno=%d, unexpected)\n", saved_errno);
+ printf("FAIL (errno=%d)\n", saved_errno);
return FAILURE;
}
}
return FAILURE;
}
+
+/* If a sequence of input bytes does not form a valid character or
+ * shift sequence in the input codeset: If the //IGNORE indicator
+ * suffix was specified when the conversion descriptor cd was opened
+ * and the byte sequence is immediately followed by a valid character
+ * or shift sequence, the sequence of bytes shall be discarded and
+ * conversion shall continue from the immediately following valid
+ * character or shift sequence.
+ *
+ * This shall not be treated as an error.
+ * --------------------------------------
+ */
+int test_iconv_ignore_ignores_invalid() {
+ iconv_t cd;
+ const char* fromcode = "ASCII";
+ const char* tocode = "ASCII//IGNORE";
+
+ printf("iconv() ignores invalid input with no errors...");
+ if (_my_iconv_open(&cd, tocode, fromcode) == FAILURE) {
+ printf("ERROR (iconv_open)\n");
+ return FAILURE;
+ }
+
+ size_t insize = 4, outsize = 3;
+ char inbuf[4], outbuf[3] = {0};
+ char* inptr = inbuf;
+ char* outptr = outbuf;
+
+ inbuf[0] = 'h';
+ inbuf[1] = ASCII_MAX + 1; // not ascii
+ inbuf[2] = 'i';
+ inbuf[3] = '\0';
+
+
+ size_t result = iconv(cd, &inptr, &insize, &outptr, &outsize);
+
+ if (result == (size_t)(-1)) {
+ int saved_errno = errno; // iconv_close() clobbers it
+
+ if (_my_iconv_close(&cd) == FAILURE) {
+ printf("ERROR (iconv_close)\n");
+ }
+ else {
+ printf("FAIL (errno=%d)\n", saved_errno);
+ }
+ return FAILURE;
+ }
+
+ if (strcmp(outbuf, "hi") != 0) {
+ printf("FAIL (outbuf=%s)\n", outbuf);
+ return FAILURE;
+ }
+ printf("OK (no error)\n");
+ return SUCCESS;
+}
+
int main(int argc, char** argv) {
- int result = test_open_close();
+ int result = SUCCESS;
/* These three are named explicitly by POSIX which says that you are
* free to append them to the destination codeset, but support is
* not guaranteed; iconv_open() is allowed to fail with an EINVAL
- * to indicate an unsupported suffix. */
- result |= test_iconv_open_supports_suffix("//IGNORE");
- result |= test_iconv_open_supports_suffix("//NON_IDENTICAL_DISCARD");
- result |= test_iconv_open_supports_suffix("//TRANSLIT");
+ * to indicate an unsupported suffix.
+ *
+ * We also need to keep track of whether or not these indicator
+ * suffixes are supported. If they work at all, then the iconv()
+ * spec imposes some conditions on the implementation. */
+ int ignore = test_iconv_open_supports_suffix("//IGNORE");
+ int non_identical_discard = test_iconv_open_supports_suffix("//NON_IDENTICAL_DISCARD");
+ int translit = test_iconv_open_supports_suffix("//TRANSLIT");
+ result |= ignore | non_identical_discard | translit;
/* The supported codeset names and suffixes are implementation-
* defined, making this a bit ambiguous, but POSIX declares EINVAL
result |= test_iconv_too_big_is_e2big();
result |= test_iconv_incomplete_multibyte_is_einval();
+ if (ignore == SUCCESS) {
+ result |= test_iconv_ignore_ignores_invalid();
+ //result |= test_iconv_ignore_ignores_untranslatable();
+ //result |= test_iconv_ignore_does_not_ignore_incomplete_multibyte();
+ }
+
+ if (translit == SUCCESS) {
+ //result |= test_translit_does_not_fail_on_untranslatable();
+ //result |= test_translit_fails_on_incomplete_multibyte();
+ }
+
+ if (non_identical_discard == SUCCESS) {
+ //result |= test_nid_drops_untranslatable();
+ //result |= test_nid_fails_on_incomplete_multibyte();
+ }
+
return result;
}