/*
 * $Id: rk_modread.c,v 1.6.2.1 1999/02/08 05:55:05 yamasita 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
 *
 * Author: OMRON SOFTWARE Co., Ltd. <freewnn@rd.kyoto.omronsoft.co.jp>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Emacs; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Commentary:
 *
 * Change log:
 *
 * Last modified date: 8,Feb.1999
 *
 * Code:
 *
 */
/***********************************************************************
			rk_modread.c
						87.11.17  改 正

	モード定義表の読み込みを担当するプログラム。
***********************************************************************/
/*	Version 3.1	88/06/15	H.HASHIMOTO
 */
#ifndef OMRON_LIB
#include "rk_header.h"
#include "rk_extvars.h"
#ifdef	MULTI
#include "rk_multi.h"
#endif	/*MULTI*/
#include <sys/types.h>
#include <sys/stat.h>
#include "rext.h"

#ifdef WNNDEFAULT
#	include "config.h"
 /* マクロLIBDIRの定義（のためだけ）。コンパイル時は、ヘッダファイルの
    サーチパスに、Wnnのインクルードファイルのありかを設定しておくこと。*/
#endif
#include <pwd.h>
#endif

#define xyint(X, Y) (((X) << 24) | (Y))
#define Terminator 0 /* intの列（naibu[]）の終止コード */

#ifdef OMRON_SPEC
#define mystrcmp(s1, s2)	strcmp(s1, s2)
#define mystrcpy(s1, s2)	strcpy(s1, s2)
#endif

static void cond_evl(), rd_bcksla(), rd_ctrl(), look_choose(), hyouse(),
  mystrcpy(), ERMOPN(), ERRMOD();
static int mystrcmp(), mod_evl(), fnmsrc_tourk(), pathsrc_tourk(),
  dspnamsrc_tourk(), modsrc_tourk(), chkchar_getc(), read1tm(),
  get_hmdir(), rd_string(), ctov(), scan1tm(), modnamchk(),
  look_cond(), evlcond();
static char codeeval(), *ename();

extern	char	*chrcat(), *strend();

#ifndef	MULTI
/* 88/05/31 V3.1 */
static	FILE	*modefile;	/* モード定義表の fp */

/* 88/06/03 V3.1 */
/* エラー処理のためのもの */
static	char	*mcurdir;	/* モード表のサーチパス */
static	char	*mcurfnm;	/* モード表のファイル名 */
static	char	*mcurread;	/* モード表の現在行bufへのポインタ */
#endif	/*!MULTI*/

/* キーワードとその内部表現の対応を与える構造体。内部表現を
	持たないものに対しては0が与えられている。*/
struct	kwdpair {
	char	*name;
	int	code;
};
static /* V3.1 */
struct	kwdpair modfn[] = {
	"defmode",	0,
	"if",		xyint(2, 0),
	"when",		xyint(2, 1),
	"path",		0,
	"search",	0,
	"on_dispmode",	xyint(5, 0),
	"off_dispmode",	xyint(5, 1),
	"on_unchg",	xyint(6, 0),
	"off_unchg",	xyint(6, 1),
	NULL
}; /* 下を見よ キーワード定義表はまだあと二つあるのだ */

static /* V3.1 */
struct	kwdpair	modcond[] = {
	"not",	xyint(3, 0),
	"and",	xyint(3, 1),
	"or",	xyint(3, 2),
	"true",	xyint(3, 3),
	"false",xyint(3, 4),
	"=",	xyint(3, 5),
	"!=",	xyint(3, 6),
	"<",	xyint(3, 7),
	">",	xyint(3, 8),
	NULL
};
static /* V3.1 */
int	condarg[] = {1, 2, 2, 0, 0, 2, 2, 2, 2}; /* 条件判断関数の引数の個数 */

static /* V3.1 */
struct	kwdpair	swstat[] = {
	"on",	0,
	"off",	0,
	NULL
};

 /** キーワード（if, andなど）が正当なものかチェックし、その番号を返す */
static /* V3.1 */
int
kwdsrc(hyou, wd)
struct	kwdpair	*hyou;	/* どのキーワード表を使うか */
char	*wd;		/* チェックされるキーワード */
{
	fast	int	i;	/* V3.1 */

	for(i = 0; hyou[i] . name != NULL; i++)
		if(!mystrcmp(hyou[i] . name, wd)) return(i);
	ERRMOD(9);
	 /*NOTREACHED*/
	return(-1);
}

 /** 与えられたファイルがディレクトリなら非0を返す */
static /* V3.1 */
int
isdir(fnm)
char	*fnm;
{
	struct	stat	statbuf;

	/* Bug!!! 88/07/20 H.HASHIMOTO */
	/* return(stat(fnm, &statbuf) == 0 && (statbuf.st_mode & S_IFDIR)); */
	return(stat(fnm, &statbuf) == 0 &&
			((statbuf.st_mode & S_IFMT) == S_IFDIR));
}

/* 88/06/10 V3.1 */
 /** モード表の読み込み */
#ifdef OMRON_LIB
static
#endif
int
readmode(filename)
char	*filename;	/* ユーザが指定したモード表の名 */
{
#ifndef	MULTI
	char	modefilename[REALFN + 5];	/* V3.1 */
	char	pathname[REALFN];		/* V3.1 */
#endif	/*!MULTI*/

	struct	stat	statbuf;		/* V3.1 */
	char	*buf, *bufp;			/* V3.1 */

#ifdef RKMODPATH
	char	*genv, *pathenv, *pathp;
	extern	char	*getenv();	/* V3.1 */
#endif

	/* 88/06/14 V3.1 */
	/* モード定義表のパス名の長さチェックを安全性のために、
							付け加えておきます。 */
	cur_rk_table->mcurdir = "";		/* エラー処理用 */	/* V3.1 */
	if (strlen(cur_rk_table->mcurfnm = filename) > (REALFN - 1))	/* V3.1 */
		ERMOPN(3); 				/* V3.1 */
	strcpy(cur_rk_table->modefilename, filename);
	if (*(cur_rk_table->modefilename) == '\0' || *(strend(cur_rk_table->modefilename)) == KUGIRI) {
		strcat(cur_rk_table->modefilename, "mode");
	} else if(isdir(cur_rk_table->modefilename)) {
		chrcat(cur_rk_table->modefilename, KUGIRI);
		strcat(cur_rk_table->modefilename, "mode");
	}
	/* 88/06/14 V3.1 */
	if (strlen(cur_rk_table->mcurfnm = cur_rk_table->modefilename) > (REALFN - 1))	/* V3.1 */
		ERMOPN(3); 					/* V3.1 */

#ifdef RKMODPATH
	/* 88/06/10 V3.1 */
	if (!fixednamep(cur_rk_table->modefilename) &&
	   (pathenv = genv = getenv(RKMODPATH)) != NULL && *genv != '\0') {
		 /* PATHに少なくとも一つのサーチパスがある場合 */
		for ( ; ;) {
			for(pathp = cur_rk_table->pathname; *genv != ':' && *genv; genv++)
				*pathp++ = *genv;
			*pathp = '\0';

			if(*(strend(cur_rk_table->pathname)) != KUGIRI) *pathp++ = KUGIRI;
			 /* pathの区切りはDG（MV）であっても'/' */

			strcpy(pathp, cur_rk_table->modefilename);
			if ((stat(cur_rk_table->pathname, &statbuf) == 0) &&
			    ((cur_rk_table->modefile = fopen(cur_rk_table->pathname, "r")) != NULL)) {
				 /* Now Mode-hyo found */
				 /*
				if (cur_rk->flags & RK_VERBOS) {
					fprintf(stderr,
					"romkan: using Mode-hyo %s ...\r\n",
								pathname);
				}
				*/
				cur_rk_table->mcurdir = cur_rk_table->pathname;
				 /* この時点ではファイル名込みだが、
				    あとでパス名だけになる */
				cur_rk_table->mcurfnm = ename(cur_rk_table->modefilename);	/* V3.1 */
				break;
			}

			if (*genv != ':') {
				 /* Mode-hyo not found */
				 /*
			 	if (flags & RK_VERBOS){
				 	fprintf(stderr,
						"no %s in ",cur_rk->modefilename);
					for(genv = pathenv; *genv; genv++){
						fputc((*genv == ':' ?
						      ' ' : *genv), stderr);
					}
					fprintf(stderr, ".\n");
				}
				*/
			 	ERMOPN(1);
			}
			 /* coutinues searching Mode-hyo */
			genv++; 
		}
	} else
#endif
	{
		/* 88/06/10 V3.1 */
		if (stat(cur_rk_table->modefilename, &statbuf) != 0) ERMOPN(1);
		if ((cur_rk_table->modefile = fopen(cur_rk_table->modefilename, "r")) == NULL) ERMOPN(2);
		/*
		if (cur_rk->flags & RK_VERBOS) {
			fprintf(stderr, "romkan: using Mode-hyo %s ...\r\n",
								cur_rk->modefilename);
		}
		*/
		strcpy(cur_rk_table->pathname, cur_rk_table->modefilename);
	}

	 /* サーチパスの先頭に、モード表のあるディレクトリを設定している。*/
	*(ename(cur_rk_table->pathname)) = '\0';			/* V3.1 */
	pathsrc_tourk(cur_rk_table->pathname);			/* V3.1 */

	/* 88/06/14 V3.1 */
	malloc_for_modenaibu(&(cur_rk_table->rk_modenaibu), statbuf.st_size + 1);
	*(cur_rk_table->rk_modenaibu.next) = Terminator;

	malloc_for_modebuf(&(cur_rk_table->rk_modebuf), statbuf.st_size + 1);
	cur_rk_table->mcurread = buf = cur_rk_table->rk_modebuf.org;	/* エラー処理用 */ /* V3.1 */

	 /* モード定義表の読み込み */
	while (bufp = buf, read1tm(&bufp, 0))	/* V3.1 */
		mod_evl(buf);			/* V3.1 */
	fclose(cur_rk_table->modefile);

	/* 88/06/14 V3.1 */
	free_for_modebuf(&(cur_rk_table->rk_modebuf));
	realloc_for_modenaibu(&(cur_rk_table->rk_modenaibu));

	return(cur_rk_table->rk_taiouhyo.count);	/* V3.1 */
}

 /**	モード表の一かたまり（リスト、ファイル名、モード表示文字列）を
	解釈する。返す値は、defmode,search及びpathの時0、それ以外なら1。*/
static /* V3.1 */
int
mod_evl(s)
char	*s; /* モード表の内部表現の列へのポインタ */
{
	char	md1[MDT1LN];			/* V3.1 */
	fast	char	*bgn, *end;		/* V3.1 */
	fast	int	num, retval = 1;	/* V3.1 */

	if(*s != '('){
		if(*s != '"'){
			num = fnmsrc_tourk(s);
			*(cur_rk_table->rk_modenaibu.next++) = xyint(4, num);
		} else {
			s++;
			if(*(end = strend(s)) != '"') ERRMOD(10);
			*end = '\0';
			num = dspnamsrc_tourk(s);
			*(cur_rk_table->rk_modenaibu.next++) = xyint(5, 0);
			*(cur_rk_table->rk_modenaibu.next++) = 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)){ /* 初期on-off */
					switch(kwdsrc(swstat, md1)){
						case 0:
							cur_rk_table->rk_modesw.point[num]= 1;
							break;
						case 1:
							cur_rk_table->rk_modesw.point[num]= 0;
							break;
					}
					scan1tm(&s, md1, 2); /* あればerr */
				} else cur_rk_table->rk_modesw.point[num]= 0; /* defaultはoff */
				break;
			case 1: /* if */
			case 2: /* when */
				*(cur_rk_table->rk_modenaibu.next++) = modfn[num] . code;
				scan1tm(&s, md1, 1); /* condition */
				cond_evl(md1);
				while(scan1tm(&s, md1, 0)){
					if(mod_evl(md1) == 0) ERRMOD(8);
				}
				*(cur_rk_table->rk_modenaibu.next++) = Terminator;
				break;
			case 3: /* path */
				/* 88/06/09 V3.1 */
				cur_rk_table->rk_path.count = 1;		
				cur_rk_table->rk_path.next = cur_rk_table->rk_path.point[1];
			case 4: /* search */
				retval = 0;
				/* 88/06/29 V3.1 */
				if(cur_rk_table->rk_taiouhyo.point[0] != NULL) ERRMOD(11);
				 /* サーチパスの指定はファイル名の出現より
				    先行しなければならないとしておく。*/

				while(scan1tm(&s, md1, 0)){/* find pathname */
					pathsrc_tourk(md1);
				}
				break;
			case 5: /* on_dispmode */
			case 6:	/* off_dispmode */
				*(cur_rk_table->rk_modenaibu.next++) = modfn[num] . code;
				scan1tm(&s, md1, 1); /* dispmode string */

				if(*(bgn = md1) != '"') ERRMOD(12);
				bgn++;
				if(*(end = strend(bgn)) != '"') ERRMOD(10);
				*end = '\0';
				*(cur_rk_table->rk_modenaibu.next++) = dspnamsrc_tourk(bgn);
				scan1tm(&s, md1, 2); /* あればerr */
				break;
			case 7: /* on-unchg */
			case 8: /* off-unchg */
				*(cur_rk_table->rk_modenaibu.next++) = modfn[num] . code;
				scan1tm(&s, md1, 2); /* あればerr */
				break;
		}

	}
	*(cur_rk_table->rk_modenaibu.next) = Terminator;
	return(retval);
}

 /** 条件式（モード名 又はnot,andなどの式）一つを解釈 */
static /* V3.1 */
void
cond_evl(cod)
char	*cod; /* 条件式の内部表現の列へのポインタ */
{
	char	md1[MDT1LN];
	fast	int	num, i;		/* V3.1 */

	if(*cod != '('){
		num = modsrc_tourk(cod, 1);
		*(cur_rk_table->rk_modenaibu.next++) = xyint(1, num);
	} else {
		cod++;
		scan1tm(&cod, md1, 1); /* not;and;or */
		num = kwdsrc(modcond, md1);
		*(cur_rk_table->rk_modenaibu.next++) = xyint(3, num);
		for(i = condarg[num]; i; i--){
			scan1tm(&cod, md1, 0);
			cond_evl(md1);
		}
		scan1tm(&cod, md1, 2);
	}
	*(cur_rk_table->rk_modenaibu.next) = Terminator;
}

/* 88/06/09 V3.1 */
static int
serach_in_modetable_struct(ptr, s, np)
fast	modetable *ptr;
fast	char	*s;
fast	int	*np;
{
	fast	int	n;

	for (n = 0; n < ptr->count; n++) {
		if (!mystrcmp(ptr->point[n], s)) {
			*np = n;
			return(1);	/* found */
		}
	}
	*np = n;
	return(0);	/* not found */
}

/* 88/06/13 V3.1 */
static
void
entry_to_modetable_struct(ptr, n, s)
fast	modetable *ptr;
int	n;
char	*s;
{
	ptr->point[n] = ptr->next;
	mystrcpy(ptr->next, s);
	strtail(ptr->next);
	++(ptr->next);
}


/* 88/06/07 V3.1 */
 /**	sで指定されたファイル名が既登録か探し、なければ登録。但し、既登録か
	どうかのチェックは厳密ではないが（例えば、同じファイルでも、
	パス名付きと無しとでは、同じと見ない）、ファイル名が既登録かどうか
	チェックするのは、メモリ節約のために同じ表を読み込むのを防ぐため
	だけなので、それ以外には別に困る点はない。*/
static /* V3.1 */
int
fnmsrc_tourk(s)
char	*s;
{
	int	n;

	if (serach_in_modetable_struct(&(cur_rk_table->rk_taiouhyo), s, &n))
		return(n);

	check_and_realloc_for_modetable_struct(&(cur_rk_table->rk_taiouhyo), n, s,
				RK_TAIOUHYO_MAX_LOT, RK_TAIOUHYO_MEM_LOT);

	/* チェックのみ */
	if(!(filnamchk(s))) ERRMOD(3);

	entry_to_modetable_struct(&(cur_rk_table->rk_taiouhyo), n, s);
	return(n);
}

/* 88/06/07 V3.1 */
 /**	sで指定されたサーチパス名が既登録か探し、なければ登録。但し、fnmsrc_
	tourk()同様、既登録かどうかのチェックは厳密ではないが問題ない。*/
/* 88/06/07 V3.1 */
static /* V3.1 */
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);
	s = fnm_addsla;
	 /* パス名が'/'で終わってなければ、それを付加する。*/

	if (serach_in_modetable_struct(&(cur_rk_table->rk_path), s, &n))
		return(n);

	check_and_realloc_for_modetable_struct(&(cur_rk_table->rk_path), n, s,
				RK_PATH_MAX_LOT, RK_PATH_MEM_LOT);

	entry_to_modetable_struct(&(cur_rk_table->rk_path), n, s);
	return(n);
}

/* 88/06/07 V3.1 */
 /** sで指定されたモード表示文字列が既登録か探し、なければ登録 */
static /* V3.1 */
int
dspnamsrc_tourk(s)
char	*s;
{
	int	n;

	if (serach_in_modetable_struct(&(cur_rk_table->rk_dspmode), s, &n))
		return(n);

	check_and_realloc_for_modetable_struct(&(cur_rk_table->rk_dspmode), n, s,
				RK_DSPMODE_MAX_LOT, RK_DSPMODE_MEM_LOT);

	entry_to_modetable_struct(&(cur_rk_table->rk_dspmode), n, s);
	return(n);
}

/* 88/06/09 V3.1 */
 /**	sで指定されたモード名を探し、なければ登録。但し、flgが非0なら、
	見つからなければエラー */
static /* V3.1 */
int
modsrc_tourk(s, flg)
char	*s;
int	flg;
{
	int	n;

	if(!modnamchk(s)) ERRMOD(4);	/* V3.1 */

	if (serach_in_modetable_struct(&(cur_rk_table->rk_defmode), s, &n))
		return(n);

	if(flg) ERRMOD(5);

	check_and_realloc_for_modetable_struct(&(cur_rk_table->rk_defmode), n, s,
				RK_DEFMODE_MAX_LOT, RK_DEFMODE_MEM_LOT);

	check_and_realloc_for_modesw(&(cur_rk_table->rk_modesw), n,
				RK_DEFMODE_MAX_LOT, RK_DEFMODE_MEM_LOT);

	entry_to_modetable_struct(&(cur_rk_table->rk_defmode), n, s);
	return(n);
}

 /** ファイルから一文字読む（空白文字は飛ばす）。読んだ文字がEOFなら0を返す */
static /* V3.1 */
char	fspcpass()
{	
	fast	 int	c;	/* V3.1 */

	while(EOF != (c = chkchar_getc(cur_rk_table->modefile)) && is_nulsp(c));
	return(c == EOF ? '\0' : c);
}

 /**	モード表には空白文字以外のコントロール文字は生では混じらないものと
	する。混じっていた場合はチェックしつつ、getcを行う。*/
static /* V3.1 */
int
chkchar_getc(f)
fast FILE	*f;
{
	fast	 int	c;	/* V3.1 */

	c = getc(f);
	if(is_cntrl(c) && !isspace(c)){
		sprintf(cur_rk_table->mcurread, "\\%03o", c);
		ERRMOD(16);
	}
	return(c);
}	

 /**	モード表の一かたまり（リスト、ファイル名、モード表示文字列）を
	切り出す。その際、特殊な表記（'^','\'による）は、'\（8進）;' の
	形に直す。flgが非0なら、EOFでエラーを起こし、')'で0を返す。*/
static /* V3.1 */
int
read1tm(sptr, flg)
char	**sptr;	/* モード表の内部表現の列へのポインタへのポインタ。
		   rd_bcksla()、rd_ctrl()、codeeval()でも同様 */
int	flg;
{
	fast	int	c;	/* V3.1 */
	fast	char	*head;	/* V3.1 */
	int	retval = 1;	/* V3.1 */
	char	*s;		/* V3.1 */

	s = *sptr;

	while((c = fspcpass()) == ';'){
	  /* 注釈文を検出したら、行末までとばして再試行。*/
		while((c= chkchar_getc(cur_rk_table->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(cur_rk_table->modefile)) != '"'){
				switch(c){
					case EOF: ERRMOD(0);
#ifndef OMRON_SPEC
					case '\\': rd_bcksla(&s); break;
					case '^': rd_ctrl(&s); break;
#endif
					default: *s++ = c;
				}
			}
			*s++ = '"';
			break;
		default:
		 /* 先頭が @ 又は ~ の時は、特殊処理。*/
			if(c == '@'){ /* @HOME, @MODEDIR, @LIBDIR */
				*s++ = c;
				head = s;
				rd_string(&s, 1);

				if(mystrcmp("HOME", head) == 0){
					s = --head;
					if(get_hmdir(&s, (char *)NULL) != 0){
						cur_rk_table->mcurread = head;
			  /* エラー表示の調整。抜けずにすぐにエラー表示
			     ルーチンに行くのでheadの中身は失われない。*/
						ERRMOD(13);
					}
				} else
				if(mystrcmp("MODEDIR", head) == 0){
					/* 88/06/09 V3.1 */
					strcpy(s = --head, cur_rk_table->rk_path.point[0]);
					if(KUGIRI== *(s= strend(s))) *s= '\0';
				} else
#ifdef WNNDEFAULT
				if(mystrcmp("LIBDIR", head) == 0){
					strcpy(s = --head, LIBDIR);
					strtail(s);
				} else
#endif
				{
					cur_rk_table->mcurread = --head;
					ERRMOD(14);
				}
			} else
			if(c == '~'){ /* ~user */
				*s++ = c;
				head = s;
				rd_string(&s, 1);

				mystrcpy(head, head);
				s = head - 1;
				if((get_hmdir(&s, (*head ? head : NULL)))!= 0){
					cur_rk_table->mcurread = --head;
					 /* エラー表示の調整 */
					ERRMOD(15);
				}
			} else {
				ungetc(c, cur_rk_table->modefile);
			}
			 /* 先頭の特殊処理 終わり */

			if((c = rd_string(&s, 0)) == EOF && flg) ERRMOD(0);
			if(c == ')' && !flg) ERRMOD(1);
	}

	*s = '\0';
	*sptr = s;
	return(retval);
}

/* 88/06/03 V3.1 */
 /**	socの名のユーザのログイン・ディレクトリ名をdestに入れ、*destにその末尾を
	指させる。但しsocが空列または、NULLなら自分のログイン・ディレクトリ名、
	いずれの場合も、不成功時は、何もしない。返値は、不成功時-1。*/
static /* V3.1 */
int
get_hmdir(dest, soc)
char	**dest, *soc;
{
	fast	struct	passwd	*usr;	/* V3.1 */
	extern	struct	passwd	*getpwnam(), *getpwuid();	/* V3.1 */

	/* 88/05/26 V3.1 */
	if(soc == NULL || *soc == '\0'){
		if(NULL == (usr = getpwuid(getuid()))) return(-1);
	} else {
		if(NULL == (usr = getpwnam(soc))) return(-1);
	}
	strcpy(*dest, usr -> pw_dir);
	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, cur_rk_table->pathname);
			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が非0なら、'/'が来ても終わる。返値は、次に読まれる文字。*/
static /* V3.1 */
int
rd_string(sptr, flg)
char	**sptr;
int	flg;
{
	fast	int	c;	/* V3.1 */

	while(c = chkchar_getc(cur_rk_table->modefile), !(is_nulsp(c) || c == EOF ||
	      c == '(' || c == ')' || (flg == 1 && c == KUGIRI))){
		switch(c){
#ifndef OMRON_SPEC
			case '\\': rd_bcksla(sptr); break;
			case '^': rd_ctrl(sptr); break;
#endif
			default: *(*sptr)++ = c;
		}
	}
	**sptr = '\0';
	return(ungetc(c, cur_rk_table->modefile));
}	

/* 88/06/02 V3.1 */
#ifndef OMRON_SPEC
 /**	モード表からバックスラッシュ形式の一文字分を取り出し、'\（8進）;' の
	形に直す。但し、先頭の'\\'は既に読まれたあと。*/
static /* V3.1 */
void
rd_bcksla(sptr)
char	**sptr;
{
	fast	int	c, code = 0, digflg = 0;	/* V3.1 */

	switch(c = chkchar_getc(cur_rk_table->modefile)){
		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(cur_rk_table->modefile), is_octal(c)){
				code <<= 3;
				code += ctov(c);
				digflg = 1;
			}
			if(c != ';') ungetc(c, cur_rk_table->modefile);
			break;
		case 'd':
			while(c = chkchar_getc(cur_rk_table->modefile), is_digit(c)){
				code *= 10;
				code += ctov(c);
				digflg = 1;
			}
			if(c != ';') ungetc(c, cur_rk_table->modefile);
			break;
		case 'x':
			while(c = chkchar_getc(cur_rk_table->modefile), is_xdigit(c)){
				code <<= 4;
				code += ctov(c);
				digflg = 1;
			}
			if(c != ';') ungetc(c, cur_rk_table->modefile);
			break;
		default:
			if(is_octal(c)){
				digflg = 1;
				code = ctov(c);
				while(c= chkchar_getc(cur_rk_table->modefile), is_octal(c)){
					code <<= 3;
					code += ctov(c);
				}
				if(c != ';') ungetc(c, cur_rk_table->modefile);
			} else {
				code = c;
				digflg = 1;
			}
	}

	if(digflg == 0) ERRMOD(7);
	sprintf(*sptr, "\\%o;", code);
	strtail(*sptr);
}


 /**	モード表からコントロールコード形式の一文字分を取り出し、'\（8進）;' の
	形に直す。但し、先頭の'^'は既に読まれたあと。*/
static /* V3.1 */
void
rd_ctrl(sptr)
char	**sptr;
{
	fast	int	c;	/* V3.1 */

	if(!(' ' <= (c = chkchar_getc(cur_rk_table->modefile)) && c < '\177')) ERRMOD(7);
	if(c == '?') c = '\177'; else c &= 0x1f;

	sprintf(*sptr, "\\%o;", c);
	strtail(*sptr);
}
#endif

 /**	8・10・16進コード用のキャラクタを実際のコードに直す。入力のチェックは
	しない。*/
static
int
ctov(c)
fast 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 /* V3.1 */
int
scan1tm(socp, dest, flg)
char	**socp, *dest;
	 /* socpの指しているポインタが指している所から取り出してdestに入れる。
	    その後、socpが指しているポインタを進める。*/
int	flg;
{
	fast	char	c;		/* V3.1 */
	fast	int	retval = 1;	/* V3.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 /* V3.1 */
int
modnamchk(s)
fast	char	*s;	/* V3.1 */
{
	if(is_digit(*s)) return(0);
	for(; *s; s++) {
		if(!is_alnum(*s)) {
			if (*s != '_') {
				return(0);
			}
		}
	}
	return(1);
/*
	if(is_digit(*s)) return(0);
	for(; *s; s++) if(!is_alnum(*s) && *s != '_') return(0);
	return(1);
*/
}

 /** 変換対応表の選択を行う */
#ifdef OMRON_LIB
static
#endif
void
choosehyo()
{
	fast	int	i;	/* V3.1 */
	int	*naibup;	/* V3.1 */

	if (cur_rk->rk_usehyo.usehyo != NULL)
		cur_rk->rk_usehyo.usemaehyo[0] = cur_rk->rk_usehyo.usehyo[0] =
						cur_rk->rk_usehyo.useatohyo[0] = -1;
	for(i = 0; i < 2; i++){
		cur_rk->dspmod[1][i] = cur_rk->dspmod[0][i];
		cur_rk->dspmod[0][i] = NULL;
	}

	if ((naibup = cur_rk_table->rk_modenaibu.org) != NULL)
		look_choose(&naibup, 1);	/* V3.1 */
}

 /**	モード表の内部形式を順次見ていき、使用表の選択及びモード表示文字列の
	選択を行っていく。但しflgが0ならスキップするだけ */
static /* V3.1 */
void
look_choose(naibupp, flg)
int	**naibupp;   /* モード表の内部表現の列へのポインタへのポインタ。
			look_cond()、evlcond()でも同様 */
int	flg;
{
	fast	int	naibu1, naibu2, branch, lcrsl;	/* V3.1 */
	int	*naibup;				/* V3.1 */

	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) cur_rk->dspmod[0][LWRMSK(naibu1)] =
					    cur_rk_table->rk_dspmode.point[naibu2];
				break;
			case 6: /* romkanがそれぞれon・off時のモード表示
				   文字列を前のままに */
				if(flg) cur_rk->dspmod[0][LWRMSK(naibu1)] = 
					    cur_rk->dspmod[1][LWRMSK(naibu1)];
				break;
			default:
				BUGreport(6);
		}
	}

	*naibupp = naibup;
}

 /**	*naibupp が、内部表現の列で条件式を表すところを指している筈なので、
	それを評価し、真ならその続きを解釈しにいく。偽なら読み飛ばす。
	返値は、最初に評価した条件式の真偽値。*/
static /* V3.1 */
int
look_cond(naibupp, flg)
int	**naibupp, flg;
{
	fast	int	condrsl;	/* V3.1 */
	int	*naibup;

	naibup = *naibupp;

	/* 88/06/06 V3.1 */
	condrsl = evlcond(&naibup); /* 必ず評価しないといけないため */
	look_choose(&naibup, flg &= condrsl);
	*naibupp = naibup;
	return(flg);
}

 /** 条件式の真偽値の評価 */
static /* V3.1 */
int
evlcond(naibupp)
int	**naibupp;
{
	int	*naibup, tmpval[ARGMAX];	/* V3.1 */
	fast int	naibu1, retval = 0, imax;	/* V3.1 */
	fast int	i;			/* V3.1 */

	naibup = *naibupp;

	naibu1 = *naibup++;
	switch(SHUBET(naibu1)){
		case 7: /* 数値 */
			 retval = *naibup++; break;
		case 1: /* モード名 */
			 retval = cur_rk->rk_modesw.point[LWRMSK(naibu1)]; break;
		case 3: /* if;when */
			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;

			}
	}

	*naibupp = naibup;
	return(retval);
}

 /** num番目の表を、使用するものとして登録する。前・本・後処理の区別もする */
static /* V3.1 */
void
hyouse(num)
int	num;
{
	fast	int	*ptr = NULL;	/* V3.1 */

	switch(cur_rk_table->rk_hyo.point[num].hyoshu){
		case 1: ptr = cur_rk->rk_usehyo.usemaehyo; break;
		case 2: ptr = cur_rk->rk_usehyo.usehyo; break;
		case 3: ptr = cur_rk->rk_usehyo.useatohyo; break;
		default: BUGreport(11);
	}
	if (ptr == NULL) return;
	for(; *ptr != -1; ptr++) if(*ptr == num) return;
	*ptr = num;
	*++ptr = -1;
}

/* 88/06/02 V3.1 */
#ifndef OMRON_SPEC
 /** strcmpと同等  但し、'\（8進）;'も解釈する。*/
static /* V3.1 */
int
mystrcmp(s1, s2)
char	*s1, *s2;
{
	fast	char	c1, c2;	/* V3.1 */

	while((c1 = codeeval(&s1)) == (c2 = codeeval(&s2)))
		if(c1 == '\0') return(0);
	return(c1 > c2 ? 1 : -1);
}

 /** strcpyと同等 但し'\（8進）;'も解釈する。s1 <= s2なら正常動作するはず */
static /* V3.1 */
void
mystrcpy(s1, s2)
fast char	*s1;
char		*s2;
{
	while(*s1++ = codeeval(&s2));
}

 /**	一文字の解釈を行う。普通の文字はそのまま、'\（8進）;'は実際のコードに
	直す。その後、文字列へのポインタを一文字分進めておく（少なくとも
	１バイト分進むことが保証されるはず）。*/
static /* V3.1 */
char	codeeval(sptr)
fast	 char	**sptr;		/* V3.1 */
{
	fast	char	c;		/* V3.1 */
	fast	char	code = 0;	/* V3.1 */

	if((c = *(*sptr)++) != '\\') return(c);
	while(c = *(*sptr)++, is_octal(c)){
		code <<= 3;
		code += ctov(c);
	}
	if(c != ';') BUGreport(12);
	return(code);
}
#endif

 /** romkanがon時のモード表示文字列を返す関数。無指定であってRK_DSPNILフラグが
     立っている時は空文字列を返す。*/
char	*romkan_dispmode()
{
	return(cur_rk->dspmod[0][0] == NULL && (cur_rk_table->flags & RK_DSPNIL) ?
	       "" : cur_rk->dspmod[0][0]);	/* V3.1 */
}

 /** romkanがoff時のモード表示文字列を返す関数。無指定であってRK_DSPNILフラグ
     が立っている時は空文字列を返す。*/
char	*romkan_offmode()
{
	return(cur_rk->dspmod[0][1] == NULL && (cur_rk_table->flags & RK_DSPNIL) ?
	       "" : cur_rk->dspmod[0][1]);	/* V3.1 */
}

/* 88/06/02 V3.1 */
 /** ファイル名からパス名を除いた部分の先頭を返す。*/
static
char	*ename(s)
char	*s;
{
	fast	char	*p;	/* V3.1 */

	p = rindex(s, KUGIRI);
	return(p == NULL ? s : p + 1);
}

 /**	ファイル名のチェック。先頭（パス名は除く）が'1'〜'3'でないといけない。
	正当なものなら1〜3（前・本・後処理表の区別を表す）を返し、そうでないと
	0を返す。*/
#ifdef OMRON_LIB
static
#endif
int
filnamchk(s)
char	*s;
{
	fast	int	c;	/* V3.1 */

	c = *(ename(s)) - '0';
	return((1 <= c && c <= 3) ? c : 0);
}

/* 88/06/02 V3.1 */
static
void
ERMOPN(n) /* モード定義表がオープンできない */
fast	unsigned int	n;	/* V3.1 */
{
	static	char	*ermes[] = {
 /*  0 */	"Unprintable error",
		"Mode-hyo does't exist",
		"Can't open Mode-hyo",
		"Too many long Mode-hyo filenames",
	};

	if(n >= numberof(ermes)) n = 0;

	fprintf(stderr, "\r\nromkan: Mode-hyo %s ---\r\n", cur_rk_table->mcurfnm);
	fprintf(stderr, "\tError No.%d: %s.\r\n", n, ermes[n]);
	longjmp(cur_rk_table->env0, 1);
}

/* 88/06/03 V3.1 */
static
void
ERRMOD(n) /* モード定義表のエラー */
fast	unsigned int	n;	/* V3.1 */
{
	static	char	*ermes[] = {
 /*  0 */	"Table incomplete",
		"')' mismatch",
		"Unprintable error",
		"Illegal filename",
		"Illegal modename",
 /*  5 */	"Undefined mode",
		"Illegal content(s) of list",
		"Illegal ^,\\o,\\x or \\d expression",
		"Illegal defmode",
		"Unrecognized keyword",
 /* 10 */	"Incomplete string",
		"Search path specified after filename",
		"Argument must be a string",
		"Can't get home directory",
		"Illegal @-keyword",
 /* 15 */	"User doesn't exist",
 		"Illegal character",
	};

	if(n >= numberof(ermes)) n = 2;

	fprintf(stderr, "\r\nromkan: Mode-hyo %s%s ---\r\n%s\r\n",
			cur_rk_table->mcurdir, cur_rk_table->mcurfnm, cur_rk_table->mcurread);
	fprintf(stderr, "\tError No.%d: %s.\r\n", n, ermes[n]);
	fclose(cur_rk_table->modefile);
	longjmp(cur_rk_table->env0, 1);
}
