Mercurial > freewnn
diff Wnn/romkan/rk_modread.c @ 0:bbc77ca4def5
initial import
author | Yoshiki Yazawa <yaz@cc.rim.or.jp> |
---|---|
date | Thu, 13 Dec 2007 04:30:14 +0900 |
parents | |
children | a7ccf412ba02 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Wnn/romkan/rk_modread.c Thu Dec 13 04:30:14 2007 +0900 @@ -0,0 +1,1353 @@ +/* + * $Id: rk_modread.c,v 1.9 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, 2002 + * + * 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_modread.c + 88. 6.16 改 正 + + モード定義表の読み込みを担当するプログラム。 +***********************************************************************/ +/* Version 3.0 */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if STDC_HEADERS +# include <stdlib.h> +# include <string.h> +#elif HAVE_STRINGS_H +# include <strings.h> +#endif /* STDC_HEADERS */ +#include <sys/types.h> +#include <pwd.h> +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "rk_header.h" +#include "rk_extvars.h" +#ifdef WNNDEFAULT +# include "wnn_config.h" + /* マクロLIBDIRの定義(のためだけ)。コンパイル時は、ヘッダファイルの + サーチパスに、Wnnのインクルードファイルのありかを設定しておくこと。 */ +#endif + +#define Terminator 0 /* intの列(naibu[])の終止コード */ + +extern char *chrcat (), *strend (), *ename (); +extern void romkan_clear (); +char *modhyopath; + +static void cond_evl (), mystrcpy (), rd_bcksla (), rd_ctrl (), hyouse (), look_choose (); +void choosehyo (); +static int mystrcmp (), read1tm (), mod_evl (), fnmsrc_tourk (), dspnamsrc_tourk (), +scan1tm (), modsrc_tourk (), chk_get_int (), pathsrc_tourk (), modnamchk (), ctov (), look_cond (), evlcond (), chkchar_getc (); +static char codeeval (); +extern void ERRMOD (), ERMOPN (), BUGreport (); +extern int filnamchk (); + +struct kwdpair +{ + /* キーワードとその内部表現の対応を与える構造体。内部表現を + 持たないものに対しては0が与えられている。 */ + char *name; + int code; +} +modfn[] = +{ + "defmode", 0, + "if", XY2INT (2, 0), "when", XY2INT (2, 1), "path", 0, "search", 0, "on_dispmode", XY2INT (5, 0), "off_dispmode", XY2INT (5, 1), "on_unchg", XY2INT (6, 0), "off_unchg", XY2INT (6, 1), NULL}; /* 下を見よ キーワード定義表はまだあと二つあるのだ */ + +struct kwdpair modcond[] = { + "not", XY2INT (3, 0), + "and", XY2INT (3, 1), + "or", XY2INT (3, 2), + "true", XY2INT (3, 3), + "false", XY2INT (3, 4), + "=", XY2INT (3, 5), + "!=", XY2INT (3, 6), + "<", XY2INT (3, 7), + ">", XY2INT (3, 8), + NULL +}; +int condarg[] = { 1, 2, 2, 0, 0, 2, 2, 2, 2 }; /* 条件判断関数の引数個数 */ + +struct kwdpair swstat[] = { + "on", 0, + "off", 0, + NULL +}; + /* 1×2^24代はモード名、4×2^24代はモード表示文字列、7×2^24代はモード状態定 + 数の内部表現に使っている */ + + /** キーワード(if, andなど)が正当なものかチェックし、その番号を返す */ +static int +kwdsrc (hyo, wd) + struct kwdpair *hyo; /* どのキーワード表を使うか */ + char *wd; /* チェックされるキーワード */ +{ + int i; + + for (i = 0; hyo[i].name != NULL; i++) + if (!mystrcmp (hyo[i].name, wd)) + return (i); + ERRMOD (9); + /*NOTREACHED*/ +} + + /** モード表の読み込み */ +void +readmode (modfname) + char *modfname; /* モード表の名 */ +{ + char buf[MDHMAX], *bufp; +#ifdef RKMODPATH + char *genv, *pathenv, *pathp; +#endif + + mcurread = buf; /* エラー処理用 */ + +#ifdef RKMODPATH + if (!fixednamep (modfname) && NULL != (pathenv = genv = getenv (RKMODPATH)) && *genv != '\0') + { + /* PATHに少なくとも一つのサーチパスがある場合 */ + for (;;) + { + /* サーチパスの各々を入れる領域には、pathmeimemを借用 + している。後で、サーチパスの先頭に、モード表のある + ディレクトリを設定するので、その便宜のためもある */ + for (pathp = pathmeimem; *genv != ':' && *genv; genv++) + *pathp++ = *genv; + *pathp = '\0'; + + if (*(strend (pathmeimem)) != KUGIRI) + *pathp++ = KUGIRI; + /* pathの区切りはDG(MV)であっても'/' */ + + strcpy (pathp, modfname); + if (NULL != (modefile = fopen (pathmeimem, "r"))) + { + /* Now Mode-hyo found */ + if (flags & RK_VERBOS) + fprintf (stderr, "romkan: using Mode-hyo %s ...\r\n", pathmeimem); + curdir = pathmeimem; /* この時点ではファイル名 + 込みだが、あとでパス名だけになる */ + curfnm = ename (modfname); + break; + } + + if (*genv != ':') + { /* Mode-hyo not found */ + if (flags & RK_VERBOS) + { + fprintf (stderr, "no %s in ", modfname); + for (genv = pathenv; *genv; genv++) + { + fputc ((*genv == ':' ? ' ' : *genv), stderr); + } + fprintf (stderr, ".\n"); + } + ERMOPN (0); + } + else + genv++; /* coutinues searching Mode-hyo */ + } + } + else +#endif + { + if (NULL == (modefile = fopen (modfname, "r"))) + ERMOPN (0); + if (flags & RK_VERBOS) + fprintf (stderr, "romkan: using Mode-hyo %s ...\r\n", modfname); + strcpy (pathmeimem, modfname); + } + + /* サーチパスの先頭に、モード表のあるディレクトリを設定している。 */ + *(ename (pathmeimem)) = '\0'; + modhyopath = *pathmeiptr++ = pathmeimem; + *pathmeiptr = NULL; + strtail (pathmeimem); + *(pathareaorg = ++pathmeimem) = '\0'; + /* pathareaorgは、pathmeimem_[]のうちモード表のあるディレクトリ名を + 格納した残りの部分の先頭を指す。 */ + + while (bufp = buf, read1tm (&bufp, 0)) + mod_evl (buf); + fclose (modefile); +} + + /** 固定された(サーチパスを見る必要のない)ファイル名に対しては非0を + 返す。現在のところ、 / ./ ../ のどれかで始まるものとしているが(作者の + 独断)、適当に変えてよい。strchr(s,'/')!=NULL とする方が良いかも */ +int +fixednamep (s) + char *s; +{ + return (!strncmp ("/", s, 1) || !strncmp ("./", s, 2) || !strncmp ("../", s, 3)); +} + + /** モード表の一かたまり(リスト、ファイル名、モード表示文字列)を + 解釈する。返す値は、defmode,search及びpathの時0、それ以外なら1。*/ +static int +mod_evl (s) + char *s; /* モード表の内部表現の列へのポインタ */ +{ + char md1[MDT1LN], *bgn, *end; + int num, retval = 1; + + if (*s != '(') + { + if (*s != '"') + { + num = fnmsrc_tourk (s); + *naibu++ = XY2INT (4, num); + } + else + { + s++; + if (*(end = strend (s)) != '"') + ERRMOD (10); + *end = '\0'; + num = dspnamsrc_tourk (s); + *naibu++ = XY2INT (5, 0); + *naibu++ = num; + } + } + else + { + s++; + scan1tm (&s, md1, 1); + switch (num = kwdsrc (modfn, md1)) + { + case 0: /* defmode */ + retval = 0; + scan1tm (&s, md1, 1); /* modename */ + num = modsrc_tourk (md1, 0); + if (scan1tm (&s, md1, 0) == 0) + { + /* 初期on-offについて何も書いてない時の + defaultはoff */ + modesw[num].moderng = 2; + modesw[num].curmode = 0; + break; + } + + if (*md1 == '(') + { + char tmp[MDT1LN], *s; + unsigned int i, j; + + s = md1 + 1; + + scan1tm (&s, tmp, 1); + if (chk_get_int (tmp, &i, 0) != 0) + ERRMOD (8); + modesw[num].moderng = i; + scan1tm (&s, tmp, 1); + if (chk_get_int (tmp, &j, modesw[num].moderng) != 0) + ERRMOD (8); + modesw[num].curmode = j; + if ( +#ifdef ModeNotInt + modesw[num].moderng != i || modesw[num].curmode != j || +#endif + i == 1 || (i != 0 && j >= i)) + { + ERRMOD (8); + } + scan1tm (&s, tmp, 2); + } + else + { + switch (kwdsrc (swstat, md1)) + { + case 0: + modesw[num].curmode = 1; + break; + case 1: + modesw[num].curmode = 0; + break; + } + modesw[num].moderng = 2; + } + scan1tm (&s, md1, 2); /* あればerr */ + break; + case 1: /* if */ + case 2: /* when */ + *naibu++ = modfn[num].code; + scan1tm (&s, md1, 1); /* condition */ + cond_evl (md1); + while (scan1tm (&s, md1, 0)) + { + if (mod_evl (md1) == 0) + ERRMOD (17); + } + *naibu++ = Terminator; + break; + case 3: /* path */ + *(pathmeimem = pathareaorg) = '\0'; + *(pathmeiptr = pathmeiorg) = NULL; + case 4: /* search */ + retval = 0; + if (hyomeiptr != hyomeiorg) + ERRMOD (11); + /* サーチパスの指定はファイル名の出現より + 先行しなければならないとしておく。 */ + + while (scan1tm (&s, md1, 0)) + { /* find pathname */ + pathsrc_tourk (md1); + } + break; + case 5: /* on_dispmode */ + case 6: /* off_dispmode */ + *naibu++ = modfn[num].code; + scan1tm (&s, md1, 1); /* dispmode string */ + + if (*(bgn = md1) != '"') + ERRMOD (12); + bgn++; + if (*(end = strend (bgn)) != '"') + ERRMOD (10); + *end = '\0'; + *naibu++ = dspnamsrc_tourk (bgn); + scan1tm (&s, md1, 2); /* あればerr */ + break; + case 7: /* on_unchg */ + case 8: /* off_unchg */ + *naibu++ = modfn[num].code; + scan1tm (&s, md1, 2); /* あればerr */ + break; + } + + } + *naibu = Terminator; + return (retval); +} + + /** 条件式(モード名 又はnot,andなどの式)一つを解釈 */ +static void +cond_evl (cod) + char *cod; /* 条件式の内部表現の列へのポインタ */ +{ + char md1[MDT1LN]; + unsigned int num; + int i; + + if (is_digit (*cod) || *cod == '-') + { + *naibu++ = XY2INT (7, 0); + if (0 != chk_get_int (cod, &num, 0)) + ERRMOD (4); + *naibu++ = num; + } + else if (*cod != '(') + { + num = modsrc_tourk (cod, 1); + *naibu++ = XY2INT (1, num); + } + else + { + cod++; + scan1tm (&cod, md1, 1); /* not;and;or */ + num = kwdsrc (modcond, md1); + *naibu++ = XY2INT (3, num); + for (i = condarg[num]; i; i--) + { + scan1tm (&cod, md1, 0); + cond_evl (md1); + } + scan1tm (&cod, md1, 2); + } + *naibu = Terminator; +} + + /** sで指定されたファイル名が既登録か探し、なければ登録。但し、既登録か + どうかのチェックは厳密ではないが(例えば、同じファイルでも、 + パス名付きと無しとでは、同じと見ない)、ファイル名が既登録かどうか + チェックするのは、メモリ節約のために同じ表を読み込むのを防ぐため + だけなので、それ以外には別に困る点はない。*/ +static int +fnmsrc_tourk (s) + char *s; +{ + int n; + + for (n = 0; hyomeiorg[n] != NULL; n++) + if (!mystrcmp (hyomeiorg[n], s)) + return (n); + + if (hyomeiorg + n != hyomeiptr) + BUGreport (101); + + *hyomeiptr++ = hyomeimem; + *hyomeiptr = NULL; + mystrcpy (hyomeimem, s); + if (!(hyoshu[n] = filnamchk (hyomeimem))) + ERRMOD (3); + strtail (hyomeimem); + *++hyomeimem = '\0'; + return (n); +} + + /** sで指定されたサーチパス名が既登録か探し、なければ登録。但し、fnmsrc_ + tourk()同様、既登録かどうかのチェックは厳密ではないが問題ない。*/ +static int +pathsrc_tourk (s) + char *s; +{ + int n; + char fnm_addsla[MDT1LN]; + + mystrcpy (fnm_addsla, s); + if (!(*fnm_addsla == '\0' || *(strend (fnm_addsla)) == KUGIRI)) + chrcat (fnm_addsla, KUGIRI); + /* パス名が'/'で終わってなければ、それを付加する。 */ + + for (n = 0; pathmeiorg[n] != NULL; n++) + if (!strcmp (pathmeiorg[n], fnm_addsla)) + return (n); + + if (pathmeiorg + n != pathmeiptr) + BUGreport (104); + + *pathmeiptr++ = pathmeimem; + *pathmeiptr = NULL; + strcpy (pathmeimem, fnm_addsla); + + strtail (pathmeimem); + + *++pathmeimem = '\0'; + return (n); +} + + /** sで指定されたモード表示文字列が既登録か探し、なければ登録 */ +static int +dspnamsrc_tourk (s) + char *s; +{ + int n; + + for (n = 0; dspnambgn[n] != NULL; n++) + if (!mystrcmp (dspnambgn[n], s)) + return (n); + + if (dspnambgn + n != dspnamptr) + BUGreport (103); + + *dspnamptr++ = dspcod; + *dspnamptr = NULL; + mystrcpy (dspcod, s); + strtail (dspcod); + *++dspcod = '\0'; + return (n); +} + + /** 登録されているモード名の中から、sで指定されたモード名を探す。*np に + モード番号が入る。見つからないと現モード名の総数が入る。その場合 + 返値は0。*/ +static int +modnam_src (s, np) + char *s; + int *np; +{ + for (*np = 0; modmeibgn[*np] != NULL; (*np)++) + if (!mystrcmp (modmeibgn[*np], s)) + return (1); + return (0); +} + + /** sで指定されたモード名を探し、なければ登録。但し、flgが非0なら、 + 見つからなければエラー */ +static int +modsrc_tourk (s, flg) + char *s; + int flg; +{ + int n; + + if (modnam_src (s, &n)) + return (n); + + if (flg) + ERRMOD (5); + + if (modmeibgn + n != modmeiptr) + BUGreport (102); + + *modmeiptr++ = modmeimem; + *modmeiptr = NULL; + mystrcpy (modmeimem, s); + if (!modnamchk (modmeimem)) + ERRMOD (4); + strtail (modmeimem); + *++modmeimem = '\0'; + return (n); +} + + /** ファイルから一文字読む(空白文字は飛ばす)。読んだ文字がEOFなら0を返す */ +static char +fspcpass () +{ + register int c; + + while (EOF != (c = chkchar_getc (modefile)) && is_nulsp (c)); + return (c == EOF ? '\0' : c); +} + + /** モード表には空白文字以外のコントロール文字は生では混じらないものと + する。混じっていた場合はチェックしつつ、getcを行う。*/ +static int +chkchar_getc (f) + FILE *f; +{ + register int c; + + c = getc (f); + if (is_cntrl (c) && !isspace (c)) + { + sprintf (mcurread, "\\%03o", c); + ERRMOD (16); + } + return (c); +} + +static int +modehyo_getc () +{ + return (chkchar_getc (modefile)); +} + +static int +modehyo_ungetc (c) + register int c; +{ + return (ungetc (c, modefile)); +} + + /** socの名のユーザのログイン・ディレクトリ名をdestに入れ、*destにその + 末尾を指させる。但しsocが空列なら自分のログイン・ディレクトリ名、 + NULLなら自分のホーム・ディレクトリ名。いずれの場合も、不成功時は + 何もしない。返値は、不成功時-1(getenv("HOME")失敗時だけは-2)。*/ +static int +get_hmdir (dest, soc) + char **dest, *soc; +{ + struct passwd *usr; + char *p; + + if (soc == NULL) + { + if (NULL == (p = getenv ("HOME"))) + return (-2); + } + else + { + if (NULL == (usr = (*soc ? getpwnam (soc) : getpwuid (getuid ())))) + return (-1); + p = usr->pw_dir; + } + strcpy (*dest, p); + strtail (*dest); + return (0); +} + + /** モード表・対応表中の、ファイル名の部分の読み込み。先頭が @ 又は ~ の + 時は、特殊処理を行う。引数は、一字読み込み・一字戻し・文字列取り出しの + 関数と、結果を入れるエリアの番地へのポインタ、次に読まれる文字を入れる + ポインタ。返値は、正常終了時0、@HOMEでホーム・ディレクトリが取れない時 + 1、@のあとに変なものが来たら2、~で自分のホーム・ディレクトリが取れない + 時3、~のあとに存在しないユーザ名が来たら4。*/ +int +readfnm (readchar_func, unreadc_func, readstr_func, areap, lastcptr) + register int (*readchar_func) (), (*unreadc_func) (), (*readstr_func) (); + char **areap; + int *lastcptr; +{ + char *head; + register int c; + + c = (*readchar_func) (); + if (c == '@') + { /* @HOME, @MODEDIR, @LIBDIR */ + *(*areap)++ = c; + head = *areap; + (*readstr_func) (areap, 1); + + if (mystrcmp ("HOME", head) == 0) + { + *areap = --head; + if (get_hmdir (areap, (char *) NULL) != 0) + { + *areap = head; + return (1); + } + } + else if (mystrcmp ("MODEDIR", head) == 0) + { + strcpy (*areap = --head, modhyopath); + if (KUGIRI == *(*areap = strend (*areap))) + **areap = '\0'; + } + else +#ifdef WNNDEFAULT + if (mystrcmp ("LIBDIR", head) == 0) + { + strcpy (*areap = --head, LIBDIR); + strtail (*areap); + } + else +#endif + { + *areap = --head; + return (2); + } + + } + else if (c == '~') + { /* ~user */ + int err; + + *(*areap)++ = c; + head = *areap; + (*readstr_func) (areap, 1); + + mystrcpy (head, head); + *areap = head - 1; + if ((err = get_hmdir (areap, (*head ? head : NULL))) != 0) + { + *areap = --head; + return (err == -2 ? 3 : 4); + } + + } + else + { + (*unreadc_func) (c); + } + + *lastcptr = (*readstr_func) (areap, 0); + return (0); +} + + /** モード表から一文字分取り出す作業を、空白・括弧のどれか + 又はEOFが来るまで続ける。flg & 01が非0なら、'/'が来ても + 終わる。返値は、次に読まれる文字。*/ +static int +rd_string (readfile, sptr, flg) + register FILE *readfile; + char **sptr; + int flg; +{ + int c; + + while (EOF != (c = chkchar_getc (readfile)) && !(is_nulsp (c) || c == '(' || c == ')') && !(flg & 01 && c == KUGIRI)) + { + switch (c) + { + case '\\': + rd_bcksla (readfile, sptr); + break; + case '^': + rd_ctrl (readfile, sptr); + break; + default: + *(*sptr)++ = c; + } + } + **sptr = '\0'; + return (ungetc (c, readfile)); +} + +static int +rd_str_from_modefile (sptr, flg) + char **sptr; + int flg; +{ + return (rd_string (modefile, sptr, flg)); +} + + + /** モード表からバックスラッシュ形式の一文字分を取り出し、'\(8進);' + の形に直す。但し、先頭の'\\'は既に読まれたあと。*/ +static void +rd_bcksla (readfile, sptr) + register FILE *readfile; + char **sptr; +{ + int c, code = 0, digflg = 0; + + switch (c = chkchar_getc (readfile)) + { + case 'n': + code = '\n'; + digflg = 1; + break; + case 't': + code = '\t'; + digflg = 1; + break; + case 'b': + code = '\b'; + digflg = 1; + break; + case 'r': + code = '\r'; + digflg = 1; + break; + case 'f': + code = '\f'; + digflg = 1; + break; + case 'e': + case 'E': + code = ESCCHR; + digflg = 1; + break; + case 'o': + while (c = chkchar_getc (readfile), is_octal (c)) + { + code <<= 3; + code += ctov (c); + digflg = 1; + } + if (c != ';') + ungetc (c, readfile); + break; + case 'd': + while (c = chkchar_getc (readfile), is_digit (c)) + { + code *= 10; + code += ctov (c); + digflg = 1; + } + if (c != ';') + ungetc (c, readfile); + break; + case 'x': + while (c = chkchar_getc (readfile), is_xdigit (c)) + { + code <<= 4; + code += ctov (c); + digflg = 1; + } + if (c != ';') + ungetc (c, readfile); + break; + default: + if (is_octal (c)) + { + digflg = 1; + code = ctov (c); + while (c = chkchar_getc (readfile), is_octal (c)) + { + code <<= 3; + code += ctov (c); + } + if (c != ';') + ungetc (c, readfile); + } + else + { + code = c; + digflg = 1; + } + } + + if (digflg == 0) + ERRMOD (7); + sprintf (*sptr, "\\%o;", code); + strtail (*sptr); +} + + /** モード表からコントロールコード形式の一文字分を取り出し、 + '\(8進);' の形に直す。但し、先頭の'^'は既に読まれたあと。*/ +static void +rd_ctrl (readfile, sptr) + register FILE *readfile; + char **sptr; +{ + int c; + + if (!(' ' <= (c = chkchar_getc (readfile)) && c < '\177')) + ERRMOD (7); + if (c == '?') + c = '\177'; + else + c &= 0x1f; + + sprintf (*sptr, "\\%o;", c); + strtail (*sptr); +} + + /** モード表の一かたまり(リスト、ファイル名、モード表示文字列)を + 切り出す。その際、特殊な表記('^','\'による)は、'\(8進);' の + 形に直す。flgが非0なら、EOFでエラーを起こし、')'で0を返す。*/ +static int +read1tm (sptr, flg) + char **sptr; /* モード表の内部表現の列へのポインタへのポインタ。 + rd_bcksla()、rd_ctrl()、codeeval()でも同様 */ + int flg; +{ + int c, err, retval = 1; + char *s; + + s = *sptr; + + while ((c = fspcpass ()) == ';') + { + /* 注釈文を検出したら、行末までとばして再試行。 */ + while ((c = chkchar_getc (modefile)) != '\n' && c != EOF); + } + + switch (c) + { + case '\0': /* EOFを表す */ + if (flg) + ERRMOD (0); + else + retval = 0; + break; + case ')': + if (flg) + retval = 0; + else + ERRMOD (1); + break; + case '(': + *s++ = c; + *s++ = ' '; + while (read1tm (&s, 1)) + *s++ = ' '; + *s++ = ')'; + break; + case '"': + *s++ = c; + while ((c = chkchar_getc (modefile)) != '"') + { + switch (c) + { + case EOF: + ERRMOD (0); + case '\\': + rd_bcksla (modefile, &s); + break; + case '^': + rd_ctrl (modefile, &s); + break; + default: + *s++ = c; + } + } + *s++ = '"'; + break; + default: + ungetc (c, modefile); + /* 先頭が @ 又は ~ の時は、特殊処理。 */ + err = readfnm (modehyo_getc, modehyo_ungetc, rd_str_from_modefile, &s, &c); + if (err) + { + mcurread = s; + switch (err) + { + case 1: + case 3: + ERRMOD (13); + case 2: + ERRMOD (14); + case 4: + ERRMOD (15); + } + } + + if (c == EOF && flg) + ERRMOD (0); + if (c == ')' && !flg) + ERRMOD (1); + } + + *s = '\0'; + *sptr = s; + return (retval); +} + + /** 8・10・16進コード用のキャラクタを実際のコードに直す。入力のチェックは + しない。*/ +static int +ctov (c) + char c; +{ + if (is_upper (c)) + return (c - 'A' + 10); + if (is_lower (c)) + return (c - 'a' + 10); + return (c - '0'); +} + + /** リストの中身のscanに専用。')'で0を返す。EOLは来ないはず。 + flg == 1 のとき、取り出しに失敗したらエラー。 + flg == 2 のとき、取り出しに成功したらエラー。 + 特殊なコード表記は既に全て '\(8進);' の形に直っている筈。*/ +static int +scan1tm (socp, dest, flg) + char **socp, *dest; + /* socpの指しているポインタが指している所から取り出してdestに入れる。 + その後、socpが指しているポインタを進める。 */ + int flg; +{ + char c; + int retval = 1; + + while (c = *(*socp)++, is_nulsp (c)) + if (c == '\0') + ERRMOD (6); + switch (c) + { + case ')': + retval = 0; + break; + case '(': + *dest++ = c; + *dest++ = ' '; + while (scan1tm (socp, dest, 0)) + { + strtail (dest); + *dest++ = ' '; + } + *dest++ = ')'; + break; + case '"': + *dest++ = c; + while ((c = *dest++ = *(*socp)++) != '"') + { + if (c == '\\') + { /* '\(8進);'の解釈 */ + while (c = *dest++ = *(*socp)++, is_octal (c)); + } + } + break; + default: + *dest++ = c; + while (!is_nulsp (**socp)) + *dest++ = *(*socp)++; + } + + *dest = '\0'; + if ((flg == 1 && retval == 0) || (flg == 2 && retval == 1)) + ERRMOD (6); + return (retval); +} + + /** モード名として正当かチェック。英数字からなっていればいい */ +static int +modnamchk (s) + char *s; +{ + if (is_digit (*s)) + return (0); + for (; *s; s++) + if (!is_alnum (*s) && *s != '_') + return (0); + return (1); +} + +#define modu1(a, b) ((b) ? ((a) % (b)) : (a)) +#define curmod(num) (modesw[num] . curmode) +#define modrng(num) (modesw[num] . moderng) + + /** num番目のモードをチェンジし、変換表を選択し直す。引数 mode の値が0なら + モードをoff、1ならonすることになる。なお、旧modeの値を返す。*/ +/* *INDENT-OFF* */ +modetyp +chgmod (num, mode) + int num; + modetyp mode; +/* *INDENT-ON* */ +{ + modetyp oldmod; + + oldmod = curmod (num); + curmod (num) = modu1 (mode, modrng (num)); + choosehyo (); + return (oldmod); +} + + /** 全モードをまとめて切り替える */ +void +allchgmod (mode) + modetyp mode; +{ + int i; + + for (i = 0; modmeibgn[i] != NULL; i++) + { + curmod (i) = modu1 (mode, modrng (i)); + } + choosehyo (); +} + + /** num番目のモードを指定した数だけインクリメントし、旧modeの値を返す。*/ +/* *INDENT-OFF* */ +modetyp +incmod (num, dmode) + int num; + modetyp dmode; +/* *INDENT-ON* */ +{ + modetyp oldmod, newmod; + + newmod = oldmod = curmod (num); + newmod += dmode; + if (oldmod > newmod) + newmod -= modrng (num); + return (chgmod (num, newmod)); +} + + /** num番目のモードを指定した数だけデクリメントし、旧modeの値を返す。都合 + により、incmodとは別に用意しなくてはならない。*/ +/* *INDENT-OFF* */ +modetyp +decmod (num, dmode) + int num; + modetyp dmode; +/* *INDENT-ON* */ +{ + modetyp oldmod, newmod; + + newmod = oldmod = curmod (num); + newmod -= dmode; + if (oldmod < newmod) + newmod += modrng (num); + return (chgmod (num, newmod)); +} + + /** nameの名のモードがなければ非0を返し、あればそのモード番号・及びその + 状態の最大値+1と現在の状態を取ってくる */ +int +romkan_getmode (name, nump, modep, moderngp) + char *name; + int *nump; + modetyp *modep, *moderngp; +{ + if (!modnam_src (name, nump)) + return (-1); + *modep = curmod (*nump); + *moderngp = modrng (*nump); + return (0); +} + + /** nameの名のモードがなければ非0を返し、あればその状態をセットして + 変換表を再選択の後、旧状態を取り込んで0を返す。*/ +int +romkan_setmode (name, modep) + char *name; + modetyp *modep; +{ + modetyp oldmode, moderng; + int modenum; + + if (romkan_getmode (name, &modenum, &oldmode, &moderng) != 0) + return (-1); + chgmod (modenum, *modep); + *modep = oldmode; + return (0); +} + + /** 変換表のクリア */ +void +romkan_reset () +{ + naibu_[0] = Terminator; + choosehyo (); + romkan_clear (); +} + + /** 変換対応表の選択を行う */ +void +choosehyo () +{ + int *naibup, i; + + naibup = naibu_; + usemaehyo[0] = usehyo[0] = useatohyo[0] = -1; + for (i = 0; i < 2; i++) + { + dspmod[1][i] = dspmod[0][i]; + dspmod[0][i] = NULL; + } + + look_choose (&naibup, 1); +} + + /** モード表の内部形式を順次見ていき、使用表の選択及びモード表示文字列の + 選択を行っていく。但しflgが0ならスキップするだけ */ +static void +look_choose (naibupp, flg) + int **naibupp; /* モード表の内部表現の列へのポインタへのポインタ。 + look_cond()、evlcond()でも同様 */ + int flg; +{ + int *naibup, naibu1, naibu2, branch, lcrsl; + + naibup = *naibupp; + + while ((naibu1 = *naibup++) != Terminator) + { + switch (SHUBET (naibu1)) + { + case 4: /* 表名 */ + if (flg) + hyouse (LWRMSK (naibu1)); + break; + case 2: /* 条件式 */ + branch = LWRMSK (naibu1); /* if;when */ + lcrsl = look_cond (&naibup, flg); + if (branch == 0 && lcrsl) + flg = 0; + break; + case 5: /* romkanがon・off時それぞれの + モード表示文字列 */ + naibu2 = *naibup++; + if (flg) + dspmod[0][LWRMSK (naibu1)] = dspnambgn[naibu2]; + break; + case 6: /* romkanがそれぞれon・off時のモード表示 + 文字列を前のままに */ + if (flg) + dspmod[0][LWRMSK (naibu1)] = dspmod[1][LWRMSK (naibu1)]; + break; + default: + BUGreport (6); + } + } + + *naibupp = naibup; +} + + /** *naibupp が、内部表現の列で条件式を表すところを指している筈なので、 + それを評価し、真ならその続きを解釈しにいく。偽なら読み飛ばす。 + 返値は、最初に評価した条件式の真偽値。*/ +static int +look_cond (naibupp, flg) + int **naibupp, flg; +{ + int *naibup, condrsl; + + naibup = *naibupp; + + condrsl = evlcond (&naibup); /* 必ず評価しないといけないため */ + flg = flg && condrsl; + look_choose (&naibup, flg); + + *naibupp = naibup; + return (flg); +} + + /** 条件式の真偽値の評価 返値は0か1とは限らんぞ */ +static int +evlcond (naibupp) + int **naibupp; +{ + int *naibup, naibu1, retval = -1, tmpval[ARGMAX], i, imax; + + naibup = *naibupp; + + naibu1 = *naibup++; + switch (SHUBET (naibu1)) + { + case 7: /* 数値 */ + retval = *naibup++; + break; + case 1: /* モード名 */ + retval = modesw[LWRMSK (naibu1)].curmode; + break; + case 3: /* andなど */ + imax = condarg[LWRMSK (naibu1)]; + for (i = 0; i < imax; i++) + tmpval[i] = evlcond (&naibup); + switch (LWRMSK (naibu1)) + { + /* 上から順にtrue,false,not,and,or */ + case 0: + retval = !tmpval[0]; + break; + case 1: + retval = tmpval[0] && tmpval[1]; + break; + case 2: + retval = tmpval[0] || tmpval[1]; + break; + case 3: + retval = 1; + break; + case 4: + retval = 0; + break; + case 5: + retval = (tmpval[0] == tmpval[1]); + break; + case 6: + retval = (tmpval[0] != tmpval[1]); + break; + case 7: + retval = ((unsigned int) tmpval[0] < (unsigned int) tmpval[1]); + break; + case 8: + retval = ((unsigned int) tmpval[0] > (unsigned int) tmpval[1]); + break; + } + break; + } + + *naibupp = naibup; + return (retval); +} + + /** num番目の表を、使用するものとして登録する。前・本・後処理の区別もする */ +static void +hyouse (num) + int num; +{ + int *ptr; + + switch (hyoshu[num]) + { + case 1: + ptr = usemaehyo; + break; + case 2: + ptr = usehyo; + break; + case 3: + ptr = useatohyo; + break; + default: + BUGreport (11); + return; + } + for (; *ptr != -1; ptr++) + if (*ptr == num) + return; + *ptr = num; + *++ptr = -1; +} + + /** strcmpと同等 但し、'\(8進);'も解釈する。*/ +static int +mystrcmp (s1, s2) + char *s1, *s2; +{ + char c1, c2; + + while ((c1 = codeeval (&s1)) == (c2 = codeeval (&s2))) + if (c1 == '\0') + return (0); + return (c1 > c2 ? 1 : -1); +} + + /** strcpyと同等 但し'\(8進);'も解釈する。s1 <= s2なら正常動作するはず */ +static void +mystrcpy (s1, s2) + char *s1, *s2; +{ + while (*s1++ = codeeval (&s2)); +} + + /** 一文字の解釈を行う。普通の文字はそのまま、'\(8進);'は実際のコードに + 直す。その後、文字列へのポインタを一文字分進めておく(少なくとも + 1バイト分進むことが保証されるはず)。*/ +static char +codeeval (sptr) + register char **sptr; +{ + register char c; + char code = 0; + + if ((c = *(*sptr)++) != '\\') + return (c); + while (c = *(*sptr)++, is_octal (c)) + { + code <<= 3; + code += ctov (c); + } + if (c != ';') + BUGreport (12); + return (code); +} + + /** romkanがon時のモード表示文字列を返す関数。無指定であってRK_DSPNILフラグが + 立っている時は空文字列を返す。*/ + +char * +romkan_dispmode () +{ + return (dspmod[0][0] == NULL && (flags & RK_DSPNIL) ? nulstr : dspmod[0][0]); +} + + /** romkanがoff時のモード表示文字列を返す関数。無指定であってRK_DSPNILフラグ + が立っている時は空文字列を返す。*/ +char * +romkan_offmode () +{ + return (dspmod[0][1] == NULL && (flags & RK_DSPNIL) ? nulstr : dspmod[0][1]); +} + + /** 文字列が10進整数ならその解釈をし、そうでなければ非0を返す */ +static int +chk_get_int (p, ip, range) + char *p; + unsigned int *ip; + modetyp range; +{ + int sgn = 1; + modetyp out; + + if (*p == '-') + { + p++; + sgn = -1; + } + for (out = 0; *p; p++) + { + if (!is_digit (*p)) + return (-1); + out *= 10; + out += ctov (*p); + } + if (range != 0) + out %= range; + if (sgn == -1 && out != 0) + out = range - out; + *ip = out; + return (0); +}