diff lib/Xsj3clib/table.c @ 0:92745d501b9a

initial import from kinput2-v3.1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 04:44:30 +0900
parents
children 7353de876e93
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/Xsj3clib/table.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,897 @@
+#ifndef lint
+static char *rcsid = "$Id: table.c,v 2.3 1993/09/21 09:42:45 nao Exp $";
+#endif
+/*
+ * Copyright 1991 Sony Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Sony not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  Sony makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * SONY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SONY
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Author: Naoshi Suzuki, SONY Corporation.  (nao@sm.sony.co.jp)
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "common.h"
+#include "sj3ctype.h"
+#include "util.h"
+
+extern Xsj3cCVServerIF      serverIF[SERVER_NUM];
+extern Xsj3cSymbol          _Xsj3cSymbolInit();
+extern Xsj3cHinsi           _Xsj3cHinsiInit();
+
+Xsj3cRKTable               *_Xsj3cRKInit();
+Xsj3cHKTable               *_Xsj3cHKInit();
+Xsj3cZHTable               *_Xsj3cZHInit();
+unsigned char              *_Xsj3cSetPlosive();
+unsigned char              *_Xsj3cSetDouble();
+void                        Xsj3cInitializeTables();
+
+int                         _Xsj3cmINtowOUT();
+int                         _Xsj3cReadAscii();
+wchar                      *_Xsj3cStoreWchar();
+
+static Xsj3cRKTable        *_Xsj3cAllocRKTable();
+static Xsj3cHKTable        *_Xsj3cAllocHKTable();
+static Xsj3cZHTable        *_Xsj3cAllocZHTable();
+
+static unsigned char       *_Xsj3cStoreChar();
+
+static int                  _Xsj3cmINtomPS();
+
+static int                  _Xsj3cReadRK();
+static int                  _Xsj3cReadHK();
+static int                  _Xsj3cReadZH();
+
+static unsigned char       *chnowp = NULL,     *chmaxp = NULL;
+static wchar               *wnowp = NULL,      *wmaxp = NULL;
+
+static Xsj3cRKTable        *rktable[2] = {NULL, NULL};
+static Xsj3cHKTable        *hktable[2] = {NULL, NULL};
+static Xsj3cZHTable        *zhtable[2] = {NULL, NULL};
+static unsigned char       *plosive[2] = {NULL, NULL};
+static unsigned char       *rkdouble[2] = {NULL, NULL};
+
+static Xsj3cRKTable        *rknowtp = NULL, *rkmaxtp = NULL;
+static Xsj3cHKTable        *hknowtp = NULL, *hkmaxtp = NULL;
+static Xsj3cZHTable        *zhnowtp = NULL, *zhmaxtp = NULL;
+
+static unsigned char *
+_Xsj3cStoreChar(ch, len)
+    unsigned char  *ch;
+    int             len;
+{
+    register unsigned char  *chp;
+
+    if (chnowp == NULL || chnowp + len > chmaxp) {
+        chp = (unsigned char *)malloc(BUFSIZ);
+        if (chp == NULL)
+            return (NULL);
+        chnowp = chp;
+        chmaxp = chnowp + (BUFSIZ/sizeof(unsigned char));
+        strcpy (chp, ch);
+        chnowp += len;
+        return (chp);
+    } else {
+        chp = chnowp;
+        strcpy (chp, ch);
+        chnowp += len;
+        return (chp);
+    }
+}
+
+wchar *
+_Xsj3cStoreWchar(wch, len)
+    wchar          *wch;
+    int             len;
+{
+    register wchar  *wp;
+
+    if (wnowp == NULL || wnowp + len > wmaxp) {
+        wp = (wchar *)malloc(BUFSIZ);
+        if (wp == NULL)
+            return (NULL);
+        wnowp = wp;
+        wmaxp = wnowp + (BUFSIZ/sizeof(wchar));
+        (void)_Xsj3cWcpy (wp, wch);
+        wnowp += len;
+        return (wp);
+    } else {
+        wp = wnowp;
+        (void)_Xsj3cWcpy (wp, wch);
+        wnowp += len;
+        return (wp);
+    }
+}
+
+static int
+_Xsj3cmINtomPS(buf, file, src, dest)
+    Xsj3cBuf                buf;
+    char                   *file;
+    register unsigned char *src,   *dest;
+{
+    register unsigned char  c;
+    register wchar          s,     (*conv)();
+    register int            i = 0,  kanji = OFF, kana = OFF;
+
+    conv = CodeConvFunc[in_lang][serverIF[buf->server].lang];
+    while ((*src != '\t' && *src != '\n'
+            && *src != ' ' && *src != '#')||(kanji || kana)) {
+        c = *src++;
+        switch (c) {
+        case ESC:
+            c = *src++;
+            if (c == '$') {
+                c = *src++;
+                i++;
+                if (c == 'B' || c == '@') {
+                    kanji++;
+                    i++;
+                } else
+                    goto badcode;
+            } else if (c == '(') {
+                c = *src++;
+                i++;
+                if (c == 'J' || c == 'B' || c == 'H') {
+                    kanji = OFF;
+                    i++;
+                } else if (c == '$' && *src == 'B') {
+                    src++;
+                    kanji++;
+                    i++;
+                } else 
+                    goto badcode;
+            } else if (c == '&' && !strcmp("@\033$B", src)) {
+                src += 4;
+                i += 5;
+                kanji++;
+            } else
+                goto badcode;
+            break;
+        case SI:
+            kana = OFF;
+            break;
+        case SO:
+            kana++;
+            break;
+        case '\\':
+            if (!kanji & !kana) {
+                *dest++ = *src++;
+                i++;
+                break;
+            }
+        default:
+            if (kanji) {
+                s = (c << 8) + (*src++ & 0x7f);
+                s = conv(s);
+                *dest++ = (s >> 8) & 0xff;
+                *dest++ = s & 0xff;
+                i++;
+            } else if (iskan1(c, in_lang) && iskan2(*src, in_lang)) {
+                if (conv) {
+                    s = (c << 8) + (*src++ & 0xff);
+                    s = conv(s);
+                    *dest++ = (s >> 8) & 0xff;
+                    *dest++ = s & 0xff;
+                } else {
+                    *dest++ = c;
+                    *dest++ = *src++;
+                }
+                i++;
+            } else if (iskana(c)) {
+                if (in_lang == JP_SJIS || in_lang == JP_JIS8)
+                    *dest++ = c;
+                else if (in_lang == JP_EUC && iseuckana(c) && iskana2(*src)) {
+                    *dest++ = *src++;
+                    i++;
+                } else 
+                    goto badcode;
+            } else if (iseuckana(c) && iskana2(*src)) {
+                if (in_lang == JP_EUC)
+                    *dest++ = *src++;
+                else 
+                    goto badcode;
+                i++;
+            } else if (kana) {
+                *dest++ = (c | MSB);
+            } else if (isascii(c)) {
+                *dest++ = c;
+            } else {
+badcode:
+                Xsj3cError("There is bad code %s in %s",_Xsj3cXtoa(c),file);
+            }
+            break;
+        }
+        i++;
+    }
+    *dest = '\0';
+    return (i);
+}
+
+int
+_Xsj3cReadAscii(file, src, dest)
+    char                   *file;
+    register unsigned char *src,   *dest;
+{
+    register int            i = 0,  kana = OFF;
+
+    while ((*src != '\t' && *src != ' '
+            && *src != '\0' && *src != '\n' && *src != '#')|| kana) {
+        if (kana) {
+            *dest++ = (*src++ | MSB);
+        } if (*src == '\\') {
+            src++;
+            i++;
+            *dest++ = *src++;
+        } else if (isascii(*src)) {
+            *dest++ = *src++;
+        } else if (isdakuten(*src)) {
+            *dest++ = *src++;
+        } else if (iseuckana(*src) && isdakuten(*(src + 1))) {
+            src++;
+            i++;
+            *dest++ = *src++;
+        } else if (*src == SO) {
+            src++;
+            kana++;
+        } else if (*src == SI) {
+            src++;
+            kana = OFF;
+        } else {
+            int             n = *src;
+            Xsj3cError("There is bad code %s in %s",_Xsj3cXtoa(n),file);
+        }
+        i++;
+    }
+    *dest = '\0';
+    return (i);
+}
+
+int
+_Xsj3cmINtowOUT(file, src, dest, len)
+    char                   *file;
+    register unsigned char *src;
+    register wchar         *dest;
+    register int           *len;
+{
+    register unsigned char  c;
+    register int            i = 0,  kanji = OFF, kana = OFF;
+    register wchar          (*conv)();
+
+    *len = 0;
+    conv = CodeConvFunc[in_lang][out_lang];
+    while ((*src != '\t' && *src != ' ' && *src != '\0'
+            && *src != '\n' && *src != '#')||(kanji || kana)) {
+        c = *src++;
+        switch (c) {
+        case ESC:
+            c = *src++;
+            if (c == '$') {
+                c = *src++;
+                i++;
+                if (c == 'B' || c == '@') {
+                    kanji++;
+                    i++;
+                } else
+                    goto badcode;
+            } else if (c == '(') {
+                c = *src++;
+                i++;
+                if (c == 'J' || c == 'B' || c == 'H') {
+                    kanji = OFF;
+                    i++;
+                } else if (c == '$' && *src == 'B') {
+                    src++;
+                    kanji++;
+                    i++;
+                } else
+                    goto badcode;
+            } else if (c == '&' && !strcmp("@\033$B", src)) {
+                src += 4;
+                i += 5;
+                kanji++;
+            } else
+                goto badcode;
+            break;
+        case SI:
+            kana = OFF;
+            break;
+        case SO:
+            kana++;
+            break;
+        case '\\':
+            if (!kanji && !kana) {
+                *dest++ = *src++;
+                i++;
+                break;
+            }
+        default:
+            if (kanji || (iskan1(c, in_lang) && iskan2(*src, in_lang))) {
+                if (conv) 
+                    *dest++ = conv((c << 8) + (*src++ & 0xff));
+                else
+                    *dest++ = (c << 8) + (*src++ & 0xff);
+                i++;
+            } else if (iskana(c)) {
+                if (in_lang == JP_SJIS || in_lang == JP_JIS8)
+                    *dest++ = c;
+                else if (in_lang == JP_EUC && iseuckana(c) && iskana2(*src)) {
+                    *dest++ = *src++;
+                    i++;
+                } else 
+                    goto badcode;
+            } else if (iseuckana(c) && iskana2(*src)) {
+                if (in_lang == JP_EUC)
+                    *dest++ = *src++;
+                else 
+                    goto badcode;
+                i++;
+            } else if (kana) {
+                *dest++ = (c | MSB);
+            } else if (isascii(c)) {
+                *dest++ = c;
+            } else {
+badcode:
+                Xsj3cError("There is bad code %s in %s",_Xsj3cXtoa(c),file);
+            }
+            (*len)++;
+            break;
+        }
+        i++;
+    }
+    *dest = '\0';
+    return (i);
+}
+
+/*
+ * Xsj3cInitializeTables()
+ *  Read definition files and initialize conversion tables.
+ */
+void
+Xsj3cInitializeTables(buf, home, sjrk, sjhk, sjzh, sjsb)
+    Xsj3cBuf            buf;
+    char               *home;
+    char               *sjrk,   *sjhk,  *sjzh,  *sjsb;
+{
+    buf->rktable = _Xsj3cRKInit(buf, sjrk, home);
+    buf->hktable = _Xsj3cHKInit(buf, sjhk, home);
+    buf->zhtable = _Xsj3cZHInit(buf, sjzh, home);
+    buf->symbol = _Xsj3cSymbolInit(sjsb, home);
+    buf->plosive = _Xsj3cSetPlosive(buf);
+    buf->rkdouble = _Xsj3cSetDouble(buf);
+    buf->hinsi = _Xsj3cHinsiInit(buf);
+}
+
+/*
+ * _Xsj3cRKInit()
+ *  Decide sjrk file to read, then read it and make table for roman-kana
+ * conversion table.
+ */
+Xsj3cRKTable *
+_Xsj3cRKInit(buf, sjrk, home)
+    Xsj3cBuf            buf;
+    char               *sjrk;
+    char               *home;
+{
+    extern char        *getenv();
+    register char      *p;
+    char                rkfile[BUFSIZ];
+    int                 value,  error;
+
+    if (rktable[serverIF[buf->server].lang])
+        return (rktable[serverIF[buf->server].lang]);
+
+    if (sjrk) {
+        if ((value = _Xsj3cReadRK(buf, sjrk, &error)) > 0 )
+            Xsj3cError("can't open sjrk file %s", sjrk);
+        else if (value < 0)
+            Xsj3cError("read failed line %s sjrk file %s",
+                    _Xsj3cItoa(error), sjrk);
+    } else {
+        rkfile[0] = '\0';
+        if ((p = getenv("SJRK")) && *p != '\0') {
+            if (*p != '/') {
+                if (home)
+                    strcpy(rkfile, home);
+                strcat(rkfile, "/");
+            }
+            strcat(rkfile, p);
+        } else if (home) {
+            strcpy(rkfile, home);
+            strcat(rkfile, "/.sjrk");
+        } else {
+            strcpy(rkfile, SJ3DEFPATH);
+            strcat(rkfile, DEF_SJRK_FILE);
+        }
+        if ((value = _Xsj3cReadRK(buf, rkfile, &error)) > 0 ) {
+            strcpy(rkfile, SJ3DEFPATH);
+            strcat(rkfile, DEF_SJRK_FILE);
+            if ((value = _Xsj3cReadRK(buf, rkfile, &error)) > 0 ) {
+                Xsj3cError("can't open sjrk file %s", rkfile);
+            } else if (value < 0) {
+                Xsj3cError("read failed line %s sjrk file %s",
+                        _Xsj3cItoa(error), rkfile);
+            }
+        } else if (value < 0) {
+            Xsj3cError("read failed line %s sjrk file %s",
+                    _Xsj3cItoa(error), rkfile);
+        }
+    }
+    return (rktable[serverIF[buf->server].lang]);
+}
+
+#define RKTBMAX (BUFSIZ/sizeof(Xsj3cRKTable))
+
+static Xsj3cRKTable *
+_Xsj3cAllocRKTable()
+{
+    register Xsj3cRKTable  *rktp;
+
+    if (rknowtp == NULL || rknowtp > rkmaxtp) {
+        rktp = (Xsj3cRKTable *)malloc(sizeof(Xsj3cRKTable) * RKTBMAX);
+        if (rktp == NULL)
+            return (NULL);
+        rknowtp = rktp;
+        rkmaxtp = rknowtp + RKTBMAX - 1;
+        rknowtp++;
+    } else {
+        rktp = rknowtp;
+        rknowtp++;
+    }
+    rktp->roma = NULL;
+    rktp->yomi = NULL;
+    rktp->str = NULL;
+    rktp->rlen = 0;
+    rktp->ylen = 0;
+    rktp->next = NULL;
+    return (rktp);
+}
+
+/*
+ *  _Xsj3cReadRK()
+ *   Read sjrk file like a format sj3's roman-kana conversion file,
+ *  and make conversion table.
+ *   But this routine distinguishes upper-case and lower-case.
+ *  It's a big difference.
+ */
+static int
+_Xsj3cReadRK(buf, file, error)
+    Xsj3cBuf                buf;
+    register char          *file;
+    register int           *error;
+{
+    register FILE          *fp;
+    unsigned char           line[256];
+    unsigned char          *p;
+    unsigned char           roma[RBUFSIZ],  yomi[YBUFSIZ],  str[RBUFSIZ];
+    register int            begin = 0,  rlen,   ylen;
+    register Xsj3cRKTable  *rktp,  *rktq,  *rktr;
+
+    if ((fp = fopen(file, "r")) == NULL)
+        return (OPEN_FAILED);
+    
+    *error = 1;
+    rktp = rktable[serverIF[buf->server].lang] = _Xsj3cAllocRKTable();
+    while (fgets(line, sizeof(line), fp) != NULL) {
+        p = line;
+        while (*p != '\n' && *p != '#') {
+            p += _Xsj3cReadAscii(file, p, roma);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, yomi);
+            SKIP(p);
+            p += _Xsj3cReadAscii(file, p, str);
+            if (roma[0] == '\0' || yomi[0] == '\0')
+                break;
+            if (begin++) {
+                rktr = _Xsj3cAllocRKTable();
+                if (!rktr) {
+                    Xsj3cWarning("can't allocate roman-kana conversion table");
+                    return (ALLOC_FAILED);
+                }
+                rktp = rktq->next = rktr;
+            }
+            rktp->roma = _Xsj3cStoreChar(roma, (rlen  = strlen(roma)) + 1);
+            rktp->yomi = _Xsj3cStoreChar(yomi, (ylen  = strlen(yomi)) + 1);
+            rktp->str = _Xsj3cStoreChar(str, strlen(str) + 1);
+            if (!rktp->roma || !rktp->yomi || !rktp->str) {
+                Xsj3cWarning("can't allocate roman-kana conversion table");
+                return(ALLOC_FAILED);
+            }
+            rktp->rlen = rlen;
+            rktp->ylen = ylen;
+            rktq = rktp;
+        }
+        (*error)++;
+    }
+    rktp->next = NULL;
+    fclose(fp);
+    return (OK);
+}
+
+/*
+ * _Xsj3cHKInit()
+ *  Decide sjhk file to read, then read it and make hiragana-katakana
+ * conversion table.
+ */
+Xsj3cHKTable *
+_Xsj3cHKInit(buf, sjhk, home)
+    Xsj3cBuf            buf;
+    char               *sjhk;
+    char               *home;
+{
+    extern char        *getenv();
+    register char      *p;
+    char                hkfile[BUFSIZ];
+    int                 value,  error;
+
+    if (hktable[serverIF[buf->server].lang])
+        return (hktable[serverIF[buf->server].lang]);
+
+    if (sjhk) {
+        if ((value = _Xsj3cReadHK(buf, sjhk, &error)) > 0 )
+            Xsj3cError("can't open sjhk file %s", sjhk);
+        else if (value < 0)
+            Xsj3cError("read failed line %s sjhk file %s",
+                    _Xsj3cItoa(error), sjhk);
+    } else {
+        hkfile[0] = '\0';
+        if ((p = getenv("SJHK")) && *p != '\0') {
+            if (*p != '/') {
+                if (home)
+                    strcpy(hkfile, home);
+                strcat(hkfile, "/");
+            }
+            strcat(hkfile, p);
+        } else if (home) {
+            strcpy(hkfile, home);
+            strcat(hkfile, "/.sjhk");
+        } else  {
+            strcpy(hkfile, SJ3DEFPATH);
+            strcat(hkfile, DEF_SJHK_FILE);
+        }
+        if ((value = _Xsj3cReadHK(buf, hkfile, &error)) > 0 ) {
+            strcpy(hkfile, SJ3DEFPATH);
+            strcat(hkfile, DEF_SJHK_FILE);
+            if ((value = _Xsj3cReadHK(buf, hkfile, &error)) > 0 ) {
+                Xsj3cError("can't open sjhk file %s", hkfile);
+            } else if (value < 0) {
+                Xsj3cError("read failed line %s sjhk file %s",
+                        _Xsj3cItoa(error), hkfile);
+            }
+        } else if (value < 0) {
+            Xsj3cError("read failed line %s sjhk file %s",
+                    _Xsj3cItoa(error), hkfile);
+        }
+    }
+    return (hktable[serverIF[buf->server].lang]);
+}
+
+#define HKTBMAX (BUFSIZ/sizeof(Xsj3cHKTable))
+
+static Xsj3cHKTable *
+_Xsj3cAllocHKTable()
+{
+    register Xsj3cHKTable  *hktp;
+
+    if (hknowtp == NULL || hknowtp > hkmaxtp) {
+        hktp = (Xsj3cHKTable *)malloc(sizeof(Xsj3cHKTable) * HKTBMAX);
+        if (hktp == NULL)
+            return (NULL);
+        hknowtp = hktp;
+        hkmaxtp = hknowtp + HKTBMAX - 1;
+        hknowtp++;
+    } else {
+        hktp = hknowtp;
+        hknowtp++;
+    }
+    hktp->hira = NULL;
+    hktp->zkata = NULL;
+    hktp->hkata = NULL;
+    hktp->halpha = NULL;
+    hktp->hlen = 0;
+    hktp->next = NULL;
+    return (hktp);
+}
+
+static int
+_Xsj3cReadHK(buf, file, error)
+    Xsj3cBuf                buf;
+    register char          *file;
+    register int           *error;
+{
+    register FILE          *fp;
+    unsigned char           line[256];
+    unsigned char          *p;
+    unsigned char           zhira[RBUFSIZ],  zkata[YBUFSIZ];
+    unsigned char           hkata[RBUFSIZ], halpha[RBUFSIZ];
+    register int            begin = 0,      hkata_len,      halpha_len;
+    register Xsj3cHKTable  *hktp,  *hktq,  *hktr;
+
+    if ((fp = fopen(file, "r")) == NULL)
+        return (OPEN_FAILED);
+    
+    *error = 1;
+    hktp = hktable[serverIF[buf->server].lang] = _Xsj3cAllocHKTable();
+    while (fgets(line, sizeof(line), fp) != NULL) {
+        p = line;
+        while (*p != '\n' && *p != '#') {
+            p += _Xsj3cmINtomPS(buf, file, p, zhira);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, zkata);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, hkata);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cReadAscii(file, p, halpha);
+            if (zhira[0] == '\0' || zkata[0] == '\0'
+                    || hkata[0] == '\0' || halpha[0] == '\0')
+                break;
+            if (begin++) {
+                hktr = _Xsj3cAllocHKTable();
+                if (!hktr) {
+            Xsj3cWarning("can't allocate hiragana-katakana conversion table");
+                    return (ALLOC_FAILED);
+                }
+                hktp = hktq->next = hktr;
+            }
+            hktp->hira = _Xsj3cStoreChar(zhira, strlen(zhira) + 1);
+            hktp->zkata = _Xsj3cStoreChar(zkata, strlen(zkata) + 1);
+            hktp->hkata = _Xsj3cStoreChar(hkata, 
+                    (hkata_len = strlen(hkata)) + 1);
+            hktp->halpha = _Xsj3cStoreChar(halpha, 
+                    (halpha_len = strlen(halpha)) + 1);
+            if (!hktp->hira || !hktp->zkata || !hktp->hkata || !hktp->halpha) {
+            Xsj3cWarning("can't allocate hiragana-katakana conversion table");
+                return(ALLOC_FAILED);
+            }
+            hktp->hlen = hkata_len;
+            if (hkata_len != halpha_len)
+                return (READ_FAILED);
+            hktq = hktp;
+        }
+        (*error)++;
+    }
+    hktp->next = NULL;
+    fclose(fp);
+    return (OK);
+}
+
+/*
+ * _Xsj3cZHInit()
+ *  Decide sjzh file to read, then read it and make hankaku-zenkaku
+ * conversion table.
+ */
+Xsj3cZHTable *
+_Xsj3cZHInit(buf, sjzh, home)
+    Xsj3cBuf            buf;
+    char               *sjzh;
+    char               *home;
+{
+    extern char        *getenv();
+    register char      *p;
+    char                zhfile[BUFSIZ];
+    int                 value,  error;
+
+    if (zhtable[serverIF[buf->server].lang])
+        return (zhtable[serverIF[buf->server].lang]);
+
+    if (sjzh) {
+        if ((value = _Xsj3cReadZH(buf, sjzh, &error)) > 0 )
+            Xsj3cError("can't open sjzh file %s", sjzh);
+        else if (value < 0)
+            Xsj3cError("read failed line %s sjzk file %s",
+                    _Xsj3cItoa(error), sjzh);
+    } else {
+        zhfile[0] = '\0';
+        if ((p = getenv("SJZH")) && *p != '\0') {
+            if (*p != '/') {
+                if (home)
+                    strcpy(zhfile, home);
+                strcat(zhfile, "/");
+            }
+            strcat(zhfile, p);
+        } else if (home) {
+            strcpy(zhfile, home);
+            strcat(zhfile, "/.sjzh");
+        } else {
+            strcpy(zhfile, SJ3DEFPATH);
+            strcat(zhfile, DEF_SJZH_FILE);
+        }
+        if ((value = _Xsj3cReadZH(buf, zhfile, &error)) > 0 ) {
+            strcpy(zhfile, SJ3DEFPATH);
+            strcat(zhfile, DEF_SJZH_FILE);
+            if ((value = _Xsj3cReadZH(buf, zhfile, &error)) > 0 ) {
+                Xsj3cError("can't open sjzh file %s", zhfile);
+            } else if (value < 0) {
+                Xsj3cError("read failed line %s sjzk file %s",
+                        _Xsj3cItoa(error), zhfile);
+            }
+        } else if (value < 0) {
+            Xsj3cError("read failed line %s sjzk file %s",
+                    _Xsj3cItoa(error), zhfile);
+        }
+    }
+    return (zhtable[serverIF[buf->server].lang]);
+}
+
+#define ZHTBMAX (BUFSIZ/sizeof(Xsj3cZHTable))
+
+static Xsj3cZHTable *
+_Xsj3cAllocZHTable()
+{
+    register Xsj3cZHTable  *zhtp;
+
+    if (zhnowtp == NULL || zhnowtp > zhmaxtp) {
+        zhtp = (Xsj3cZHTable *)malloc(sizeof(Xsj3cZHTable) * ZHTBMAX);
+        if (zhtp == NULL)
+            return (NULL);
+        zhnowtp = zhtp;
+        zhmaxtp = zhnowtp + ZHTBMAX - 1;
+        zhnowtp++;
+    } else {
+        zhtp = zhnowtp;
+        zhnowtp++;
+    }
+    zhtp->zkana = NULL;
+    zhtp->hkata = NULL;
+    zhtp->halpha = NULL;
+    zhtp->zalpha = NULL;
+    zhtp->next = NULL;
+    return (zhtp);
+}
+
+static int
+_Xsj3cReadZH(buf, file, error)
+    Xsj3cBuf        buf;
+    char           *file;
+    register int   *error;
+{
+    register FILE          *fp;
+    unsigned char           line[256];
+    unsigned char          *p;
+    unsigned char           hanalpha[RBUFSIZ],    zenalpha[YBUFSIZ];
+    unsigned char           zkana[RBUFSIZ],     hkata[RBUFSIZ];
+    register int            begin = 0;
+    register Xsj3cZHTable  *zhtp,  *zhtq,  *zhtr;
+
+    if ((fp = fopen(file, "r")) == NULL)
+        return (OPEN_FAILED);
+    
+    *error = 1;
+    zhtp = zhtable[serverIF[buf->server].lang] = _Xsj3cAllocZHTable();
+    while (fgets(line, sizeof(line), fp) != NULL) {
+        p = line;
+        while (*p != '\n' && *p != '#') {
+            p += _Xsj3cReadAscii(file, p, hanalpha);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, zenalpha);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, zkana);
+            CHECK_END(p);
+            SKIP(p);
+            p += _Xsj3cmINtomPS(buf, file, p, hkata);
+            if (hanalpha[0] == '\0' || zenalpha[0] == '\0'
+                    || zkana[0] == '\0' || hkata[0] == '\0')
+                break;
+            if (begin++) {
+                zhtr = _Xsj3cAllocZHTable();
+                if (!zhtr) {
+                    Xsj3cWarning("can't allocate zen/hankaku conversion table");
+                    return (ALLOC_FAILED);
+                }
+                zhtp = zhtq->next = zhtr;
+            }
+            zhtp->halpha = _Xsj3cStoreChar(hanalpha, strlen(hanalpha) + 1);
+            zhtp->zalpha = _Xsj3cStoreChar(zenalpha, strlen(zenalpha) + 1);
+            zhtp->zkana = _Xsj3cStoreChar(zkana, strlen(zkana) + 1);
+            zhtp->hkata = _Xsj3cStoreChar(hkata, strlen(hkata) + 1);
+            if (!zhtp->halpha || !zhtp->zalpha
+                    || !zhtp->zkana || !zhtp->hkata) {
+                Xsj3cWarning("can't allocate zen/han-kaku conversion table");
+                return(ALLOC_FAILED);
+            }
+            zhtq = zhtp;
+        }
+        (*error)++;
+    }
+    zhtp->next = NULL;
+    fclose(fp);
+    return (OK);
+}
+
+/*
+ * _Xsj3cSetPlosive()
+ * Set roman-kana plosive conversion data.
+ */
+unsigned char *
+_Xsj3cSetPlosive(buf)
+    Xsj3cBuf                buf;
+{
+    register Xsj3cRKTable  *rktp;
+    register int            i;
+    register wchar          s;
+    unsigned char           tmp[KANABUFSIZ];
+    
+    if (plosive[serverIF[buf->server].lang])
+        return(plosive[serverIF[buf->server].lang]);
+    if (!rktable[serverIF[buf->server].lang])
+        Xsj3cError("Null roman-kana conversion table");
+    i = 0;
+    for (rktp = rktable[serverIF[buf->server].lang];
+            rktp->next != NULL; rktp = rktp->next) {
+        if (*rktp->str != '\0') {
+            s = (*rktp->yomi << 8) + *(rktp->yomi + 1);
+            if (isplosive(s, serverIF[buf->server].lang)) {
+                tmp[i] = *rktp->str;
+                i++;
+            }
+        }
+    }
+    tmp[i] = '\0';
+    if ((plosive[serverIF[buf->server].lang]
+            = _Xsj3cStoreChar(tmp, strlen(tmp) + 1)) == NULL)
+        Xsj3cError("can't allocate for roma-kana plosive conversion table");
+    return(plosive[serverIF[buf->server].lang]);
+}
+
+/*
+ * _Xsj3cSetDouble()
+ * Set roman-kana double conversion data.
+ */
+unsigned char *
+_Xsj3cSetDouble(buf)
+    Xsj3cBuf                buf;
+{
+    register Xsj3cRKTable  *rktp,   *rktp2;
+    register int            i;
+    unsigned char           tmp[KANABUFSIZ];
+    
+    if (rkdouble[serverIF[buf->server].lang])
+        return(rkdouble[serverIF[buf->server].lang]);
+    if (!rktable[serverIF[buf->server].lang])
+        Xsj3cError("Null roman-kana conversion table");
+    i = 0;
+    for (rktp = rktable[serverIF[buf->server].lang];
+            rktp->next != NULL; rktp = rktp->next) {
+        if (rktp->rlen == 1) {
+            if (isvowel(*rktp->roma))
+                continue;
+            tmp[i] = *rktp->roma;
+            for (rktp2 = rktable[serverIF[buf->server].lang];
+                    rktp2->next != NULL; rktp2 = rktp2->next) {
+                if (rktp2->rlen == 2 && *(rktp2->roma + 1) == tmp[i]
+                        && *(rktp2->roma) == tmp[i]) {
+                    if (!strcmp(rktp->yomi, rktp2->yomi)) {
+                        i++;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    tmp[i] = '\0';
+    if ((rkdouble[serverIF[buf->server].lang]
+            = _Xsj3cStoreChar(tmp, strlen(tmp) + 1)) == NULL)
+        Xsj3cError("can't allocate for roma-kana double conversion table");
+    return(rkdouble[serverIF[buf->server].lang]);
+}