/*
 * CONSLIB : Consoleライブラリ：メインルーチン
 * Copyright(C) 1997-1999 Eiichiro Ito, Yasuhiro Mizukoshi
 *
 * 1997/07/11:Eiichiro Ito
 *  (1) 作成開始
 * 修正履歴
 * 1998/02/15:Yasuhiro Mizukoshi
 *  (1) ESCシーケンス対応
 * 1998/02/19:Eiichiro Ito
 *  (1) 画面更新ロジック修正
 * 1999/10/24:Eiichiro Ito
 *  (1) catdirを.から始まるファイルを扱えるよう変更した
 *
 */
#define		STRICT
#include	"conslib.h"
#include	"client.h"
#include	<excpt.h>
#include	<windowsx.h>
#include	<winuser.h>
#include	<stdlib.h>
#include	<tchar.h>
#include	"resource.h"

#define		MAX_ARGV	(50)
#define		RCVSIZE		(256)
#define		REG_SUBKEY	TEXT("Software\\Gawaro\\Console\\Env")

HINSTANCE		hInst ;
HWND			hMainWnd ;
LPTSTR			CmdLine = 0 ;
HWND			hServer = 0 ;
HANDLE			hBreak = 0 ;
DWORD			Argc = 0 ;
LPTSTR			Argv[ MAX_ARGV ] ;
BOOL			Quote[ MAX_ARGV ] ;

static	TCHAR	ArgBuf[ 256 ] = TEXT("") ;
static	HANDLE	hGetchar = 0 ;
static	TCHAR	RcvBuffer[ RCVSIZE ] ;
static	DWORD	RcvIn = 0, RcvOut = 0 ;
static	TCHAR	MessageBuf[ 256 ] ;
static	DWORD	Retcode = 0 ;

static	BOOL	init_application( void ) ;
static	BOOL	init_instance( int nCmdShow ) ;
static	LRESULT CALLBACK	MainWndFunc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) ;
static	BOOL	InitClient( void ) ;
static	void	ReleaseClient( void ) ;
static	void	start_main( void ) ;
static	void	accept_char( TCHAR c ) ;
static	void	accept_str( TCHAR *buf ) ;

int WINAPI
#ifdef	_WIN32_WCE
WinMain( HINSTANCE hThisInst, HINSTANCE hPrevInst, LPTSTR lpszCmdLine, int nCmdShow )
#else	/* _WIN32_WCE */
WinMain( HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow )
#endif	/* _WIN32_WCE */
{
	MSG		msg ;

	hInst = hThisInst ;
#ifdef	_WIN32_WCE
	CmdLine = lpszCmdLine ;
#else	/* _WIN32_WCE */
	CmdLine = TEXT("") ;
#endif	/* _WIN32_WCE */
	_tcscpy( ArgBuf, CmdLine ) ;
	/* ウィンドウクラスを登録する */
	if ( !init_application() ) {
		goto ExitMain ;
	}
	/* ウィンドウを作成する */
	if ( !init_instance( nCmdShow ) ) {
		goto ExitMain ;
	}
	if ( !InitClient() ) {
		goto ExitMain ;
	}
	PostMessage( hMainWnd, WM_COMMAND, IDM_STARTMAIN, 0 ) ;
	while ( GetMessage( &msg, NULL, 0, 0 ) ) {
		TranslateMessage( &msg ) ;
		DispatchMessage( &msg ) ;
	}
	ReleaseClient() ;

ExitMain:
	return TRUE ;
}

static	BOOL
init_application( void )
{
	WNDCLASS	wcl ;

	/* メインウィンドウクラスを登録する */
	wcl.hInstance = hInst ;
	wcl.lpszClassName = ClassName ;
	wcl.lpfnWndProc = MainWndFunc ;
	wcl.style = CS_HREDRAW|CS_VREDRAW ;
	wcl.hIcon = NULL ;
	wcl.hCursor = NULL ;
	wcl.lpszMenuName = 0 ;
	wcl.cbClsExtra = 0 ;
	wcl.cbWndExtra = 0 ;
	wcl.hbrBackground = GetStockObject( WHITE_BRUSH ) ;
	if ( !RegisterClass( &wcl ) ) {
		wsprintf( MessageBuf, TEXT("RegisterClass:%d"), GetLastError() ) ;
		MessageBox( NULL, MessageBuf, AppName, MB_OK ) ;
		return FALSE ;
	}
	return TRUE ;
}

static	BOOL
init_instance( int nCmdShow )
{
	/* メインウィンドウを作成する */
	hMainWnd = CreateWindowEx( 0, ClassName, AppName,
#ifdef	_WIN32_WCE
						 WS_CAPTION,
						 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
#else	/* _WIN32_WCE */
						 WS_CAPTION|WS_VISIBLE,
						 0, 0, 240, 120,
#endif	/* _WIN32_WCE */
						 NULL, NULL, hInst, NULL ) ;
	if ( !hMainWnd ) {
		wsprintf( MessageBuf, TEXT("CreateWindow:%d"), GetLastError() ) ;
		MessageBox( NULL, MessageBuf, AppName, MB_OK ) ;
		return FALSE;
	}
	return TRUE ;
}

LRESULT CALLBACK
MainWndFunc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	HANDLE	hThread ;
	DWORD	threadID ;

	switch ( message ) {
	case WM_DESTROY:
		PostQuitMessage( 0 ) ;
		break ;
	case WM_COPYDATA:
		{
			COPYDATASTRUCT *cds = (COPYDATASTRUCT *)lParam ;
			accept_str( cds->lpData ) ;
		}
		return TRUE;
	case WM_COMMAND:
		switch ( GET_WM_COMMAND_ID( wParam, lParam ) ) {
		case IDM_EXIT:
			SendMessage( hWnd, WM_CLOSE, 0, 0 ) ;
			break;
		case IDM_ADDCHAR:
			accept_char( (TCHAR) lParam ) ;
			break ;
		case IDM_STARTMAIN:
			hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) start_main, 0, 0, &threadID ) ;
			if ( hThread == NULL ) {
				MessageBox( hMainWnd, TEXT("CreateThread failed"), TEXT("start_main"), MB_OK ) ;
			} else {
				CloseHandle( hThread ) ;
			}
			return TRUE ;
		case IDM_BREAK:
			SetEvent( hBreak ) ;
			accept_char( 'C' - '@' ) ;
			break ;
		}
		break ;
	case WM_CLOSE:
		DestroyWindow( hWnd ) ;
		break;
	default:
		return DefWindowProc( hWnd, message, wParam, lParam ) ;
	}
	return 0 ;
}

static	BOOL
InitClient( void )
{
	DWORD		i ;

	for ( i = 0 ; i < MAX_ARGV ; i ++ ) {
		Argv[ i ] = TEXT("") ;
	}
	Argc = 0 ;

	hServer = FindWindow( TcpServer, NULL ) ;
	if ( hServer == NULL ) {
#ifdef	_WIN32_WCE
		PROCESS_INFORMATION		pi ;

		if ( !CreateProcess( TcpServer, TEXT(""),
				NULL, NULL, FALSE,
				0,
				NULL, NULL, NULL,
				&pi ) ) {
			return FALSE ;
		}
		CloseHandle( pi.hProcess ) ;
		CloseHandle( pi.hThread ) ;
		Sleep( 2000 ) ;
		hServer = FindWindow( TcpServer, NULL ) ;
#else	/* _WIN32_WCE */
		return FALSE ;
#endif	/* _WIN32_WCE */
	} else {
		SetForegroundWindow( hServer ) ;
	}

	hGetchar = CreateEvent( NULL, FALSE, FALSE, NULL ) ;
	if ( hGetchar == NULL ) {
		wsprintf( MessageBuf, TEXT("Can't create event(%d)"), GetLastError() ) ;
		MessageBox( NULL, MessageBuf, TEXT("InitClient"), MB_OK ) ;
		return FALSE ;
	}
	hBreak = CreateEvent( NULL, FALSE, FALSE, NULL ) ;
	if ( hBreak == NULL ) {
		wsprintf( MessageBuf, TEXT("Can't create event(%d)"), GetLastError() ) ;
		MessageBox( NULL, MessageBuf, TEXT("InitClient"), MB_OK ) ;
		return FALSE ;
	}
	return SendMessage( hServer, WM_COMMAND, IDM_ADDCLIENT, (LPARAM) hMainWnd ) ;
}

static	void
ReleaseClient( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_DELCLIENT, (LPARAM) hMainWnd ) ;

	if ( hBreak ) {
		CloseHandle( hBreak ) ;
	}
	if ( hGetchar ) {
		CloseHandle( hGetchar ) ;
	}
}

static	void
start_main( void )
{
	TCHAR	retstr[ 11 ] ;

	cmdline2args( ArgBuf, &Argc, Argv, Quote, NULL ) ;
	__try {
		Retcode = Main( Argc, Argv ) ;
	} __except ( GetExceptionCode() == EXCEPTION_QUIT ) {
		// do nothing
	}
	wsprintf( retstr, TEXT("%d"), Retcode ) ;
	SetEnvVar( TEXT("RC"), retstr ) ;
	PostMessage( hMainWnd, WM_COMMAND, IDM_EXIT, 0 ) ;
	ExitThread( 0 ) ;
}

static	void
addtail_char( TCHAR c )
{
	DWORD	next = (RcvIn + 1) % RCVSIZE ;

	if ( RcvOut == next ) {
		return ;
	}
	RcvBuffer[ RcvIn ] = c ;
	RcvIn = next ;
}

static	void
accept_char( TCHAR c )
{
	addtail_char( c ) ;
	SetEvent( hGetchar ) ;
}

static void
accept_str( TCHAR *buf )
{
	TCHAR *p = buf ;

	while ( p && *p ) {
		addtail_char( *p++ ) ;
	}
	SetEvent( hGetchar ) ;
}

/* -------------------------------------------------- */
BOOL
Kbhit( void )
{
	return RcvIn != RcvOut ;
}

TCHAR
Getch( void )
{
	TCHAR	c ;
	DWORD	rc ;

	while ( 1 ) {
		while ( RcvIn == RcvOut ) {
			rc = WaitForSingleObject( hGetchar, 20 * 1000 ) ;
			if ( rc == WAIT_TIMEOUT ) {
				continue ;
			} else if ( rc != WAIT_OBJECT_0 ) {
				MessageBox( hMainWnd, TEXT("error"), TEXT("wait"), MB_OK ) ;
				continue ;
			}
		}
		c = RcvBuffer[ RcvOut ] ;
		RcvOut = (RcvOut + 1) % RCVSIZE ;
		return c ;
	}
}

int
CgetsVT( char *buf, int len )
{
	int		ret = 0 ;
	char	*p = buf ;

	if ( len > RCVSIZE ) {
		len = RCVSIZE ;
	}
	while ( RcvIn != RcvOut && ret < len ) {
		*p = (char) RcvBuffer[RcvOut] ;
		RcvOut = (RcvOut + 1) % RCVSIZE ;
		p ++ ;
		ret ++ ;
	}
	return ret ;
}

LPTSTR
Cgets( LPTSTR buf )
{
	LPTSTR	org = buf ;
	DWORD	col, line ;

	ResetBreak() ;
	GetCurPos( &col, &line ) ;
	while ( 1 ) {
		TCHAR	c = Getch() ;
		if ( c == '\r' ) {
			*buf = 0 ;
			c = TEXT('\n') ;
			Putch( c ) ;
			return org ;
		}
		if ( c == 3 ) {
			if ( SleepWithBreak( 0 ) ) {
				Putch( TEXT('\n') ) ;
				*org = 0 ;
				return org ;
			}
		} else if ( c == 8 ) {
			/* BS の場合 */
			if ( org != buf ) {
				c = *--buf ;
				*buf = 0 ;
				if ( c < ' ' ) {
					Putch( 8 ) ;
				}
				Putch( 8 ) ;
			}
		} else if ( c == 27 ) {
			*org = 0 ;
			buf = org ;
			SetCurPos( col, line ) ;
			ClearEol() ;
		} else if ( c == 0x7F ) {
		} else if ( c >= TEXT(' ') ) {
			*buf++ = c ;
			Putch( c ) ;
		} else {
			*buf++ = c ;
			Putch( TEXT('^') ) ;
			c += TEXT( '@' ) ;
			Putch( c ) ;
		}
	}
}

void
Putch( TCHAR c )
{
	SendMessage( hServer, WM_COMMAND, IDM_ADDCHAR, (LPARAM) c ) ;
}

void
Cputs( LPCTSTR buf )
{
	COPYDATASTRUCT	cds ;

	cds.dwData = TTY_NORMAL ;
	cds.cbData = (_tcslen( buf ) + 1) * sizeof (TCHAR) ;
	cds.lpData = (PBYTE) buf ;
	SendMessage( hServer, WM_COPYDATA, (WPARAM) hMainWnd, (LPARAM) &cds ) ;
}

void
CputsVT( LPCTSTR buf )
{
	COPYDATASTRUCT	cds ;

	cds.dwData = TTY_VT100 ;
	cds.cbData = (_tcslen(buf) + 1) * sizeof(TCHAR) ;
	cds.lpData = (PBYTE)buf ;
	SendMessage( hServer, WM_COPYDATA, (WPARAM) hMainWnd, (LPARAM) &cds ) ;
}

void
ResetTerminal( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_RESETTERMINAL, 0 ) ;
}

void
Cls( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_CLS, 0 ) ;
}

void
GetWH( LPDWORD w, LPDWORD h )
{
	DWORD	value ;

	value = SendMessage( hServer, WM_COMMAND, IDM_GETWH, 0 ) ;
	*w = value % 256 ;
	*h = value / 256 ;
}

void
GetCurPos( LPDWORD col, LPDWORD line )
{
	DWORD	value ;

	value = SendMessage( hServer, WM_COMMAND, IDM_GETCUR, 0 ) ;
	*col = value % 256 ;
	*line = value / 256 ;
}

void
SetCurPos( DWORD col, DWORD line )
{
	DWORD	value = line * 256 + col ;
	SendMessage( hServer, WM_COMMAND, IDM_SETCUR, value ) ;
}

void
SetCaret( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_SETCARET, 0 ) ;
}

void
ClearEol( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_CLEAR, 0 ) ;
}

void
ScrollUp( DWORD from, DWORD to )
{
	DWORD	value = from * 256 + to ;
	SendMessage( hServer, WM_COMMAND, IDM_SCROLLUP, value ) ;
}

void
ScrollDown( DWORD from, DWORD to )
{
	DWORD	value = from * 256 + to ;
	SendMessage( hServer, WM_COMMAND, IDM_SCROLLDOWN, value ) ;
}

DWORD
GetIndex( HWND hWnd )
{
	return SendMessage( hServer, WM_COMMAND, IDM_GETINDEX, (LPARAM) hWnd ) ;
}

BOOL
SwitchClient( DWORD index )
{
	return SendMessage( hServer, WM_COMMAND, IDM_SWITCH, (LPARAM) index ) ;
}

BOOL
SleepWithBreak( DWORD msec )
{
	DWORD	rc = WaitForSingleObject( hBreak, msec ) ;
	if ( rc == WAIT_TIMEOUT ) {
		return FALSE ;
	} else if ( rc != WAIT_OBJECT_0 ) {
		/* エラーもBREAKと同一視 */
		return TRUE ;
	}
	/* BREAK */
	return TRUE ;
}

void
ResetBreak( void )
{
	ResetEvent( hBreak ) ;
}

void
SendBreak( void )
{
	SendMessage( hServer, WM_COMMAND, IDM_BREAK, 0 ) ;
}

BOOL
ExecuteProcess( LPCTSTR cmd, LPTSTR arg )
{
#ifdef	_WIN32_WCE
	PROCESS_INFORMATION		pi ;

	ResetBreak() ;
	if ( CreateProcess( cmd, arg,
						NULL, NULL, FALSE,
						0,
						NULL, NULL, NULL,
						&pi ) ) {
		DWORD	ret ;
		CloseHandle( pi.hProcess ) ;
		while ( GetExitCodeThread( pi.hThread, &ret ) && ret == STILL_ACTIVE ) {
			if ( SleepWithBreak( 500 ) ) {
				break ;
			}
		}
		CloseHandle( pi.hThread ) ;
		return TRUE ;
	}
#endif	/* _WIN32_WCE */
	return FALSE ;
}

void
Exit( int code )
{
	Retcode = code ;
	RaiseException( EXCEPTION_QUIT, 0, 0, 0 ) ;
}

/*
 * コマンドライン関係
 */
void
cmdline2args( LPTSTR cmdline, LPDWORD argc, LPTSTR *argv, LPBOOL quote, LPTSTR args )
{
	TCHAR	c ;
	BOOL	f_skip = TRUE, f_quote = FALSE ;

	if ( args ) {
		*args = 0 ;
	}
	argv[ 0 ] = TEXT("") ;
	quote[ 0 ] = FALSE ;
	*argc = 1 ;
	while ( c = *cmdline ) {
		if ( f_skip ) {
			if ( !(c == TEXT(' ') || c == TEXT('\t')) ) {
				if ( args && *argc == 2 ) {
					_tcscpy( args, cmdline ) ;
				}
				if ( c == TEXT('"') ) {
					f_quote = TRUE ;
					cmdline ++ ;
				}
				quote[ (*argc) ] = f_quote ;
				argv[ (*argc) ++ ] = cmdline ;
				f_skip = FALSE ;
			}
		} else {
			if ( f_quote ) {
				if ( c == TEXT('"') ) {
					f_quote = FALSE ;
					*cmdline = 0 ;
					f_skip = TRUE ;
				}
			} else if ( (c == TEXT(' ') || c == TEXT('\t')) ) {
				*cmdline = 0 ;
				f_skip = TRUE ;
			}
		}
		cmdline ++ ;
	}
}

void
chop_quote( LPTSTR dst )
{
	TCHAR	c ;
	BOOL	f_first = TRUE ;
	LPTSTR	src = dst, last_quote = 0 ;

	while ( c = *src++ ) {
		if ( c == TEXT('"') ) {
			last_quote = dst ;
			if ( f_first ) {
				f_first = FALSE ;
				continue ;
			}
		} else {
			f_first = FALSE ;
			if ( c != TEXT(' ') ) {
				last_quote = 0 ;
			}
		}
		*dst++ = c ;
	}
	*dst = 0 ;
	if ( last_quote ) {
		*last_quote = 0 ;
	}
}

void
slash2backslash( LPTSTR str )
{
	LPTSTR	ptr ;

	/* '/'を'\'に変換する */
	while ( ptr = _tcschr( str, TEXT('/') ) ) {
		*ptr = TEXT('\\') ;
	}
}

void
catdir( LPTSTR body, LPCTSTR adddir, LPTSTR pPrevDir )
{
	DWORD	len ;
	LPTSTR	ptr ;

	slash2backslash( body ) ;
	if ( pPrevDir ) {
		_tcscpy( pPrevDir, TEXT("..") ) ;
	}
	while ( *adddir ) {
		if ( _tcschr( TEXT("/\\"), *adddir ) ) {
			/* 先頭が"/"か"\"の場合は読み飛ばす */
			adddir ++ ;
			continue ;
		}
		if ( adddir[0] == TEXT('.') && adddir[1] == TEXT('.') ) {
			ptr = _tcsrchr( body, TEXT('\\') ) ;
			if ( ptr && pPrevDir ) {
				_tcscpy( pPrevDir, ptr + 1 ) ;
			}
			if ( ptr && ptr != body ) {
				*ptr = 0 ;
			} else {
				_tcscpy( body, TEXT("\\") ) ;
			}
			adddir += 2 ;
		} else if ( adddir[0] == TEXT('.') && (adddir[1] == TEXT('\\') || adddir[1] == TEXT('\0')) ) {
			adddir ++ ;
		} else {
			break ;
		}
	}
	if ( !*adddir ) {
		return ;
	}
	len = _tcslen( body ) ;
	if ( !len || body[ len - 1 ] != TEXT('\\') ) {
		body[ len ++ ] = TEXT('\\') ;
	}
	_tcscpy( &body[ len ], adddir ) ;
	slash2backslash( body ) ;
}

void
rel2absdir( LPTSTR dst, DWORD chDst, LPCTSTR src )
{
	if ( !_tcschr( TEXT("/\\"), *src ) ) {
		GetCurDir( chDst, dst ) ;
		catdir( dst, src, NULL ) ;
	} else {
//		while ( src[1] && _tcschr( TEXT("/\\"), src[1] ) ) {
//			src ++ ;
//		}
		_tcscpy( dst, src ) ;
		slash2backslash( dst ) ;
	}
}

BOOL
is_dir( LPCTSTR path )
{
	DWORD	attr = GetFileAttributes( path ) ;
	if ( attr == 0xFFFFFFFF ) {
		return FALSE ;
	} else if ( attr & FILE_ATTRIBUTE_DIRECTORY ) {
		return TRUE ;
	}
	return FALSE ;
}

BOOL
is_file( LPCTSTR path )
{
	DWORD	attr = GetFileAttributes( path ) ;
	if ( attr == 0xFFFFFFFF ) {
		return FALSE ;
	}
	return TRUE ;
}

BOOL
UpDir( LPTSTR dir )
{
	DWORD	len = _tcslen( dir ), i ;

	if ( len < 2 ) {
		return FALSE ;
	}
	for ( i = len - 1 ; i ; i -- ) {
		if ( _tcschr( TEXT("/\\"), dir[ i ] ) ) {
			dir[ i ] = 0 ;
			return TRUE ;
		}
	}
	_tcscpy( dir, TEXT("\\") ) ;
	return TRUE ;
}

/*-------------------------------------------------------------------------*
 * 環境変数関係
 *-------------------------------------------------------------------------*/
BOOL
SetEnvVar( LPCTSTR name, LPCTSTR value )
{
	HKEY	hk ;
	LONG	lret ;
	LPBYTE	lpData ;
	DWORD	ret, dwType, cbData ;

	/* レジストリキーを作成する */
	lret = RegCreateKeyEx( HKEY_CURRENT_USER, REG_SUBKEY, 0, TEXT(""), 0,
#ifdef	_WIN32_WCE
						   0,
#else	/* _WIN32_WCE */
						   KEY_ALL_ACCESS,
#endif	/* _WIN32_WCE */
						   NULL, &hk, &ret ) ;
	if ( lret != ERROR_SUCCESS ) {
		return FALSE ;
	}
	if ( *value ) {
		dwType = REG_SZ ;
		lpData = (LPBYTE) value ;
		cbData = (_tcslen(value) + 1) * sizeof (*value) ;
		lret = RegSetValueEx( hk, name, 0, dwType, lpData, cbData ) ;
	} else {
		lret = RegDeleteValue( hk, name ) ;
	}
	RegCloseKey( hk ) ;
	return lret == ERROR_SUCCESS ;
}

DWORD
GetEnvVar( LPCTSTR name, LPTSTR value, DWORD size )
{
	HKEY	hk ;
	LONG	lret ;
	LPBYTE	lpData ;
	DWORD	dwType, cbData ;

	/* レジストリキーをオープンする */
	lret = RegOpenKeyEx( HKEY_CURRENT_USER,
						 REG_SUBKEY,
						 0,
						 KEY_QUERY_VALUE,
						 &hk ) ;
	if ( lret != ERROR_SUCCESS ) {
		return FALSE ;
	}
	dwType = REG_SZ ;
	lpData = (LPBYTE) value ;
	cbData = size * sizeof (*value) ;
	lret = RegQueryValueEx( hk, name, NULL, &dwType, lpData, &cbData ) ;
	RegCloseKey( hk ) ;
	if ( lret != ERROR_SUCCESS ) {
		return 0 ;
	} else if ( lret == ERROR_MORE_DATA ) {
		return cbData / sizeof (*value) ;
	}
	return (cbData / sizeof (*value)) - 1 ;
}

void
ExpandEnvVar( LPTSTR buf )
{
	LONG	rc ;
	HKEY	hKey ;
	LONG	lret ;
	DWORD	ValueSize = 0, ValueType, i ;
	static TCHAR	var[ MAX_PATH ] ;
	static TCHAR	ValueName[ MAX_PATH ] ;
	static TCHAR	ValueData[ MAX_PATH ] ;

	/* レジストリキーをオープンする */
	lret = RegOpenKeyEx( HKEY_CURRENT_USER,
						 REG_SUBKEY,
						 0,
						 KEY_QUERY_VALUE,
						 &hKey ) ;
	if ( lret != ERROR_SUCCESS ) {
		return ;
	}
	for ( i = 0 ; ; i ++ ) {
		DWORD	dwcValueName = MAX_PATH ;

		*ValueName = 0 ;
		ValueSize = sizeof ValueData ;

		rc = RegEnumValue( hKey, i, ValueName, &dwcValueName,
						   NULL, &ValueType, (LPBYTE) ValueData, &ValueSize ) ;
		if ( rc != (DWORD) ERROR_SUCCESS && rc != ERROR_INSUFFICIENT_BUFFER ) {
			break ;
		}
		wsprintf( var, TEXT("%%%s%%"), ValueName ) ;
		ExpandVar( buf, var, ValueData ) ;
	}
	RegCloseKey( hKey ) ;
	ClearUndefEnvVar( buf ) ;
}

void
ExpandVar( LPTSTR ptr, LPCTSTR var, LPCTSTR arg )
{
	static TCHAR	tmp[ MAX_PATH ] ;
	DWORD			len = wcslen( var ) ;
	DWORD			len_arg = wcslen( arg ) ;

	while ( ptr = _wcsistr( ptr, var ) ) {
		wcscpy( tmp, ptr + len ) ;
		wcscpy( ptr, arg ) ;
		ptr += len_arg ;
		wcscpy( ptr, tmp ) ;
	}
}

void
PrintEnvVar( LPCTSTR name )
{
	LONG	rc ;
	HKEY	hKey ;
	LONG	lret ;
	DWORD	ValueSize = 0, ValueType, i ;
	static TCHAR	buf[ MAX_PATH ] ;
	static TCHAR	ValueName[ MAX_PATH ] ;
	static TCHAR	ValueData[ MAX_PATH ] ;

	/* レジストリキーをオープンする */
	lret = RegOpenKeyEx( HKEY_CURRENT_USER,
						 REG_SUBKEY,
						 0,
						 KEY_QUERY_VALUE,
						 &hKey ) ;
	if ( lret != ERROR_SUCCESS ) {
		return ;
	}
	for ( i = 0 ; ; i ++ ) {
		DWORD	dwcValueName = MAX_PATH ;

		*ValueName = 0 ;
		ValueSize = sizeof ValueData ;

		rc = RegEnumValue( hKey, i, ValueName, &dwcValueName,
						   NULL, &ValueType, (LPBYTE) ValueData, &ValueSize ) ;
		if ( rc != (DWORD) ERROR_SUCCESS && rc != ERROR_INSUFFICIENT_BUFFER ) {
			break ;
		}
		if ( !name || !_wcsicmp( ValueName, name ) ) {
			wsprintf( buf, TEXT("%s=%s\n"), ValueName, ValueData ) ;
			Cputs( buf ) ;
		}
	}
	RegCloseKey( hKey ) ;
}

void
ClearUndefEnvVar( LPTSTR src )
{
	TCHAR	c ;
	LPTSTR	dst = src ;

	while ( c = *src++ ) {
		if ( c == TEXT('%') ) {
			while ( (c = *src++) && c != TEXT('%') ) {
			}
			if ( !c ) {
				break ;
			}
			continue ;
		}
		*dst++ = c ;
	}
	*dst = 0 ;
}

BOOL
SetCurDir( LPCTSTR dir )
{
	return SetEnvVar( TEXT("CD"), dir ) ;
}

DWORD
GetCurDir( DWORD size, LPTSTR buf )
{
	if ( GetEnvVar( TEXT("CD"), buf, size ) ) {
		return TRUE ;
	}
	_tcscpy( buf, TEXT("\\") ) ;
	return TRUE ;
}

/*
 * 文字列中から別の文字列を探す(大文字／小文字を区別しない)
 */
LPTSTR
_wcsistr( LPCTSTR org, LPCTSTR find )
{
	int		ret ;
	TCHAR	c = *find ;
	DWORD	len_org = wcslen( org ) ;
	DWORD	len_find = wcslen( find ) ;

	if ( !len_find || !len_org || len_org < len_find ) {
		return 0 ;
	}
	len_org -= len_find ;
	do {
		ret = _wcsnicmp( org, find, len_find ) ;
		if ( !ret ) {
			return (LPTSTR) org ;
		}
		org ++ ;
	} while ( len_org -- ) ;
	return 0 ;
}

/*
 * Consoleのバージョンを得る
 */
DWORD
GetConsoleVer( void )
{
	return SendMessage( hServer, WM_COMMAND, IDM_GETVERSION, 0 ) ;
}

/*
 * 表示する画面の属性を設定する
 */
void
SetAttr( BYTE attr )
{
	SendMessage( hServer, WM_COMMAND, IDM_SETATTR, (LPARAM) attr ) ;
}
