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

#include  "include_c.h"


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

void  Globals_init_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 )
{

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


 
/*=================================================================*/
/* <<< [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_isDiff] >>> 
************************************************************************/
int  FileT_isDiff( const TCHAR* Path1, const TCHAR* Path2, bool* bDiff )
{
  int    e;
  bool   b;
  FILE*  f1 = NULL;
  FILE*  f2 = NULL;
  char*  buf1 = NULL;
  char*  buf2 = NULL;
  errno_t  en;


  //=== Open files
  en= _tfopen_s( &f1, Path1, _T("r") );
  if ( en == ENOENT )  { f1 = NULL; en = 0; }
  IF(en)goto err_no;

  en= _tfopen_s( &f2, Path2, _T("r") );
  if ( en == ENOENT )  { f2 = NULL; en = 0; }
  IF(en)goto err_no;


  //=== Whether exists or not
  if ( f1 == NULL ) {
    if ( f2 == NULL )  { *bDiff = false; e=0; goto fin; }
    else  { *bDiff = true; e=0; goto fin; }
  }
  else {
    if ( f2 == NULL )  { *bDiff = true; e=0; goto fin; }
  }


  //=== Compare the contents in files
  {
    enum { size = 0x100000 };
    size_t  size1, size2;

    buf1 = (char*) malloc( size ); IF(buf1==NULL)goto err_fm;
    buf2 = (char*) malloc( size ); IF(buf2==NULL)goto err_fm;

    b = true;
    for (;;) {
      size1 = fread( buf1, 1, size, f1 );
      size2 = fread( buf2, 1, size, f2 );

      if ( size1 != size2 )  break;
      if ( memcmp( buf1, buf2, size1 ) != 0 )  break;
      if ( size1 != size )  { b = false; break; }
    }
    *bDiff = b;
  }

  e=0;
fin:
  if ( buf1 !=NULL)free(buf1);
  if ( buf2 !=NULL)free(buf2);
  if( f1 !=NULL){en=fclose(f1);IF(en)b=true;}
  if( f2 !=NULL){en=fclose(f2);IF(en)b=true;}
  return  e;
err_no: e = E_Errno; goto fin;
err_fm: e = E_FewMemory; goto fin;
}

 
/***********************************************************************
  <<< [FileT_isSameText] >>> 
************************************************************************/
int  FileT_isSameText( TCHAR* Path1, TCHAR* Path2, int Format1, int Format2, bool* out_bSame )
{
  int    e;
  FILE*  f1 = NULL;
  FILE*  f2 = NULL;
  TCHAR  line1[4096];
  TCHAR  line2[4096];

  ASSERT_R( Format1 == 0, goto err );
  ASSERT_R( Format2 == 0, goto err );

  e= FileT_openForRead( &f1, Path1 ); IF(e)goto fin;
  e= FileT_openForRead( &f2, Path2 ); IF(e)goto fin;
  for (;;) {
    line1[0] = _T('\0');
    line2[0] = _T('\0');
    _fgetts( line1, _countof(line1), f1 );
    _fgetts( line2, _countof(line2), f2 );
    if ( _tcscmp( line1, line2 ) != 0 ) {
      *out_bSame = false;  e=0;  goto fin;
    }
    if ( feof( f1 ) ) {
      if ( feof( f2 ) )  break;
      else {
        *out_bSame = false;  e=0;  goto fin;
      }
    }
  }
  *out_bSame = true;

  e=0;
fin:
  e= FileT_close( f1, e );
  e= FileT_close( f2, e );
  return  e;

err:  e = E_Others;  goto fin;
}


 
/***********************************************************************
  <<< [FileT_isSameBinaryFile] >>> 
************************************************************************/
int  FileT_isSameBinaryFile( const TCHAR* PathA, const TCHAR* PathB, int Flags, bool* out_IsSame )
{
  int       e;
  HANDLE    file_a = INVALID_HANDLE_VALUE;
  HANDLE    file_b = INVALID_HANDLE_VALUE;
  HANDLE    mem_a  = INVALID_HANDLE_VALUE;
  HANDLE    mem_b  = INVALID_HANDLE_VALUE;
  void*     ptr_a = NULL;
  void*     ptr_b = NULL;
  size_t    size_a;
  size_t    size_b;
  BOOL      b;
  bool      is_same = false;

  UNREFERENCED_VARIABLES(( Flags ));


  // file_a,  file_b : open PathA,  PathB
  file_a = CreateFile( PathA, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL, 0 );
  IF( file_a == INVALID_HANDLE_VALUE ) goto err_gt;

  file_b = CreateFile( PathB, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL, 0 );
  IF( file_b == INVALID_HANDLE_VALUE ) goto err_gt;

  // size_a,  size_b : size of file_a, file_b
  size_a = GetFileSize( file_a, NULL );
  size_b = GetFileSize( file_b, NULL );

  if ( size_a == size_b  &&  size_a == 0 ) {
    is_same = true;
  }
  else if ( size_a == size_b ) {

    // mem_a,  mem_b : map to memory from file_a,  file_b
    mem_a = CreateFileMapping( file_a, NULL, PAGE_READONLY, 0, size_a, NULL );
    IF( mem_a == INVALID_HANDLE_VALUE ) goto err_gt;
    mem_b = CreateFileMapping( file_b, NULL, PAGE_READONLY, 0, size_b, NULL );
    IF( mem_b == INVALID_HANDLE_VALUE ) goto err_gt;

    // ptr_a,  ptr_b : pointer of contents in file_a, file_b
    ptr_a = MapViewOfFile( mem_a, FILE_MAP_READ, 0, 0, 0 ); IF(ptr_a==NULL)goto err_gt;
    ptr_b = MapViewOfFile( mem_b, FILE_MAP_READ, 0, 0, 0 ); IF(ptr_b==NULL)goto err_gt;

    // is_same : is same file_a and file_b
    if ( memcmp( ptr_a, ptr_b, size_a ) == 0 )  is_same = true;
  }
  e=0;
fin:
  if ( ptr_a != NULL )                  { b= UnmapViewOfFile( ptr_a ); IF(!b&&!e) e=E_GetLastError; }
  if ( mem_a  != INVALID_HANDLE_VALUE ) { b= CloseHandle( mem_a );     IF(!b&&!e) e=E_GetLastError; }
  if ( file_a != INVALID_HANDLE_VALUE ) { b= CloseHandle( file_a );    IF(!b&&!e) e=E_GetLastError; }
  if ( ptr_b != NULL )                  { b= UnmapViewOfFile( ptr_b ); IF(!b&&!e) e=E_GetLastError; }
  if ( mem_b  != INVALID_HANDLE_VALUE ) { b= CloseHandle( mem_b );     IF(!b&&!e) e=E_GetLastError; }
  if ( file_b != INVALID_HANDLE_VALUE ) { b= CloseHandle( file_b );    IF(!b&&!e) e=E_GetLastError; }
  *out_IsSame = is_same;
  return  e;

err_gt:  e = E_GetLastError;  goto fin;
}


 
/***********************************************************************
  <<< [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_close] >>> 
************************************************************************/
int  FileT_close( FILE* File, int e )
{
  if ( File != NULL ) {
    int r = fclose( File );
    IF(r&&!e)e=E_Errno;
  }
  return e;
}



 
/*=================================================================*/
/* <<< [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_trim] >>> 
************************************************************************/
errnum_t  StrT_trim( TCHAR* out_Str, size_t out_Str_Size, const TCHAR* in_Str )
{
	const TCHAR*  p1;
	const TCHAR*  p2;
	TCHAR   c;

	p1 = in_Str;  while ( *p1 == _T(' ') || *p1 == _T('\t') )  p1++;
	for ( p2 = _tcschr( p1, _T('\0') ) - 1;  p2 >= p1;  p2-- ) {
		c = *p2;
		if ( c != _T(' ') && c != _T('\t') && c != _T('\n') && c != _T('\r') )
			break;
	}
	return  stcpy_part_r( out_Str, out_Str_Size, out_Str, NULL, p1, p2+1 );
}


 
/***********************************************************************
  <<< [StrT_cutLastOf] >>> 
************************************************************************/
errnum_t  StrT_cutLastOf( TCHAR* in_out_Str, TCHAR Charactor )
{
	TCHAR*  last = _tcschr( in_out_Str, _T('\0') );

	if ( last > in_out_Str ) {
		if ( *( last - 1 ) == Charactor )
			{ *( last - 1 ) = _T('\0'); }
	}
	return  0;
}


 
/***********************************************************************
  <<< [StrT_cutLineComment] >>> 
************************************************************************/
int  StrT_cutLineComment( TCHAR* out_Str, size_t out_Str_Size, const TCHAR* in_Str, const TCHAR* CommentSign )
{
	const TCHAR*  p1;
	const TCHAR*  p2;
	TCHAR   c;

	p1 = in_Str;  while ( *p1 == _T(' ') || *p1 == _T('\t') )  p1++;

	p2 = _tcsstr( p1, CommentSign );
	if ( p2 == NULL )  p2 = _tcschr( p1, _T('\0') );

	for ( p2 = p2 - 1;  p2 >= p1;  p2-- ) {
		c = *p2;
		if ( c != _T(' ') && c != _T('\t') && c != _T('\n') && c != _T('\r') )
			break;
	}
	return  stcpy_part_r( out_Str, out_Str_Size, out_Str, NULL, p1, p2+1 );
}


 
/**************************************************************************
  <<< [StrT_meltCSV] >>> 
*************************************************************************/
int  StrT_meltCSV( TCHAR* out_Str, size_t out_Str_Size, const TCHAR** pCSV )
{
	int  e = 0;
	TCHAR*  t;
	TCHAR*  t_last = (TCHAR*)( (char*)out_Str + out_Str_Size - sizeof(TCHAR) );
	const TCHAR*  s;
	TCHAR  dummy[2];
	TCHAR  c;

	t = out_Str;
	s = *pCSV;
	if ( out_Str_Size <= 1 )  { t = dummy;  t_last = dummy; }

	if ( s == NULL ) { *t = _T('\0');  return 0; }


	/* 頭の空白を除く */
	while ( *s == _T(' ') || *s == _T('\t') )  s++;

	switch ( *s ) {

		/* "" で囲まれている場合 */
		case _T('"'):
			s++;
			c = *s;
			while ( c != _T('"') || *(s+1) == _T('"') ) {  /* " 文字まで */
				if ( t == t_last ) { e = E_FewArray;  t = dummy;  t_last = dummy + 1; }
				if ( c == *(s+1) && c == _T('"') )  s++;  /* " 文字自体 */
				if ( c == _T('\0') )  break;
				*t = c;  t++;  s++;  c = *s;
			}
			*t = _T('\0');

			s++;
			for (;;) {
				if ( *s == _T(',') )  { s = s+1;  break; }
				if ( *s == _T('\0') ) { s = NULL;  break; }
				s++;
			}
			*pCSV = s;
			return  e;

		/* 空の項目の場合 */
		case ',':
			*t = _T('\0');
			*pCSV = s+1;
			return  0;

		case '\0':
			*t = _T('\0');
			*pCSV = NULL;
			return  0;

		/* "" で囲まれていない場合 */
		default: {
			TCHAR*  sp = NULL;  /* 最後の連続した空白の先頭 */

			c = *s;
			while ( c != _T(',') && c != _T('\0') && c != _T('\r') && c != _T('\n') ) {  /* , 文字まで */

				/* sp を設定する */
				if ( c == ' ' ) {
					if ( sp == NULL )  sp = t;
				}
				else  sp = NULL;

				if ( t == t_last ) { e = E_FewArray;  t = dummy;  t_last = dummy + 1; }

				/* コピーする */
				*t = c;  t++;  s++;  c = *s;
			}

			/* 返り値を決定する */
			if ( c == _T(',') )  s = s + 1;
			else  s = NULL;

			/* 末尾の空白を取り除く */
			if ( sp != NULL )  *sp = '\0';
			else  *t = _T('\0');

			*pCSV = s;
			return  e;
		}
	}
}


 
/***********************************************************************
  <<< [StrT_getExistSymbols] >>> 
************************************************************************/
int  StrT_getExistSymbols( unsigned* out, bool bCase, const TCHAR* Str, const TCHAR* Symbols, ... )
{
	int  e, i;
	TCHAR** syms = NULL;
	bool*   syms_exists = NULL;
	bool    b_nosym = false;
	TCHAR*  sym = NULL;
	size_t  sym_size = ( _tcslen( Symbols ) + 1 ) * sizeof(TCHAR);
	int     n_sym = 0;
	const TCHAR*  p;

	UNREFERENCED_VARIABLES(( bCase ));

	sym = (TCHAR*) malloc( sym_size ); IF(sym==NULL)goto err_fm;


	//=== Get Symbols
	p = Symbols;
	do {
		e= StrT_meltCSV( sym, sym_size, &p ); IF(e)goto fin;
		if ( sym[0] != _T('\0') )  n_sym ++;
	} while ( p != NULL );

	syms = (TCHAR**) malloc( n_sym * sizeof(TCHAR*) ); IF(syms==NULL)goto err_fm;
	memset( syms, 0, n_sym * sizeof(TCHAR*) );
	syms_exists = (bool*) malloc( n_sym * sizeof(bool) ); IF(syms_exists==NULL)goto err_fm;
	memset( syms_exists, 0, n_sym * sizeof(bool) );

	p = Symbols;  i = 0;
	do {
		e= StrT_meltCSV( sym, sym_size, &p ); IF(e)goto fin;
		if ( sym[0] != _T('\0') ) {
			syms[i] = StrT_malloc_cpy( sym );
			IF( syms[i] == NULL )goto err_fm;
			i++;
		}
	} while ( p != NULL );


	//=== Check Str whether having Symbols
	p = Str;
	do {
		e= StrT_meltCSV( sym, sym_size, &p ); IF(e)goto fin;
		if ( sym[0] != _T('\0') ) {
			for ( i = 0; i < n_sym; i++ ) {
				if ( _tcscmp( sym, syms[i] ) == 0 )  { syms_exists[i] = true;  break; }
			}
			if ( i == n_sym )  b_nosym = true;
		}
	} while ( p != NULL );


	//=== Sum numbers
	{
		va_list   va;
		unsigned  num;

		va_start( va, Symbols );
		*out = 0;
		for ( i = 0; i < n_sym; i++ ) {
			num = va_arg( va, unsigned );
			if ( syms_exists[i] )  *out |= num;
		}
		va_end( va );
	}

	e = ( b_nosym ? E_NotFoundSymbol : 0 );
fin:
	if ( syms != NULL ) {
		for ( i = 0; i < n_sym; i++ ) {
			if ( syms[i] != NULL )  free( syms[i] );
		}
		free( syms );
	}
	if ( syms_exists != NULL )  free( syms_exists );
	if ( sym != NULL )  free( sym );
	return  e;
err_fm: e= E_FewMemory; goto fin;
}

 
/**************************************************************************
  <<< [StrT_meltCmdLine] >>> 
*************************************************************************/
int  StrT_meltCmdLine( TCHAR* out_Str, size_t out_Str_Size, const TCHAR** pLine )
{
	int  e = 0;
	TCHAR*  t;
	TCHAR*  t_last = (TCHAR*)( (char*)out_Str + out_Str_Size - sizeof(TCHAR) );
	const TCHAR*  s;
	TCHAR  dummy;
	TCHAR  c;

	t = out_Str;
	s = *pLine;
	if ( out_Str_Size <= 1 )  { t = &dummy;  t_last = &dummy; }

	if ( s == NULL ) { *t = _T('\0');  return 0; }


	/* 頭の空白を除く */
	while ( *s == _T(' ') || *s == _T('\t') )  s++;

	switch ( *s ) {

		/* "" で囲まれている場合 */
		case _T('"'):
			s++;
			c = *s;
			while ( c != _T('"') || *(s+1) == _T('"') ) {  /* " 文字まで */
				if ( t == t_last ) { e = E_FewArray;  t = &dummy;  t_last = &dummy + 1; }
				if ( c == *(s+1) && c == _T('"') )  s++;  /* " 文字自体 */
				if ( c == _T('\0') )  break;
				*t = c;  t++;  s++;  c = *s;
			}
			*t = _T('\0');

			s++;
			for (;;) {
				if ( *s == _T(' ') )  { s = s+1;  break; }
				if ( *s == _T('\0') ) { s = NULL;  break; }
				s++;
			}
			*pLine = s;
			return  e;

		case '\0':
			*t = _T('\0');
			*pLine = NULL;
			return  0;

		/* "" で囲まれていない場合 */
		default: {
			c = *s;
			while ( c != _T(' ') && c != _T('\0') && c != _T('\r') && c != _T('\n') ) {  /* 空白文字まで */

				if ( t == t_last ) { e = E_FewArray;  t = &dummy;  t_last = &dummy + 1; }

				/* コピーする */
				*t = c;  t++;  s++;  c = *s;
			}

			/* *pLineを決定する */
			while ( *s == _T(' ') )  s = s + 1;
			if ( *s == _T('\0') )  s = NULL;

			/* 末尾 */
			*t = _T('\0');

			*pLine = s;
			return  e;
		}
	}
}


 
/***********************************************************************
  <<< [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;
}


 
/***********************************************************************
  <<< [Strs_init] >>> 
************************************************************************/
enum { Strs_FirstSize = 0x0F00 };

int  Strs_init( Strs* m )
{
	char*  p;

	m->MemoryAddress = NULL;

	p = (char*) malloc( Strs_FirstSize );
	IF( p == NULL )  return  E_FewMemory;

	m->MemoryAddress = p;
	m->MemoryOver    = p + Strs_FirstSize;
	m->NextElem      = p + sizeof(TCHAR*);
	m->PointerToNextStrInPrevElem = (TCHAR**) p;
	m->Prev_PointerToNextStrInPrevElem = NULL;
	*(TCHAR**) p = NULL;

	m->FirstOfStrs = m;
	m->NextStrs = NULL;

	return  0;
}


 
/***********************************************************************
  <<< [Strs_finish] >>> 
************************************************************************/
int  Strs_finish( Strs* m, int e )
{
	Strs*  mp;
	Strs*  next_mp;

	if ( m->MemoryAddress == NULL )  return 0;

	mp = m->FirstOfStrs;
	for (;;) {
		free( mp->MemoryAddress );
		if ( mp == m )  break;

		next_mp = mp->NextStrs;
		free( mp );
		mp = next_mp;
	}
	m->MemoryAddress = NULL;

	return  e;
}


 
/***********************************************************************
  <<< [Strs_toEmpty] >>> 
************************************************************************/
int  Strs_toEmpty( Strs* m )
{
	Strs_finish( m, 0 );
	return  Strs_init( m );
}


 
/***********************************************************************
  <<< [Strs_add] >>> 
************************************************************************/
int  Strs_add( Strs* m, const TCHAR* Str, const TCHAR** out_AllocStr )
{
	return  Strs_addBinary( m, Str, _tcschr( Str, _T('\0') ) + 1, out_AllocStr );
}


int  Strs_addBinary( Strs* m, const TCHAR* Str, const TCHAR* StrOver, const TCHAR** out_AllocStr )
{
	int     e;
	size_t  str_size;
	size_t  elem_size;

	str_size  = ( (char*) StrOver - (char*) Str );
	elem_size = ( sizeof(TCHAR*) + str_size + sizeof(void*) - 1 ) & ~(sizeof(void*) - 1);

	if ( m->NextElem + elem_size > m->MemoryOver )
		{ e= Strs_expandSize( m, str_size ); IF(e)goto fin; }


	// [ NULL     | ... ]
	// [ FirstStr | NULL    | TCHAR[] | ... ]
	// [ FirstStr | NextStr | TCHAR[] | NULL    | TCHAR[] | ... ]
	// [ FirstStr | NextStr | TCHAR[] | NextStr | TCHAR[] ], [ NULL | TCHAR[] | ... ]

	if ( out_AllocStr != NULL )  *out_AllocStr = (TCHAR*)( m->NextElem + sizeof(TCHAR*) );

	//=== fill elem
	*(TCHAR**) m->NextElem = NULL;
	memcpy( m->NextElem + sizeof(TCHAR*),  Str,  str_size );

	//=== link to elem from previous elem
	*m->PointerToNextStrInPrevElem = (TCHAR*)( m->NextElem + sizeof(TCHAR*) );

	//=== update m
	m->Prev_PointerToNextStrInPrevElem = m->PointerToNextStrInPrevElem;
	m->PointerToNextStrInPrevElem = (TCHAR**) m->NextElem;
	m->NextElem = m->NextElem + elem_size;

	e=0;
fin:
	return  e;
}


 
/***********************************************************************
  <<< [Strs_freeLast] >>> 
************************************************************************/
int  Strs_freeLast( Strs* m, TCHAR* AllocStr )
{
	int     e;
	TCHAR*  str;
	TCHAR*  last_str;
	TCHAR*  prev_of_last_str;
	Strs*   mp;
	Strs*   prev_of_last_mp;

	if ( m->Prev_PointerToNextStrInPrevElem == NULL ) {
		prev_of_last_str = NULL;
		last_str = NULL;
		for ( Strs_forEach( m, &str ) ) {
			prev_of_last_str = last_str;
			last_str = str;
		}
	}
	else {
		prev_of_last_str = (TCHAR*)( m->Prev_PointerToNextStrInPrevElem + 1 );
		last_str = (TCHAR*)( m->PointerToNextStrInPrevElem + 1 );
	}

	// [ NULL     | ... ]
	IF( last_str != AllocStr ) goto err;

	// [ FirstStr | NULL    | TCHAR[] | ... ]
	if ( prev_of_last_str == NULL ) {
		m->NextElem = m->MemoryAddress + sizeof(TCHAR*);
		m->PointerToNextStrInPrevElem = (TCHAR**) m->MemoryAddress;
	}

	// [ FirstStr | NextStr | TCHAR[] | NULL    | TCHAR[] | ... ]
	else if ( (char*) prev_of_last_str >= m->MemoryAddress  &&
	          (char*) prev_of_last_str <  m->MemoryOver ) {
		m->NextElem = (char*)last_str - sizeof(TCHAR*);
		m->PointerToNextStrInPrevElem = (TCHAR**)( (char*)prev_of_last_str - sizeof(TCHAR*) );
	}

	// [ FirstStr | NextStr | TCHAR[] | NextStr | TCHAR[] ], [ NULL | TCHAR[] | ... ]
	else {
		prev_of_last_mp = NULL;
		for ( mp = m->FirstOfStrs;  mp->NextStrs != m;  mp = mp->NextStrs ) {
			prev_of_last_mp = mp;
		}

		free( m->MemoryAddress );

		*m = *mp;

		if ( prev_of_last_mp == NULL ) {
			m->FirstOfStrs = m;
			m->NextStrs = NULL;
		}
		else {
			prev_of_last_mp->NextStrs = m;
		}

		free( mp );
	}
	*m->PointerToNextStrInPrevElem = NULL;
	m->Prev_PointerToNextStrInPrevElem = NULL;

	e=0;
fin:
	return  e;

err:  e = E_Others;  goto fin;
}


 
/***********************************************************************
  <<< [Strs_expandSize] >>> 
************************************************************************/
int  Strs_expandSize( Strs* m, size_t FreeSize )
{
	Strs*   mp;
	Strs*   mp2;
	size_t  elem_size = ( sizeof(TCHAR*) + FreeSize + sizeof(void*) - 1 ) & ~(sizeof(void*) - 1);
	size_t  memory_size;
	char*   new_memory;

	// [ NULL     | ... ]
	// [ FirstStr | NULL    | TCHAR[] | ... ]
	// [ FirstStr | NextStr | TCHAR[] | NULL    | TCHAR[] | ... ]
	// [ FirstStr | NextStr | TCHAR[] | NextStr | TCHAR[] ], [ NULL | TCHAR[] | ... ]

	while ( m->NextElem + elem_size > m->MemoryOver ) {

		//=== alloc
		mp = (Strs*) malloc( sizeof(Strs) ); IF(mp==NULL) goto err_fm;
		memory_size = ( m->MemoryOver - m->MemoryAddress ) * 2;
		new_memory = (char*) malloc( memory_size );
		IF( new_memory == NULL )  { free( mp );  goto err_fm; }

		//=== move old memory
		if ( m->FirstOfStrs == m ) {
			m->FirstOfStrs = mp;
		}
		else {
			for ( mp2 = m->FirstOfStrs;  mp2->NextStrs != m;  mp2 = mp2->NextStrs );
			mp2->NextStrs = mp;
		}
		*mp = *m;
		mp->NextStrs = m;

		//=== setup new memory
		m->MemoryAddress = new_memory;
		m->MemoryOver    = new_memory + memory_size;
		m->NextElem      = new_memory;
		// m->PointerToNextStrInPrevElem is same value
		// m->FirstOfStrs is same value
		// m->NextStrs is always NULL
	}
	return  0;

err_fm:  return  E_FewArray;
}


/***********************************************************************
  <<< [Strs_commit] >>>
************************************************************************/
int  Strs_commit( Strs* m, TCHAR* StrOver )
{
	size_t  elem_size;

	if ( StrOver == NULL )  StrOver = _tcschr( (TCHAR*)( m->NextElem + sizeof(TCHAR*) ), _T('\0') ) + 1;
	elem_size = ( ( (char*)StrOver - m->NextElem ) + sizeof(void*) - 1 ) & ~(sizeof(void*) - 1);

	//=== fill elem
	*(TCHAR**) m->NextElem = NULL;

	//=== link to elem from previous elem
	*m->PointerToNextStrInPrevElem = (TCHAR*)( m->NextElem + sizeof(TCHAR*) );

	//=== update m
	m->PointerToNextStrInPrevElem = (TCHAR**) m->NextElem;
	m->NextElem = m->NextElem + elem_size;

	return  0;
}


 
/***********************************************************************
  <<< [StrArr] >>> 
************************************************************************/
int  StrArr_init( StrArr* m )
{
	int  e;

	Set2_initConst( &m->Array );
	Strs_initConst( &m->Chars );

	e= Set2_init( &m->Array, 0x100 ); IF(e)goto cancel;
	e= Strs_init( &m->Chars ); IF(e)goto cancel;
	return  0;

cancel:  StrArr_finish( m, e );  return  e;
}


int  StrArr_finish( StrArr* m, int e )
{
	if ( ! Set2_isInited( &m->Array ) )  return  0;

	e= Set2_finish( &m->Array, e );
	e= Strs_finish( &m->Chars, e );
	return  e;
}


int  StrArr_add( StrArr* m, const TCHAR* Str, int* out_I )
{
	int  e;

	e= StrArr_expandCount( m, _tcslen( Str ) ); IF(e)goto fin;
	_tcscpy_s( StrArr_getFreeAddr( m ), StrArr_getFreeCount( m ), Str );
	e= StrArr_commit( m ); IF(e)goto fin;
	if ( out_I != NULL )  *out_I = Set2_getCount( &m->Array, TCHAR* ) - 1;

	e=0;
fin:
	return  e;
}


int  StrArr_commit( StrArr* m )
{
	int      e;
	TCHAR*   p;
	TCHAR**  pp  = NULL;
	Set2*    arr = &m->Array;
	Strs*    ss  = &m->Chars;

	p = Strs_getFreeAddr( ss );
	e= Set2_alloc( arr, &pp, TCHAR* ); IF(e)goto fin;
	e= Strs_commit( ss, NULL ); IF(e)goto fin;
	*pp = p;

	e=0;
fin:
	if ( e &&  pp != NULL )  e= Set2_freeLast( arr, pp, TCHAR*, e );
	return  e;
}


int  StrArr_fillTo( StrArr* m, int n, const TCHAR* Str )
{
	int      e;
	TCHAR*   p;
	TCHAR**  pp;
	TCHAR**  pp_over;

	n -= Set2_getCount( &m->Array, TCHAR* );
	if ( n <= 0 ) return 0;

	if ( Str == NULL ) {
		p = NULL;
	}
	else {
		e= Strs_add( &m->Chars, Str, &p ); IF(e)goto fin;
	}

	e= Set2_allocMulti( &m->Array, &pp, TCHAR*, n ); IF(e)goto fin;
	pp_over = pp + n;
	for ( ;  pp < pp_over;  pp++ )
		*pp = p;

	e=0;
fin:
	return  e;
}


int  StrArr_toEmpty( StrArr* m )
{
	int  e, ee;

	e=0;
	ee= Set2_toEmpty( &m->Array ); IF(ee&&!e)e=ee;
	ee= Strs_toEmpty( &m->Chars ); IF(ee&&!e)e=ee;
	return  e;
}


 
/***********************************************************************
  <<< [StrArr_parseCSV] >>> 
************************************************************************/
int  StrArr_parseCSV( StrArr* m, const TCHAR* CSVLine )
{
	int           e;
	const TCHAR*  p = CSVLine;

	e= StrArr_toEmpty( m ); IF(e)goto fin;

	do {
		e= StrT_meltCSV( StrArr_getFreeAddr( m ), StrArr_getFreeSize( m ), &p );
		if ( e == E_FewArray ) {
			e= StrArr_expandSize( m, StrArr_getFreeSize( m ) * 2 ); IF(e)goto fin;
			continue;
		}
		IF(e)goto fin;

		e = StrArr_commit( m ); IF(e)goto fin;
	} while ( p != NULL );

	e=0;
fin:
	return  e;
}


 
/*=================================================================*/
/* <<< [SetX/SetX.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [Set2_init] >>> 
************************************************************************/
int  Set2_init( Set2* m, int FirstSize )
{
  m->First = malloc( FirstSize );
  if ( m->First == NULL )  return  E_FewMemory;
  m->Next = m->First;
  m->Over = (char*)m->First + FirstSize;
  return  0;
}
 
/***********************************************************************
  <<< [Set2_finish] >>> 
************************************************************************/
int  Set2_finish( Set2* m, int e )
{
  if ( m->First != NULL )  { free( m->First );  m->First = NULL; }
  return  e;
}

 
/***********************************************************************
  <<< [Set2_ref_imp] >>> 
************************************************************************/
int  Set2_ref_imp( Set2* m, int iElem, void* out_pElem, size_t ElemSize )
{
  int    e;
  char*  p;

  IF( iElem < 0 ) goto err_ns;
  p = (char*) m->First + ( (unsigned)iElem * ElemSize );
  IF( p >= (char*)m->Next ) goto err_ns;
  *(char**)out_pElem = p;

  e=0;
fin:
  return  e;

err_ns:  e = E_NotFoundSymbol;  goto fin;
}


 
/***********************************************************************
  <<< [Set2_alloc_imp] >>> 
************************************************************************/
int  Set2_alloc_imp( Set2* m, void* pp, size_t size )
{
  int  e;

  e= Set2_expandIfOverByAddr( m, (char*) m->Next + size ); IF(e)goto fin;
  *(void**)pp = m->Next;
  m->Next = (char*) m->Next + size;

  MEMSET_TO_NOT_INIT( *(void**)pp, size );

  e=0;
fin:
  return  e;
}


int  Set2_allocMulti_sub( Set2* m, void* out_pElem, size_t ElemsSize )
{
  int    e;
  char*  p;

  e= Set2_expandIfOverByAddr( m, (char*) m->Next + ElemsSize ); IF(e)goto fin;
  p = m->Next;
  m->Next = p + ElemsSize;
  *(char**)out_pElem = p;

  e=0;
fin:
  return  e;
}


 
/***********************************************************************
  <<< [Set2_expandIfOverByAddr_imp] >>> 
************************************************************************/
int  Set2_expandIfOverByAddr_imp( Set2* m, void* OverAddrBasedOnNowFirst )
{
  void*     new_first;
  unsigned  offset_of_over;
  unsigned  offset_of_next;

  if ( OverAddrBasedOnNowFirst <= m->Over )  return  E_Others;

  offset_of_next = (unsigned)( (char*)OverAddrBasedOnNowFirst - (char*)m->First );
  offset_of_over = (unsigned)( ( (char*)m->Over - (char*)m->First ) ) * 2;
  IF_D( offset_of_next >= 0x80000000 ) goto err;
  while ( offset_of_over < offset_of_next )  offset_of_over *= 2;
  IF( offset_of_over >= 0x10000000 ) goto err;

  new_first = realloc( m->First, offset_of_over * 2 );
  IF( new_first == NULL ) goto err_fm;

  m->Next = (char*) new_first + ( (char*)m->Next - (char*)m->First );
  m->Over = (char*) new_first + offset_of_over * 2;
  m->First = new_first;

  return  0;

err:     return  E_Others;
err_fm:  return  E_FewMemory;
}

 
/***********************************************************************
  <<< [Set2_separate] >>> 
************************************************************************/
int  Set2_separate( Set2* m, int NextSize, void** allocate_Array )
{
  int    e;
  void*  p = m->First;

  if ( NextSize == 0 ) {
    MEMSET_TO_NOT_INIT( m, sizeof(*m) );
    m->First = NULL;
  }
  else {
    e= Set2_init( m, NextSize ); IF(e)goto fin;
  }
  *allocate_Array = p;

  e=0;
fin:
  return  e;
}


 
/*=================================================================*/
/* <<< [Print/Print2.c] >>> */ 
/*=================================================================*/
 
/***********************************************************************
  <<< [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

 
/***********************************************************************
  <<< [g_Error4_String] >>> 
************************************************************************/
TCHAR  g_Error4_String[4096];


 
/***********************************************************************
  <<< [Error4_printf] >>> 
************************************************************************/
void  Error4_printf( const TCHAR* format, ... )
{
	va_list  va;
	va_start( va, format );
	vstprintf_r( g_Error4_String, sizeof(g_Error4_String), format, va );
	va_end( va );
}


 
/***********************************************************************
  <<< [Error4_getErrStr] >>> 
************************************************************************/
void  Error4_getErrStr( int ErrNum, TCHAR* out_ErrStr, size_t ErrStrSize )
{
	switch ( ErrNum ) {

		case  0:
			stprintf_r( out_ErrStr, ErrStrSize, _T("no error") );
			break;

		#ifndef  __linux__
		case  E_GetLastError: {
			DWORD   err_win = GetLastError();
			TCHAR*  str_pointer;

			stprintf_part_r( out_ErrStr, ErrStrSize, out_ErrStr, &str_pointer,
				_T("<ERROR GetLastError=\"0x%08X\" GetLastErrorStr=\""), err_win );
			FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, err_win, LANG_USER_DEFAULT,
				str_pointer,  (TCHAR*)( (char*)out_ErrStr + ErrStrSize ) - str_pointer, NULL );
			str_pointer = _tcschr( str_pointer, _T('\0') );
			if ( *( str_pointer - 2 ) == _T('\r') && *( str_pointer - 1 ) == _T('\n') )
				str_pointer -= 2;
			stcpy_part_r( out_ErrStr, ErrStrSize, str_pointer, NULL, _T("\"/>"), NULL );
			break;
		}
		#endif

		default:
			if ( g_Error4_String[0] != '\0' )
				stprintf_r( out_ErrStr, ErrStrSize, _T("%s"), g_Error4_String );
			else
				stprintf_r( out_ErrStr, ErrStrSize, _T("<ERROR errnum=\"%d\"/>"), ErrNum );
			break;
	}
}


 
/***********************************************************************
  <<< [Error4_showToStdErr] >>> 
************************************************************************/
void  Error4_showToStdErr( int err_num )
{
	TCHAR  msg[1024];
	#if _UNICODE
		char  msg2[1024];
	#endif

	if ( err_num == 0 )  return;

	Error4_getErrStr( err_num, msg, sizeof(msg) );
	#if _UNICODE
		setlocale( LC_ALL, ".OCP" );
		sprintf_s( msg2, sizeof(msg2), "%S", msg );
		fprintf( stderr, "%s\n", msg2 );  // _ftprintf_s では日本語が出ません
	#else
		fprintf( stderr, "%s\n", msg );
	#endif

	#if 0
		#if ERR2_ENABLE_ERROR_BREAK
			fprintf( stderr, "（開発者へ）メイン関数で Err2_setBreakErrID( %d ); を呼び出してください。\n",
				g_Err2.ErrID );
		#else
			fprintf( stderr, "ERR2_ENABLE_ERROR_BREAK を定義して再コンパイルしてください。\n" );
		#endif
	#endif

	IfErrThenBreak();
}


 
/***********************************************************************
  <<< [Error4_raiseErrno] >>> 
************************************************************************/
#include <errno.h>

int  Error4_raiseErrno()
{
	int   e, e2;
	TCHAR  msg[256];

	e2 = _get_errno( &e );
	if ( e2 != 0 )  { Error4_printf( _T("ERROR in _get_errno") );  return  E_Unknown; }
	e2 = _tcserror_s( msg, sizeof(msg)/sizeof(TCHAR), e );
	if ( e2 != 0 )  { Error4_printf( _T("ERROR in strerror_s") );  return  E_Unknown; }

	Error4_printf( _T("ERROR (%d) %s\n"), e, msg );
	return  E_Errno;
}


 
