]> gitweb.michael.orlitzky.com - iconv-quirks.git/commitdiff
main.c: add three real iconv() tests
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 5 Jul 2026 01:05:48 +0000 (21:05 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Sun, 5 Jul 2026 01:05:48 +0000 (21:05 -0400)
main.c

diff --git a/main.c b/main.c
index 38df4780b70d8e209ebd18458862bd29abfccd2a..c19c7e07a8fc7e98d6e1dd7b567d7914b357d867 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,6 +1,7 @@
 #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
@@ -28,6 +29,9 @@
  * (d) 0xC0, 0xC1, 0xF5-0xFF are never valid
  */
 
+#define ASCII_MAX 0x7F
+#define MULTIBYTE_MIN 0xC3
+
 #define SUCCESS 0
 #define FAILURE 1
 
@@ -110,7 +114,6 @@ int test_open_close() {
     return FAILURE;
   }
 
-
   return SUCCESS;
 }
 
@@ -195,13 +198,13 @@ int test_iconv_open_does_not_support_invalid_from_codeset() {
   printf("iconv_open() does not support invalid \"from\" codeset...");
   iconv_t cd = iconv_open(tocode, fromcode);
   if (cd == (iconv_t)(-1)) {
-    switch (errno) {
-      case EINVAL:
-       printf("OK (EINVAL, expected)\n");
-       return SUCCESS;
-      default:
-       printf("FAIL (errno=%d, unexpected)\n", errno);
-        return FAILURE;
+    if (errno == EINVAL) {
+      printf("OK (EINVAL, expected)\n");
+      return SUCCESS;
+    }
+    else {
+      printf("FAIL (errno=%d, unexpected)\n", errno);
+      return FAILURE;
     }
   }
   printf("FAIL (invalid codeset was accepted)\n");
@@ -209,6 +212,148 @@ int test_iconv_open_does_not_support_invalid_from_codeset() {
   return FAILURE;
 }
 
+/* If a sequence of input bytes does not form a valid character or
+ * shift sequence in the input codeset... conversion shall stop after
+ * the previous successfully converted character or shift sequence.
+ *
+ *
+ *  [EILSEQ]
+ *    Input conversion stopped due to an input byte that does not belong
+ *    to the input codeset.
+ */
+int test_iconv_invalid_char_is_eilseq() {
+  iconv_t cd;
+  const char* tocode = "ASCII";
+
+  printf("iconv() returns EILSEQ on invalid input...");
+  if (_my_iconv_open(&cd, tocode, tocode) == FAILURE) {
+    printf("ERROR (iconv_open)\n");
+    return FAILURE;
+  }
+
+  size_t insize = 1, outsize = 1;
+  char* inbuf = (char*)malloc(insize);
+  char* outbuf = (char*)malloc(outsize);
+
+  inbuf[0] = ASCII_MAX + 1; // not ascii
+
+  size_t result = iconv(cd, &inbuf, &insize, &outbuf, &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");
+      return FAILURE;
+    }
+
+    if (saved_errno == EILSEQ) {
+      printf("OK (EILSEQ, expected)\n");
+      return SUCCESS;
+    }
+    else {
+      printf("FAIL (errno=%d, unexpected)\n", saved_errno);
+      return FAILURE;
+    }
+  }
+
+  printf("FAIL (no error)\n");
+  return FAILURE;
+}
+
+/* If the input buffer ends with an incomplete character or shift
+ * sequence, conversion shall stop after the previous successfully
+ * converted bytes.
+ *
+ * [EINVAL]
+ *   Input conversion stopped due to an incomplete character or shift
+ *   sequence at the end of the input buffer.
+ */
+int test_iconv_incomplete_multibyte_is_einval() {
+  iconv_t cd;
+  const char* tocode = "UTF-8";
+
+  printf("iconv() returns EINVAL on incomplete multibyte...");
+  if (_my_iconv_open(&cd, tocode, tocode) == FAILURE) {
+    printf("ERROR (iconv_open)\n");
+    return FAILURE;
+  }
+
+  size_t insize = 1, outsize = 1;
+  char* inbuf = (char*)malloc(insize);
+  char* outbuf = (char*)malloc(outsize);
+
+  // 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);
+
+  if (result == (size_t)(-1)) {
+    int saved_errno = errno; // iconv_close() clobbers it
+
+    if (_my_iconv_close(&cd) == FAILURE) {
+      printf("ERROR (iconv_close)\n");
+      return FAILURE;
+    }
+
+    if (saved_errno == EINVAL) {
+      printf("OK (EINVAL, expected)\n");
+      return SUCCESS;
+    }
+    else {
+      printf("FAIL (errno=%d, unexpected)\n", saved_errno);
+      return FAILURE;
+    }
+  }
+
+  printf("FAIL (no error)\n");
+  return FAILURE;
+}
+
+int test_iconv_too_big_is_e2big() {
+  iconv_t cd;
+  const char* tocode = "ASCII";
+
+  printf("iconv() returns E2BIG if the input is too big...");
+  if (_my_iconv_open(&cd, tocode, tocode) == FAILURE) {
+    printf("ERROR (iconv_open)\n");
+    return FAILURE;
+  }
+
+  size_t insize = 2, outsize = 1;
+  char* inbuf = (char*)malloc(insize);
+  char* outbuf = (char*)malloc(outsize);
+
+  // 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);
+
+  if (result == (size_t)(-1)) {
+    int saved_errno = errno; // iconv_close() clobbers it
+
+    if (_my_iconv_close(&cd) == FAILURE) {
+      printf("ERROR (iconv_close)\n");
+      return FAILURE;
+    }
+
+    if (saved_errno == E2BIG) {
+      printf("OK (E2BIG, expected)\n");
+      return SUCCESS;
+    }
+    else {
+      printf("FAIL (errno=%d, unexpected)\n", saved_errno);
+      return FAILURE;
+    }
+  }
+
+  printf("FAIL (no error)\n");
+  return FAILURE;
+}
+
 int main(int argc, char** argv) {
   int result = test_open_close();
 
@@ -230,5 +375,9 @@ int main(int argc, char** argv) {
   result |= test_iconv_open_does_not_support_invalid_to_codeset();
   result |= test_iconv_open_does_not_support_invalid_from_codeset();
 
+  result |= test_iconv_invalid_char_is_eilseq();
+  result |= test_iconv_too_big_is_e2big();
+  result |= test_iconv_incomplete_multibyte_is_einval();
+
   return result;
 }