/* << MSC V5.1 >> [FM-TOWNS] ************************************************
*
*	環境変数領域の情報取得(MS-DOS汎用)
*	------------------------------  FUNCTION  ----------------------------
*	getMCB()		: 先頭のMCBｱﾄﾞﾚｽ取得
*	chkEnviron()	: 環境変数領域のﾁｪｯｸ
*	getEnviron()	: 環境ｾｸﾞﾒﾝﾄ取得
*	getEnvSeg()		: 環境ｾｸﾞﾒﾝﾄ取得
*	getEnvName()	: 環境変数取得
*	getEnvOff()		: 環境変数名の位置取得
*	delEnvName()	: 環境変数削除
*	clrEnvName()	: 環境変数を全て削除
*	chkEnvUpdate()	: 環境変数更新ｻｲｽﾞﾁｪｯｸ
*	chkEnvName()	: 文字列ﾁｪｯｸと変換(余分な空白文字の削除)
*	setEnvName()	: 環境変数追加
*	----------------------------------------------------------------------
*	All Rights Reserved, Copyright (C) Y.Hirata 1993.
*	Programmed by Y.Hirata ( NIFTY-ID: NAB03321  パオパオ )
*
*	NOTE: TAB=4
****************************************************************************/

#include <stdio.h>		/*  NULL		*/
#include <dos.h>		/*  intdosx		*/
#include <string.h>		/*  memcmp, str**	*/
#include <jstring.h>	/*  jstr**		*/
#include <memory.h>		/*  movedata	*/
#include "lib\mem.h"
#include "lib\strlib.h"
#include "environ.h"

static WORD	DOSver ;							/*  MS-DOS Version識別	*/
static WORD	ROOTpsp = 0 ;						/*  ﾙｰﾄのCOMMAND.COM	*/
static WORD	ROOTenv = 0 ;						/*  ﾙｰﾄの環境ｾｸﾞﾒﾝﾄ		*/
static WORD	PSPseg = 0 ;						/*  親のPSPｾｸﾞﾒﾝﾄ		*/
static WORD	ENVseg = 0 ;						/*  親の環境ｾｸﾞﾒﾝﾄ		*/

WORD getMCB( void )
/*===========================================================================
*	先頭のMCBｾｸﾞﾒﾝﾄ取得
*	< RET >	: 先頭のMCBｾｸﾞﾒﾝﾄ
===========================================================================*/
{
	union	REGS	regs ;
	struct	SREGS	sregs ;

	regs.h.ah = 0x52 ;
	intdosx( &regs,&regs,&sregs ) ;
	return ( (WORD)_peek( sregs.es,regs.x.bx-2 ) ) ;
}

int chkEnviron( WORD seg,WORD size )
/*===========================================================================
*	環境変数領域のﾁｪｯｸ
*	< IN  >	: seg	ｾｸﾞﾒﾝﾄ
*			: size	ﾒﾓﾘｻｲｽﾞ(ﾊﾟﾗｸﾞﾗﾌ単位)
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				=0	環境ｾｸﾞﾒﾝﾄを見つけた
*				=1	環境ｾｸﾞﾒﾝﾄではない
===========================================================================*/
{
	register WORD	cp, ep ;
	WORD	sp ;

	if ( size >= 0x1000 ) return 1 ;			/*  64KB以上は無視		*/
	size <<= 4 ;								/*  ﾊﾟﾗｸﾞﾗﾌ→ﾊﾞｲﾄ		*/
	for ( sp=0;; ) {
		for ( ep=sp; ep<size; ep++ ) {
			if ( _peekb( seg,ep ) == '=' ) break ;
		}
		if ( ep >= size ) return 1 ;			/*  '='が見つからない	*/
		for ( cp=sp; cp<size; cp++ ) {
			if ( _peekb( seg,cp ) == '\0' ) break ;
		}
		if ( cp >= size ) return 1 ;			/*  '\0'が見つからない	*/
		if ( cp <= ep ) return 1 ;				/*  '環境変数='でない	*/
		sp = cp + 1 ;
		if ( _peekb( seg,sp ) == '\0' ) return 0 ;		/*  OK!			*/
	}
}

int getEnviron( ENVINFS *env )
/*===========================================================================
*	環境ｾｸﾞﾒﾝﾄの取得
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				=0	正常終了
*				=1	環境ｾｸﾞﾒﾝﾄが見つからない
===========================================================================*/
{
	register WORD	cnt ;
	WORD	seg, size, res, tail ;
	BYTE	flg ;

	seg = getMCB() ;
	while ( _peekb( seg,0 ) != 'Z' ) {
		size = (WORD)_peek( seg,3 ) ;
		if ( !chkEnviron( seg+1,size ) ) {		/*  環境ｾｸﾞﾒﾝﾄ		*/
			size <<= 4 ;						/*  ﾊﾟﾗｸﾞﾗﾌ→ﾊﾞｲﾄ	*/
			seg++ ;								/*  MCBの次のｾｸﾞﾒﾝﾄ	*/
			cnt = 0 ;
			while ( _peek( seg,cnt ) != 0x0000 ) cnt++ ;
			cnt++ ;
			if ( cnt >= size ) {
				flg = FALSE ;					/*  環境ｾｸﾞﾒﾝﾄ外	*/
			} else if ( cnt <= size-3 ) {
				if ( _peek( seg,cnt+1 ) == 0x0001 ) {
					flg = FALSE ;				/*  実行ﾌﾟﾛｸﾞﾗﾑ環境	*/
				} else {
					flg = TRUE ;				/*  環境ｾｸﾞﾒﾝﾄ		*/
				}
			} else {
				flg = TRUE ;					/*  環境ｾｸﾞﾒﾝﾄ		*/
			}
			if ( flg ) {						/*  環境ｾｸﾞﾒﾝﾄ		*/
				tail = cnt ;
				cnt++ ;
				res = size - cnt ;
				env->seg  = seg ;
				env->size = size ;
				env->res  = res ;
				env->tail = tail ;
				return 0 ;
			} else {							/*  実行ﾌﾟﾛｸﾞﾗﾑ環境	*/
				size >>= 4 ;					/*  ﾊﾞｲﾄ→ﾊﾟﾗｸﾞﾗﾌ	*/
				seg-- ;							/*  MCBｾｸﾞﾒﾝﾄに戻す	*/
			}
		}
		seg += ( size + 1 ) ;					/*  次のMCBｾｸﾞﾒﾝﾄ	*/
	}
	return 1 ;
}

static void chkseg( WORD seg )
/*===========================================================================
*	ﾙｰﾄと親の環境ｾｸﾞﾒﾝﾄを調べる
===========================================================================*/
{
	WORD	psp, oya, env ;

	psp = (WORD)_peek( seg,1 ) ;				/*  MCBを要求したPSP	*/
	if ( !psp ) return ;
	oya = (WORD)_peek( psp,0x16 ) ;				/*  親のPSPｾｸﾞﾒﾝﾄ		*/
	if ( ROOTpsp == psp && psp != seg+1 && !ROOTenv ) ROOTenv = seg + 1 ;

	if ( psp == PSPseg && psp != seg+1 ) {
		env = (WORD)_peek( PSPseg,0x2C ) ;		/*  親の環境ｾｸﾞﾒﾝﾄ		*/
		switch ( DOSver ) {
		case 0	:								/*  MS-DOS V3.1以前		*/
			if ( !ENVseg ) ENVseg = seg + 1 ;
			break ;
		case 1	:								/*  MS-DOS V3.2以降		*/
			if ( env != seg+1 && !ENVseg ) ENVseg = seg + 1 ;
			break ;
		case 2	:								/*  MS-DOS V4.0以降		*/
			if ( env ) {
				if ( env == seg+1 && !ENVseg ) ENVseg = env ;
			} else {
				if ( !ENVseg ) ENVseg = seg + 1 ;
			}
			break ;
		}
	}
}

static void getEnvInf( ENVINFS *env )
/*===========================================================================
*	環境ｾｸﾞﾒﾝﾄの情報取得
===========================================================================*/
{
	register WORD	cnt ;

	cnt = 0 ;
	while ( _peek( env->seg,cnt ) != 0x0000 ) cnt++ ;
	cnt++ ;
	if ( cnt < env->size ) {					/*  環境ｾｸﾞﾒﾝﾄ			*/
		env->tail = cnt ;
		cnt++ ;
		env->res  = env->size - cnt ;
	} else {
		env->tail = env->size - 1 ;
		env->res  = 0 ;
	}
}

int getEnvSeg( ENVINFS *env,int sw )
/*===========================================================================
*	環境ｾｸﾞﾒﾝﾄの取得
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				=0	正常終了
*				=1	環境ｾｸﾞﾒﾝﾄが見つからない
===========================================================================*/
{
	union	REGS	regs ;
	WORD	seg ;
	BYTE	mark ;

	regs.h.ah = 0x30 ;
	intdos( &regs,&regs ) ;
	if ( regs.h.al > 3 ) {						/*  V4.0以降			*/
		DOSver = 2 ;
	} else if ( regs.h.ah > 0x0A && regs.h.al == 3 ) {
		DOSver = 1 ;							/*  V3.2以降			*/
	} else {
		DOSver = 0 ;							/*  V3.1以前			*/
	}
	regs.h.ah = 0x62 ;
	intdos( &regs,&regs ) ;
	PSPseg  = _peek( regs.x.bx,0x16 ) ;			/*  親のPSPｾｸﾞﾒﾝﾄ		*/
	ROOTpsp = (WORD)_peek( 0,0x2E*4+2 ) ;		/*  先頭command.com		*/
	ENVseg  = ROOTenv = 0 ;
	seg = getMCB() ;							/*  先頭MCBｾｸﾞﾒﾝﾄ		*/
	for ( ;; ) {
		chkseg( seg ) ;
		mark = (BYTE)_peekb( seg,0 ) ;			/*  MCB識別子			*/
		if ( mark == 'Z' ) break ;
		if ( mark != 'M' ) return 1 ;			/*  ﾒﾓﾘ状態が異常		*/
		seg += ( (WORD)_peek( seg,3 ) + 1 ) ;	/*  次のMCBｾｸﾞﾒﾝﾄ		*/
	}
	switch ( sw ) {
	case ENV_ROOT	:							/*  ﾙｰﾄの環境ｾｸﾞﾒﾝﾄ		*/
		if ( !ROOTenv ) return 1 ;
		env->seg  = ROOTenv ;
		break ;
	case ENV_PRE	:							/*  親の環境ｾｸﾞﾒﾝﾄ		*/
		if ( !ENVseg ) return 1 ;
		env->seg = ENVseg ;
		break ;
	default			:							/*  その他				*/
		return 1 ;
	}
	env->size = (WORD)_peek( env->seg-1,3 ) ;	/*  環境ｾｸﾞﾒﾝﾄのｻｲｽﾞ	*/
	if ( env->size >= 0x1000 ) return 1 ;		/*  64KB以上は無視		*/
	env->size <<= 4 ;
	getEnvInf( env ) ;							/*  環境ｾｸﾞﾒﾝﾄ情報取得	*/
	return 0 ;
}

int getEnvName( ENVINFS *env,BYTE *buf,WORD len,int sw )
/*===========================================================================
*	環境変数取得
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: buf	環境変数格納先
*			: len	bufｻｲｽﾞ(ﾊﾞｲﾄ)
*			: sw	1 ならば 最初の環境変数から検索
*					0 ならば 続きのﾊﾟｽを検索
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	継続ﾃﾞｰﾀあり
*				=-1	ﾃﾞｰﾀ終了
===========================================================================*/
{
	static WORD	nextp=0 ;
	register WORD	cnt ;

	if ( sw ) nextp = 0 ;						/*  最初から検索		*/
	for ( cnt=0; cnt<len; cnt++ ) {
		buf[cnt] = (BYTE)_peekb( env->seg,nextp ) ;	/*  1文字取得		*/
		nextp++ ;
		if ( buf[cnt] == '\0' ) {
			if ( cnt == 0 ) {
				nextp-- ;
				return -1 ;
			}
			return 0 ;
		}
	}
	nextp-- ;
	buf[len-1] = '\0' ;
	return 1 ;									/*  継続ﾃﾞｰﾀあり		*/
}

int getEnvOff( ENVINFS *env,char *name,WORD *off )
/*===========================================================================
*	環境変数の位置取得
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: name	環境変数名
*	< OUT >	: off	環境変数名の位置
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	環境変数が見つからない
*
*	※ 環境変数名は大文字に変換されるので, 必要ならば退避しておくこと.
===========================================================================*/
{
	register WORD	cnt, cp ;
	WORD	sp, len, cmplen, ep ;
	int		ret ;
	char	ch ;

	if ( _peek( env->seg,0 ) == 0x0000 ) return 1 ;
	cmplen = strlen( name ) + 1 ;				/*  ﾇﾙ文字を含んだｻｲｽﾞ	*/
	for ( ep=0; ep<cmplen; ep++ ) {
		if ( name[ep] == '=' ) break ;
	}
	ch = name[ep] ;
	name[ep] = '\0' ;
	strupper( name ) ;							/*  大文字変換			*/
	cmplen = strlen( name ) ;
	for ( sp=cp=0;; ) {
		while ( _peekb( env->seg,cp ) != '=' ) cp++ ;
		len = cp - sp ;
		*off = sp ;
		if ( len == cmplen ) {
			for ( cnt=0; cnt<len; cnt++ ) {
				if ( _peekb( env->seg,sp ) != name[cnt] ) break ;
				sp++ ;
			}
			if ( cnt >= len ) {
				ret = 0 ;
				break ;
			}
		}
		while ( _peekb( env->seg,cp ) != 0x00 ) cp++ ;
		sp = cp + 1 ;
		if ( _peekb( env->seg,sp ) == 0x00 ) {
			*off = 0 ;
			ret = 1 ;
			break ;
		}
	}
	name[ep] = ch ;

	return ret ;
}

int delEnvName( ENVINFS *env,char *name )
/*===========================================================================
*	環境変数削除
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: name	環境変数名
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	環境変数が見つからない
*
*	※ 環境変数名は大文字に変換されるので, 必要ならば退避しておくこと.
===========================================================================*/
{
	WORD	sp, dp, len, size ;

	if ( getEnvOff( env,name,&sp ) ) return 1 ;

	dp = sp ;
	while ( _peekb( env->seg,sp ) != 0x00 ) sp++ ;
	sp++ ;
	len  = sp - dp ;
	size = env->tail - sp + 1 ;
	env->res  += len ;							/*  残りﾊﾞｲﾄ数更新		*/
	env->tail -= len ;							/*  終端位置更新		*/
	if ( _peekb( env->seg,sp ) == 0x00 ) {		/*  最後の環境変数の時	*/
		_pokeb( env->seg,dp,0x00 ) ;
		return 0 ;
	}
	movedata( env->seg,sp,env->seg,dp,size ) ;

	return 0 ;
}

void clrEnvName( ENVINFS *env )
/*===========================================================================
*	環境変数を全て削除
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
===========================================================================*/
{
	_poke( env->seg,0,0x0000 ) ;				/*  環境変数全て削除	*/
	env->res  = env->size - 1 ;
	env->tail = 0 ;
}

int chkEnvUpdate( ENVINFS *env,char *name )
/*===========================================================================
*	環境変数更新ｻｲｽﾞﾁｪｯｸ
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: name	環境変数名
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	環境変数領域が不足
===========================================================================*/
{
	WORD	size, off, len ;

	size = strlen( name ) + 1 ;					/*  ﾇﾙ文字を含んだｻｲｽﾞ	*/
	if ( !getEnvOff( env,name,&off ) ) {		/*  環境変数あり		*/
		len = off ;
		while ( _peekb( env->seg,len ) != 0x00 ) len++ ;
		len++ ;
		len -= off ;
	} else {									/*  環境変数なし		*/
		len = 0 ;
	}
	if ( env->res + len < size ) return 1 ;		/*  環境変数領域不足	*/
	return 0 ;
}

int chkEnvName( char *name )
/*===========================================================================
*	環境変数領域に設定するための文字列ﾁｪｯｸ (余分な空白文字を削除)
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: name	環境変数名
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	環境変数に設定する文字列がない
*				= 2	環境変数名の指定誤り
*
*	※ 環境変数名の文字列は設定用に変換するので, 必要ならば退避しておくこと.
===========================================================================*/
{
	register WORD	cp, ep ;
	WORD	size ;

	size = strlen( name ) + 1 ;					/*  ﾇﾙ文字を含んだｻｲｽﾞ	*/
	if ( size == 0 ) return 2 ;
	if ( jstrchr( name,'<' ) != NULL ) return 2 ;
	if ( jstrchr( name,'>' ) != NULL ) return 2 ;
	if ( jstrchr( name,'|' ) != NULL ) return 2 ;
	for ( cp=0; cp<size; cp++ ) {
		if ( name[cp] == ' ' || name[cp] == '\t' ) break ;
	}
	for ( ep=0; ep<size; ep++ ) {
		if ( name[ep] == '=' ) break ;
	}
	if ( ep >= size ) return 2 ;				/*  '='がない			*/
	if ( cp < ep ) {						/*  '='の前に空白文字あり	*/
		strcpy( name+cp,name+ep ) ;				/*  '環境変数名='にする	*/
		size -= ( ep - cp ) ;
		ep = cp ;
	}
	if ( ep < 1 ) return 2 ;					/*  環境変数名がない	*/
	for ( cp=ep+1; cp<size; cp++ ) {
		if ( name[cp] != ' ' && name[cp] != '\t' ) break ;
	}
	if ( cp > ep+1 ) {
		strcpy( name+ep+1,name+cp ) ;			/*  '='後の空白文字削除	*/
		size -= ( cp - ep - 1 ) ;
	}
	for ( cp=size-2; cp>ep; cp-- ) {
		if ( name[cp] != ' ' && name[cp] != '\t' ) break ;
	}
	if ( cp < size - 2 ) {						/*  余分な空白文字削除	*/
		name[cp+1] = '\0' ;
		size = cp + 2 ;
	}
	if ( ep == size - 2 ) return 1 ;			/*  設定文字列なし		*/
}

int setEnvName( ENVINFS *env,char *name )
/*===========================================================================
*	環境変数追加
*	< IN  >	: env	環境ｾｸﾞﾒﾝﾄ情報
*			: name	環境変数名
*	< RET >	: ｴﾗｰｺｰﾄﾞを返す.
*				= 0	正常終了
*				= 1	環境変数に設定する文字列がない
*				= 2	環境変数領域が不足
*				= 3	環境変数名の指定誤り
*
*	※ 環境変数名の文字列は設定用に変換するので, 必要ならば退避しておくこと.
===========================================================================*/
{
	WORD	size ;
	int		ret ;

	ret = chkEnvName( name ) ;					/*  文字列ﾁｪｯｸ&変換		*/
	if ( ret == 2 ) return 3 ;					/*  環境変数名誤り		*/
	size = strlen( name ) + 1 ;					/*  ﾇﾙ文字を含んだｻｲｽﾞ	*/
	if ( chkEnvUpdate( env,name ) ) return 2 ;	/*  環境変数領域不足	*/
	delEnvName( env,name ) ;					/*  環境変数削除		*/
	if ( ret == 1 ) return 1 ;					/*  設定文字列なし		*/

	movedata( _seg( name ),_off( name ),env->seg,env->tail,size ) ;
	env->res  -= size ;							/*  残りﾊﾞｲﾄ数更新		*/
	env->tail += size ;							/*  終端位置更新		*/
	_pokeb( env->seg,env->tail,0x00 ) ;

	return 0 ;
}

/*	HISTORY
-----------------------------------------------------------------------------
*	1993.03.21 : CREATE
---------------------------------------------------------------------------*/
