/* The source file was composed by module mixer */ 

#include  "include_c.h"


 
/*=================================================================*/
/* <<< [Global0/Global0.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [Globals_init_const] >>> 
************************************************************************/

void  Globals_init_const()
{
    AppKey_initGlobal_const(); 
}


 
/***********************************************************************
  <<< [Globals_init] >>> 
************************************************************************/
int  Globals_init()
{
  int  e;

    e= Locale_init(); IF(e)goto fin;

  e=0;
  goto fin;  // for avoid warning of no goto fin
fin:
  return  e;
}


 
/***********************************************************************
  <<< [Globals_finish] >>> 
************************************************************************/
int  Globals_finish( int e )
{
    e= AppKey_finishGlobal( e ); 

  return  e;
}


 
/*=================================================================*/
/* <<< [PlatformSDK_plus/PlatformSDK_plus.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [GetCommandLineUnnamed] >>> 
************************************************************************/
int  GetCommandLineUnnamed( int Index1, TCHAR* out_AParam, size_t AParamSize )
{
  TCHAR*  line = GetCommandLine();
  int     index;
  TCHAR*  p;
  TCHAR*  p2;
  TCHAR   c;

  #if UNDER_CE
    Index1 --;
  #endif
  IF( Index1 < 0 ) goto err_nf;
  index = Index1;

  p = line;

  for (;;) {
    while ( *p == _T(' ') )  p++;

    c = *p;

    if ( c == _T('\0') )  goto err_nf;  // Here is not decided to error or not


    //=== Skip named option
    else if ( c == _T('/') ) {
      p++;
      for (;;) {
        c = *p;
        if ( c == _T('"') || c == _T(' ') || c == _T('\0') )  break;
        p++;
      }

      if ( c == _T('"') ) {
        p++;
        while ( *p != _T('"') && *p != _T('\0') )  p++;
        if ( *p == _T('"') )  p++;
      }
    }

    //=== Skip or Get unnamed parameter
    else {
      while ( *p == _T(' ') )  p++;

      c = *p;
      p2 = p + 1;

      if ( c == _T('"') ) {
        p ++;
        while ( *p2 != _T('"') && *p2 != _T('\0') )  p2++;
      }
      else {
        while ( *p2 != _T(' ') && *p2 != _T('\0') )  p2++;
      }

      if ( index == 0 ) {
        int  e;

        e= stcpy_part_r( out_AParam, AParamSize, out_AParam, NULL, p, p2 );
          ASSERT_D( !e, __noop() );
        return  e;
      }
      else {
        p = ( *p2 == _T('"') ) ? p2+1 : p2;
        index --;
      }
    }
  }

err_nf:
  if ( AParamSize >= sizeof(TCHAR) )  *out_AParam = _T('\0');
  IF ( Index1 >= 2 )  return  E_NotFoundSymbol;
  return  0;
}


 
/***********************************************************************
  <<< [GetCommandLineNamed] >>> 
************************************************************************/
int  GetCommandLineNamed_sub( const TCHAR* Name, bool bCase, bool* out_IsExist, TCHAR* out_Value, size_t ValueSize );

int  GetCommandLineNamed( const TCHAR* Name, bool bCase, TCHAR* out_Value, size_t ValueSize )
{
  bool  is_exist;
  return  GetCommandLineNamed_sub( Name, bCase, &is_exist, out_Value, ValueSize );
}


int  GetCommandLineNamed_sub( const TCHAR* Name, bool bCase, bool* out_IsExist, TCHAR* out_Value, size_t ValueSize )
{
  TCHAR*  line = GetCommandLine();
  TCHAR*  p;
  TCHAR*  p2;
  TCHAR   c;
  const size_t  name_len = _tcslen( Name );
  bool    bMatch;

  *out_IsExist = true;

  p = line;
  for (;;) {
    c = *p;

    //=== Compare option name
    if ( c == _T('/') ) {
      p++;
      p2 = p;
      for (;;) {
        c = *p2;
        if ( c == _T(':') || c == _T(' ') || c == _T('\0') )  break;
        p2++;
      }
      if ( bCase )
        bMatch = ( p2-p == (int)name_len && _tcsncmp( p, Name, p2-p ) == 0 );
      else
        bMatch = ( p2-p == (int)name_len && _tcsnicmp( p, Name, p2-p ) == 0 );


      //=== Get the value
      if ( c == _T(':') ) {
        p = p2 + 1;
        if ( *p == _T('"') ) {
          p++;
          p2 = p;
          while ( *p2 != _T('"') && *p2 != _T('\0') )  p2++;
          if ( bMatch )
            return  stcpy_part_r( out_Value, ValueSize, out_Value, NULL, p, p2 );
          else
            p = p2+1;
        }
        else {
          p2 = p;
          while ( *p2 != _T(' ') && *p2 != _T('\0') )  p2++;
          if ( bMatch )
            return  stcpy_part_r( out_Value, ValueSize, out_Value, NULL, p, p2 );
          else
            p = p2;
        }
      }
      else {
        IF( bMatch ) return  E_NotFoundSymbol;  // no value error
      }
    }

    else if ( c == _T('\0') )  break;

    //=== Skip
    else if ( c == _T('"') ) {
      p++;
      while ( *p != _T('"') && *p != _T('\0') )  p++;
      while ( *p != _T(' ') && *p != _T('\0') )  p++;
    }
    else {
      while ( *p != _T(' ') && *p != _T('\0') )  p++;
    }
    while ( *p == _T(' ') )  p++;
  }

  *out_IsExist = false;
  return  E_NotFoundSymbol;
}


 
/***********************************************************************
  <<< [GetCommandLineNamedI] >>> 
************************************************************************/
int  GetCommandLineNamedI( const TCHAR* Name, bool bCase, int* out_Value )
{
  int    e;
  bool   is_exist;
  TCHAR  s[20];

  e= GetCommandLineNamed_sub( Name, bCase, &is_exist, s, sizeof(s) ); IF(e)goto fin;  //[out] s
  if ( s[0] == _T('0') && s[1] == _T('x') )
    *out_Value = _tcstoul( s, NULL, 16 );
  else
    *out_Value = _ttoi( s );
  //e=0;
fin:
  return  e;
}


 
/***********************************************************************
  <<< [GetCommandLineNamedC8] >>> 
************************************************************************/
#if  _UNICODE
int  GetCommandLineNamedC8( const TCHAR* Name, bool bCase, char* out_Value, size_t ValueSize )
{
  int     e;
  bool    is_exist;
  TCHAR*  s = NULL;

  s = (TCHAR*) malloc( ValueSize * sizeof(TCHAR) );
  e= GetCommandLineNamed_sub( Name, bCase, &is_exist, (TCHAR*) s, ValueSize * sizeof(TCHAR) ); IF(e)goto fin;

  sprintf_s( out_Value, ValueSize, "%S", s );
fin:
  if ( s != NULL )  free( s );
  return  e;
}
#endif


 
/***********************************************************************
  <<< [GetCommandLineExist] >>> 
************************************************************************/
bool  GetCommandLineExist( const TCHAR* Name, bool bCase )
{
  int     e;
  bool    is_exist;
  TCHAR   v[1];

  e = GetCommandLineNamed_sub( Name, bCase, &is_exist, v, sizeof(v) );
  if ( e == E_NotFoundSymbol )  Err2_clear();
  return  is_exist;
}


 
/***********************************************************************
  <<< [env_part] >>> 
************************************************************************/
int  env_part( TCHAR* Str,  unsigned StrSize,  TCHAR* StrStart, TCHAR** out_StrLast,
   const TCHAR* Input )
{
  int  e;
  const TCHAR*  p;
  const TCHAR*  p2;
  TCHAR*  o;
  TCHAR*  o_last;
  TCHAR   c;
  DWORD   r;
  DWORD   count;
  TCHAR   name[128];

  p = Input;  o = StrStart;  o_last = (TCHAR*)( (char*)Str + StrSize - sizeof(TCHAR) );
  IF_D( StrStart < Str || StrStart >= o_last ) goto err;

  c = *p;
  IF_D( StrSize <= 2 ) return  E_FewArray;

  while ( c != _T('\0') ) {
    if ( c == _T('%') ) {
      p++;
      if ( *p == _T('%') ) {

        //=== % 文字をコピーする
        IF( o == o_last )goto err_fa;
        *o = c;
        p++;  o++;
      }
      else {

        //=== 環境変数名を name に取得する
        p2 = _tcschr( p, _T('%') ); IF( p2 == NULL )goto err_ns;
        e= StrT_cpy( name, sizeof(name), _T("abc") ); IF(e)goto fin;
        e= stcpy_part_r( name, sizeof(name), name, NULL, p, p2 ); IF(e)goto fin;

        //=== 環境変数の値を o に取得する
        count = ( o_last + sizeof(TCHAR) - o ) / sizeof(TCHAR);
        r= GetEnvironmentVariable( name, o, count );
        IF( r==0 ) goto err_ns;  // 環境変数が見つからない
        IF( r > count ) goto err_fa;

        //=== p, o を更新する
        p = p2 + 1;
        o = _tcschr( o, _T('\0') );
      }
    }
    else {

      //=== 環境変数ではない部分をコピーする
      IF( o == o_last )goto err_fa;
      *o = c;
      p++;  o++;
    }
    c = *p;
  }

  e=0;
fin:
  *o = _T('\0');
  if ( out_StrLast != NULL )  *out_StrLast = o;
  return  e;

err_fa:  e = E_FewArray;  goto fin;
err_ns:  e = E_NotFoundSymbol; goto fin;
err:  e = E_Others;  goto fin;
}


 
/***********************************************************************
  <<< [env_malloc] >>> 
************************************************************************/
int  env_malloc( TCHAR** out_Value, size_t* out_ValueLen, const TCHAR* Name )
{
  int     e;
  DWORD   r;
  TCHAR*  value = NULL;

  r= GetEnvironmentVariable( Name, NULL, 0 );
  if ( r == 0 ) {
    *out_Value = NULL;
    return  0;  // 環境変数が定義されていないときでも、エラーにしない
  }

  value = (TCHAR*) malloc( r * sizeof(TCHAR) );
  IF( value == NULL ) goto err_fm;
  GetEnvironmentVariable( Name, value, r );

  *out_Value = value;
  if ( out_ValueLen != NULL )  *out_ValueLen = r - 1;

  e=0;
fin:
  return  e;

err_fm:  e = E_FewMemory;  goto fin;
}


 
/*=================================================================*/
/* <<< [Locale/Locale.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [g_LocaleSymbol] >>> 
************************************************************************/
char*  g_LocaleSymbol = "";


 
/***********************************************************************
  <<< [Locale_init] >>> 
************************************************************************/
int  Locale_init()
{
  g_LocaleSymbol = ".OCP";
  setlocale( LC_ALL, ".OCP" );
  return  0;
}

 
/***********************************************************************
  <<< [Locale_isInited] >>> 
************************************************************************/
int  Locale_isInited()
{
  return  ( g_LocaleSymbol[0] != '\0' );
   // ここが false を返すときの対処法は、Locale_isInited のヘルプを参照してください。
}

 
/*=================================================================*/
/* <<< [FileT/FileT.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [FileT_isExist] >>> 
************************************************************************/
bool  FileT_isExist( const TCHAR* path )
{
 #if ! FileT_isExistWildcard

  DWORD  r;

  if ( path[0] == _T('\0') )  return  false;
  r = GetFileAttributes( path );
  return  r != (DWORD)-1;

 #else

  HANDLE  find;
  WIN32_FIND_DATA  data;

  find = FindFirstFileEx( path, FindExInfoStandard, &data,
    FindExSearchNameMatch, NULL, 0 );

  if ( find == INVALID_HANDLE_VALUE ) {
    return  false;
  }
  else {
    FindClose( find );
    return  true;
  }

 #endif
}


 
/***********************************************************************
  <<< [FileT_isFile] >>> 
************************************************************************/
bool  FileT_isFile( const TCHAR* path )
{
  DWORD  r = GetFileAttributes( path );
  return  ( r & (FILE_ATTRIBUTE_DIRECTORY | 0x80000000) ) == 0;
    // 0x80000000 は、ファイルやフォルダが存在しないことを判定するため
}


 
/***********************************************************************
  <<< [FileT_isDir] >>> 
************************************************************************/
bool  FileT_isDir( const TCHAR* path )
{
  DWORD  r = GetFileAttributes( path );
  return  ( r & (FILE_ATTRIBUTE_DIRECTORY | 0x80000000) ) == FILE_ATTRIBUTE_DIRECTORY;
    // 0x80000000 は、ファイルやフォルダが存在しないことを判定するため
}


 
/***********************************************************************
  <<< [FileT_callByNestFind] サブフォルダも含めて各ファイルのパスを渡す >>> 
************************************************************************/
typedef struct {
  /*--- inherit from FileT_CallByNestFindData */
  void*     CallerArgument;
  TCHAR*    AbsPath;  // abstruct path
  TCHAR*    StepPath;
  TCHAR*    FileName;
  DWORD     FileAttributes;

  /*---*/
  BitField  Flags;
  FuncType  CallbackFromNestFind;
  TCHAR     AbsPathMem[4096];
} FileT_CallByNestFindDataIn;

int  FileT_callByNestFind_sub( FileT_CallByNestFindDataIn* m );


int  FileT_callByNestFind( const TCHAR* Path, BitField Flags, void* Argument, FuncType Callback )
{
  int  e;
  FileT_CallByNestFindDataIn  data;

  {
    TCHAR*  p;

    e= StrT_cpy( data.AbsPathMem, sizeof(data.AbsPathMem), Path ); IF(e)goto fin;


    /* AbsPathMem の最後に \ が無いなら追加する */
    p = _tcschr( data.AbsPathMem, _T('\0') );
    p--;
    if ( *p != _T('\\') ) {
      p++;
      IF( p >= data.AbsPathMem + (sizeof(data.AbsPathMem) / sizeof(TCHAR)) - 1 )goto err_fa;
      *p = _T('\\');
    }


    /* data を初期化する */
    data.CallerArgument = Argument;
    data.AbsPath = data.AbsPathMem;
    data.StepPath = p + 1;
    data.FileName = p + 1;
    data.Flags = Flags;
    data.CallbackFromNestFind = Callback;
  }

  /* 再起呼び出し関数へ */
  e= FileT_callByNestFind_sub( &data ); IF(e)goto fin;

  e=0;
fin:
  return  e;
err_fa: e= E_FewArray; goto fin;
}


int  FileT_callByNestFind_sub( FileT_CallByNestFindDataIn* m )
{
  int  e;
  HANDLE  find;
  WIN32_FIND_DATA  data;
  TCHAR*  p;
  int  done;


  /* Path に指定したフォルダに対してコールバックする */
  if ( m->Flags & FileT_FolderBeforeFiles ) {
    *( m->FileName - 1 ) = _T('\0');  // m->AbsPath の最後の \ を一時的にカット
    *( m->FileName ) = _T('\0');  // m->FileName, m->StepPath を "" にする
    m->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;

    if ( m->StepPath[0] == _T('\0') ) {
      TCHAR*  step_path = m->StepPath;
      TCHAR*  fname     = m->FileName;

      m->StepPath = _T(".");
      m->FileName = StrT_refFName( m->AbsPath );
      e= m->CallbackFromNestFind( m ); IF(e)goto fin;
      m->StepPath = step_path;
      m->FileName = fname;
    }
    else if ( m->FileName[0] == _T('\0') ) {
      TCHAR*  fname = m->FileName;

      m->FileName = StrT_refFName( m->AbsPath );
      e= m->CallbackFromNestFind( m ); IF(e)goto fin;
      m->FileName = fname;
    }
    else {
      e= m->CallbackFromNestFind( m ); IF(e)goto fin;
    }
    *( m->FileName - 1 ) = _T('\\');
  }


  /* * を追加 */
  p = m->FileName;
  IF( p >= m->AbsPathMem + (sizeof(m->AbsPathMem) / sizeof(TCHAR)) - 2 )goto err_fa;
  *p = _T('*');  *(p+1) = _T('\0');


  /* ファイルかフォルダを列挙します */
  find = FindFirstFileEx( m->AbsPathMem, FindExInfoStandard, &data,
    FindExSearchNameMatch, NULL, 0 );
  done = ( find == INVALID_HANDLE_VALUE );

  while (!done)
  {
    if ( _tcscmp( data.cFileName, _T(".") ) == 0 ||
         _tcscmp( data.cFileName, _T("..") ) == 0 ) {
      done = ! FindNextFile( find, &data );
      continue;
    }

    StrT_cpy( m->FileName,
      sizeof(m->AbsPathMem) - ( (char*)m->FileName - (char*)m->AbsPathMem ),
      data.cFileName );
    m->FileAttributes = data.dwFileAttributes;

    if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
      TCHAR*  prev_fname = m->FileName;

      p = _tcschr( m->FileName, _T('\0') );

      IF( p >= m->AbsPathMem + (sizeof(m->AbsPathMem) / sizeof(TCHAR)) - 2 )goto err_fa;
      *p = _T('\\');  *(p+1) = _T('\0');
      m->FileName = p + 1;

      e= FileT_callByNestFind_sub( m ); IF(e)goto fin;  /* 再起呼び出し */

      m->FileName = prev_fname;
    }
    else {
      e= m->CallbackFromNestFind( m ); IF(e)goto fin;
    }

    done = ! FindNextFile( find, &data );
  }
  FindClose( find );


  /* Path に指定したフォルダに対してコールバックする */
  if ( m->Flags & FileT_FolderAfterFiles ) {
    TCHAR*  step_path = m->StepPath;
    TCHAR*  fname     = m->FileName;

    *( m->FileName - 1 ) = _T('\0');
    m->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
    if ( ( *( m->StepPath - 1 ) == _T('\0') ) && ( m->StepPath > m->AbsPath ) ) {
      m->StepPath = _T(".");
    }
    m->FileName = StrT_refFName( m->AbsPath );

    e= m->CallbackFromNestFind( m ); IF(e)goto fin;

    m->StepPath = step_path;
    m->FileName = fname;
  }

  e=0;
fin:
  return  e;
err_fa: e= E_FewArray; goto fin;
}


 
/***********************************************************************
  <<< [FileT_openForRead] >>> 
************************************************************************/
int  FileT_openForRead( FILE** out_pFile, const TCHAR* Path )
{
  errno_t  en;

  assert( Locale_isInited() );

  #if DEBUGTOOLS_USES
    { int e= Debug_onOpen( Path ); if(e) return e; }
  #endif

  en = _tfopen_s( out_pFile, Path, _T("r")_T(fopen_ccs) );
  if ( en == 2 ) {
    _tprintf( _T("not found \"%s\"\n"), Path );

    #ifndef UNDER_CE
    {
      TCHAR  cwd[512];

      if ( _tgetcwd( cwd, _countof(cwd) ) != NULL )
        _tprintf( _T("current = \"%s\"\n"), cwd );
    }
    #endif

    return  E_NotFoundSymbol;
  }
  IF(en)return  E_Others;

  return  0;
}


 
/***********************************************************************
  <<< [FileT_openForWrite] >>> 
************************************************************************/
int  FileT_openForWrite( FILE** out_pFile, const TCHAR* Path, int Flags )
{
	int      e;
	errno_t  en;
	TCHAR*   open_type;
	BOOL     b;
	int      retry_count;

	IF_D( ! Locale_isInited() )  return  E_NotInitGlobal;

	#if Uses_AppKey
		e= AppKey_addNewWritableFolder( Path ); IF(e)goto fin;
	#endif

	if ( Flags & F_Append )
		open_type = ( Flags & F_Unicode ? _T("a")_T(fopen_ccs) : _T("at") );
	else
		open_type = ( Flags & F_Unicode ? _T("w")_T(fopen_ccs) : _T("wt") );

	#if DEBUGTOOLS_USES
		{ int e= Debug_onOpen( Path ); if(e) return e; }
	#endif

	for ( retry_count = 0;  ;  retry_count ++ ) {
		en = _tfopen_s( out_pFile, Path, open_type );
		if ( en != EACCES )  break;

		retry_count += 1;
		if ( retry_count == 15 )  break;
		Sleep( 1000 );
	}
	if ( en == 2 ) {  // ENOENT
		e= FileT_mkdir( Path ); IF(e)goto fin;
		b= RemoveDirectory( Path ); IF(!b)goto err_gt;
		en = _tfopen_s( out_pFile, Path, open_type );

		IF ( en == 2 ) {
			_tprintf( _T("cannot open \"%s\"\n"), Path );

			#ifndef UNDER_CE
			{
				TCHAR  cwd[512];

				if ( _tgetcwd( cwd, _countof(cwd) ) != NULL )
					_tprintf( _T("current = \"%s\"\n"), cwd );
			}
			#endif

			e = E_NotFoundSymbol;
			goto fin;
		}
	}
	IF(en)goto err;

	e=0;
fin:
	return  e;

err:  e = E_Others;  goto fin;
err_gt:  e = E_GetLastError;  goto fin;
}


 
/***********************************************************************
  <<< [FileT_close] >>> 
************************************************************************/
int  FileT_close( FILE* File, int e )
{
  if ( File != NULL ) {
    int r = fclose( File );
    IF(r&&!e)e=E_Errno;
  }
  return e;
}



 
/***********************************************************************
  <<< [FileT_mkdir] >>> 
************************************************************************/
int  FileT_mkdir( const TCHAR* Path )
{
  int    e;
  DWORD  r;
  TCHAR* p;
  BOOL   b;
  int    n_folder = 0;
  TCHAR  path2[MAX_PATH];


  e= StrT_getAbsPath( path2, sizeof(path2), Path, NULL ); IF(e)goto fin;
  #if Uses_AppKey
    e= AppKey_addNewWritableFolder( path2 ); IF(e)goto fin;
  #endif


  //=== 存在するフォルダを探す
  for (;;) {
    r = GetFileAttributes( path2 );
    if ( r != (DWORD)-1 ) break;  // "C:" もフォルダと判定される

    p = StrT_refFName( path2 ) - 1;  // 絶対パスなので必ず *p=='\\'
    *p = _T('\0');
    n_folder ++;
  }
  IF ( ! (r & FILE_ATTRIBUTE_DIRECTORY) ) goto err;  // ファイルならエラー


  //=== フォルダを作成する
  for ( ;  n_folder > 0;  n_folder -- ) {
    *p = _T('\\');
    b= CreateDirectory( path2, NULL ); IF(!b)goto err;
    p = _tcschr( p, _T('\0') );
  }

  e=0;
fin:
  return  e;

err:  e = E_Others;  goto fin;
}



 
/***********************************************************************
  <<< [FileT_copy] >>> 
************************************************************************/
int  FileT_copy_sub( FileT_CallByNestFindData* m );

int  FileT_copy( const TCHAR* SrcPath, const TCHAR* DstPath )
{
  const TCHAR*  p_last;
  int    e;
  BOOL   b;
  TCHAR  path[MAX_PATH*4];


  #if Uses_AppKey
    e= AppKey_addNewWritableFolder( DstPath ); IF(e)goto fin;
  #endif

  p_last = _tcschr( SrcPath, _T('\0') );
  IF_D( p_last <= SrcPath + 1 )goto err_ni;


  //=== フォルダをコピーする
  if ( *(p_last - 1) == _T('*') ) {
    IF_D( *(p_last - 2) != _T('\\') ) goto err_ni;

    e= StrT_getParentAbsPath( path, sizeof(path), SrcPath, NULL ); IF(e)goto fin;
    IF_D( ! FileT_isDir( path ) )goto err_nf;

    e= FileT_callByNestFind( path, FileT_FolderBeforeFiles, (void*) DstPath, (FuncType) FileT_copy_sub );
    IF(e)goto fin;
  }


  //=== ファイルをコピーする
  else {
    IF_D( _tcschr( SrcPath, _T('*') ) != NULL )goto err_ni;
    IF_D( ! FileT_isFile( SrcPath ) ) goto err_nf;

    b= CopyFile( SrcPath, DstPath, FALSE );
    if (!b) {
      if ( FileT_isDir( DstPath ) ) {
        e= stprintf_r( path, sizeof(path), _T("%s\\%s"), DstPath, StrT_refFName( SrcPath ) ); IF(e)goto fin;
        b= CopyFile( SrcPath, path, FALSE ); IF(!b)goto err_gt;
      }
      else {
        int  ee;

        p_last = _tcschr( DstPath, _T('\0') ) - 1;
        IF_D( p_last < DstPath )goto err;
        if ( *p_last == _T('\\') ) {
          ee= FileT_mkdir( DstPath ); IF(ee)goto fin;
          e= stprintf_r( path, sizeof(path), _T("%s%s"), DstPath, StrT_refFName( SrcPath ) ); IF(e)goto fin;
          b= CopyFile( SrcPath, path, FALSE ); IF(!b)goto err_gt;
        }
        else {
          e = E_AccessDenied;
          ee= StrT_getParentAbsPath( path, sizeof(path), DstPath, NULL ); IF(ee)goto fin;
          ee= FileT_mkdir( path ); IF(ee)goto fin;
          b= CopyFile( SrcPath, DstPath, FALSE ); IF(!b)goto err_gt;
        }
      }
    }
  }

  e=0;
fin:
  return  e;

err_ni:  e = E_NotImplementYet;  goto fin;
err_nf:  e = E_PathNotFound;  goto fin;
err_gt:  e = E_GetLastError;  goto fin;
err:  e = E_Others;  goto fin;
}


int  FileT_copy_sub( FileT_CallByNestFindData* m )
{
  const  TCHAR*  DstPath = (const TCHAR*) m->CallerArgument;
  int    e;
  BOOL   b;
  TCHAR  path[MAX_PATH*4];

  e= stprintf_r( path, sizeof(path), _T("%s\\%s"), DstPath, m->StepPath ); IF(e)goto fin;

  if ( m->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
    if ( ! FileT_isDir( path ) )
      { b= CreateDirectory( path, NULL ); IF(!b)goto err_gt; }
  }
  else {
    b= CopyFile( m->AbsPath, path, FALSE ); IF(!b)goto err_gt;
  }

  e=0;
fin:
  return  e;

err_gt:  e = E_GetLastError;  goto fin;
}



 
/***********************************************************************
  <<< [FileT_del] >>> 
************************************************************************/
int  FileT_del_sub( FileT_CallByNestFindData* m );

int  FileT_del( const TCHAR* Path )
{
  int    e;
  DWORD  r;
  TCHAR  abs_path[MAX_PATH];

  e= StrT_getAbsPath( abs_path, sizeof(abs_path), Path, NULL ); IF(e)goto fin;
  #if Uses_AppKey
    e= AppKey_addNewWritableFolder( abs_path ); IF(e)goto fin;
  #endif

  r= GetFileAttributes( Path );
  if ( r != (DWORD)-1 ) {
    if ( r & FILE_ATTRIBUTE_DIRECTORY ) {
      e= FileT_callByNestFind( Path, FileT_FolderAfterFiles, NULL, (FuncType) FileT_del_sub );
    }
    else {
      BOOL  b= DeleteFile( Path ); IF(!b)goto err_gt;
    }
  }
  IF_D( FileT_isExist( Path ) )goto err_ad;

  e=0;
fin:
  return  e;

err_gt:  e = E_GetLastError;  goto fin;
err_ad:  e = E_AccessDenied;  goto fin;
}


int  FileT_del_sub( FileT_CallByNestFindData* m )
{
  int   e;
  BOOL  b;

  if ( m->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
    b= RemoveDirectory( m->AbsPath ); IF(!b)goto err_gt;
  }
  else {
    b= DeleteFile( m->AbsPath ); IF(!b)goto err_gt;
  }

  e=0;
fin:
  return  e;

err_gt:  e = E_GetLastError;  goto fin;
}
 
/***********************************************************************
  <<< [AppKey] >>> 
************************************************************************/
int   AppKey_create( AppKey** out_m );
bool  AppKey_isSame( AppKey* m );
void  Writables_init_const( Writables* m );
int   Writables_init( Writables* m, AppKey* Key );
int   Writables_finish( Writables* m, int e );
int   Writables_isInited( Writables* m );
int   Writables_clearPaths( Writables* m );
int   Writables_create( Writables** out_m, AppKey* Key );
int   Writables_copyToConst( Writables* To, Writables* From );


typedef struct _CurrentWritables  CurrentWritables;
struct _CurrentWritables {
  Writables  m_CurrentWritables;
  TCHAR*   m_ProgramFiles;  size_t  m_ProgramFiles_Len;
  TCHAR*   m_windir;        size_t  m_windir_Len;
  TCHAR*   m_APPDATA;       size_t  m_APPDATA_Len;
  TCHAR*   m_LOCALAPPDATA;  size_t  m_LOCALAPPDATA_Len;
};
void  CurrentWritables_init_const( CurrentWritables* m );
int   CurrentWritables_init( CurrentWritables* m );
int   CurrentWritables_finish( CurrentWritables* m, int e );
int   CurrentWritables_askFileAccess( CurrentWritables* m, const TCHAR* AbsPath );


//////////////////////////////


static  AppKey   g_AppKey;
static  AppKey*  g_AppKeyPrivate;

// under g_AppKey
 Writables  g_DefaultWritables;
 Writables  g_CurrentWritables; // public
 static CurrentWritables  g_CurrentWritablesPrivate;


static int  AppKey_create( AppKey** out_m )
{
  int  e;

  IF( g_AppKeyPrivate != NULL ) { e=1; goto fin; }
    // AppKey_newWritable の in_out_m = NULL のときは、2回呼び出すことはできません。

  Writables_init_const( &g_DefaultWritables );
  CurrentWritables_init_const( &g_CurrentWritablesPrivate );

  e= CurrentWritables_init( &g_CurrentWritablesPrivate ); IF(e)goto fin;
  e= Writables_copyToConst( &g_CurrentWritables, NULL ); IF(e)goto fin;

  *out_m = &g_AppKey;
  g_AppKeyPrivate = &g_AppKey;

  e=0;
fin:
  return  e;
}


static bool  AppKey_isSame( AppKey* m )
{
  return  ( m == g_AppKeyPrivate );
}


void  AppKey_initGlobal_const()
{
  Writables_init_const( &g_DefaultWritables );
}


int  AppKey_finishGlobal( int e )
{
  int ee;

  e= Writables_finish( &g_DefaultWritables, e );
  e= CurrentWritables_finish( &g_CurrentWritablesPrivate, e );
  ee= Writables_copyToConst( &g_CurrentWritables, NULL ); IF(ee&&!e)e=ee;

  return  e;
}


 
/***********************************************************************
  <<< [AppKey_newWritable] >>> 
************************************************************************/
int  AppKey_newWritable( AppKey** in_out_m,  Writables** out_Writable,  ... )
{
  int  e;
  AppKey*  m = NULL;
  Writables*  wr = NULL;
 #if Uses_OutMallocIDTool
  bool  is_prev_out_malloc;

  is_prev_out_malloc = OutMallocID_setEnable( false );
 #endif


  //=== AppKey* m を有効にする
  if ( in_out_m == NULL ) {  // 今回の関数の中だけで参照する
    e= AppKey_create( &m ); IF(e)goto resume;
  }
  else if ( *in_out_m == NULL ) {  // １回目に本関数を呼び出したモジュールに渡す
    e= AppKey_create( &m ); IF(e)goto resume;
    *in_out_m = m;
  }
  else {  // 本関数を呼び出したモジュールから渡ったものを使う
    m = *in_out_m;
  }


  //=== 正規の AppKey かチェックする
  IF( ! AppKey_isSame( m ) )goto err;


  //=== Writable を生成する
  if ( out_Writable == NULL ) {
    wr = &g_DefaultWritables;
    e= Writables_finish( wr, 0 ); IF(e)goto resume;
    e= Writables_init( wr, m ); IF(e)goto resume;
  }
  else {
    e= Writables_create( &wr, m ); IF(e)goto resume;
    *out_Writable = wr;
  }


  //=== Writable にパスを登録する
  {
    va_list  va;
    TCHAR*   path;
    int  i;

    va_start( va, out_Writable );
    for ( i=0; ; i++ ) {
      path = va_arg( va, TCHAR* );
      if ( path == NULL )  break;
      IF( i == 5 ) goto err;  // 最後の NULL 忘れ対策

      e= Writables_add( wr, m, path ); IF(e)goto resume;
    }
    va_end( va );
  }
  #if defined(TempFile_get)
  {
    TempFile*   temp;

    e= TempFile_get( &temp ); IF(e)goto resume;
    e= Writables_add( wr, m, temp->TempPath ); IF(e)goto resume;
  }
  #endif


  //=== すぐに Writable を有効にする
  if ( out_Writable == NULL ) {
    e= Writables_enable( wr ); IF(e)goto resume;
  }

  e=0;
fin:
  #if Uses_OutMallocIDTool
    OutMallocID_setEnable( is_prev_out_malloc );
  #endif
  return  e;

err:  e = E_Others;  goto resume;
resume:
  if ( wr != NULL ) {
    if ( out_Writable == NULL )
      e= Writables_finish( wr, e );  // g_DefaultWritables
    else
      e= Writables_delete( wr, e );
  }
  goto fin;
}


 
/***********************************************************************
  <<< [AppKey_addNewWritableFolder] チェックする、または追加する >>> 
（追加は未対応）
************************************************************************/
int  AppKey_addNewWritableFolder( const TCHAR* Path )
{
  int  e;
  TCHAR**     pp;
  TCHAR**     pp_over;
  Writables*  wr = &g_CurrentWritablesPrivate.m_CurrentWritables;
  size_t      path_len;
  TCHAR       abs_path[MAX_PATH];

  if ( g_AppKeyPrivate == NULL ) {
    e= AppKey_newWritable( NULL, NULL, ".", NULL ); IF(e)goto fin;
  }

  e= StrT_getAbsPath( abs_path, sizeof(abs_path), Path, NULL ); IF(e)goto fin;

  pp_over = wr->m_Paths + wr->m_nPath;
  for ( pp = wr->m_Paths;  ;  pp++ ) {
    IF ( pp >= pp_over )goto err_wr;
      // Path (abs_path) は、AppKey_newWritable で許可されていません。
      // ウォッチ・ウィンドウに g_CurrentWritables.m_Paths,3 などを入力して
      // 許可されているパスを確認してください。

    path_len = _tcslen( *pp );
    if ( _tcsnicmp( *pp, abs_path, path_len ) == 0 &&
         ( abs_path[ path_len ] == _T('\\') || abs_path[ path_len ] == _T('\0') ) )  break;
  }

  e=0;
fin:
  return  e;

err_wr:  e = E_OutOfWritable;  goto fin;
}



 
/***********************************************************************
  <<< [Writables_init] >>> 
************************************************************************/
static void  Writables_init_const( Writables* m )
{
  m->m_Paths = NULL;
  m->m_nPath = -1;
}


static int  Writables_init( Writables* m, AppKey* Key )
{
  IF( ! AppKey_isSame( Key ) ) return E_Others;

  m->m_Paths = NULL;
  m->m_nPath = 0;
  return  0;
}


static int  Writables_finish( Writables* m, int e )
{
  int  ee;

  ee= Writables_clearPaths( m ); IF(ee&&!e) e=ee;
  m->m_nPath = -1;
  return  e;
}


static int  Writables_isInited( Writables* m )
{
  return  ( m->m_nPath != -1 );
}


static int  Writables_clearPaths( Writables* m )
{
  TCHAR**  p;
  TCHAR**  p_over;

  if ( m->m_Paths != NULL ) {
    p_over = m->m_Paths + m->m_nPath;
    for ( p = m->m_Paths;  p < p_over;  p++ ) {
      free( *p );
    }
    free( m->m_Paths );  m->m_Paths = NULL;
  }
  m->m_nPath = 0;
  return  0;
}


static int  Writables_create( Writables** out_m, AppKey* Key )
{
  Writables*  m;  int  e;

  m = (Writables*) malloc( sizeof(*m) ); IF(m==NULL)return E_FewMemory;
  Writables_init_const( m );
  e= Writables_init( m, Key ); IF(e)goto resume;
  *out_m = m;
  return  0;
resume: Writables_delete( m, 0 ); free(m); return  e;
}


int  Writables_delete( Writables* m, int e )
{
  if ( m == NULL ) goto fin;
  e= Writables_finish( m, e );
  free( m );
fin:
  return  e;
}


 
/***********************************************************************
  <<< [Writables_add] >>> 
************************************************************************/
int  Writables_add( Writables* m, AppKey* Key, TCHAR* Path )
{
  int  e;
  TCHAR**   pp;
  TCHAR*    p  = NULL;
  size_t    path_size;
  TCHAR     abs_path[MAX_PATH];

  IF( ! AppKey_isSame( Key ) )goto err;
  if ( Path[0] == _T('\0') ) return 0;

  e= StrT_getAbsPath( abs_path, sizeof(abs_path), Path, NULL ); IF(e)goto resume;

  e= CurrentWritables_askFileAccess( &g_CurrentWritablesPrivate, abs_path ); IF(e)goto resume;

  pp = (TCHAR**) realloc( m->m_Paths, (m->m_nPath + 1) * sizeof(TCHAR*) );
  IF( pp == NULL ) goto err;
  m->m_Paths = pp;

  path_size = (_tcslen( abs_path ) + 1) * sizeof(TCHAR);
  p = (TCHAR*) malloc( path_size );
  m->m_Paths[ m->m_nPath ] = p;
  IF( p == NULL )goto err;

  memcpy( p, abs_path, path_size );
  if ( p[ path_size/sizeof(TCHAR) - 2 ] == _T('\\') )
       p[ path_size/sizeof(TCHAR) - 2 ] =  _T('\0');
  m->m_nPath ++;

  e=0;
fin:
  return  e;

err:  e = E_Others;  goto resume;
resume:
  if ( p != NULL )  free( p );
  goto fin;
}


int  Writables_remove( Writables* m, TCHAR* Path )
{
  TCHAR**  pp;
  TCHAR**  pp_over;

  pp_over = m->m_Paths + m->m_nPath;
  for ( pp = m->m_Paths;  ;  pp++ ) {
    if ( pp >= pp_over )  return  0;
    if ( _tcscmp( *pp, Path ) == 0 )  break;
  }
  free( *pp );
  memmove( pp, pp+1, (char*)pp - (char*)pp_over - sizeof(TCHAR) );
  m->m_nPath --;

  #if _DEBUG
    *( pp_over - 1 ) = NULL;
  #endif

  return  0;
}


 
/***********************************************************************
  <<< [Writables_enable] >>> 
************************************************************************/
int  Writables_enable( Writables* m )
{
  int  e;

  e= Writables_copyToConst( &g_CurrentWritablesPrivate.m_CurrentWritables, m ); IF(e)goto fin;
  e= Writables_copyToConst( &g_CurrentWritables, &g_CurrentWritablesPrivate.m_CurrentWritables ); IF(e)goto fin;

  e=0;
fin:
  return  e;
}


int  Writables_disable( Writables* m, int e )
{
  int  ee;

  ee= Writables_copyToConst( &g_CurrentWritablesPrivate.m_CurrentWritables, NULL ); IF(ee&&!e)goto fin;
  ee= Writables_copyToConst( &g_CurrentWritables, NULL ); IF(ee&&!e)goto fin;

  e=0;
fin:
  return  e;
}


static int  Writables_copyToConst( Writables* To, Writables* From )
{
  int  e;
  TCHAR**  pp;
  TCHAR**  pp_over;
  TCHAR*   p2;
  TCHAR**  pp2;
  size_t   path_size;

  if ( To->m_Paths != NULL ) {
    free( To->m_Paths );
    To->m_Paths = NULL;
    To->m_nPath = 0;
  }

  if ( From != NULL && From->m_nPath > 0 ) {

    path_size = 0;
    pp_over = From->m_Paths + From->m_nPath;
    for ( pp = From->m_Paths;  pp < pp_over;  pp++ ) {
      path_size += _tcslen( *pp ) + 1;
    }

    path_size = From->m_nPath * sizeof(TCHAR*) + path_size * sizeof(TCHAR);
    To->m_Paths = (TCHAR**) malloc( path_size );
    IF( To->m_Paths == NULL ) goto err;

    p2 = (TCHAR*)( (char*)To->m_Paths + From->m_nPath * sizeof(TCHAR*) );
    pp2 = To->m_Paths;
    for ( pp = From->m_Paths;  pp < pp_over;  pp++ ) {
      *pp2 = p2;
      path_size = (_tcslen( *pp ) + 1) * sizeof(TCHAR);
      memcpy( p2, *pp, path_size );
      p2 = (TCHAR*)( (char*)p2 + path_size );
      pp2 ++;
    }
    To->m_nPath = From->m_nPath;
  }

  e=0;
fin:
  return  e;

err:  e = E_Others;  goto fin;
}

 
/***********************************************************************
  <<< [CurrentWritables] >>> 
************************************************************************/
static void  CurrentWritables_init_const( CurrentWritables* m )
{
  Writables_init_const( &m->m_CurrentWritables );
  m->m_ProgramFiles = NULL;
  m->m_windir = NULL;
  m->m_APPDATA = NULL;
  m->m_LOCALAPPDATA = NULL;
}


static int  CurrentWritables_init( CurrentWritables* m )
{
  int    e;
 #if Uses_OutMallocIDTool
  bool  is_prev_out_malloc;

  is_prev_out_malloc = OutMallocID_setEnable( false );
 #endif

  e= Writables_copyToConst( &m->m_CurrentWritables, NULL ); IF(e)goto fin;

  e= env_malloc( &m->m_ProgramFiles, &m->m_ProgramFiles_Len, _T("ProgramFiles") ); IF(e)goto fin;
  e= env_malloc( &m->m_windir,       &m->m_windir_Len,       _T("windir") ); IF(e)goto fin;
  e= env_malloc( &m->m_APPDATA,      &m->m_APPDATA_Len,      _T("APPDATA") ); IF(e)goto fin;
  // e= env_malloc( &m->m_LOCALAPPDATA, &m->m_LOCALAPPDATA_Len, _T("LOCALAPPDATA") ); IF(e)goto fin;

  e=0;
fin:
 #if Uses_OutMallocIDTool
  OutMallocID_setEnable( is_prev_out_malloc );
 #endif
  return  e;
}


static int  CurrentWritables_finish( CurrentWritables* m, int e )
{
  int  ee;

  ee= Writables_copyToConst( &m->m_CurrentWritables, NULL ); IF(ee&&!e)e=ee;

  if ( m->m_ProgramFiles != NULL )  free( m->m_ProgramFiles );
  if ( m->m_windir != NULL )        free( m->m_windir );
  if ( m->m_APPDATA != NULL )       free( m->m_APPDATA );
  if ( m->m_LOCALAPPDATA != NULL )  free( m->m_LOCALAPPDATA );

  m->m_ProgramFiles = NULL;
  m->m_windir = NULL;
  m->m_APPDATA = NULL;
  m->m_LOCALAPPDATA = NULL;

  return  e;
}


int  CurrentWritables_askFileAccess_sub(
  const TCHAR* SystemPath,  size_t SystemPath_Len,  const TCHAR* AbsPath, size_t AbsPath_Len );

static int  CurrentWritables_askFileAccess( CurrentWritables* m, const TCHAR* AbsPath )
{
  int  e;
  size_t  abs_len;

  abs_len = _tcslen( AbsPath );
  e= CurrentWritables_askFileAccess_sub( m->m_ProgramFiles, m->m_ProgramFiles_Len, AbsPath, abs_len ); IF(e)goto fin;
  e= CurrentWritables_askFileAccess_sub( m->m_windir,       m->m_windir_Len,       AbsPath, abs_len ); IF(e)goto fin;
  e= CurrentWritables_askFileAccess_sub( m->m_APPDATA,      m->m_APPDATA_Len,      AbsPath, abs_len ); IF(e)goto fin;
  e= CurrentWritables_askFileAccess_sub( m->m_LOCALAPPDATA, m->m_LOCALAPPDATA_Len, AbsPath, abs_len ); IF(e)goto fin;

  e=0;
fin:
  return  e;
}


static int  CurrentWritables_askFileAccess_sub(
  const TCHAR* SystemPath,  size_t SystemPath_Len,  const TCHAR* AbsPath, size_t AbsPath_Len )
{
  if ( SystemPath == NULL )  return  0;

  IF ( _tcsncmp( SystemPath, AbsPath, SystemPath_Len ) == 0 )
    return  E_OutOfWritable;  // システムフォルダの中を書き込み許可しようとしている

  IF ( _tcsncmp( SystemPath, AbsPath, AbsPath_Len ) == 0 )
    return  E_OutOfWritable;  // システムフォルダを含めて許可書き込み許可しようとしている

  return  0;
}


 
/*=================================================================*/
/* <<< [StrT/StrT.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [StrT_cpy] >>> 
- _tcscpy is raising exception, if E_FewArray
************************************************************************/
int  StrT_cpy( TCHAR* Dst, size_t DstSize, const TCHAR* Src )
{
	size_t  size;

	size = ( _tcslen( Src ) + 1 ) * sizeof(TCHAR);
	if ( size <= DstSize ) {
		memcpy( Dst, Src, size );
		return  0;
	}
	else {
		memcpy( Dst, Src, DstSize - sizeof(TCHAR) );
		*(TCHAR*)( (char*) Dst + DstSize ) = _T('\0');
		return  E_FewArray;
	}
}

 
/***********************************************************************
  <<< [StrT_malloc_cpy] >>> 
************************************************************************/
TCHAR*  StrT_malloc_cpy( const TCHAR* Src )
{
	TCHAR*  p;
	size_t  size;

	size = ( _tcslen( Src ) + 1 ) * sizeof(TCHAR);
	p = (TCHAR*) malloc( size );  IF(p==NULL) return NULL;
	memcpy( p, Src, size );
	return  p;
}

 
/***********************************************************************
  <<< [StrT_malloc_cpy_from_char] >>> 
************************************************************************/
TCHAR*  StrT_malloc_cpy_from_char( const char* Src )
{
	#if _UNICODE
		TCHAR*  p = NULL;
		size_t  size;
		int     r;

		size = ( strlen( Src ) + 1 ) * sizeof(TCHAR);
		p = (TCHAR*) malloc( size );  IF(p==NULL) return NULL;

		r = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, Src, -1, p, size / 2 );
		IF( r == 0 ) goto err;
		return  p;

	err:
		free( p );
		return  NULL;

	#else
		TCHAR*  p;
		size_t  size;

		size = ( _tcslen( Src ) + 1 ) * sizeof(TCHAR);
		p = (TCHAR*) malloc( size );  IF(p==NULL) return NULL;
		memcpy( p, Src, size );

		return  p;
	#endif
}

 
/***********************************************************************
  <<< [StrT_chrs] >>> 
************************************************************************/
TCHAR*  StrT_chrs( const TCHAR* s, const TCHAR* keys )
{
	if ( *keys == _T('\0') )  return  NULL;

	for ( ; *s != _T('\0'); s++ ) {
		if ( _tcschr( keys, *s ) != NULL )
			return  (TCHAR*) s;
	}
	return  NULL;
}


 
/***********************************************************************
  <<< [StrT_skip] >>> 
************************************************************************/
TCHAR*  StrT_skip( const TCHAR* s, const TCHAR* keys )
{
	if ( *keys == _T('\0') )  return  (TCHAR*) s;

	for ( ; *s != _T('\0'); s++ ) {
		if ( _tcschr( keys, *s ) == NULL )
			break;
	}
	return  (TCHAR*) s;
}


 
/***********************************************************************
  <<< [StrT_refFName] >>> 
************************************************************************/
TCHAR*  StrT_refFName( const TCHAR* s )
{
	const TCHAR*  p;
	TCHAR  c;

	p = _tcschr( s, _T('\0') );

	if ( p == s )  return  (TCHAR*) s;

	for ( p--; p>s; p-- ) {
		c = *p;
		if ( c == _T('\\') || c == _T('/') )  return  (TCHAR*) p+1;
	}
	if ( *p == _T('\\') || *p == _T('/') )  return  (TCHAR*) p+1;

	return  (TCHAR*) s;
}
 
/***********************************************************************
  <<< [StrT_refExt] >>> 
************************************************************************/
TCHAR*  StrT_refExt( const TCHAR* s )
{
	const TCHAR*  p;

	p = _tcschr( s, _T('\0') );

	if ( p == s )  return  (TCHAR*) s;

	for ( p--; p>s; p-- ) {
		if ( *p == _T('.') )  return  (TCHAR*) p+1;
		if ( *p == _T('/') || *p == _T('\\') )  return  (TCHAR*) _tcschr( p, _T('\0') );
	}
	if ( *p == _T('.') )  return  (TCHAR*) p+1;

	return  (TCHAR*) _tcschr( s, _T('\0') );
}


 
/***********************************************************************
  <<< [StrT_isAbsPath] >>> 
************************************************************************/
bool  StrT_isAbsPath( const TCHAR* s )
{
	const TCHAR*  bs = _tcschr( s, _T('\\') );
	const TCHAR*  sl = _tcschr( s, _T('/') );
	const TCHAR*  co = _tcschr( s, _T(':') );

	return  ( co != NULL  && ( bs == co+1  ||  sl == co+1 ) );
}

 
/***********************************************************************
  <<< [StrT_getAbsPath_part]:shlwapi >>> 
************************************************************************/
int  StrT_getAbsPath_part( TCHAR* Out, size_t OutSize, TCHAR* OutStart,
	TCHAR** out_OutLast, const TCHAR* StepPath, const TCHAR* BasePath )
{
	int     e;
	TCHAR*  p;
	TCHAR   base_path_x[512];

	IF_D( OutStart < Out ||  (char*) OutStart >= (char*)Out + OutSize )goto err;

	if ( StepPath[0] == _T('\0') ) {
		*OutStart = _T('\0');
		return  0;
	}
	if ( StrT_isAbsPath( StepPath ) ) {
		if ( Out == StepPath )
			return  0;
		else
			return  stcpy_part_r( Out, OutSize, OutStart, out_OutLast, StepPath, NULL );
	}

	if ( BasePath == NULL ) {
		p = _tgetcwd( base_path_x, _countof(base_path_x) ); IF( p == NULL )goto err;
		BasePath = base_path_x;
	}


	//=== 絶対パスにする。 ".." も処理する
	IF( OutSize - ((char*)OutStart - (char*)Out) < MAX_PATH*sizeof(TCHAR) ) return E_FewArray;
	_tcscpy_s( OutStart, MAX_PATH, BasePath );
	PathAppend( OutStart, StepPath );  // PathAppend の第1引数のサイズは MAX_PATH*sizeof(TCHAR) 以上


	//=== 末尾の \ をカットする
	p = _tcschr( OutStart, _T('\0') );
	if ( *(p-1) == _T('\\') && p > OutStart + 3 ) { p--;  *p = _T('\0'); }


	if ( out_OutLast != NULL )  *out_OutLast = p;

	e=0;
fin:
	return  e;

err:  e = E_Others;  goto fin;
}


 
/***********************************************************************
  <<< [StrT_getParentAbsPath_part] >>> 
************************************************************************/
int  StrT_getParentAbsPath_part( TCHAR* Str, size_t StrSize, TCHAR* StrStart,
	TCHAR** out_StrLast, const TCHAR* StepPath, const TCHAR* BasePath )
{
	int  e;
	TCHAR*  p;

	IF_D( StrStart < Str ||  (char*) StrStart >= (char*)Str + StrSize )goto err;

	if ( StepPath[0] == _T('\0') ) {
		*StrStart = _T('\0');
		return  0;
	}

	/* 絶対パスにする */
	e= StrT_getAbsPath( StrStart,
		StrSize - ( (char*)StrStart - (char*)Str ),
		StepPath, BasePath ); IF(e)goto fin;


	/* 親へ */
	p = StrT_refFName( StrStart );
	if ( p > StrStart )  p--;
	*p = _T('\0');


	/* ルートなら \ を付ける */
	if ( p == StrStart + 2 ) {
		*p = _T('\\');  p++;  *p = _T('\0');
	}

	if ( out_StrLast != NULL )  *out_StrLast = p;

	e=0;
fin:
	return  e;

err:  e = E_Others;  goto fin;
}


 
/***********************************************************************
  <<< [StrT_getBaseName_part] >>> 
************************************************************************/
int  StrT_getBaseName_part( TCHAR* Str, size_t StrSize, TCHAR* StrStart,
	TCHAR** out_StrLast, const TCHAR* SrcPath )
{
	const TCHAR*  p1;
	const TCHAR*  p2;
	const TCHAR*  p3;
	const TCHAR*  ps;

	p1 = StrT_refFName( SrcPath );


	//=== # が無いとき、最後のピリオドの前までが、BaseName
	ps = _tcschr( p1, _T('#') );
	if ( ps == NULL ) {
		p2 = _tcsrchr( p1, _T('.') );
		if ( p2 == NULL )  p2 = _tcsrchr( p1, _T('\0') );
	}

	//=== # があるとき、# より前で、最後のピリオドの前までが、BaseName
	else {
		p2 = ps;

		p3 = p1;
		for (;;) {
			p3 = _tcschr( p3, _T('.') );
			if ( p3 == NULL || p3 > ps )  break;
			p2 = p3;
			p3 ++;
		}
	}

	return  stcpy_part_r( Str, StrSize, StrStart, out_StrLast, p1, p2 );
}

 
/***********************************************************************
  <<< [StrT_addLastOfFileName] >>> 
************************************************************************/
int  StrT_addLastOfFileName( TCHAR* out_Path, size_t PathSize,
                             const TCHAR* BasePath, const TCHAR* AddName )
{
	TCHAR           c;
	size_t          copy_size;
	size_t          free_size;
	char*           out_pos;
	const TCHAR*    last_pos_in_base = _tcschr( BasePath, _T('\0') );
	const TCHAR*    term_pos_in_base;
	const TCHAR*     add_pos_in_base;
	const TCHAR*  period_pos_in_base = _tcsrchr( BasePath, _T('.') );  // > term_pos_in_base
	const TCHAR*    last_pos_in_add  = _tcschr( AddName, _T('\0') );
	const TCHAR*    term_pos_in_add;
	const TCHAR*  period_pos_in_add  = _tcsrchr( AddName,  _T('.') );  // > term_pos_in_add


	MEMSET_TO_NOT_INIT( out_Path, PathSize );


	//=== term_pos_in_base
	for ( term_pos_in_base = last_pos_in_base;  term_pos_in_base >= BasePath;  term_pos_in_base -- ) {
		c = *term_pos_in_base;
		if ( c == _T('/') || c == _T('\\') )  break;
	}


	//=== term_pos_in_add
	for ( term_pos_in_add = last_pos_in_add;  term_pos_in_add >= AddName;  term_pos_in_add -- ) {
		c = *term_pos_in_add;
		if ( c == _T('/') || c == _T('\\') )  break;
	}


	//=== add_pos_in_base
	if ( term_pos_in_base < period_pos_in_base ) {
		add_pos_in_base = period_pos_in_base;
	}
	else {
		if ( term_pos_in_base < BasePath )
			add_pos_in_base = _tcschr( BasePath, _T('\0') );
		else
			add_pos_in_base = _tcschr( term_pos_in_base, _T('\0') );
	}


	//=== setup output parameters
	out_pos   = (char*) out_Path;
	free_size = PathSize;


	//=== copy BasePath .. add_pos_in_base
	copy_size = (char*)add_pos_in_base - (char*)BasePath;
	if ( copy_size > free_size ) goto err_fa;
	memcpy( out_pos,  BasePath,  copy_size );
	out_pos   += copy_size;
	free_size -= copy_size;


	//=== copy AddName .. last_pos_in_add
	copy_size = (char*)last_pos_in_add - (char*)AddName;
	if ( copy_size > free_size ) goto err_fa;
	memcpy( out_pos,  AddName,  copy_size );
	out_pos   += copy_size;
	free_size -= copy_size;


	//=== add name and not change extension
	if ( period_pos_in_add == NULL ) {

		//=== copy period_pos_in_base .. last_pos_in_base
		if ( period_pos_in_base > term_pos_in_base ) {
			copy_size = (char*)last_pos_in_base - (char*)period_pos_in_base + sizeof(TCHAR);
			if ( copy_size > free_size ) goto err_fa;
			memcpy( out_pos,  period_pos_in_base,  copy_size );
		}
		else {
			*(TCHAR*)out_pos = _T('\0');
		}
	}


	//=== add name and change extension
	else {

		if ( *(period_pos_in_add + 1) == _T('\0') )
			*( (TCHAR*)out_pos - 1 ) = _T('\0');
		else
			*(TCHAR*)out_pos = _T('\0');
	}

	return  0;

err_fa:
	return  E_FewArray;
}


 
/*=================================================================*/
/* <<< [Print/Print2.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [PrintfCounterClass] >>> 
************************************************************************/
#if USE_PRINTF_COUNTER
	PrintfCounterClass  g_PrintfCounter;
#endif


 
/***********************************************************************
  <<< [printf_to_debugger] >>> 
************************************************************************/
#ifdef  _MSC_VER
void  printf_to_debugger( const char* format, ... )
{
  va_list  va;
  char     s[1024];

  va_start( va, format );
  vsprintf_r( s, sizeof(s), format, va );
  va_end( va );

  OutputDebugStringA( s );
}
#endif


 
/***********************************************************************
  <<< [wprintf_to_debugger] >>> 
************************************************************************/
#ifdef  _MSC_VER
void  wprintf_to_debugger( const wchar_t* format, ... )
{
  va_list  va;
  wchar_t  s[1024];

  va_start( va, format );
  vswprintf_r( s, sizeof(s), format, va );
  va_end( va );

  OutputDebugStringW( s );
}
#endif


 
/***********************************************************************
  <<< [vsprintf_r] >>> 
************************************************************************/
int  vsprintf_r( char* s, size_t s_size, const char* format, va_list va )
{
	#if _MSC_VER
		#pragma warning(push)
		#pragma warning(disable: 4996)
	#endif

	int  r = _vsnprintf( s, s_size, format, va );

	#if _MSC_VER
		#pragma warning(pop)
	#endif

	if ( r == (int) s_size || r == -1 ) { s[s_size-1] = '\0';  return E_FewArray; }
	else  return  0;
}
 
/***********************************************************************
  <<< [vswprintf_r] >>> 
************************************************************************/
#ifndef  __linux__
int  vswprintf_r( wchar_t* s, size_t s_size, const wchar_t* format, va_list va )
{
	size_t  tsize = s_size / sizeof(wchar_t);

	#pragma warning(push)
	#pragma warning(disable: 4996)
		int  r = _vsnwprintf( s, tsize, format, va );
	#pragma warning(pop)

	if ( r == (int) tsize || r == -1 ) { s[tsize-1] = '\0';  return E_FewArray; }
	else  return  0;
}
#endif
 
/***********************************************************************
  <<< [stprintf_r] >>> 
************************************************************************/
int  stprintf_r( TCHAR* s, size_t s_size, const TCHAR* format, ... )
{
   int e;
   va_list  va;

   va_start( va, format );
   e = vstprintf_r( s, s_size, format, va );
   va_end( va );
   return e;
}


 
/***********************************************************************
  <<< [stcpy_part_r] >>> 
************************************************************************/
int  stcpy_part_r( TCHAR* s, size_t s_size, TCHAR* s_start, TCHAR** p_s_last,
                   const TCHAR* src, const TCHAR* src_over )
{
  size_t  s_space = (char*)s + s_size - (char*)s_start;
  size_t  src_size;

  IF_D( s_start < s || (char*)s_start >= (char*)s + s_size ) return 1;

  if ( src_over == NULL )  src_over = _tcschr( src, _T('\0') );
  src_size = (char*)src_over - (char*)src;
  if ( src_size < s_space ) {
    memcpy( s_start, src, src_size + sizeof(TCHAR) );
    s_start = (TCHAR*)((char*)s_start + src_size);  *s_start = _T('\0');
    if ( p_s_last != NULL )  *p_s_last = s_start;
    return 0;
  }
  else {
    s_space -= sizeof(TCHAR);  memcpy( s, src, s_space );
    s_start = (TCHAR*)((char*)s_start + s_space );  *s_start = '\0';
    if ( p_s_last != NULL )  *p_s_last=s_start;
    return E_FewArray;
  }
}


 
/***********************************************************************
  <<< [stprintf_part_r] >>> 
************************************************************************/
int  stprintf_part_r( TCHAR* s, size_t s_size, TCHAR* s_start, TCHAR** p_s_last,
                      const TCHAR* format, ... )
{
  int e;  va_list  va;  va_start( va, format );

  IF_D( s_start < s || (char*)s_start >= (char*)s + s_size ) return 1;

  e = vstprintf_r( s_start, s_size - ( (char*)s - (char*)s_start), format, va );
  va_end( va );  if ( p_s_last != NULL )  *p_s_last = _tcschr( s_start, '\0' );
  return e;
}

 
/*=================================================================*/
/* <<< [DebugTools/DebugTools.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [TestableDebugBreak] >>> 
************************************************************************/
dll_global_g_DebugBreakCount int  g_bTestableDebugBreak_Disable;
dll_global_g_DebugBreakCount int  g_DebugBreakCount;


 
/***********************************************************************
  <<< [TestableDebugBreak_isEnabled] >>> 
************************************************************************/
int  TestableDebugBreak_isEnabled()
{
  return  g_bTestableDebugBreak_Disable == 0;
}


 
/*=================================================================*/
/* <<< [Error4/Error4.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [Err2_setBreakErrID] >>> 
************************************************************************/
#if ERR2_ENABLE_ERROR_BREAK

Err2  g_Err2;  /* 初期値はすべてゼロ */

void  Err2_setBreakErrID( int ID )
{
	Err2*  m = &g_Err2;

	m->BreakErrID = ID;
}


int  TryOnIfTrue_imp()
 // 返り値は、ブレークするかどうか
{
	//=== エラー時の中断処理（ジャンプ）をしているとき
	if ( g_Err2.IsErr )  return  0;


	//=== エラーが初めて起きたとき
	else {
		Err2*  m = &g_Err2;

		m->IsErr = 1;
		m->ErrID ++;

		#if ERR2_ENABLE_ERROR_LOG
			printf( "<ERRORLOG msg=\"raised\" err_id=\"%d\" g_err2=\"0x%08X\"/>\n", m->ErrID, (int) m );
		#endif

		return  ( m->ErrID == m->BreakErrID );
	}
}


//[Err2_clear]
void  Err2_clear()
{
	Err2*  m = &g_Err2;

	#if ERR2_ENABLE_ERROR_LOG
	if ( m->IsErr != 0 )
		printf( "<ERRORLOG msg=\"cleared\" err_id=\"%d\" g_err2=\"0x%08X\"/>\n", m->ErrID, (int) m );
	#endif

	m->IsErr = 0;
}


//[IfErrThenBreak]
void  IfErrThenBreak()
{
	if ( g_Err2.IsErr && ( g_Err2.ErrID != g_Err2.BreakErrID || g_Err2.BreakErrID == 0 ) ) {  TestableDebugBreak();
		// ウォッチで、g_Err2.ErrID の値(Nとする)を確認して、
		// メイン関数で Err2_setBreakErrID( N ); を呼び出してください。
		// 正常終了しているつもりなのにここでブレークするときは、
		// Err2_clear() を忘れている可能性があります。
		#if ERR2_ENABLE_ERROR_LOG
			printf( "<ERRORLOG msg=\"IfErrThenBreak\" ErrID=\"%d\" BreakErrID=\"%d\"/>\n", g_Err2.ErrID, g_Err2.BreakErrID );
		#endif
	}
	Err2_clear();
}

//[PushErr]
void  PushErr( ErrStackAreaClass* ErrStackArea )
{
	ErrStackArea->ErrID = g_Err2.ErrID;
	ErrStackArea->IsErr = g_Err2.IsErr;
	g_Err2.IsErr = 0;
}

//[PopErr]
void  PopErr(  ErrStackAreaClass* ErrStackArea )
{
	if ( ErrStackArea->IsErr )
		g_Err2.IsErr = 1;
}


#endif // ERR2_ENABLE_ERROR_BREAK

 
