/**
***     iconv_open(), iconv(), iconv_close() wrappers for the OS/400.
***
***     See Copyright for the status of this software.
***
***     Author: Patrick Monnerat <pm@datasphere.ch>, DATASPHERE S.A.
**/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include "/QIBM/include/iconv.h"        /* Force system definition. */

#define USE_SYSTEM_ICONV
#include "iconv.h"                      /* Use local definitions. */



/**
***     Bring-in the name-->CCSID mapping DFA tables.
**/

#include "ianatables.c"



static int
findEncoding(const unsigned char * * namep)

{
        t_staterange curstate;
        t_ccsid ccsid;
        t_ccsid final;
        t_transrange l;
        t_transrange h;
        const unsigned char * name;

        /**
        ***     Get the CCSID correspong to the name at *`namep'.
        ***     If success, update pointer at `namep' to 1st byte after matched
        ***             name and return the CCSID.
        ***     If failure, set errno and return -1.
        **/

        if (!namep || !(name = *namep)) {
                errno = EINVAL;
                return -1;
                }

        curstate = 0;
        final = 0;

        for (;;) {
                if (curstate < sizeof final_array / sizeof final_array[0])
                        if (final_array[curstate]) {
                                final = final_array[curstate];
                                *namep = name;
                                }

                l = trans_array[curstate] - 1;
                h = trans_array[curstate + 1];

                do {
                        if (++l >= h) {
                                if (!final) {
                                        errno = EINVAL;
                                        return -1;
                                        }

                                return final - 1;
                                }
                } while (label_array[l] != *name);

                curstate = goto_array[l];
                name++;
                }

        /* NOTREACHED. */
}


static void
makeos400codename(char * buf, unsigned int ccsid)

{
        ccsid &= 0xFFFF;
        memset(buf, 0, 32);
        sprintf(buf, "IBMCCSID%05u0000000", ccsid);
}


Iconv_t
IconvOpen(const char * tocode, const char * fromcode)

{
        int toccsid = findEncoding(&tocode);
        int fromccsid = findEncoding(&fromcode);
        char fromibmccsid[33];
        char toibmccsid[33];
        iconv_t * cd;

        if (toccsid < 0 || fromccsid < 0)
                return (Iconv_t) -1;

        makeos400codename(fromibmccsid, fromccsid);
        makeos400codename(toibmccsid, toccsid);
        memset(toibmccsid + 13, 0, sizeof toibmccsid - 13);

        cd = (iconv_t *) malloc(sizeof *cd);

        if (!cd)
                return (Iconv_t) -1;

        *cd = iconv_open(toibmccsid, fromibmccsid);

        if (cd->return_value) {
                free((char *) cd);
                return (Iconv_t) -1;
                }

        return (Iconv_t) cd;
}


size_t
Iconv(Iconv_t cd, char * * inbuf, size_t * inbytesleft,
                                        char * * outbuf, size_t * outbytesleft)

{
        if (!cd || cd == (Iconv_t) -1) {
                errno = EINVAL;
                return (size_t) -1;
                }

        return iconv(*(iconv_t *) cd, inbuf, inbytesleft, outbuf, outbytesleft);
}


int
IconvClose(Iconv_t cd)

{
        if (!cd || cd == (Iconv_t) -1) {
                errno = EINVAL;
                return -1;
                }

        if (iconv_close(*(iconv_t *) cd))
                return -1;

        free((char *) cd);
        return 0;
}