/*
* $Id: rk_main.c,v 1.4 2005/04/10 15:26:38 aonoto Exp $
*/
/*
* FreeWnn is a network-extensible Kana-to-Kanji conversion system.
* This file is part of FreeWnn.
*
* Copyright Kyoto University Research Institute for Mathematical Sciences
* 1987, 1988, 1989, 1990, 1991, 1992
* Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991, 1992, 1999
* Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991, 1992
* Copyright FreeWnn Project 1999, 2000
*
* Maintainer: FreeWnn Project <freewnn@tomo.gr.jp>
*
* This library 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.
*
* This library 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 this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/***********************************************************************
rk_main.c
88. 6.10 改 正
ローマ字かな変換・その他、入力コードを変換するプログラム。
ASCIIコード使用を、前提としている。
このファイルは、変換のメインルーチン。
***********************************************************************/
#include "rk_header.h"
#include "rk_extvars.h"
#include "rk_fundecl.h"
struct matchpair /* 現在マッチされている変数の番号と、マッチした文字の
組を保持。 */
{
int hennum;
letter ltrmch;
};
letter memory[SIZALL]; /* 変換対応表や、変数の変域などを入れる領域 */
struct dat data[LINALL]; /* 対応表の一行ごとのデータへのポインタ */
letter *hensudefhyo[VARTOT]; /* 変数ごとの変域データへのポインタ */
struct matchpair henmatch[VARTOT]; /* 変数のマッチ状況をためておく */
#ifdef KDSP
# ifdef MVUX
letter displine[DSPLIN]; /* デバッグ用 表示の一行分を記憶 */
# endif
#endif
letter *curdis;
int codein_len;
letter *codeout, *remainkbf;
letter ungetc_buf = EOLTTR; /* romkan_ungetcが一文字退避しておくバッファ */
letter unnext_buf = EOLTTR; /* romkan_unnextが 〃 */
letter (*keyin_method) (); /* キーイン関数のアドレス */
int (*bytcnt_method) (); /* バイトカウント関数のアドレス */
int (*kbytcnt_method) (); /* キー入力解釈用バイトカウント関数のアドレス */
char prv_modfnm[REALFN] = "\0"; /* モード表名(又はそのパス名)を保存 */
/* 初期設定がいい加減! */
#define DISOUT ((flags & RK_NONISE) ? rk_output : disout)
/* フラグの値はromkan_init3()の中で設定され、そのRK_NONISEのビットの値の
標準は0。従ってDISOUTの標準値はdisout。これをrk_outputに変えると、偽コード
(除:LTREOF)を一切出さない(但し、キー入力があれば必ず何かを返すように
してある時に限り、EOLTTRをも返す)。 */
char eofflg; /* romkan_next()からLTREOFが来たときに、romkan_henkan()内で1
にする。これの値が非0なら変換対応表の(error)を無視して、
本処理バッファ内の未確定コードも、強制的に変換させる。更に
LTREOFが来た時に特別にある出力を出すように指定されていれば
その処理も行う(但し、その処理は急ごしらえで不完全)。その
処理中はeofflgは2。 */
letter evalbuf[2][2][OUTSIZ];
char ebf_sw = 0;
letter saishu_out[OUTSIZ];
letter delchr, delchr2 = EOLTTR, nisedl; /* DELのキャラクタは二個まで持てる */
modetyp chgmod (), incmod (), decmod ();
/* letterを返す関数の定義及び宣言(一部にはcharのものも混じっている) */
static letter mchedsrc ();
letter romkan_next (), romkan_unnext ();
letter romkan_getc (), romkan_ungetc (), *romkan_henkan ();
letter to_zenalpha (), to_zenhira (), to_zenkata ();
/* to_zenhira;kata は濁点を持つ文字を一まとめにしない。 */
void BUGreport ();
static void maeato_henkan (), add_at_eof (), rk_delete (), set_rubout (), mchevl (), codeout_chg (), ltrevlcpy ();
int match ();
static int p_eq (), hen_ikisrc (), henkan_ok (), rk_rst (), head_bytecount ();
extern void readdata (), romkan_reset (), hank_setup (), to_hankata (), to_digit (), dakuadd (), handakuadd (), allchgmod ();
extern int ltov ();
static letter *
ltrcpy (lp1, lp2)
letter *lp1, *lp2;
{
letter *org;
org = lp1;
while ((*lp1++ = *lp2++) != EOLTTR);
return (org);
}
#ifdef notuse
letter *
ltrncpy (lp1, lp2, n) /* 末尾に'EOLTTR'をセット */
letter *lp1, *lp2;
int n;
{
letter *org;
org = lp1;
for (; n; --n)
if (EOLTTR == (*lp1++ = *lp2++))
return (org);
*lp1 = EOLTTR;
return (org);
}
#endif
/** letterの列の末尾に一文字つなげる */
static letter *
ltr1cat (lp, l)
letter *lp, l;
{
letter *org;
org = lp;
totail (lp);
*lp++ = l;
*lp = EOLTTR;
return (org);
}
/** 文字列の末尾に一文字つなげる。ltr1catのchar版 */
char *
chrcat (s, c)
char *s, c;
{
char *org;
org = s;
strtail (s);
*s++ = c;
*s = '\0';
return (org);
}
static letter *
ltrcat (lp1, lp2)
letter *lp1, *lp2;
{
letter *org;
org = lp1;
totail (lp1);
ltrcpy (lp1, lp2);
return (org);
}
/** letterの列lp2の各要素の最上位ビットを立ててから、lp1の後ろにつなげる。*/
static letter *
bitup_ltrcat (lp1, lp2)
letter *lp1, *lp2;
{
letter *org;
org = lp1;
totail (lp1);
while ((*lp1 = *lp2++) != EOLTTR)
*lp1++ |= HG1BIT;
/** lp2 の要素全ての最上位ビットを立てる。*lp2がNISEBP(rk_spclval.h
でdefine)などのときは最初から最上位が立ってるからよい */
return (org);
}
/** ltrcatしたあと、結果の文字列の末尾にポインタをもっていく。*/
letter *
ltrgrow (lp1, lp2)
letter *lp1, *lp2;
{
totail (lp1);
while ((*lp1 = *lp2++) != EOLTTR)
lp1++;
return (lp1);
}
static int
ltrlen (lp)
letter *lp;
{
letter *org;
for (org = lp; *lp != EOLTTR; lp++);
return (lp - org);
}
/** letterの列の最後の文字へのポインタを返す。但し空文字列に対しては
そのまま返す。*/
letter *
ltrend (lp)
letter *lp;
{
return ((*lp != EOLTTR) ? (lp + ltrlen (lp) - 1) : lp);
}
/** 文字列の最後の文字へのポインタを返す。ltrend()のchar版。ltrend()と
同様、空文字列のときは特例がある。*/
char *
strend (s)
char *s;
{
return (*s ? (s + strlen (s) - 1) : s);
}
int
ltrcmp (lp1, lp2)
letter *lp1, *lp2;
{
for (; *lp1 == *lp2; lp1++, lp2++)
if (*lp1 == EOLTTR)
return (0);
return (*lp1 > *lp2 ? 1 : -1);
}
#ifdef notuse
int
ltrncmp (lp1, lp2, n)
letter *lp1, *lp2;
int n;
{
for (; n && *lp1 == *lp2; n--, lp1++, lp2++)
if (*lp1 == EOLTTR)
return (0);
return (n == 0 ? 0 : (*lp1 > *lp2 ? 1 : -1));
}
letter *
ltr_index (lp, l)
letter l, *lp;
{
for (; *lp != EOLTTR; lp++)
if (*lp == l)
return (lp);
return (NULL);
}
#endif
static letter *
ltr_rindex (lp, l)
letter l, *lp;
{
letter *lp2;
for (lp += ltrlen (lp2 = lp); lp2 != lp;)
if (*--lp == l)
return (lp);
return (NULL);
}
/** 文字列の末尾一文字をカットする。*/
letter *
ltr1cut (lp)
letter *lp;
{
int l;
if (0 == (l = ltrlen (lp)))
BUGreport (0); /* Illegal POP */
*(lp + --l) = EOLTTR;
return (lp);
}
/** letterの一文字を長さ1の文字列に変換する */
static letter *
ltr_to_ltrseq (lp, l)
letter *lp, l;
{
*lp++ = l;
*lp-- = EOLTTR;
return (lp);
}
/** charの列からletterを一つ取り出す。但し'\0'はEOLTTRに変える */
/* *INDENT-OFF* */
letter
letterpick (lbfpptr)
uns_chr **lbfpptr;
/* *INDENT-ON* */
{
letter l = 0;
register int i;
for (i = (*bytcnt_method) (*lbfpptr); i; i--)
l = (l << 8) + *(*lbfpptr)++;
return (l == 0 ? EOLTTR : l);
}
/** 入力されたコードを文字単位にまとめる。但しEOFはLTREOF(rk_spclval.h
にて定義)に変える。*/
/* *INDENT-OFF* */
letter
romkan_next ()
/* *INDENT-ON* */
{
letter in;
int i, n;
uns_chr c;
/* unnextされている場合は、それを取り出す。 */
if (unnext_buf != EOLTTR)
return (in = unnext_buf, unnext_buf = EOLTTR, in);
if ((letter) EOF == (in = (*keyin_method) ()))
return (LTREOF);
c = (in &= 0xff);
n = (*kbytcnt_method) (&c);
for (i = 1; i < n; i++)
in = (in << 8) + ((*keyin_method) () & 0xff);
return (in);
}
/* letterの文字 l とcharの文字 c の比較。エンドマーク同士(letter列の
エンドマークはEOLTTR、char列のは'\0')も一致とみなす。 */
#define ltrchreq(l, c) ((l) == ((c) != 0 ? (c) : EOLTTR))
/** letterの文字列とcharの文字列の比較 但し大小比較は符号なしとしてやる */
int
ltrstrcmp (l, s)
register letter *l;
register char *s;
{
for (; ltrchreq (*l, *s); l++, s++)
if (*s == 0)
return (0);
return ((*l < (uns_chr) * s || *l == EOLTTR) ? -1 : 1);
}
/** 最後にマッチした文字 つまりurabufの最後。urabufが空だとEOLTTRを返す */
static letter
lastmch ()
{
return (*(ltrend (urabuf)));
}
/* end of 'letter' functions */
#ifdef OMAKE
/** お ま け
TVアニメのご案内(一部特撮を含む)。引数が非負整数の時はどれか一つ、
-1なら全てを出力し、それ以外なら何も出力しない。いずれの場合も、データ
の個数を返す。*/
takeoka (i)
int i;
{
#define OPIMAX (numberof(opinion))
static char *opinion[] = {
"「魔法のアイドル パステルユーミ」は良い番組だ。by竹岡",
"「機動戦士ガンダムΖΖ」の地球編は良い番組だ。by服部",
"「魔法のスター マジカルエミ」は良い番組だ。by竹岡",
"(参考意見)「マジカルエミ〔蝉時雨〕」は良いアニメだ。by森島",
"「魔法の天使 クリィミーマミ」は良い番組だ。by竹岡",
"「サザエさん」は良い番組だ。by鈴木",
"「ムーミン」は良い番組だ。by鈴木",
"「マンガ大作戦」のロードランナーシリーズは良い番組だ。by新出",
"「スプーンおばさん」は良い番組だ。by鴨",
"「はじめ人間ギャートルズ」は良い番組だ。by富永",
"「宇宙の騎士テッカマン」は良い番組だ。by駒嵐",
"「妖怪人間ベム」は良い番組だ。by鴨",
"「アルプスの少女ハイジ」は良い番組だ。by富永",
"「ときめきトゥナイト」は良い番組だ。by駒嵐",
"「あしたのジョー」は良い番組だったね。by立木(ついき)",
"「きまぐれオレンジ・ロード」は良い番組だ。by三好・服部",
"「ゲッターロボ」(「ゲッターロボG」を含む)は良い番組だ。by新出",
"「テッちゃん」(「じゃりン子チエ」のこと)は良い番組だ。by松田",
"「チキチキマシン猛レース」は良い番組だ。by服部",
"「小公女セーラ」は良い番組だ。by森島",
"「秘密戦隊ゴレンジャー」は良い番組だ。by新出",
"(白黒時代の)「鉄人28号」は良い番組だ。by桜川"
};
if (i >= 0)
printf ("%s\n", opinion[i % OPIMAX]);
else if (i == -1)
for (i = 0; i < OPIMAX; i++)
printf ("%s\n", opinion[i]);
return (OPIMAX);
}
#endif /* OMAKE */
/** 変換バッファのクリア */
void
romkan_clear ()
{
#ifdef KDSP
# ifdef MVUX
*displine = EOLTTR;
# endif
#endif
ungetc_buf = *keybuf = *urabuf = *disout = *rk_output = EOLTTR;
curdis = DISOUT;
lastoutlen = lastkbflen = 0;
}
/** 表を読み込んで変換の初期設定をする。(part 1)*/
int
romkan_init (modhyo, delchr_, chmoutf, keyinfn, bytcntfn)
char *modhyo, chmoutf;
letter delchr_;
letter (*keyinfn) ();
int (*bytcntfn) (); /* 引数の詳細はromkan_init3を参照 */
{
return (romkan_init2 (modhyo, delchr_, chmoutf, keyinfn, bytcntfn, 0, 0, 0));
}
#define ifflg(a, b) ((a) ? (b) : 0)
/** 表を読み込んで変換の初期設定をする。(part 2: キー入力に対し必ず何か
返すようにするか、キーバッファをクリアするかどうかなども指定可)*/
int
romkan_init2 (modhyo, delchr_, chmoutf, keyinfn, bytcntfn, keyackf, restartf, nonisecodf)
char *modhyo, chmoutf, keyackf, restartf, nonisecodf;
letter delchr_;
letter (*keyinfn) ();
int (*bytcntfn) (); /* 引数の詳細はromkan_init3を参照 */
{
return (romkan_init3 (modhyo, delchr_, toNISE (delchr_), EOLTTR,
keyinfn, bytcntfn, (int (*)()) NULL, restartf, ifflg (chmoutf, RK_CHMOUT) | ifflg (keyackf, RK_KEYACK) | ifflg (nonisecodf, RK_NONISE) | RK_DSPNIL));
}
/** 表を読み込んで変換の初期設定をする。(part 3)*/
int
romkan_init3 (modhyo, delchr_, nisedl_, delchr2_, keyinfn, bytcntfn, kbytcntfn, restartf, flags_)
char *modhyo; /* モード定義表の名又はそのパス名 */
letter delchr_; /* DELとして使うコード */
letter nisedl_; /* 偽DELとして使うコード */
letter delchr2_; /* DELとして使うコードが二つある場合そのもう一方 */
letter (*keyinfn) (); /* キーイン関数 */
int (*bytcntfn) (); /* バイトカウント関数 */
int (*kbytcntfn) (); /* キーインの解釈用バイトカウント関数。NULL指定の時は
bytcntfnと同じものが使われる */
char restartf;
/* rk_rst()内から呼ばれた時に立つフラグ。これが立つと、バッファをクリア
せずに済ます。この時はエラー検出時のメッセージも少し異なる */
int flags_;
/* 以下のフラグが利用可。これらはrk_spclval.hにて定義。
RK_CHMOUT:モードチェンジを知らせるコードを返すか?
RK_KEYACK:キーインに対し必ず何かを返すか
RK_DSPNIL:romkan_disp(off)modeのデフォルトを空文字列にするか
RK_NONISE:偽コードを出さないようにするか
RK_REDRAW:Wnn用特殊フラグ(redraw用のフラグを出すかどうか)
RK_SIMPLD:deleteの動作を単純にするか
RK_VERBOS:verboseモードで起こすか
*/
{
int errcod; /* 今の所1のみ */
/* 偽コードを出すかの設定はromkan_clearで使うので、フラグの設定を
先にやっておかないといけない。 */
if ((flags = flags_) & RK_VERBOS)
{
fprintf (stderr, "romkan_init invoked.\r\n");
fflush (stderr);
}
#ifdef OMAKE
if (modhyo == NULL)
takeoka (-1); /* おまけ;通常は実行されない */
#endif /* OMAKE */
keyin_method = keyinfn;
bytcnt_method = (bytcntfn == NULL ? head_bytecount : bytcntfn);
kbytcnt_method = (kbytcntfn == NULL ? bytcnt_method : kbytcntfn);
strcpy (prv_modfnm, modhyo);
delchr = delchr_;
nisedl = nisedl_;
delchr2 = delchr2_;
/** 実はDELのキャラクタを二個まで持てる。二個目:delchr2は、未設定の
とき(値がEOLTTR)は無視される。それ以外は、delchr2が入力されると
delchrが入力されたのと同じ振る舞いをする。*/
if ((errcod = setjmp (env0)) != 0)
{
fprintf (stderr, "romkan_init failed.");
if (!restartf)
{
romkan_reset ();
/* fprintf(stderr,
"No conversion is currently performed."); */
fprintf (stderr, "\r\n");
}
else
{
fprintf (stderr, "Conversion method was unchanged.\r\n");
}
}
else
{
readdata (memory, data, hensudefhyo, modhyo);
if (!restartf)
{
romkan_clear ();
hank_setup ();
}
}
if (!errcod && (flags & RK_VERBOS))
{
fprintf (stderr, "romkan_init finished.\r\n");
fflush (stderr);
}
return (errcod); /* 正常終了なら0 */
}
/** バイトカウント関数のデフォルト。sの指している所に入っているのが
何バイトコードかを返す。
romkan_nextに注意。そちらでは、バイトカウント関数の値は
引数の一文字目にのみ依存すると仮定している。*/
static int
head_bytecount (s)
uns_chr *s;
{
#ifdef IKIS
return ((*s <= 0xa0 || *s == 0xff) ? 1 : 2);
#else
return (((*s <= 0xa0 && *s != HNKAK1) || *s == 0xff) ? 1 : 2);
#endif
}
/** 変換された文字を順次返す */
/* *INDENT-OFF* */
letter
romkan_getc ()
/* *INDENT-ON* */
{
letter l;
/* ungetcされている場合は、それを取り出す。 */
if (ungetc_buf != EOLTTR)
return (l = ungetc_buf, ungetc_buf = EOLTTR, l);
while (*curdis == EOLTTR)
{
/* romkan_next()の値がLTREOFの時も、そのまま送れば良い。 */
curdis = romkan_henkan (romkan_next ());
/* keyackflgが非0なら、キーインがあれば必ず何か返す。その
ため、返すべきものがない時はEOLTTRを返すことにする。 */
if (flags & RK_KEYACK)
break;
}
if (EOLTTR != (l = *curdis))
curdis++;
return (l);
/* 偽物の文字なら、HG1BITが立っている。 */
/* 特別なコード(例えば偽のBEEPとしてNISEBP)を返すときがある。それらは
rk_spclval.hに定義されている。 */
}
/** romkan_getcの下位関数として、入力を一文字受け取って
変換結果の文字列を出力する。*/
letter *
romkan_henkan (mae_in)
letter mae_in;
{
letter mae_out[2], *p;
/* if(*curdis != EOLTTR){p = curdis; curdis = nil; return(p);} */
/* ↑これではmae_inが無視される */
curdis = nil;
/* 前・後処理への入力は常にただ一文字 */
mae_out[0] = mae_out[1] = EOLTTR;
eofflg = rk_errstat = 0;
*rk_output = *disout = EOLTTR;
maeato_henkan (mae_in, mae_out, usemaehyo);
rk_input = *mae_out;
if (rk_input == LTREOF)
{
/* LTREOFが来た場合、(error)を無視し、本処理バッファの末尾迄
強制変換する。そのためにeofflgを1にする。その後、結果の
末尾に、LTREOFが来た時の特別コード(指定されていれば)と、
LTREOFをつなぐ。 */
eofflg = 1;
match ();
eofflg = 2;
add_at_eof (); /* codeoutに、LTREOFが来た時出すコードが入る。 */
ltr1cat (codeout, LTREOF);
ltrcat (disout, codeout);
ltrcat (rk_output, codeout);
}
else if (rk_input == EOLTTR)
{
/* EOLTTRが来た場合も、上と同様の処理を行うが、LTREOFは
つながない。なお、これはromkan_getc()を呼んでいる時は
起こらない(romkan_next()がEOLTTRを返さないから)。 */
eofflg = 1;
match ();
}
else if (rk_input == delchr || rk_input == delchr2)
{
/* delchr2が未設定ならその値はEOLTTRなのでrk_inputと等しくない。 */
rk_delete (rk_input);
}
else
{
ltr1cat (keybuf, rk_input);
ltr1cat (disout, toNISE (rk_input));
match ();
}
if (!(flags & RK_CHMOUT))
{
/* chmoutflgが0の時は、CHMSIGを出さない。 */
for (p = DISOUT;; p++)
{
while (*p == CHMSIG)
ltrcpy (p, p + 1);
if (*p == EOLTTR)
break;
}
}
if ((flags & RK_REDRAW) && NULL != (p = ltr_rindex (disout, nisedl)))
{
for (p++; *p != EOLTTR || (ltr1cat (disout, REDRAW), 0); p++)
if (!isSPCL (*p))
break;
}
/* wnnのredrawのフラグが立っていて、disoutがnisedl以後特殊コードのみ
で終わっていたら、REDRAWを出して、Wnnに変換行のredrawをさせる。 */
return (DISOUT);
}
/* デバッグ用関数 */
#ifdef KDSP
# ifdef MVUX
void
pridbg2 (a, b, c)
char *a, *c;
letter *b;
{
printf ("%s", a);
dump_fordbg (b);
printf ("%s", c);
}
int
ltr_displen (l)
letter l;
{
while (l >= 256)
l >>= 8;
if (l == 0)
return (0);
if (l < ' ')
return (0);
if (168 <= l && l <= 170)
return (1);
return (l > 160 ? 2 : 1);
}
void
dump_fordbg (lp)
letter *lp;
{
while (*lp != EOLTTR)
printf ("%x/", *lp++);
}
void
print_fordbg (lp)
letter *lp;
{
while (*lp != EOLTTR)
putletter (*lp++ & ~HG1BIT);
}
void
print_ltrseq (lp)
letter *lp;
{
while (*lp != EOLTTR)
print_ltr (*lp++ & ~HG1BIT);
}
void
print_ltr (l)
letter l;
{
letter *disptail;
int i;
if (!isSPCL (l))
l &= ~HG1BIT;
if (l == CHMSIG)
#ifdef CHMDSP
printf ("...mode=%s\n", romkan_dispmode ())
#endif
;
else
#define BEEPCH '\007'
#define NEWLIN '\n'
if (l == BEEPCH || l == NISEBP)
putchar (BEEPCH);
else if (l == NEWLIN)
{
*displine = *keybuf = *urabuf = EOLTTR;
lastoutlen = lastkbflen = 0;
putchar (l);
}
else if (l == delchr || l == nisedl)
{
if (*displine == EOLTTR)
putchar (BEEPCH);
else
{
disptail = ltrend (displine);
for (i = ltr_displen (*disptail); i; i--)
printf ("\031 \031");
*disptail = EOLTTR;
}
}
else
{
ltr1cat (displine, l);
putletter (l);
}
}
# endif /* of #ifdef MVUX */
#endif
/* デバッグ用関数終わり */
/** DELが入力されたときの処理をする */
static void
rk_delete (input_del)
letter input_del;
{
if (ltrlen (keybuf) > ((flags & RK_SIMPLD) ? 0 : lastkbflen))
{
ltr1cut (keybuf);
set_rubout (disout, 1, nisedl);
}
else
{
if (*urabuf != EOLTTR && !(flags & RK_SIMPLD))
{
ltr1cut (ltrcpy (keybuf, urabuf));
*urabuf = EOLTTR;
set_rubout (rk_output, lastoutlen, input_del);
set_rubout (disout, lastkbflen, nisedl);
bitup_ltrcat (ltrcat (disout, rk_output), keybuf);
lastkbflen = lastoutlen = 0;
}
else
{
set_rubout (disout, 1, input_del);
set_rubout (rk_output, 1, input_del);
}
}
}
/** letterの列 lp1 と lp2 のマッチを試みる。返値は、lp1がlp2の頭部と
一致の時 -1、lp1またはその頭部とlp2が一致のときは一致長(lp2が
空文字列の時を含む。この場合返値は0)、それ以外は -2。
lp2側に式が含まれていたら評価をする。lp1側には式を含んではだめ */
static int
prefixp (lp1, lp2)
letter *lp1, *lp2;
{
/* 1行マッチさせるごとに、まずmatch情報をクリアしてから。
つまり、henmatch[0] . ltrmch = EOLTTR; としておく。 */
register int mch_len = 0, d_len;
for (;;)
{
if (*lp2 == EOLTTR)
return (mch_len);
if (*lp1 == EOLTTR)
return (-1);
if ((d_len = p_eq (&lp2, &lp1)) < 0)
return (d_len);
mch_len += d_len;
}
}
/** num番目の変数が既にある文字とマッチしていると仮定して、その文字を返す */
static letter
mchedsrc (num)
int num;
{
struct matchpair *pairptr;
for (pairptr = henmatch; pairptr->ltrmch != EOLTTR; pairptr++)
{
if (num == pairptr->hennum)
return (pairptr->ltrmch);
}
return (BUGreport (8), 0);
}
/** num番目の変数が文字 l とマッチするか調べる。その変数がunboundだった
場合は l にbindする。マッチしたら(bindの時を含む)1、しないと0を返す */
static int
mchsrc (num, l)
int num;
letter l;
{
struct matchpair *pairptr;
if (hen_ikisrc (num, l) == 0)
return (0);
for (pairptr = henmatch; pairptr->ltrmch != EOLTTR; pairptr++)
{
if (num == pairptr->hennum)
return (pairptr->ltrmch == l);
}
pairptr->ltrmch = l;
pairptr->hennum = num;
(++pairptr)->ltrmch = EOLTTR;
return (1);
}
/** l1pから一単位を取って評価し、文字l2と一致するかどうかを返す。評価した
結果が一文字にならなかったら、当然一致しない。*/
static int
l_eq (l1p, l2)
letter *l1p, l2;
{
letter evlrsl[RSLMAX];
switch (SHUBET (*l1p))
{
case 0:
return (*l1p == l2);
case 1:
return (mchsrc ((int) LWRMSK (*l1p), l2));
case 2:
mchevl (&l1p, evlrsl);
return (evlrsl[0] == l2 && evlrsl[1] == EOLTTR);
default:
return (BUGreport (1), 0);
}
}
/** prefixp内で使用 但し引数の順序は逆、すなわち式が含まれうるのはl1pp側
のみ。l1ppから一単位ぶん取って評価したものとl2ppのマッチを試みる。それ
がl2ppまたはその頭部とマッチすれば一致長を返し(l1ppの評価結果が空文字
列の時を含む。この場合返値は0)、逆にl1ppの評価結果の頭部とl2ppがマッ
チした時は -1を返す。マッチが失敗したら返値は -2。*/
static int
p_eq (l1pp, l2pp)
register letter **l1pp, **l2pp;
{
int num;
letter evlrsl[RSLMAX], *rslptr;
register int retval = -2;
/* l2pp側には式を含まない筈 */
if (!is_HON (**l2pp))
{
/* if(is_HON(**l1pp)) retval = p_eq(l2pp, l1pp); else */
BUGreport (9);
}
else
{
switch (SHUBET (**l1pp))
{
case 0: /* 文字同士 */
retval = (*(*l1pp)++ == *(*l2pp)++ ? 1 : -2);
break;
case 1: /* 変数と文字 */
num = LWRMSK (*(*l1pp)++);
retval = (mchsrc (num, *(*l2pp)++) ? 1 : -2);
break;
case 2: /* 式と文字 */
mchevl (l1pp, rslptr = evlrsl);
for (retval = 0; *rslptr != EOLTTR; retval++)
{
if (**l2pp == EOLTTR)
{
retval = -1;
break;
}
else if (*rslptr++ != *(*l2pp)++)
{
retval = -2;
break;
}
}
break;
default:
BUGreport (2);
}
}
return (retval);
}
/** l1pから一単位評価してl2pに入れる */
static void
mchevl (l1pp, l2p)
letter **l1pp, *l2p;
{
letter *l1p, tmpevl[RSLMAX];
l1p = *l1pp;
switch (SHUBET (*l1p))
{
case 0:
*l2p++ = *l1p++;
break;
case 1:
*l2p++ = mchedsrc ((int) LWRMSK (*l1p++));
break;
case 2: /* toupper, tolower, error, … */
switch (LWRMSK (*l1p++))
{
case 2:
mchevl (&l1p, tmpevl);
*l2p++ = to_upper (*tmpevl);
break;
case 3:
mchevl (&l1p, tmpevl);
*l2p++ = to_lower (*tmpevl);
break;
case 4:
*l2p++ = CHMSIG;
*l2p++ = LWRMSK (*l1p++);
*l2p++ = 0;
break; /* EOLではない */
case 5:
*l2p++ = CHMSIG;
*l2p++ = LWRMSK (*l1p++);
*l2p++ = 1;
break;
case 6:
*l2p++ = CHMSIG;
*l2p++ = XY2INT (2, LWRMSK (*l1p++));
*l2p++ = 1;
break;
case 7:
mchevl (&l1p, tmpevl);
*l2p++ = to_updown (*tmpevl);
break;
case 8:
mchevl (&l1p, tmpevl);
*l2p++ = to_zenalpha (*tmpevl);
break;
case 9:
mchevl (&l1p, tmpevl);
*l2p++ = to_hira (*tmpevl);
break;
case 10:
mchevl (&l1p, tmpevl);
*l2p++ = to_kata (*tmpevl);
break;
case 11:
mchevl (&l1p, tmpevl);
to_hankata (*tmpevl, &l2p);
break; /* 特殊 */
case 12:
mchevl (&l1p, tmpevl);
*l2p++ = to_zenhira (*tmpevl);
break;
case 13:
mchevl (&l1p, tmpevl);
*l2p++ = to_zenkata (*tmpevl);
break;
case 14:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
*l2p += *tmpevl;
LWRCUT (*l2p++);
break;
case 15:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
*l2p -= *tmpevl;
LWRCUT (*l2p++);
break;
case 16:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
*l2p *= *tmpevl;
LWRCUT (*l2p++);
break;
case 17:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
if (!*tmpevl)
*l2p = LTRHUG;
else
*l2p /= *tmpevl;
LWRCUT (*l2p++);
break;
case 18:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
if (!*tmpevl)
*l2p = LTRHUG;
else
*l2p %= *tmpevl;
LWRCUT (*l2p++);
break;
/* 19〜21・30は、条件を満たすと空文字列、
そうでないとUNUSDCを文字列として返す。 */
case 19:
mchevl (&l1p, tmpevl);
if (lastmch () != *tmpevl)
*l2p++ = UNUSDC;
break;
case 20:
if (!modesw[LWRMSK (*l1p++)].curmode)
*l2p++ = UNUSDC;
break;
case 21:
if (modesw[LWRMSK (*l1p++)].curmode)
*l2p++ = UNUSDC;
break;
case 22:
*l2p++ = REASIG;
break;
case 23:
*l2p++ = delchr;
break;
case 24:
*l2p++ = CHMSIG;
*l2p++ = XY2INT (1, 0);
/* これで「all」を表す */
*l2p++ = 0;
break;
case 25:
*l2p++ = CHMSIG;
*l2p++ = XY2INT (1, 0);
*l2p++ = 1;
break;
case 26:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
*l2p &= *tmpevl;
LWRCUT (*l2p++);
break;
case 27:
mchevl (&l1p, tmpevl);
*l2p = *tmpevl;
mchevl (&l1p, tmpevl);
*l2p |= *tmpevl;
LWRCUT (*l2p++);
break;
case 28:
mchevl (&l1p, tmpevl);
*l2p = ~(*tmpevl);
LWRCUT (*l2p++);
break;
case 29:
*l2p++ = URBFCL;
break;
case 30:
if (eofflg != 2 || *keybuf != EOLTTR)
*l2p++ = UNUSDC;
break;
case 31:
{
letter code, basenum;
mchevl (&l1p, tmpevl);
code = *tmpevl;
mchevl (&l1p, tmpevl);
if ((basenum = *tmpevl) <= 1 || BASEMX < basenum)
basenum = 10;
to_digit (code, basenum, &l2p);
}
break;
case 32:
mchevl (&l1p, tmpevl);
dakuadd (*tmpevl, &l2p);
break; /* 特殊 */
case 33:
mchevl (&l1p, tmpevl);
handakuadd (*tmpevl, &l2p);
break; /* 特殊 */
case 34:
mchevl (&l1p, tmpevl);
*l2p++ = ltov (*tmpevl);
break;
case 35:
*l2p++ = ERRCOD;
break;
/* case 36: omitted */
case 37:
*l2p++ = CHMSIG;
*l2p++ = LWRMSK (*l1p++);
*l2p++ = *l1p++;
break;
case 38:
*l2p++ = CHMSIG;
*l2p++ = XY2INT (2, LWRMSK (*l1p++));
*l2p++ = *l1p++;
break;
case 39:
*l2p++ = CHMSIG;
*l2p++ = XY2INT (3, LWRMSK (*l1p++));
*l2p++ = *l1p++;
break;
case 40:
{
letter modnum;
modnum = LWRMSK (*l1p++);
if (modesw[modnum].curmode != *l1p++)
*l2p++ = UNUSDC;
break;
}
case 41:
{
letter modnum;
modnum = LWRMSK (*l1p++);
if (modesw[modnum].curmode == *l1p++)
*l2p++ = UNUSDC;
break;
}
case 42:
{
letter modnum;
modnum = LWRMSK (*l1p++);
if (modesw[modnum].curmode >= *l1p++)
*l2p++ = UNUSDC;
break;
}
case 43:
{
letter modnum;
modnum = LWRMSK (*l1p++);
if (modesw[modnum].curmode <= *l1p++)
*l2p++ = UNUSDC;
break;
}
case 44:
mchevl (&l1p, tmpevl);
*l2p++ = SENDCH;
*l2p++ = *tmpevl;
break;
default: /* case 0及び上記以外 */ ;
BUGreport (7);
}
}
*l2p = EOLTTR;
*l1pp = l1p;
}
/** num番目の変数の変域が文字 l を含むかどうかを返す */
static int
hen_ikisrc (num, l)
int num;
letter l;
{
letter *defptr;
defptr = hyo_n[hyonum].hensudef[num];
if (*defptr == VARRNG)
{
for (defptr++; *defptr != EOLTTR;)
if (*defptr++ <= l && l <= *defptr++)
return (1);
return (0);
}
for (; *defptr != EOLTTR; defptr++)
{
if (l == *defptr)
return (1);
}
return (0);
}
/** 変換のメインルーチン。本処理を行うが、ついでに後処理もやっている。
ちなみに前処理は、romkan_getcの下位関数romkan_henkanの中で、
この関数を呼ぶ前にやっている。
この関数は、romkan_nextから一文字来る度に呼ばれる。呼び出された直後は
outputは空文字列、disoutには入力コード一文字が入っている。
この関数で得られる文字の列が、romkan_henkanに渡り、
romkan_getcは、それを文字ごとに分解して返す。
(error)でエラーが引き起こされた場合は0を返し、正常終了時は1を返す */
int
match ()
{
int henkanflg = 0, okcode = 0, chm_exist;
letter *p;
letter urabufcreate[KBFSIZ], orgkeybuf[KBFSIZ], kbftail[KBFSIZ];
letter *urabufjunbi, *outcutptr, *dis_end;
if (*keybuf == EOLTTR)
{
*urabuf = EOLTTR;
return (1);
}
ltrcpy (urabufjunbi = orgkeybuf, keybuf);
outcutptr = rk_output;
while ((okcode = henkan_ok ()) > 0)
{
henkanflg = 1;
codeout_chg ();
ltrcat (rk_output, codeout);
ltrcpy (kbftail, keybuf + codein_len);
ltrcat (ltrcpy (keybuf, remainkbf), kbftail);
if (okcode == 2)
{
ltrcpy (urabufjunbi = urabufcreate, keybuf);
totail (outcutptr);
}
}
if (okcode == 0)
{
ltr1cut (ltrcpy (keybuf, orgkeybuf));
ltr_to_ltrseq (disout, NISEBP);
*rk_output = EOLTTR;
return (0);
}
if (henkanflg)
{
ltrcpy (urabuf, urabufjunbi);
set_rubout (disout, ltrlen (orgkeybuf) - 1, nisedl);
dis_end = disout;
totail (dis_end);
ltrcpy (dis_end, rk_output);
/* モードチェンジを直ちに知らせるため CHMSIGを出力
(flags中のRK_CHMOUTが立ってないと、あとで一文字ずつに
分解する時点で、CHMSIGをカット)。
但し、rk_outputからは、CHMSIGを抜く。
また、CHMSIGは末尾に1回しか出力しない
(2回以上あっても、1回にまとめて、末尾に置く)。 */
for (chm_exist = 0, p = rk_output;; p++)
{
while (*p == CHMSIG)
{
chm_exist = 1;
if (ltrcpy (p, p + 1) < outcutptr)
outcutptr--;
}
if (*p == EOLTTR)
break;
}
if (chm_exist)
{
/* CHMSIGを1つにまとめたものをdis_endにつなげ直す。
このif文をカットすれば、CHMSIGのとりまとめはやらない */
ltr1cat (ltrcpy (dis_end, rk_output), CHMSIG);
}
bitup_ltrcat (disout, keybuf);
lastoutlen = ltrlen (outcutptr);
lastkbflen = ltrlen (keybuf);
}
return (1);
}
/** LTREOFが入ったときに、何か出すように指定されているか調べて、
codeoutをその結果の文字列(指定がなかったら当然空)にポイントする。
超急ごしらえで、特殊コード等は一切無視する。*/
static void
add_at_eof ()
{
register struct dat *datptr;
register int i;
int hyoseq;
letter evlrsl[RSLMAX], *p;
for (hyoseq = 0; (hyonum = usehyo[hyoseq]) != -1; hyoseq++)
{
for (i = 0, datptr = hyo_n[hyonum].data; NULL != (p = datptr[i].code[0]); i++)
{
henmatch[0].ltrmch = EOLTTR;
while (*p != EOLTTR)
{
switch (SHUBET (*p))
{
case 0: /* 文字 */
case 1: /* 変数 */
/* これらがある場合は、NULLとは
マッチし得ない。 */
goto Pass;
case 2: /* 式 */
mchevl (&p, evlrsl);
if (*evlrsl != EOLTTR)
goto Pass;
/* 入力コード部に、評価すると
空文字列になるものが、他にはない
ことが前提。 */
}
}
ltrevlcpy (codeout = p = evalbuf[0][0], datptr[i].code[1]);
while (*p != EOLTTR)
{
if (isSPCL (*p))
ltrcpy (p, p + 1);
else
p++;
}
codeout_chg ();
return;
Pass:;
}
}
codeout = nil;
}
/** 一回マッチを試みる。返値は、マッチして確定した場合1(モードチェンジが
混じっている場合は2)、マッチしたが未確定の時-1、マッチしなかったら0。
実行中は、変数 l に、それまでに一致した長さの最高記録を入れており、
より長く一致するものが見つかるごとに、これを更新する。lの値は、マッチ
していても0になることもある。p_eq() 及び prefixp() の注釈文を参照。*/
static int
henkan_ok ()
{
register struct dat *datptr;
register int i, k;
int l, j, hyoseq;
char urabuf_clrf; /* モードチェンジなどで、urabufをクリアする必要が
生じた場合はこれが立ち、その結果、henkan_ok()
が1を返すべきところで2を返す。それを見て、
match()がurabufなどの調整をする。 */
register letter *p;
if (*keybuf == EOLTTR)
return (-1);
for (l = -1, hyoseq = 0; (hyonum = usehyo[hyoseq]) != -1; hyoseq++)
{
for (i = 0, datptr = hyo_n[hyonum].data; NULL != (p = datptr[i].code[0]); i++)
{
henmatch[0].ltrmch = EOLTTR;
switch (k = prefixp (keybuf, p))
{
case -2:
break;
case -1:
if (eofflg != 0)
break;
/* eofflgが立っていたら、未確定の可能性は
捨てる。 */
return (-1);
default:
if (k > l)
{
ebf_sw = !ebf_sw;
for (j = 1; j <= 2; j++)
{
ltrevlcpy (evalbuf[ebf_sw][j - 1], datptr[i].code[j]);
}
l = k;
}
}
}
}
if (l >= 0)
{
codein_len = l;
codeout = evalbuf[ebf_sw][0];
remainkbf = evalbuf[ebf_sw][1];
for (urabuf_clrf = 0, p = codeout; *p != EOLTTR;)
{
switch (*p)
{
case CHMSIG:
p++;
/* codeoutの1バイト目に関数のスイッチとモード番号、
2バイト目にモードの新状態が入ってる */
switch (SHUBET (*p))
{
case 0:
chgmod ((int) *p, (modetyp) * (p + 1));
break;
case 1:
allchgmod ((modetyp) * (p + 1));
break;
case 2:
incmod ((int) LWRMSK (*p), (modetyp) * (p + 1));
break;
case 3:
decmod ((int) LWRMSK (*p), (modetyp) * (p + 1));
break;
}
ltrcpy (p, p + 2);
/* CHMSIGだけ残して1,2バイト目cut */
urabuf_clrf = 1;
break;
case URBFCL:
/* urabufのクリアを明示的に指定する */
urabuf_clrf = 1;
ltrcpy (p, p + 1);
break;
default:
p++;
}
}
if (*codeout == ERRCOD)
{
if (eofflg == 0)
{
rk_errstat = 1;
return (0);
}
/* (error)であって、しかもeofflgが立ってたら、keybuf
の末尾まで、そのまま出す。 */
codein_len = ltrlen (keybuf);
codeout = ltrcpy (evalbuf[ebf_sw][0], keybuf);
remainkbf = nil;
rk_errstat = 2;
return (1);
}
/* (error)は単独でしか書けないので、エラー検出はこれで十分。 */
if (*codeout == REASIG)
{
*codeout = (rk_rst () != 0 ? EOLTTR : CHMSIG);
/* 再readでエラったらモードチェンジの通知はしない */
urabuf_clrf = 1;
}
/* 表の再read。但し、これが起こったことを外に知らせるのはCHMSIGで
このコードそのものは外へ出ない。(restart)は、(error)同様、
単独でしか書けないので、検出はこれで十分。 */
return (urabuf_clrf ? 2 : 1);
}
/* 表に現れていないコードはそのまま返す */
codein_len = 1;
*(codeout = oneletter) = *keybuf;
remainkbf = nil;
return (1);
}
/* rk_rst内で使うマクロ */
#define taihi(X, Y, N) {for(i = 0; i < N; i++) X[i] = Y[i];}
#define recov(X, Y, N) taihi(Y, X, N)
/** 表の動的再読み込みをする。現在の内部表現を全て退避し、前と同じ
ディレクトリ(に、現在のところ限定)から表を読み込む。もし、
読み込み中にエラーを検出すれば、もとの内部データを復活し非0を返す。*/
static int
rk_rst ()
{
register int i;
int j;
letter memoryt[SIZALL];
struct dat datat[LINALL];
struct hyo hyo_nt[HYOMAX];
letter *hensudefhyot[VARTOT];
struct modestat modeswt[MODMAX];
char hyoshut[HYOMAX];
char *modmeibgnt[MODMAX], modmeimem_t[MODMEI];
char *dspnambgnt[DMDMAX], dspcod_t[DMDCHR];
int usemaehyot[HYOMAX], usehyot[HYOMAX], useatohyot[HYOMAX];
int naibu_t[NAIBMX];
char *dspmodt[2][2];
taihi (memoryt, memory, SIZALL);
taihi (datat, data, LINALL);
taihi (hyo_nt, hyo_n, HYOMAX);
taihi (hensudefhyot, hensudefhyo, VARTOT);
taihi (modeswt, modesw, MODMAX);
taihi (hyoshut, hyoshu, HYOMAX);
taihi (modmeibgnt, modmeibgn, MODMAX);
taihi (modmeimem_t, modmeimem_, MODMEI);
taihi (dspnambgnt, dspnambgn, DMDMAX);
taihi (dspcod_t, dspcod_, DMDCHR);
taihi (usemaehyot, usemaehyo, HYOMAX);
taihi (usehyot, usehyo, HYOMAX);
taihi (useatohyot, useatohyo, HYOMAX);
taihi (naibu_t, naibu_, NAIBMX);
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
dspmodt[i][j] = dspmod[i][j];
if (0 == romkan_init3 (prv_modfnm, delchr, nisedl, delchr2, keyin_method, bytcnt_method, kbytcnt_method, 1, flags & ~RK_VERBOS))
return (0); /* 正常終了 */
recov (memoryt, memory, SIZALL);
recov (datat, data, LINALL);
recov (hyo_nt, hyo_n, HYOMAX);
recov (hensudefhyot, hensudefhyo, VARTOT);
recov (modeswt, modesw, MODMAX);
recov (hyoshut, hyoshu, HYOMAX);
recov (modmeibgnt, modmeibgn, MODMAX);
recov (modmeimem_t, modmeimem_, MODMEI);
recov (dspnambgnt, dspnambgn, DMDMAX);
recov (dspcod_t, dspcod_, DMDCHR);
recov (usemaehyot, usemaehyo, HYOMAX);
recov (usehyot, usehyo, HYOMAX);
recov (useatohyot, useatohyo, HYOMAX);
recov (naibu_t, naibu_, NAIBMX);
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
dspmod[i][j] = dspmodt[i][j];
return (1);
}
/** lp2から評価して得た文字列をlp1にコピー */
static void
ltrevlcpy (lp1, lp2)
letter *lp1, *lp2;
{
while (*lp2 != EOLTTR)
{
mchevl (&lp2, lp1);
totail (lp1);
}
*lp1 = EOLTTR;
}
static void
set_rubout (lp, n, del)
/** lpに 「del」n個の列をセットする。ここに del は 'delchr'か'nisedl' */
letter *lp, del;
int n;
{
for (; n; n--)
*lp++ = del;
*lp = EOLTTR;
}
/** これが実行されたらバグ。但し実行はそのまま続く */
void
BUGreport (n)
int n;
{
fprintf (stderr, "\r\nromkan-Bug%d!!\r\n", n);
}
/** 前処理(mae_in→mae_out)又は後処理(ato_in→ato_out)を行う。*/
static void
maeato_henkan (in, outp, m_a_hyo)
letter in; /* 入力の一文字 */
letter *outp; /* 出力はここに入る */
int *m_a_hyo; /* どの前・後処理表が選択されているかの情報 */
{
struct dat *datptr;
int i, hyoseq;
letter *curdat;
if (isSPCL (in))
{
/* LTREOFやCHMSIGが来うるので、特殊コードはそのまま返すように
細工しておかないといけない。 */
ltr_to_ltrseq (outp, in);
return;
}
for (hyoseq = 0; (hyonum = m_a_hyo[hyoseq]) != -1; hyoseq++)
{
for (i = 0, datptr = hyo_n[hyonum].data; NULL != (curdat = datptr[i].code[0]); i++)
{
henmatch[0].ltrmch = EOLTTR;
if (!l_eq (curdat, in))
continue;
ltrevlcpy (outp, datptr[i].code[1]);
return;
}
}
ltr_to_ltrseq (outp, in);
}
/** 後処理 */
static void
codeout_chg ()
{
letter *saishu_outp;
*(saishu_outp = saishu_out) = EOLTTR;
for (; *codeout != EOLTTR; codeout++)
{
maeato_henkan (*codeout, saishu_outp, useatohyo);
totail (saishu_outp);
}
codeout = saishu_out;
}
/** 一文字プッシュ・バック */
/* *INDENT-OFF* */
letter
romkan_ungetc (l)
letter l;
/* *INDENT-ON* */
{
return (ungetc_buf = l);
}
/** romkan_nextに対し一文字プッシュ・バック */
/* *INDENT-OFF* */
letter
romkan_unnext (l)
letter l;
/* *INDENT-ON* */
{
return (unnext_buf = l);
}
/** deleteとして使うキャラクタの設定(偽deleteも)。これを実行後は
romkan_clearを実行しておかないと混乱のもとになります。*/
/* (廃止)
void
romkan_setdel(delchr_, nisedl_)
letter delchr_, nisedl_;
{
delchr = delchr_;
nisedl = nisedl_;
}
*/
syntax highlighted by Code2HTML, v. 0.9.1