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);
+}