#define _GNU_SOURCE
#include <setjmp.h>
#include <errno.h>
#include <iconv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static jmp_buf jb;

/* Overrides glibc __assert_fail that's called from gconv
   in the unfixed case.  */
void __assert_fail(void)
{
  longjmp(jb, 42);
}

static void
die (const char *msg)
{
  perror (msg);
  exit (1);
}

int
main (int argc, char **argv)
{
  setvbuf (stdout, NULL, _IONBF, 0);
  setvbuf (stderr, NULL, _IONBF, 0);

  const char *fromcode = argc >= 2 ? argv[1] : "IBM1390";
  const char *tocode = argc >= 3 ? argv[2] : "UTF-8";
  size_t outsz = argc >= 4 ? strtoul (argv[3], NULL, 0) : 4;

  /* IBM13xx combined-character example: 0xECB5 is a DBCS code that expands
     to two UCS-4 code points (U+304B U+309A). */
  unsigned char input[] = { 0x0e, 0xec, 0xb5, 0x0f };
  char *inptr = (char *) input;
  size_t inleft = sizeof (input);

  if (outsz == 0)
    outsz = 1;
  char *outbuf = malloc (outsz);
  if (!outbuf)
    die ("malloc");
  memset (outbuf, 0x41, outsz);
  char *outptr = outbuf;
  size_t outleft = outsz;

  fprintf (stderr,
           "[*] iconv %s -> %s, in=0e(SO) ecb5 0f(SI), outsz=%zu\n",
           fromcode, tocode, outsz);

  iconv_t cd = iconv_open (tocode, fromcode);
  if (cd == (iconv_t) -1)
    die ("iconv_open");

  while (1)
    {
      if (setjmp(jb) == 42)
	{
	  printf("FAIL got an abort\n");
	  //return 1;
	  sleep(1);

	  /* Reset everything */
	  iconv (cd, NULL, NULL, NULL, NULL);
          inptr = (char *) input;
          inleft = sizeof (input);

          memset (outbuf, 0x41, outsz);
          outptr = outbuf;
          outleft = outsz;
	}

      errno = 0;
      long rc = iconv (cd, &inptr, &inleft, &outptr, &outleft);

      fprintf (stderr,
	       "[*] iconv rc=%ld errno=%d (%s) inleft=%zu outleft=%zu\n",
	       rc, errno, strerror (errno), inleft, outleft);

      if (rc < 0)
        {
	  /* Reset everything */
	  iconv (cd, NULL, NULL, NULL, NULL);
          inptr = (char *) input;
          inleft = sizeof (input);

          memset (outbuf, 0x41, outsz);
          outptr = outbuf;
          outleft = outsz;
        }
      else
        fwrite(outbuf, 1, outsz - outleft, stdout);
      sleep(1);
    }
  iconv_close (cd);
  free (outbuf);
  return 0;
}
