/*
 * hangul.c - $Revision: 1.14 $
 *
 * KoreanCodecs Hangul Module C Implementation
 *
 * Author  : Hye-Shik Chang <perky@FreeBSD.org>
 * Date    : $Date: 2002/07/19 00:01:53 $
 * Created : 25 April 2002
 *
 * This file is part of KoreanCodecs.
 *
 * KoreanCodecs is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * KoreanCodecs is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with KoreanCodecs; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

static char *version =
"$Id: hangul.c,v 1.14 2002/07/19 00:01:53 perky Exp $";

#include "Python.h"

enum { /* Jaeum Codes on U+3100 */
    G = 0x3131, GG, GS, N, NJ, NH, D, DD, L, LG, LM, LB,
    LS, LT, LP, LH, M, B, BB, BS, S, SS, NG, J, JJ, C, K, T, P, H
};

enum { /* Moeum Codes on U+3100 */
    A = 0x314f, AE, YA, YAE, EO, E, YEO, YE, O, WA, WAE, OE, YO,
    U, WEO, WE, WI, YU, EU, YI, I
};

#define NCHOSUNG    19
#define NJUNGSUNG   21
#define NJONGSUNG   28

#define NJAEUM      30
#define NMOEUM      21

#define JAEUM_BOTTOM G
#define JAEUM_TOP   H
#define MOEUM_BOTTOM A
#define MOEUM_TOP   I

#define HANGUL_BOTTOM 0xac00
#define HANGUL_TOP  0xd7a3

#define JBASE_CHOSUNG 0x1100
#define JBASE_JUNGSUNG 0x1161
#define JBASE_JONGSUNG 0x11A8
#define CHOSUNG_FILLER 0x115f
#define JUNGSUNG_FILLER 0x1160

static PyObject *UniNull, *UniSpace;
static PyObject *ErrorObject;

#define MAX_MULTIJAMO   3
typedef struct _jamotype {
    char *name;
    Py_UNICODE code;
    int multi[MAX_MULTIJAMO];
    char orders[3]; /* cho, jung, jong */
} jamotype;

#define CODE(c)     #c,c
#define NOMULTI     {0,0,0}
#define J_C         {0,-1,-1}
#define J_J         {-1,-1,0}
#define J_CJ        {0,-1,0}
#define M_J         {-1,0,-1}
static jamotype jamos[] = {
/* JAEUM */
    { CODE(G),  NOMULTI, J_CJ }, { CODE(GG), {G, G,}, J_CJ }, { CODE(GS), {G, S,}, J_J  },
    { CODE(N),  NOMULTI, J_CJ }, { CODE(NJ), {N, J,}, J_J  }, { CODE(NH), {N, H,}, J_J  },
    { CODE(D),  NOMULTI, J_CJ }, { CODE(DD), {D, D,}, J_C  }, { CODE(L),  NOMULTI, J_CJ },
    { CODE(LG), {L, G,}, J_J  }, { CODE(LM), {L, M,}, J_J  }, { CODE(LB), {L, B,}, J_J  },
    { CODE(LS), {L, S,}, J_J  }, { CODE(LT), {L, T,}, J_J  }, { CODE(LP), {L, P,}, J_J  },
    { CODE(LH), {L, H,}, J_J  }, { CODE(M),  NOMULTI, J_CJ }, { CODE(B),  NOMULTI, J_CJ },
    { CODE(BB), {B, B,}, J_C  }, { CODE(BS), {B, S,}, J_J  }, { CODE(S),  NOMULTI, J_CJ },
    { CODE(SS), {S, S,}, J_CJ }, { CODE(NG), NOMULTI, J_CJ }, { CODE(J),  NOMULTI, J_CJ },
    { CODE(JJ), {J, J,}, J_C  }, { CODE(C),  NOMULTI, J_CJ }, { CODE(K),  NOMULTI, J_CJ },
    { CODE(T),  NOMULTI, J_CJ }, { CODE(P),  NOMULTI, J_CJ }, { CODE(H),  NOMULTI, J_CJ },
/* MOEUM */
    { CODE(A),  NOMULTI, M_J  }, { CODE(AE), {A, I,}, M_J  }, { CODE(YA), NOMULTI, M_J  },
    { CODE(YAE), {YA,I}, M_J  }, { CODE(EO), NOMULTI, M_J  }, { CODE(E),  NOMULTI, M_J  },
    { CODE(YEO), NOMULTI, M_J }, { CODE(YE), {YEO,I}, M_J  }, { CODE(O),  NOMULTI, M_J  },
    { CODE(WA),  {O, A}, M_J  }, { CODE(WAE), {O,A,I}, M_J }, { CODE(OE), {O, I},  M_J  },
    { CODE(YO), NOMULTI, M_J  }, { CODE(U),  NOMULTI, M_J  }, { CODE(WEO), {U, EO}, M_J },
    { CODE(WE), {U, E},  M_J  }, { CODE(WI), {U, I},  M_J  }, { CODE(YU), NOMULTI, M_J  },
    { CODE(EU), NOMULTI, M_J  }, { CODE(YI), {EU, I}, M_J  }, { CODE(I),  NOMULTI, M_J  },
/* END MARKER */
    { 0, 0, NOMULTI, {0,} },
};
#undef NOMULTI
#undef CODE

static jamotype *jamo_chosung[NCHOSUNG], *jamo_jungsung[NJUNGSUNG], *jamo_jongsung[NJONGSUNG];

#define getJamotype(c) jamos[(c)-JAEUM_BOTTOM]
#define isJaeum(c) (JAEUM_BOTTOM <= (c) && (c) <= JAEUM_TOP)
#define isMoeum(c) (MOEUM_BOTTOM <= (c) && (c) <= MOEUM_TOP)
#define isHangulSyllable(c) (HANGUL_BOTTOM <= (c) && (c) <= HANGUL_TOP)
#define isChosung(c) (getJamotype(c).orders[0] >= 0)
#define isJungsung(c) (getJamotype(c).orders[1] >= 0)
#define isJongsung(c) (getJamotype(c).orders[2] >= 0)
#define getChosungOrder(c) (getJamotype(c).orders[0])
#define getJungsungOrder(c) (getJamotype(c).orders[1])
#define getJongsungOrder(c) (getJamotype(c).orders[2])


static char hangul_isJaeum__doc__[] = "isJaeum(code): Verify whether the code is Jaeum.";

static PyObject *
hangul_isJaeum(PyObject *self, PyObject *args)
{
    Py_UNICODE *code;
    int codelen, istrue = 0;

    if (!PyArg_ParseTuple(args, "u#:isJaeum", &code, &codelen))
        return NULL;

    if (codelen)
        for (istrue = 1; codelen--; code++)
            if (!isJaeum(*code)) {
                istrue = 0;
                break;
            }

    if (istrue) {
        Py_INCREF(Py_True);
        return Py_True;
    }
    else {
        Py_INCREF(Py_False);
        return Py_False;
    }
}

static char hangul_isMoeum__doc__[] = "isMoeum(code): Verify whether the code is Moeum.";

static PyObject *
hangul_isMoeum(PyObject *self, PyObject *args)
{
    Py_UNICODE *code;
    int codelen, istrue = 0;

    if (!PyArg_ParseTuple(args, "u#:isMoeum", &code, &codelen))
        return NULL;

    if (codelen)
        for (istrue = 1; codelen--; code++)
            if (!isMoeum(*code)) {
                istrue = 0;
                break;
            }

    if (istrue) {
        Py_INCREF(Py_True);
        return Py_True;
    }
    else {
        Py_INCREF(Py_False);
        return Py_False;
    }
}

static char hangul_ishangul__doc__[] = "ishangul(code): Verify whether the code is hangul.";

static PyObject *
hangul_ishangul(PyObject *self, PyObject *args)
{
    Py_UNICODE *code;
    int codelen, istrue = 0;

    if (!PyArg_ParseTuple(args, "u#:ishangul", &code, &codelen))
        return NULL;

    if (codelen)
        for (istrue = 1; codelen--; code++)
            if (!(isHangulSyllable(*code) || isJaeum(*code) || isMoeum(*code))) {
                istrue = 0;
                break;
            }

    if (istrue) {
        Py_INCREF(Py_True);
        return Py_True;
    }
    else {
        Py_INCREF(Py_False);
        return Py_False;
    }
}

static char hangul_join__doc__[] = "join([chosung, jungsung, jongsung]): Assemble hangul syllable from jamos.";

static PyObject *
hangul_join(PyObject *self, PyObject *args)
{
    PyObject *argchar, *argelems[3];
    Py_UNICODE elems[3], *uobj;
    int i;

    if (!PyArg_ParseTuple(args, "O:join", &argchar))
        return NULL;

    if (PyList_Check(argchar)) {
        if (PyList_GET_SIZE(argchar) != 3)
            goto argerr;
        for (i = 0; i < 3; i ++)
            argelems[i] = PyList_GET_ITEM(argchar, i);
    }
    else if (PyTuple_Check(argchar)) {
        if (PyTuple_GET_SIZE(argchar) != 3)
            goto argerr;
        for (i = 0; i < 3; i ++)
            argelems[i] = PyTuple_GET_ITEM(argchar, i);
    }
    else {
argerr: PyErr_Format(PyExc_ValueError, "need list or tuple with 3 unicode elements");
        return NULL;
    }

    for (i = 0; i < 3; i ++) {
        if ((uobj = PyUnicode_AsUnicode(argelems[i])) == NULL)
            goto argerr;
        if (PyUnicode_GET_SIZE(argelems[i]))
            elems[i] = *uobj;
        else
            elems[i] = 0;
    }

    if ( (elems[0] && (!isJaeum(elems[0]) || !isChosung(elems[0]))) /* Chosung validity */
         || (elems[1] && (!isMoeum(elems[1]))) /* Jungsung validity */
         || (elems[2] && (!isJaeum(elems[2]) || !isJongsung(elems[2])))   ) {
        PyErr_Format(ErrorObject, "not valid jamo combination");
        return NULL;
    }

    if ((!elems[0] || !elems[1]) && elems[2]) {
        PyErr_Format(ErrorObject, "trying to assemble character which "
                                  "is not in unicode map");
        return NULL;
    }
    else if (elems[0] && !elems[1]) {
        Py_INCREF(argelems[0]);
        return argelems[0];
    }
    else if (elems[1] && !elems[0]) {
        Py_INCREF(argelems[1]);
        return argelems[1];
    }
    else if (!elems[0]) { /* [Null, Null, Null] */
        Py_INCREF(UniSpace);
        return UniSpace;
    }
    else {
        Py_UNICODE code;

        code = ((getChosungOrder(elems[0]) * NJUNGSUNG) + getJungsungOrder(elems[1])) * 
                 NJONGSUNG + (elems[2]?getJongsungOrder(elems[2]):0) + HANGUL_BOTTOM;
        return PyUnicode_FromUnicode(&code, 1);
    }
}

static char hangul_split__doc__[] = "split(code): Disassemble hangul syllable into jamos.";

static PyObject *
hangul_split(PyObject *self, PyObject *args)
{
    Py_UNICODE *code;
    PyObject *r;
    int codelen;

    if (!PyArg_ParseTuple(args, "u#:split", &code, &codelen))
        return NULL;

    if (codelen < 1) {
        PyErr_Format(PyExc_ValueError, "need not null unicode string");
        return NULL;
    }

    if (isHangulSyllable(*code)) {
        Py_UNICODE cho, jung, jong;
        PyObject *jongobj;
        Py_UNICODE hseq, t;
        
        hseq = *code - HANGUL_BOTTOM;

        cho  = jamo_chosung[hseq / (NJUNGSUNG*NJONGSUNG)]->code;
        jung = jamo_jungsung[(hseq / NJONGSUNG) % NJUNGSUNG]->code;

        if ((t = hseq % NJONGSUNG)) {
            jong = jamo_jongsung[t]->code;
            jongobj = PyUnicode_FromUnicode(&jong, 1);
        } else {
            jongobj = UniNull;
            Py_INCREF(UniNull);
        }

        r = PyTuple_New(3);
        PyTuple_SET_ITEM(r, 0, PyUnicode_FromUnicode(&cho, 1));
        PyTuple_SET_ITEM(r, 1, PyUnicode_FromUnicode(&jung, 1));
        PyTuple_SET_ITEM(r, 2, jongobj);

        return r;
    }
    else if (isJaeum(*code)) {
        r = PyTuple_New(3);
        PyTuple_SET_ITEM(r, 0, PyUnicode_FromUnicode(code, 1));
        PyTuple_SET_ITEM(r, 1, UniNull); Py_INCREF(UniNull);
        PyTuple_SET_ITEM(r, 2, UniNull); Py_INCREF(UniNull);
        return r;
    }
    else if (isMoeum(*code)) {
        r = PyTuple_New(3);
        PyTuple_SET_ITEM(r, 0, UniNull); Py_INCREF(UniNull);
        PyTuple_SET_ITEM(r, 1, PyUnicode_FromUnicode(code, 1));
        PyTuple_SET_ITEM(r, 2, UniNull); Py_INCREF(UniNull);
        return r;
    }
    else {
        PyErr_Format(ErrorObject, "not a hangul code");
        return NULL;
    }
}

static char hangul_conjoin__doc__[] = "conjoin(unicodestring): conjoin unicode johab string into unicode syllable string";

static PyObject *
hangul_conjoin(PyObject *self, PyObject *args)
{
    PyObject *r;
    Py_UNICODE *code, *dst, *dstorg, c;
    int cho, jung, jong;
    int codelen, i;

    if (!PyArg_ParseTuple(args, "u#:conjoin", &code, &codelen))
        return NULL;

    dstorg = dst = PyMem_New(Py_UNICODE, codelen);

    for (i = 0; i < codelen; i++) {
        c = code[i];
        if ((JBASE_CHOSUNG <= c && c <= 0x1112) || c == CHOSUNG_FILLER) {
            if (codelen > i+1 && JUNGSUNG_FILLER <= code[i+1] && code[i+1] <= 0x1175) {
                if (c == CHOSUNG_FILLER) cho = -1;
                else cho = c - JBASE_CHOSUNG;
                if (code[i+1] == JUNGSUNG_FILLER) jung = -1;
                else jung = code[i+1] - JBASE_JUNGSUNG;

                if (codelen > i+2 && JBASE_JONGSUNG <= code[i+2] && code[i+2] <= 0x11c2) {
                    jong = code[i+2] - JBASE_JONGSUNG + 1;
                    i += 2;
                }
                else {
                    jong = 0; i++;
                }

                if (jong && (cho == -1 || jung == -1)) { /* can't trans to syllable */
                    if (cho >= 0)  *(dst++) = jamo_chosung[cho]->code;
                    if (jung >= 0) *(dst++) = jamo_jungsung[jung]->code;
                    *(dst++) = jamo_jongsung[jong]->code;
                }
                else if (cho == -1) /* jungsung only */
                    *(dst++) = jamo_jungsung[jung]->code;
                else if (jung == -1) /* chosung only */
                    *(dst++) = jamo_chosung[cho]->code;
                else /* full set */
                    *(dst++) = HANGUL_BOTTOM + (cho * NJUNGSUNG + jung) * NJONGSUNG + jong;
            }
            else if (c != CHOSUNG_FILLER) /* chosung only */
                *(dst++) = jamo_chosung[c-JBASE_CHOSUNG]->code;
        }
        else if (JBASE_JUNGSUNG <= c && c <= 0x1175) /* jungsung only */
            *(dst++) = jamo_jungsung[c-JBASE_JUNGSUNG]->code;
        else
            *(dst++) = c;
    }

    r = PyUnicode_FromUnicode(dstorg, dst-dstorg);
    PyMem_Del(dstorg);

    return r;
}


static char hangul_disjoint__doc__[] = "disjoint(unicodestring): disjoint unicode syllable string into unicode johab string";

static PyObject *
hangul_disjoint(PyObject *self, PyObject *args)
{
    Py_UNICODE *code, *dst, *dstorg, c;
    PyObject *r;
    int codelen, i;

    if (!PyArg_ParseTuple(args, "u#:split", &code, &codelen))
        return NULL;

    dstorg = dst = PyMem_New(Py_UNICODE, codelen*3);

    for (i = 0; i < codelen; i++) {
        c = code[i];
        if (isHangulSyllable(c)) {
            int hseq;
            Py_UNICODE jong;

            hseq = c - HANGUL_BOTTOM;
            jong = hseq % NJONGSUNG;

            *(dst++) = hseq / (NJUNGSUNG * NJONGSUNG) + JBASE_CHOSUNG;
            *(dst++) = (hseq / NJONGSUNG) % NJUNGSUNG + JBASE_JUNGSUNG;
            if (jong)
                *(dst++) = jong + JBASE_JONGSUNG - 1;
        }
        else if (isJaeum(c) && isChosung(c)) {
            *(dst++) = getChosungOrder(c) + JBASE_CHOSUNG;
            *(dst++) = JUNGSUNG_FILLER;
        }
        else if (isMoeum(c)) {
            *(dst++) = CHOSUNG_FILLER;
            *(dst++) = getJungsungOrder(c) + JBASE_JUNGSUNG;
        } else
            *(dst++) = c;
    }

    r = PyUnicode_FromUnicode(dstorg, dst-dstorg);
    PyMem_Del(dstorg);

    return r;
}


static char pseudofinal[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0 */
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 1 */
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,  /* 2 */
    1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,  /* 3 */
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,  /* 4 */
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  /* 5 */
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /* 6 */
    1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 7 */
};

static char hangul_format__doc__[] = "format(fmt, arg1, arg2, ...) or format(fmt, kw1=arg1, kw2=arg2"
                            ", ...):\nformat unicode string and fix korean suffixes after arguments";

static PyObject *
hangul_format(PyObject *self, PyObject *args, PyObject *kwargs)
{
/*--- Poor Structure of this function ;)
  hangul_format(fmt, *args, **kwargs)
    -> insert end fmtmarkers(U+115E which is not used by Unicode) after every format position
      -> PyUnicode_Format
        -> Fix and update hangul suffixes in place of fmtmarkers
          -> make PyObject and return.
 */
#define FMTMARKER 0x115E
    Py_UNICODE *fmt, *fmtout, *fcur;
    PyObject *r;
    int fmtsize;
    int inpth, infmt, escape;

    {
        PyObject *fmtobj;
        int argsize;

        argsize = PyTuple_GET_SIZE(args);
        if (!argsize || !PyUnicode_Check(fmtobj = PyTuple_GET_ITEM(args, 0))) {
            PyErr_Format(PyExc_TypeError, "needs unicode format string.");
            return NULL;
        }
        fmtsize = PyUnicode_GET_SIZE(fmtobj);
        fmt = PyUnicode_AS_UNICODE(fmtobj);

        if (!kwargs)
            args = PyTuple_GetSlice(args, 1, argsize);
    }

    fmtout = PyMem_New(Py_UNICODE, fmtsize + fmtsize/2);
    inpth = infmt = escape = 0;

    for (fcur = fmtout; fmtsize--; fmt++) {
        if (*fmt != FMTMARKER) /* skip bogus markers */
            *(fcur++) = *fmt;

        if (escape)
            escape = 0;
        else if (*fmt == '\\')
            escape = 1;
        else if (infmt) {
            if (!inpth && (('A' <= *fmt && *fmt <= 'Z') || ('a' <= *fmt && *fmt <= 'z'))) {
                *(fcur++) = FMTMARKER;
                infmt = 0;
            }
            else if (inpth && *fmt == ')')
                inpth = 0;
            else if (*fmt == '(')
                inpth = 1;
            else if (*fmt == '%')
                infmt = 0;
        }
        else if (*fmt == '%')
            infmt = 1;
    }

    r = PyUnicode_Format(
            PyUnicode_FromUnicode(fmtout, fcur-fmtout),
            kwargs?kwargs:args
        );
    if (!kwargs) {
        Py_DECREF(args);
    } /* {} to avoid gcc warning */
    if (!r)
        goto out;

    fmt       = PyUnicode_AS_UNICODE(r);
    fmtsize   = PyUnicode_GET_SIZE(r);

#define HAS_FINAL() ( \
    (past = *(fmt-1)), \
    isHangulSyllable(past) ?  \
        ((past-HANGUL_BOTTOM) % NJONGSUNG > 0) \
        : (past < 0x80 ? pseudofinal[past] : 0) \
)

#define HAS_FINAL_OR_NOTSYL() ( \
    (past = *(fmt-1)), \
    isHangulSyllable(past) ?  \
        ((past-HANGUL_BOTTOM) % NJONGSUNG > 0) \
        : 1 \
)

#define PROCESSSUFFIX(nofinal, existfinal) \
    if (next == nofinal || next == existfinal) { \
        *(fcur++) = HAS_FINAL() ? (existfinal) : (nofinal); \
        fmtsize--; fmt++; \
    }

#define PROCESSSUFFIX_IDA(jongsungadder, existfinal) \
    if (next == existfinal) { \
        if (HAS_FINAL_OR_NOTSYL()) \
            *(fcur++) = existfinal; \
        else \
            *(fcur-1) += jongsungadder; \
        fmtsize-=3; fmt+=3; \
    }

    for (fcur = fmtout; fmtsize--; fmt++) {
        if (*fmt == FMTMARKER) {
            if (fcur > fmtout && fmtsize > 0) {
                Py_UNICODE past, next = *(fmt+1);

                if (next == '(' && fmtsize > 2 && *(fmt+3) == ')') { /* ida suffxes */
                    next = *(fmt+2);
                    PROCESSSUFFIX_IDA(0, 0xc774) /* (I)DA */
                    else PROCESSSUFFIX_IDA(17, 0xc785) /* (IP)NIDA */
                    else PROCESSSUFFIX_IDA(4, 0xc778) /* (IN)- */
                }
                else if (0xac00 <= next && next <= 0xc774) {
                    PROCESSSUFFIX(0xb97c, 0xc744) /* REUL, EUL */
                    else PROCESSSUFFIX(0xb294, 0xc740) /* NEUN, EUN */
                    else PROCESSSUFFIX(0xac00, 0xc774) /* GA, I */
                    else PROCESSSUFFIX(0xc640, 0xacfc) /* WA, GWA */
                }
            }
        }
        else
            *(fcur++) = *fmt;
    }

/* these were written separatedly for win32 compilers */
#undef PROCESSSUFFIX
#undef PROCESSSUFFIX_IDA
#undef HAS_FINAL
#undef HAS_FINAL_OR_NOTSYL

    Py_DECREF(r);
    r = PyUnicode_FromUnicode(fmtout, fcur-fmtout);

out:
    PyMem_Free(fmtout);
    return r;
}

/* List of methods defined in the module */

#define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
#define meth_kw(name, func, doc) {name, (PyCFunction)func, METH_VARARGS|METH_KEYWORDS, doc}

static struct PyMethodDef hangul_methods[] = {
  meth("isJaeum",   hangul_isJaeum,     hangul_isJaeum__doc__),
  meth("isMoeum",   hangul_isMoeum,     hangul_isMoeum__doc__),
  meth("ishangul",  hangul_ishangul,    hangul_ishangul__doc__),
  meth("join",      hangul_join,        hangul_join__doc__),
  meth("split",     hangul_split,       hangul_split__doc__),
  meth("conjoin",   hangul_conjoin,     hangul_conjoin__doc__),
  meth("disjoint",  hangul_disjoint,    hangul_disjoint__doc__),
  meth_kw("format", hangul_format,      hangul_format__doc__),
  {NULL, NULL},
};

#define SET_INTCONSTANT(dict, value) \
            PyDict_SetItemString(dict, #value, PyInt_FromLong((long) value))
#define SET_STRCONSTANT(dict, value) \
            PyDict_SetItemString(dict, #value, PyString_FromString(value))
#define SET_CHARCONSTANT(dict, value) \
            PyDict_SetItemString(dict, #value, PyString_FromFormat("%c", value))

/* Initialization function for the module */

void
inithangul(void)
{
    PyObject *m, *d, *tmp;
    Py_UNICODE tuni[2];
    int i;

    /* Create the module and add the functions */
    m = Py_InitModule("hangul", hangul_methods);

    UniNull = PyUnicode_FromUnicode(NULL, 0);
    tuni[0] = 0x3000; /* Unicode Double-wide Space */
    UniSpace = PyUnicode_FromUnicode(tuni, 1);

    /* Add some symbolic constants to the module */
    d = PyModule_GetDict(m);
    SET_INTCONSTANT(d, NCHOSUNG);
    SET_INTCONSTANT(d, NJUNGSUNG);
    SET_INTCONSTANT(d, NJONGSUNG);
    {
        PyObject *Chosung, *Jungsung, *Jongsung;
        PyObject *Jaeum, *Moeum;
        PyObject *JaeumDict, *MoeumDict;
        PyObject *JaeumCodes, *MoeumCodes;
        PyObject *JaeumMulti, *MoeumMulti;
        int cur_cho, cur_jung, cur_jong;
        int cur_jaeum, cur_moeum;
        jamotype *jamo;

        /* Bind Chosung, Jungsung, Jongsung lists */
        cur_cho = cur_jung = cur_jong = 0;
        Chosung  = PyList_New(NCHOSUNG);
        Jungsung = PyList_New(NJUNGSUNG);
        Jongsung = PyList_New(NJONGSUNG);
        PyDict_SetItemString(d, "Chosung", Chosung);
        PyDict_SetItemString(d, "Jungsung", Jungsung);
        PyDict_SetItemString(d, "Jongsung", Jongsung);
        jamo_jongsung[cur_jong] = NULL;
        Py_INCREF(UniNull);
        PyList_SET_ITEM(Jongsung, cur_jong++, UniNull);

        /* Create Jaeum and Moeum meta class */
        JaeumDict = PyDict_New();
        MoeumDict = PyDict_New();
        tmp = PyString_FromString("Jaeum");
        Jaeum = PyClass_New(NULL, JaeumDict, tmp);
        Py_DECREF(tmp);
        tmp = PyString_FromString("Moeum");
        Moeum = PyClass_New(NULL, MoeumDict, tmp);
        Py_DECREF(tmp);

        /* Bind meta class members */
        PyDict_SetItemString(d, "Jaeum", Jaeum);
        PyDict_SetItemString(d, "Moeum", Moeum);
        PyDict_SetItemString(JaeumDict, "Chosung", Chosung);
        PyDict_SetItemString(MoeumDict, "Jungsung", Jungsung);
        PyDict_SetItemString(JaeumDict, "Jongsung", Jongsung);

        /* Create Jaeum and Moeum Members */
        JaeumCodes = PyTuple_New(NJAEUM);
        MoeumCodes = PyTuple_New(NMOEUM);
        JaeumMulti = PyDict_New();
        MoeumMulti = PyDict_New();
        cur_jaeum = cur_moeum = 0;
        PyDict_SetItemString(JaeumDict, "Codes", JaeumCodes);
        PyDict_SetItemString(MoeumDict, "Codes", MoeumCodes);
        PyDict_SetItemString(JaeumDict, "Width", PyInt_FromLong(NJAEUM));
        PyDict_SetItemString(MoeumDict, "Width", PyInt_FromLong(NMOEUM));
        PyDict_SetItemString(JaeumDict, "MultiElement", JaeumMulti);
        PyDict_SetItemString(MoeumDict, "MultiElement", MoeumMulti);

        for (jamo = jamos; jamo->name; jamo++) {
            PyObject *unijamo, *multicls;
            int tuplen;

            tuni[0] = jamo->code;
            unijamo = PyUnicode_FromUnicode(tuni, 1);
            PyDict_SetItemString(d, jamo->name, unijamo);

            if (isJaeum(jamo->code)) {
                PyTuple_SET_ITEM(JaeumCodes, cur_jaeum++, unijamo);
                Py_INCREF(unijamo);
                if (isChosung(jamo->code)) {
                    jamo->orders[0] = cur_cho;
                    jamo_chosung[cur_cho] = jamo;
                    PyList_SET_ITEM(Chosung,  cur_cho++, unijamo);
                    Py_INCREF(unijamo);
                    PyDict_SetItemString(JaeumDict, jamo->name, unijamo);
                }
                if (isJongsung(jamo->code)) {
                    jamo->orders[2] = cur_jong;
                    jamo_jongsung[cur_jong] = jamo;
                    PyList_SET_ITEM(Jongsung, cur_jong++, unijamo);
                    Py_INCREF(unijamo);
                    PyDict_SetItemString(JaeumDict, jamo->name, unijamo);
                }
                multicls = JaeumMulti;
            }
            else { /* Moeum */
                PyTuple_SET_ITEM(MoeumCodes, cur_moeum++, unijamo);
                Py_INCREF(unijamo);
                if (isJungsung(jamo->code)) {
                    jamo->orders[1] = cur_jung;
                    jamo_jungsung[cur_jung] = jamo;
                    PyList_SET_ITEM(Jungsung, cur_jung++, unijamo);
                    Py_INCREF(unijamo);
                    PyDict_SetItemString(MoeumDict, jamo->name, unijamo);
                }
                multicls = MoeumMulti;
            }
            if (jamo->multi[0]) {
                tuplen = jamo->multi[2] ? 3 : 2;
                tmp = PyTuple_New(tuplen);
                for (i = 0; i < tuplen; i++) {
                    tuni[0] = jamo->multi[i];
                    PyTuple_SET_ITEM(tmp, i, PyUnicode_FromUnicode(tuni, 1));
                }
                PyDict_SetItem(multicls, unijamo, tmp);
                Py_DECREF(tmp);
            }
            Py_DECREF(unijamo);
        }

        Py_DECREF(Chosung); Py_DECREF(Jungsung); Py_DECREF(Jongsung);
        Py_DECREF(JaeumDict);  Py_DECREF(MoeumDict);
        Py_DECREF(JaeumCodes); Py_DECREF(MoeumCodes);
        Py_DECREF(JaeumMulti); Py_DECREF(MoeumMulti);
    }

    tmp = PyTuple_New(2);
    tuni[0] = HANGUL_BOTTOM;
    PyTuple_SET_ITEM(tmp, 0, PyUnicode_FromUnicode(tuni, 1));
    tuni[0] = HANGUL_TOP;
    PyTuple_SET_ITEM(tmp, 1, PyUnicode_FromUnicode(tuni, 1));
    PyDict_SetItemString(d, "ZONE", tmp);
    Py_DECREF(tmp);

    tuni[0] = JBASE_CHOSUNG;
    PyDict_SetItemString(d, "JBASE_CHOSUNG", PyUnicode_FromUnicode(tuni, 1));
    tuni[0] = JBASE_JUNGSUNG;
    PyDict_SetItemString(d, "JBASE_JUNGSUNG", PyUnicode_FromUnicode(tuni, 1));
    tuni[0] = JBASE_JONGSUNG;
    PyDict_SetItemString(d, "JBASE_JONGSUNG", PyUnicode_FromUnicode(tuni, 1));
    tuni[0] = CHOSUNG_FILLER;
    PyDict_SetItemString(d, "CHOSUNG_FILLER", PyUnicode_FromUnicode(tuni, 1));
    tuni[0] = JUNGSUNG_FILLER;
    PyDict_SetItemString(d, "JUNGSUNG_FILLER", PyUnicode_FromUnicode(tuni, 1));
    PyDict_SetItemString(d, "Null", UniNull);
    PyDict_SetItemString(d, "Space", UniSpace);

    PyDict_SetItemString(d, "version", PyString_FromString(version));

    ErrorObject = PyErr_NewException("hangul.UnicodeHangulError", NULL, NULL);
    PyDict_SetItemString(d, "UnicodeHangulError", ErrorObject);
    Py_DECREF(ErrorObject);

    /* Check for errors */
    if (PyErr_Occurred())
        Py_FatalError("can't initialize the hangul module");
}


syntax highlighted by Code2HTML, v. 0.9.1