inifile.c

C:\home\SVGCats_src\src\inifile.c

[目次 | 関数]

目次

関数一覧


   1|/***********************************************************************
   2|*  1. <<< 初期化ファイル (IniFile) >>> 
   3|* 【拡張予定】
   4|* ・コメントが書けること(これに関してのみ Windows との互換を考えない)
   5|* ・ユーザが書いたコメントを消さないこと
   6|************************************************************************/
   7|
   8|#include "mixer_precomp.h"  /* Auto precompiled header, Look at mixer-... folder */
   9|// #pragma hdrstop
  10|
  11|#ifdef  USES_MXP_AUTOINC
  12| #include  <inifile.ah>  /* Auto include header, Look at mixer-... folder */
  13|#endif
  14|
  15|char* IniFile_Write_componeName = "IniFile_Write";
  16|char* IniFile_Read_componeName = "IniFile_Read";
  17|
  18|
  19| 
  20|/*---------------------------------------------------------*/
  21|/* 2. <<<◆初期化ファイル・書き込みツール [IniFile_Write] >>> */ 
  22|/*---------------------------------------------------------*/
  23|
  24|
  25| 
  26|/***********************************************************************
  27|*  3. <<< [IniFile_Write_init] 初期化する・ファイルを開く >>> 
  28|************************************************************************/
  29|void  IniFile_Write_init( IniFile_Write* m, const char* fname )
  30|{
  31|  ERRORS_FINISHCHK_FOR_INIT( IniFile_Write_finish );
  32|
  33|  m->file = fopen( fname, "wt" );
  34|  if ( m->file == NULL ) {
  35|    error2_1( FileX_Err_CannotWriteOpen,
  36|      "書きこみモードで open できません。%s", fname );
  37|  }
  38|  m->firstSection = true;
  39|}
  40|
  41|
  42| 
  43|/***********************************************************************
  44|*  4. <<< [IniFile_Write_finish] 後始末する・ファイルを閉じる >>> 
  45|************************************************************************/
  46|void  IniFile_Write_finish( IniFile_Write* m )
  47|{
  48|  ERRORS_FINISHCHK_FOR_FINISH( IniFile_Write_finish );
  49|
  50|  fputs( " \n", m->file );
  51|  fclose( m->file );
  52|}
  53|
  54|
  55| 
  56|/***********************************************************************
  57|*  5. <<< [IniFile_Write_putSection] セクションを出力する >>> 
  58|************************************************************************/
  59|void  IniFile_Write_putSection( IniFile_Write* m, const char* section )
  60|{
  61|  if ( ! m->firstSection )
  62|    fputs( " \n", m->file );
  63|  m->firstSection = false;
  64|  fprintf( m->file, "[%s] \n", section );
  65|}
  66|
  67|
  68| 
  69|/***********************************************************************
  70|*  6. <<< [IniFile_Write_putVar] 属性値を出力する >>> 
  71|*【引数】
  72|*  ・char*  attr_name;  属性名
  73|*  ・char*  types;      型リスト
  74|*  ・...                属性の値を並べたもの
  75|*【補足】
  76|*・types には、以下の型指定文字を並べた文字列を指定します。
  77|*    ・'s'  文字列型
  78|*    ・'i'  整数型(int型)
  79|*    ・'b'  論理型(bool型)
  80|*    ・'x'  整数型16進数(int型)
  81|*    ・'t'  日時型(time_t*型)
  82|*    ・'-'  属性値を無視(格納する変数を指定しない)
  83|*【例】
  84|*・attr = 1, string, 2  という属性値を出力する場合、
  85|*  printf と同様に、以下のように記述します。
  86|*   IniFile_Write_putVar( file, "attr", "isi", 1, "string", 2 );
  87|************************************************************************/
  88|void  IniFile_Write_putVar( IniFile_Write* m, const char* attr_name,
  89|  const char* types, ... )
  90|{
  91|  static char  s[4096];
  92|  va_list  va;
  93|
  94|  fprintf( m->file, "%s = ", attr_name );
  95|
  96|  va_start( va, types );
  97|
  98|  if ( *types != '\0' ) {
  99|    for (;;) {
 100|      switch ( *types ) {
 101|        case  's':
 102|          StrX_toCSV( s, va_arg( va, char* ), sizeof(s) );
 103|          fputs( s, m->file );
 104|          break;
 105|        case  'i':
 106|          fprintf( m->file, "%d", va_arg(va, int) );
 107|          break;
 108|        case  'b':
 109|		  fprintf( m->file, "%c", ( va_arg(va, bool) ? '1' : '0' ) );
 110|          break;
 111|        case  'x':
 112|          fprintf( m->file, "0x%X", va_arg(va, int) );
 113|          break;
 114|        case  't':
 115|          #ifdef USES_TIMEDATE
 116|            ASSERT( sizeof(s) >= TimeDate_loadBuf_size );
 117|            TimeDate_saveStr( va_arg(va, time_t*), s );
 118|            fputs( s, m->file );
 119|            break;
 120|          #endif
 121|        default:
 122|          ASSERT( *types == '-' );
 123|          break;
 124|      }
 125|
 126|      types ++;
 127|      if ( *types == '\0' )
 128|        break;
 129|
 130|      fputs( ", ", m->file );
 131|    }
 132|  }
 133|
 134|  va_end( va );
 135|
 136|  fputs( "\n", m->file );
 137|}
 138|
 139|
 140| 
 141|/***********************************************************************
 142|*  7. <<< [IniFile_Write_putLabel] ラベルを出力する >>> 
 143|*【引数】
 144|*  ・char*  label;  ラベル
 145|*【補足】
 146|*・ファイルには、ラベルの直後に ':' が付きます。
 147|*・出力したラベルは、IniFile_Read_getVar 関数を使ってスキップする
 148|*  必要があります。その際、型リストは "" にします。
 149|************************************************************************/
 150|void  IniFile_Write_putLabel( IniFile_Write* m, const char* label )
 151|{
 152|  fprintf( m->file, "%s:\n", label );
 153|}
 154|
 155|
 156| 
 157|/***********************************************************************
 158|*  8. <<< [IniFile_Write_putMultiLine] 複数行を出力する >>> 
 159|*【引数】
 160|*  ・char*  attr_name;  属性名
 161|*  ・char*  retStr;     改行文字("\r\n"や"\n")
 162|*  ・char*  s;          複数行の文字列
 163|*【補足】
 164|*・ファイルには、以下のように同じ属性が並びます。
 165|*  attr = text of line 1
 166|*  attr = text of line 2
 167|*  attr = text of line 3
 168|************************************************************************/
 169|#ifdef  USES_STRX
 170|void  IniFile_Write_putMultiLine( IniFile_Write* m, const char* attr_name,
 171|  const char* retStr, const char* s )
 172|{
 173|  enum { data_size = 256 };
 174|  char*  r;
 175|  char   data[data_size];
 176|
 177|  for (;;) {
 178|    r = strstr( s, retStr );
 179|    if ( r != NULL ) {
 180|      *(char*)r = '\0';
 181|      StrX_toCSV( data, s, data_size - 1 );
 182|      fprintf( m->file, "%s = %s\n", attr_name, data );
 183|      *(char*)r = *retStr;
 184|      s = r + strlen( retStr );
 185|    }
 186|    else {
 187|      if ( ! StrX_isSpaceStr( s ) ) {
 188|        StrX_toCSV( data, s, data_size - 1 );
 189|        fprintf( m->file, "%s = %s\n", attr_name, data );
 190|      }
 191|      return;
 192|    }
 193|  }
 194|}
 195|#endif
 196|
 197| 
 198|/**************************************************************************
 199|*  9. <<< [IniFile_Write_putTypeInfo] ファイルのバージョンを読みこんでチェックする >>> 
 200|*【補足】
 201|*・IniFile_Read_checkTypeInfo でデータをチェックできます。
 202|***************************************************************************/
 203|void  IniFile_Write_putTypeInfo( IniFile_Write* m, const char* appName,
 204|  int verNum )
 205|{
 206|  IniFile_Write_putVar( m, "Application", "s", appName );
 207|  IniFile_Write_putVar( m, "Version", "i", verNum );
 208|}
 209| 
 210|/***********************************************************************
 211|*  10. <<< [IniFile_Write_putMultiStrV] 複数の文字列を複数行で出力する >>> 
 212|*【引数】
 213|*  ・char*  attr_name;  属性名
 214|*  ・StrV_Set*  strs;   複数の文字列
 215|*【補足】
 216|*・ファイルには、以下のように同じ属性が並びます。
 217|*  attr = string of number 1
 218|*  attr = string of number 2
 219|*  attr = string of number 3
 220|************************************************************************/
 221|#ifdef  USES_STRV
 222|void  IniFile_Write_putMultiStrV( IniFile_Write* m, const char* attr_name,
 223|  StrV_Set* strs )
 224|{
 225|  StrV_SetP  sp;
 226|
 227|  for ( StrV_Set_forEach( strs, &sp ) ) {
 228|    fprintf( m->file, "%s = %s\n", attr_name, sp.p );
 229|  }
 230|}
 231|#endif
 232|
 233| 
 234|/***********************************************************************
 235|*  11. <<< [IniFile_Write_putMultiStrVM] 複数の文字列を複数行で出力する >>> 
 236|*【引数】
 237|*  ・char*  attr_name;  属性名
 238|*  ・StrV_Mem*  strs;   複数の文字列
 239|*【補足】
 240|*・ファイルには、以下のように同じ属性が並びます。
 241|*  attr = string of number 1
 242|*  attr = string of number 2
 243|*  attr = string of number 3
 244|************************************************************************/
 245|#ifdef  USES_STRV
 246|void  IniFile_Write_putMultiStrVM( IniFile_Write* m, const char* attr_name,
 247|  StrV_Mem* strs )
 248|{
 249|  StrV_SetP  sp;
 250|
 251|  for ( StrV_Mem_forEach( strs, &sp ) ) {
 252|    fprintf( m->file, "%s = %s\n", attr_name, sp.p );
 253|  }
 254|}
 255|#endif
 256|
 257| 
 258|/***********************************************************************
 259|*  12. <<< [IniFile_Write_putMultiStrAV] 複数の文字列を複数行で出力する >>> 
 260|*【引数】
 261|*  ・char*  attr_name;  属性名
 262|*  ・ArrX_Able*  strs;  複数の文字列(要素は StrV_AbleElem 型)
 263|*【補足】
 264|*・ファイルには、以下のように同じ属性が並びます。
 265|*  attr = string of number 1
 266|*  attr = string of number 2
 267|*  attr = string of number 3
 268|************************************************************************/
 269|#ifdef  USES_STRV
 270|void  IniFile_Write_putMultiStrAV( IniFile_Write* m, const char* attr_name,
 271|  ArrX_Able* strs )
 272|{
 273|  StrV_AbleElem*  sv;
 274|
 275|  for ( ArrX_Able_forEach( strs, &sv, StrV_AbleElem ) ) {
 276|    fprintf( m->file, "%s = %s\n", attr_name, sv->s );
 277|  }
 278|}
 279|#endif
 280|
 281| 
 282|/***********************************************************************
 283|*  13. <<< [IniFile_Write_putPathes] ファイルパスの集合を出力する >>> 
 284|*【引数】
 285|*  ・char*  section_name;  セクション名
 286|*  ・char** pathes;        ファイルパスの集合の先頭アドレス
 287|*  ・int    nPath;         ファイルパスの数
 288|*【補足】
 289|*・ファイルパスの集合は、1つのセクションを占有します。
 290|*・次の出力は、セクションの出力から始めてください。
 291|*【例】
 292|*・出力したデータは、IniFile_Read_getPathes 関数から入力することができます。
 293|************************************************************************/
 294|void  IniFile_Write_putPathes( IniFile_Write* m, const char* section_name,
 295|  char** pathes, int nPath )
 296|{
 297|  int  i;
 298|  static char  prevFolder[_MAX_PATH];
 299|  static char  s[_MAX_PATH];
 300|
 301|  IniFile_Write_putSection( m, section_name );
 302|
 303|  for ( i = 0; i < nPath; i++ ) {
 304|
 305|    /* フォルダ・パスを出力する */
 306|    ASSERT( strlen( *pathes ) < _MAX_PATH );
 307|    strcpy( s, *pathes );
 308|    StrX_cutFName( s );
 309|    if ( s[0] == '\0' )  strcpy( s, "." );
 310|    if ( i == 0 || strcmp( s, prevFolder ) != 0 ) {
 311|      IniFile_Write_putVar( m, "Folder", "s", s );
 312|      strcpy( prevFolder, s );
 313|    }
 314|
 315|    /* ファイル名を出力する */
 316|    StrX_cpyFName( s, *pathes );
 317|    IniFile_Write_putVar( m, "File", "s", s );
 318|
 319|    pathes ++;
 320|  }
 321|}
 322|
 323|
 324| 
 325|/*---------------------------------------------------------*/
 326|/* 14. <<<◆初期化ファイル・読み込みツール [IniFile_Read] >>>  */ 
 327|/*---------------------------------------------------------*/
 328|
 329|
 330|
 331| 
 332|/***********************************************************************
 333|*  15. <<< [IniFile_Read_init] 初期化する・ファイルを開く >>> 
 334|************************************************************************/
 335|void  IniFile_Read_init( IniFile_Read* m, const char* fname,
 336|  char* lineBuf, int lineBuf_sizeof )
 337|{
 338|  m->file = FileX_open( fname, "rt" );
 339|  if ( m->file == NULL ) {
 340|    error2_1( FileX_Err_CannotReadOpen,
 341|      "読みこみモードで open できません。%s", fname );
 342|  }
 343|  m->lineBuf = lineBuf;
 344|  m->lineBuf_sizeof = lineBuf_sizeof;
 345|  m->string_sizeof = 256;
 346|  strcpy( m->path, fname );
 347|
 348|  ERRORS_FINISHCHK_FOR_INIT( IniFile_Read_finish );
 349|}
 350|
 351|
 352| 
 353|/***********************************************************************
 354|*  16. <<< [IniFile_Read_setStringSize] 文字列を読み込むときの最大サイズ >>> 
 355|*【機能】
 356|*・IniFile_Read_getVar で文字列型を読み込むときに指定するバッファの
 357|*  サイズを指定します。
 358|************************************************************************/
 359|void  IniFile_Read_setStringSize( IniFile_Read* m, int size )
 360|{
 361|  m->string_sizeof = size;
 362|}
 363|
 364|
 365| 
 366|/***********************************************************************
 367|*  17. <<< [IniFile_Read_finish] 後始末する・ファイルを閉じる >>> 
 368|************************************************************************/
 369|void  IniFile_Read_finish( IniFile_Read* m )
 370|{
 371|  ERRORS_FINISHCHK_FOR_FINISH( IniFile_Read_finish );
 372|
 373|  fclose( m->file );
 374|}
 375|
 376|
 377| 
 378|/***********************************************************************
 379|*  18. <<< [IniFile_Read_setSection] セクションを選択する >>> 
 380|*【補足】
 381|*・次のセクションが指定の名前と異なるときは、IniFile_NotFoundSection エラー
 382|*  になります。
 383|*・ファイルの最後まで読んだら FileX_Err_EOF エラーになります。
 384|************************************************************************/
 385|void  IniFile_Read_setSection( IniFile_Read* m, const char* section )
 386|{
 387|  char*  buf = m->lineBuf;
 388|  char*  p;
 389|  long   pos = ftell( m->file );
 390|
 391|  /* 空行を飛ばして、セクションを読み込む */
 392|  do {
 393|    fgets( buf, m->lineBuf_sizeof, m->file );
 394|    StrX_trim( buf );
 395|
 396|    if ( feof( m->file ) ) {
 397|      error2_2( FileX_Err_EOF, "EOF in IniFile_Read_setSection %s [%s]",
 398|        m->path, section );
 399|    }
 400|  } while ( buf[0] == '\0' );
 401|
 402|  /* フォーマットのチェック */
 403|  p = strchr( buf, ']' );
 404|  if ( buf[0] != '[' || p == NULL ) {
 405|    error2_2( IniFile_Err_BadFormat,
 406|      "フォーマットがおかしい in IniFile_Read_setSection %s [%s]",
 407|      m->path, section );
 408|  }
 409|  *p = '\0';
 410|
 411|  /* セクション名のチェック */
 412|  if ( stricmp( section, buf + 1 ) != 0 ) {
 413|    fseek( m->file, pos, SEEK_SET );
 414|    error2_2( IniFile_NotFoundSection,
 415|      "セクション名が期待したものと異なる %s [%s]", m->path, section );
 416|  }
 417|}
 418|
 419|
 420| 
 421|/***********************************************************************
 422|*  19. <<< [IniFile_Read_reset] ファイルポインタを先頭に戻す >>> 
 423|*【補足】
 424|*・section に一致するセクションが無い場合は IniFile_NotFoundSection エラー
 425|*  になり、ファイルポインタは末尾に移動します。
 426|************************************************************************/
 427|void  IniFile_Read_reset( IniFile_Read* m )
 428|{
 429|  rewind( m->file );
 430|}
 431|
 432|
 433| 
 434|/***********************************************************************
 435|*  20. <<< [IniFile_Read_searchSection] セクションを検索する >>> 
 436|*【引数】
 437|*  ・char* section;   検索するセクション名
 438|*  ・bool  upward;    上方にも検索するかどうか
 439|*  ・long  返り値;    見つかったセクション中の先頭の属性のファイルアドレス
 440|*【補足】
 441|*・section に一致するセクションが無い場合は IniFile_NotFoundSection エラー
 442|*  になり、ファイルポインタは末尾に移動します。
 443|************************************************************************/
 444|long  IniFile_Read_searchSection( IniFile_Read* m, const char* section,
 445|  bool upward )
 446|{
 447|  char*  buf = m->lineBuf;
 448|  char*  p;
 449|  int  eof_count = 0;
 450|
 451|  for (;;) {
 452|
 453|    /* 1行読み込む */
 454|    fgets( buf, m->lineBuf_sizeof, m->file );
 455|    if ( buf[strlen(buf)-1] == '\n' )
 456|      buf[strlen(buf)-1] = '\0';
 457|
 458|    /* 1回目の EOF なら、ファイルの先頭へ、2回目なら例外 */
 459|    if ( feof( m->file ) ) {
 460|      eof_count ++;
 461|      if ( eof_count >= 2 || ! upward ) {
 462|        error2_1( IniFile_NotFoundSection,
 463|          "セクション %s が見つかりません", section );
 464|      }
 465|      rewind( m->file );
 466|    }
 467|
 468|    /* セクション名が一致するか判定する */
 469|    if ( buf[0] == '[' ) {
 470|      p = strchr( buf, ']' );
 471|      if ( p != NULL ) {
 472|        *p = '\0';
 473|        if ( strcmp( section, buf + 1 ) == 0 )
 474|          return  ftell( m->file );
 475|      }
 476|    }
 477|  }
 478|}
 479|
 480|
 481| 
 482|/***********************************************************************
 483|*  21. <<< [IniFile_Read_searchSection2] 第一属性が一致するセクションを検索する >>> 
 484|*【引数】
 485|*  ・char*  section;  検索するセクション名
 486|*  ・char*  attr;     第一属性名
 487|*  ・char*  type;     第一属性のタイプ
 488|*  ・...              第一属性の値と比較する値
 489|*  ・long  返り値;    見つかったセクション中の先頭の属性のファイルアドレス
 490|************************************************************************/
 491|#ifdef   USES_BIGSTACK
 492|void  IniFile_Read_searchSection2( IniFile_Read* m, const char* section,
 493|  const char* attr, const char* type, ... )
 494|{
 495|  void*  var;
 496|  va_list  va;
 497|  void*  p;
 498|  int  i;
 499|  bool b;
 500|
 501|  BigStack_start();
 502|  c_try {
 503|    switch ( *type ) {
 504|      case 's':
 505|        var = BigStack_alloc( m->string_sizeof  + 1 );
 506|        va_start( va, type );
 507|        p = va_arg( va, void* );
 508|        va_end( va );
 509|        do {
 510|          IniFile_Read_searchSection( m, section, true );
 511|          IniFile_Read_getVar( m, attr, type, var );
 512|        } while ( strcmp( var, p ) != 0 );
 513|        break;
 514|      case 'i':
 515|        var = BigStack_alloc( sizeof(int) );
 516|        va_start( va, type );
 517|        i = va_arg( va, int );
 518|        va_end( va );
 519|        do {
 520|          IniFile_Read_searchSection( m, section, true );
 521|          IniFile_Read_getVar( m, attr, type, var );
 522|        } while ( *(int*)var != i );
 523|        break;
 524|      case 'b':
 525|        var = BigStack_alloc( sizeof(bool) );
 526|        va_start( va, type );
 527|        b = va_arg( va, bool );
 528|        va_end( va );
 529|        do {
 530|          IniFile_Read_searchSection( m, section, true );
 531|          IniFile_Read_getVar( m, attr, type, var );
 532|        } while ( *(bool*)var != b );
 533|        break;
 534|    }
 535|  }
 536|  c_finally {
 537|    BigStack_end();
 538|  } c_end_finally;
 539|}
 540|#endif   /* USES_BIGSTACK */
 541|
 542|
 543|
 544| 
 545|/* 22. <<< -------- 属性値の入力 -------- >>> */ 
 546|
 547|
 548| 
 549|/***********************************************************************
 550|*  23. <<< [IniFile_Read_getCount] 同じ属性名の数を数える >>> 
 551|************************************************************************/
 552|int  IniFile_Read_getCount( IniFile_Read* m, const char* attr_name )
 553|{
 554|  int  count = 0;
 555|  long  pos;
 556|  char*  buf = m->lineBuf;
 557|  char*  p;
 558|  char   c;
 559|
 560|  pos = ftell( m->file );
 561|
 562|  for (;;) {
 563|    if ( fgets( buf, m->lineBuf_sizeof, m->file ) == NULL )  break;
 564|    while ( *buf == ' ' || *buf == '\t' ) {
 565|      if ( *buf == '\0' )  break;
 566|      buf++;
 567|    }
 568|    p = &buf[strlen(attr_name)];
 569|    c = *p;
 570|    if ( c != ' ' && c != '=' && c != ':' )  break;
 571|    *p = '\0';
 572|    if ( stricmp( buf, attr_name ) != 0 )  break;
 573|    count ++;
 574|  }
 575|
 576|  fseek( m->file, pos, SEEK_SET );
 577|  return  count;
 578|}
 579|
 580|
 581| 
 582|/***********************************************************************
 583|*  24. <<< [IniFile_Read_getVar] 属性値を入力する >>> 
 584|*【引数】
 585|*  ・char*  attr_name;  属性名
 586|*  ・char*  types;      型リスト
 587|*  ・...                属性の値を格納するアドレスを並べたもの
 588|*  ・long  返り値;      この属性行の先頭のファイルアドレス
 589|*【補足】
 590|*・types には、以下の型指定文字を並べた文字列を指定します。
 591|*    ・'s'or'S'  文字列型(char*型、引数の型は char*)
 592|*    ・'i'       整数型(int型、引数の型は int*)
 593|*    ・'b'       論理型(bool型、引数の型は bool*)
 594|*    ・'t'       日時型(time_t*型、引数の型は time_t*)
 595|*    ・'-'       属性値を無視(格納する変数を指定しない)
 596|*・属性名が違っていると、例外を投げます。
 597|*・'S' にして初期化ファイル中の属性値が省略されている場合、なにも値を格納しません。
 598|*  つまり、この関数を呼ぶ前にデフォルト値を入れておくことが出来ます。
 599|*【内部補足】
 600|*・行頭または空行の行頭にしてから、呼び出します。
 601|*・この関数から返ったとき、ファイルポインタは、属生行の次に移動しています。
 602|*【例】
 603|*・attr = 1, string, 2  という属性値を入力する場合、
 604|*  scanf と同様に、以下のように記述します。
 605|*   IniFile_Read_getVar( file, "attr", "isi", &i1, s, &i2 );
 606|************************************************************************/
 607|long  IniFile_Read_getVar( IniFile_Read* m, const char* attr_name,
 608|  const char* types, ... )
 609|{
 610|  long   pos;
 611|  char*  buf;
 612|  va_list  va;
 613|
 614|  pos = ftell( m->file );
 615|
 616|  /* 属性名をチェックする */
 617|  buf = IniFile_Read_readAttrName( m, attr_name );
 618|
 619|  /* 値を読み込む */
 620|  va_start( va, types );
 621|  IniFile_Read_readVar( m, buf, types, va );
 622|  va_end( va );
 623|
 624|  return  pos;
 625|}
 626|
 627|
 628| 
 629|/***********************************************************************
 630|*  25. <<< [IniFile_Read_getNextCount] 全属性の数を返す >>> 
 631|*【補足】
 632|*・現在のファイルポインタの位置から次のセクションまで、またはファイルの終了
 633|*  までの属性の数を返します。
 634|************************************************************************/
 635|int  IniFile_Read_getNextCount( IniFile_Read* m )
 636|{
 637|  int  count = 0;
 638|  long  pos;
 639|  char*  buf = m->lineBuf;
 640|
 641|  pos = ftell( m->file );
 642|
 643|  for (;;) {
 644|    fgets( buf, m->lineBuf_sizeof, m->file );
 645|    if ( feof( m->file ) )  break;
 646|
 647|    if ( buf[strlen(buf)-1] == '\n' )
 648|      buf[strlen(buf)-1] = '\0';
 649|    StrX_trim( buf );
 650|
 651|    if ( ! StrX_isSpaceStr( buf ) ) {
 652|      if ( buf[0] == '[' )  break;
 653|      count ++;
 654|    }
 655|  }
 656|
 657|  fseek( m->file, pos, SEEK_SET );
 658|  return  count;
 659|}
 660|
 661| 
 662|/***********************************************************************
 663|*  26. <<< [IniFile_Read_getNextVar] 次の属性名と属性値を入力する >>> 
 664|*【引数】
 665|*  ・char*  attr_name;  属性名を格納するアドレス(出力)
 666|*  ・char*  types;      型リスト
 667|*  ・...                属性の値を格納するアドレスを並べたもの(出力)
 668|*  ・long  返り値;      この属性行の先頭のファイルアドレス, -1L=次は無い
 669|*【補足】
 670|*・次の属性が無い場合(次の行は、次のセクションの場合)、-1L を返します。
 671|************************************************************************/
 672|long  IniFile_Read_getNextVar( IniFile_Read* m, char* attr_name,
 673|  const char* types, ... )
 674|{
 675|  long   pos;
 676|  char*  buf = m->lineBuf;
 677|  char*  p;
 678|  va_list  va;
 679|
 680|  pos = ftell( m->file );
 681|
 682|  /* 空行を飛ばして、属性行を読み込む */
 683|  do {
 684|    fgets( buf, m->lineBuf_sizeof, m->file );
 685|    if ( feof( m->file ) )
 686|      return  -1L;
 687|
 688|    if ( buf[strlen(buf)-1] == '\n' )
 689|      buf[strlen(buf)-1] = '\0';
 690|    StrX_trim( buf );
 691|  } while ( StrX_isSpaceStr( buf ) );
 692|
 693|  /* セクションの区切りに来たら、NULL を返す */
 694|  if ( buf[0] == '[' ) {
 695|    fseek( m->file, pos, SEEK_SET );
 696|    return  -1L;
 697|  }
 698|
 699|  /* 属性名を取得する */
 700|  p = strchr( buf, '=' );
 701|  if ( p != NULL )  *p = '\0';
 702|  StrX_trim( buf );
 703|  strcpy( attr_name, buf );
 704|
 705|  /* 値を読み込む */
 706|  if ( p == NULL )  p = strchr( buf, '\0' );
 707|  else  p = p + 1;
 708|  va_start( va, types );
 709|  IniFile_Read_readVar( m, p, types, va );
 710|  va_end( va );
 711|
 712|  return  pos;
 713|}
 714|
 715| 
 716|/***********************************************************************
 717|*  27. <<< [IniFile_Read_getVarByPos] ファイルアドレスから属性値を入力する >>> 
 718|*【引数】
 719|*  ・long   pos;        ファイルアドレス
 720|*  ・char*  types;      型リスト(IniFile_Read_getVar 関数を参照)
 721|*  ・...                属性の値を格納するアドレスを並べたもの
 722|************************************************************************/
 723|void  IniFile_Read_getVarByPos( IniFile_Read* m, long pos,
 724|  const char* types, ... )
 725|{
 726|  va_list  va;
 727|  char*  buf = m->lineBuf;
 728|  char*  p;
 729|
 730|  fseek( m->file, pos, SEEK_SET );
 731|
 732|  /* イコールまでスキップする */
 733|  for(;;) {
 734|    fgets( buf, m->lineBuf_sizeof, m->file );
 735|    p = buf;
 736|    while ( *p != '\0' ) {
 737|      if ( *p == '=' )
 738|        goto exit_for;
 739|      p++;
 740|    }
 741|  }
 742|exit_for:
 743|
 744|  /* 値の先頭までスキップする */
 745|  p++;
 746|  while ( *p == ' ' )  p++;
 747|  StrX_trim( p );
 748|
 749|  /* 値を読み込む */
 750|  va_start( va, types );
 751|  IniFile_Read_readVar( m, p, types, va );
 752|  va_end( va );
 753|}
 754|
 755|
 756| 
 757|/***********************************************************************
 758|*  28. <<< [IniFile_Read_getPlusVar] 属性値を入力する(追加用)>>> 
 759|*【補足】
 760|*・バージョンアップにより属性を追加した場合に容易に対応するための
 761|*  IniFile_Read_getVar 関数です。
 762|*・格納する変数列の後に、デフォルト値を以下のように指定します。
 763|*  IniFile_Read_getVar( file, "attr", "isi", &i1, s, &i2, 10, "abc", 5 );
 764|*・なるべく、この関数を使わずに、バージョン番号によって読み込むルーチンを
 765|*  変えるようにしてください。
 766|************************************************************************/
 767|void  IniFile_Read_getPlusVar( IniFile_Read* m, const char* attr_name,
 768|  const char* types, ... )
 769|{
 770|  char*  buf;
 771|  va_list  va;
 772|  int*   i;
 773|  char*  s;
 774|  bool*  b;
 775|
 776| // ASSERT( strlen(types) <= 1 );  /*未対応*/
 777|
 778|  /* 属性値を入力する */
 779|  if ( IniFile_Read_getCount( m, attr_name ) > 0 ) {
 780|
 781|    /* 属性名をチェックする */
 782|    buf = IniFile_Read_readAttrName( m, attr_name );
 783|
 784|    /* 値を読み込む */
 785|    va_start( va, types );
 786|    IniFile_Read_readVar( m, buf, types, va );
 787|    va_end( va );
 788|  }
 789|
 790|  /* デフォルト値を格納する */
 791|  else {
 792|    va_start( va, types );
 793|    switch ( *types ) {
 794|      case 'i':
 795|        i = va_arg( va, int* );
 796|        *i = va_arg( va, int );
 797|        break;
 798|      case 'b':
 799|        b = va_arg( va, bool* );
 800|        *b = va_arg( va, bool );
 801|        break;
 802|      case 's':
 803|        s = va_arg( va, char* );
 804|        strcpy( s, va_arg( va, char* ) );
 805|        break;
 806|    }
 807|    va_end( va );
 808|  }
 809|}
 810| 
 811|/**************************************************************************
 812|*  29. <<< [IniFile_Read_checkTypeInfo] ファイルのバージョンを読みこんでチェックする >>> 
 813|*【引数】
 814|*・int  verNum_start;  アプリが対応しているバージョンの最小値(整数)
 815|*・int  verNum_end;    アプリが対応しているバージョンの最大値(整数)
 816|*【補足】
 817|*・IniFile_Write_putTypeInfo で出力したデータをチェックします。
 818|*・エラーが発生したら内部で、error2_2 を呼び出しています。
 819|*  エラーコードは、IniFile_Err_OtherApp IniFile_Err_OtherVer です。
 820|***************************************************************************/
 821|void  IniFile_Read_checkTypeInfo( IniFile_Read* m, const char* appName,
 822|  int verNum_start, int verNum_end )
 823|{
 824|  int  n;
 825|  char  s[256];
 826|
 827|  IniFile_Read_getVar( m, "Application", "s", s );
 828|  if ( strcmp( s, appName ) != 0 ) {
 829|    error2_2( IniFile_Err_OtherApp, "アプリケーションが異なります (file:%s, app:%s)",
 830|      s, appName );
 831|  }
 832|  IniFile_Read_getVar( m, "Version", "i", &n );
 833|  if ( n < verNum_start || n > verNum_end ) {
 834|    error2_3( IniFile_Err_OtherVer, "アプリが対応していない、新しいバージョンのファイルです"
 835|      " (file:%d, app:%d〜%d)", n, verNum_start, verNum_end );
 836|  }
 837|}
 838| 
 839|/***********************************************************************
 840|*  30. <<< [IniFile_Read_getVar2] 任意数の属性値を入力する >>> 
 841|*【引数】
 842|*  ・StrX_Mem*  mem;    文字列を格納する領域 →補足
 843|*  ・char*  attr_name;  属性名
 844|*  ・char*  types;      型リスト
 845|*  ・...                属性の値を格納するアドレスを並べたもの
 846|*  ・long  返り値;      この属性行の先頭のファイルアドレス
 847|*【補足】
 848|*・types に文字列型を指定しなければ、mem には NULL を指定できます。
 849|*・types には、以下の型指定文字を並べた文字列を指定します。
 850|*    ・'s'  文字列型(char* 型、引数の型は char**)
 851|*    ・'i'  整数型(int型、引数の型は int*)
 852|*    ・'b'  論理型(bool型、引数の型は bool*)
 853|*    ・'*s' 文字列配列型(引数の型は ArrX_Buf* と ArrX*)(→[*1])
 854|*    ・'*i' 整数配列型(引数の型は ArrX_Buf* と ArrX*)
 855|*    ・'-'  属性値を無視(格納する変数を指定しない)
 856|*・[*1] types=="*s" の場合、IniFile_Read_getVar2s 関数が便利です。
 857|*・属性名が違っていると、例外を投げます。
 858|*・配列には、ArrX_Buf が用意する限り、任意の要素数の要素を格納することが
 859|*  できます。
 860|*・属性値が省略された場合、文字列型では NULL が格納され、
 861|*  整数型では何も値を格納しません。数値型では、この関数を呼ぶ前に
 862|*  デフォルト値を入れておくことが出来ます。
 863|*【内部補足】
 864|*・行頭または空行の行頭にしてから、呼び出します。
 865|*・この関数から返ったとき、ファイルポインタは、属生行の次に移動しています。
 866|*【例】
 867|*・attr = 1, string, 2  という属性値を入力する場合、
 868|*  scanf と同様に、以下のように記述します。
 869|*  ただし、文字列は、char** 型を指定することに注意してください
 870|*   IniFile_Read_getVar2( file, mem, "attr", "isi", &i1, &s, &i2 );
 871|*・attr = 1, 2, 4, 6 という属性値を配列に入力する場合、
 872|*  以下のように記述します。
 873|*   IniFile_Read_getVar2( file, mem, "attr", "*i", buf, arr );
 874|*   ただし、arr のメモリ領域を buf から確保します。
 875|*    ・ArrX_Buf* buf;  配列の領域(初期化済みを指定)
 876|*    ・ArrX* arr;      配列(未初期化状態のを指定)
 877|*・attr = 1, abc, def, ghi という属性値のうち、2列目(abc)以降を
 878|+  を配列に入力する場合、以下のように記述します。
 879|*   IniFile_Read_getVar2( file, mem, "attr", "i*s", &i, buf, arr );
 880|************************************************************************/
 881|#ifdef  USES_STRX
 882|long  IniFile_Read_getVar2( IniFile_Read* m, StrX_Mem* mem,
 883|  const char* attr_name, const char* types, ... )
 884|{
 885|  long   pos;
 886|  char*  buf;
 887|  va_list  va;
 888|
 889|  pos = ftell( m->file );
 890|
 891|  /* 属性名をチェックする */
 892|  buf = IniFile_Read_readAttrName( m, attr_name );
 893|
 894|  /* 値を読み込む */
 895|  va_start( va, types );
 896|  IniFile_Read_readVar2( m, buf, mem, types, va );
 897|  va_end( va );
 898|
 899|  return  pos;
 900|}
 901|#endif
 902|
 903| 
 904|/***********************************************************************
 905|*  31. <<< [IniFile_Read_getVar2s] 任意数の文字列型属性値を入力する(StrX_Mem) >>> 
 906|*【引数】
 907|*  ・StrX_Mem*  mem;    文字列を格納する領域 →補足
 908|*  ・char*  attr_name;  属性名
 909|*  ・StrX_Set*  set;    入力した文字列(複数)を格納する集合(出力)
 910|*  ・long  返り値;      この属性行の先頭のファイルアドレス
 911|************************************************************************/
 912|#ifdef  USES_STRX
 913|long  IniFile_Read_getVar2s( IniFile_Read* m, StrX_Mem* mem,
 914|  const char* attr_name, StrX_Set* set )
 915|{
 916|  long  ret;
 917|
 918|  StrX_Set_init1( set, mem );
 919|  ret = IniFile_Read_getVar2( m, mem, attr_name, "*s", NULL, NULL );
 920|  StrX_Set_init2( set, mem );
 921|
 922|  return  ret;
 923|}
 924|#endif
 925|
 926|
 927| 
 928|/***********************************************************************
 929|*  32. <<< [IniFile_Read_getVar2sv] 任意数の文字列型属性値を入力する(StrV_Mem) >>> 
 930|*【引数】
 931|*  ・StrV_Mem*  mem;    文字列を格納する領域 →補足
 932|*  ・char*  attr_name;  属性名
 933|*  ・StrV_Set*  set;    入力した文字列(複数)を格納する集合(出力)
 934|*  ・long  返り値;      この属性行の先頭のファイルアドレス
 935|************************************************************************/
 936|#ifdef USES_ARRX
 937|#ifdef USES_STRV
 938|void  IniFile_Read_getVar2sv( IniFile_Read* m, StrV_Mem* mem,
 939|  const char* attr_name, StrV_Set* set )
 940|{
 941|  char*  vars;
 942|  char*  nextVar;
 943|
 944|  StrV_Set_init1( set, mem );
 945|  vars = IniFile_Read_readAttrName( m, attr_name );
 946|  StrV_Mem_setMax( mem, m->string_sizeof );
 947|
 948|  for (;;) {
 949|
 950|    nextVar = StrX_meltCSV( vars, vars );
 951|    strcpy( StrV_Mem_alloc( mem ), vars );
 952|
 953|    if ( nextVar == NULL )  break;
 954|
 955|    /* 次の値を格納する準備をする */
 956|    vars = nextVar;
 957|    while ( *vars == ' ' )  vars ++;
 958|  }
 959|  StrV_Set_init2( set, mem );
 960|}
 961|#endif
 962|#endif
 963|
 964|
 965| 
 966|/***********************************************************************
 967|*  33. <<< [IniFile_Read_getVar2av] StrV_AbleElem 型の文字列を入力する >>> 
 968|*【引数】
 969|*  ・char*  attr_name;   属性名
 970|*  ・ArrX_Able*  set;    入力した文字列を格納する StrV_AbleElem 型構造体の集合
 971|*【補足】
 972|*・set の無効になっている要素に追加します。
 973|************************************************************************/
 974|#ifdef USES_ARRX
 975|#ifdef USES_STRV
 976|void  IniFile_Read_getVar2av( IniFile_Read* m, const char* attr_name,
 977|  ArrX_Able* set )
 978|{
 979|  char*  vars;
 980|  char*  nextVar;
 981|  StrV_AbleElem*  s;
 982|
 983|  vars = IniFile_Read_readAttrName( m, attr_name );
 984|
 985|  for (;;) {
 986|
 987|    nextVar = StrX_meltCSV( vars, vars );
 988|    s = ArrX_Able_getFirstDisabled( set, StrV_AbleElem );
 989|    StrV_cpy( &s->s, vars );
 990|
 991|    if ( nextVar == NULL )  break;
 992|
 993|    /* 次の値を格納する準備をする */
 994|    vars = nextVar;
 995|    while ( *vars == ' ' )  vars ++;
 996|  }
 997|}
 998|#endif
 999|#endif
1000| 
1001|/***********************************************************************
1002|*  34. <<< [IniFile_Read_getPlusVar2] 任意数の属性値を入力する(追加用)>>> 
1003|*【機能】
1004|*・StrX_Mem を指定する IniFile_Read_getPlusVar 関数です。
1005|*【補足】
1006|*・もし、文字列のデフォルト値を入力する場合、StrX_Mem を使用しないで、
1007|*  指定したデフォルト文字列を参照するようにします。もし、デフォルト文字列
1008|*  がリテラル(文字列定数)でない場合、注意してください。
1009|************************************************************************/
1010|#ifdef  USES_STRX
1011|void  IniFile_Read_getPlusVar2( IniFile_Read* m, StrX_Mem* mem,
1012|  const char* attr_name, const char* types, ... )
1013|{
1014|  char*   buf;
1015|  va_list va;
1016|  int*    i;
1017|  char**  s;
1018|  bool*   b;
1019|
1020|  ASSERT( strlen(types) == 1 );  /*未対応*/
1021|
1022|  /* 属性値を入力する */
1023|  if ( IniFile_Read_getCount( m, attr_name ) > 0 ) {
1024|
1025|    /* 属性名をチェックする */
1026|    buf = IniFile_Read_readAttrName( m, attr_name );
1027|
1028|    /* 値を読み込む */
1029|    va_start( va, types );
1030|    IniFile_Read_readVar2( m, buf, mem, types, va );
1031|    va_end( va );
1032|  }
1033|
1034|  /* デフォルト値を格納する */
1035|  else {
1036|    va_start( va, types );
1037|    switch ( *types ) {
1038|      case 'i':
1039|        i = va_arg( va, int* );
1040|        *i = va_arg( va, int );
1041|        break;
1042|      case 'b':
1043|        b = va_arg( va, bool* );
1044|        *b = va_arg( va, bool );
1045|        break;
1046|      case 's':
1047|        s = va_arg( va, char** );
1048|        *s = va_arg( va, char* );  /* 参照する */
1049|        break;
1050|    }
1051|    va_end( va );
1052|  }
1053|}
1054|#endif
1055|
1056|
1057| 
1058|/***********************************************************************
1059|*  35. <<< [IniFile_Read_getVars] 同じ属性名の構造体を連続入力する >>> 
1060|*【引数】
1061|*  ・StrX_Mem*  mem;     文字列を格納する領域
1062|*  ・ArrX_Buf*  pmem;    構造体を格納する領域
1063|*  ・char*      attr_name;  属性名
1064|*  ・char*      types;   型リスト
1065|*  ・ArrX*      arr;     構造体配列(出力)
1066|*【補足】
1067|*・型リストに関しては、IniFile_Read_getVar2 関数を参照。
1068|*・たとえば、types="isi" なら、以下の構造体の配列を arr に格納します。
1069|*  struct { int p1; char* p2; int p3; };
1070|*  ファイルでは次のような形になります。
1071|*    Struct = 1, abc, 11
1072|*    Struct = 2, efg, 22
1073|*       :         :
1074|*  ただし、構造体の領域は pmem、文字列の領域は mem を使用します。
1075|*・行を配列、列を配列要素とみなす場合、IniFile_Read_getVar2 を使います。
1076|*・型リストに指定する要素が1つの場合、構造体でなく基本型で構いません。
1077|*  たとえば、types="i" なら、ArrX は、int 型の配列になります。
1078|*・arr == NULL の場合、pmem に格納するだけです。
1079|************************************************************************/
1080|#ifdef USES_ARRX
1081|#ifdef  USES_STRX
1082|void  IniFile_Read_getVars( IniFile_Read* m, StrX_Mem* mem, ArrX_Buf* pmem,
1083|  const char* attr_name, const char* types, ArrX* arr )
1084|{
1085|  int   i,n;
1086|  int*  pp_first;  /* 最初の構造体へのポインタ */
1087|  int*  pp;        /* 現在の構造体へのポインタ */
1088|  char* buf;       /* 行バッファ中の現在の値の位置 */
1089|  char* nextBuf;   /* 次の buf */
1090|  const char* t;   /* types のポインタ */
1091|
1092|  ASSERT( sizeof(int) == sizeof(char*) );
1093|
1094|  n = IniFile_Read_getCount( m, attr_name );
1095|  pp_first = ArrX_Buf_allocs( pmem, int, n * strlen( types ) );
1096|  pp = pp_first;
1097|  for ( i = 0; i < n; i++ ) {
1098|    t = types;
1099|    buf = IniFile_Read_readAttrName( m, attr_name );
1100|    if ( *t != '\0' ) {
1101|      for (;;) {
1102|
1103|        /* 値の文字列を作る */
1104|        nextBuf = StrX_meltCSV( buf, buf );
1105|
1106|        /* それぞれの型の値を読み込む */
1107|        switch ( *t ) {
1108|          case  's':
1109|            if ( (int)strlen( buf ) > m->string_sizeof - 1 )
1110|              buf[m->string_sizeof - 1] = '\0';
1111|            *pp = (int)StrX_Mem_alloc( mem );
1112|            strcpy( (char*)*pp, buf );
1113|            break;
1114|          case  'i':
1115|            *pp = atoi( buf );
1116|            break;
1117|          #ifdef USES_TIMEDATE
1118|            case 't':
1119|              TimeDate_loadStr( pp, buf );
1120|              break;
1121|          #endif
1122|          default:
1123|            Errors_notSupport();
1124|        }
1125|        pp ++;
1126|        t ++;
1127|        if ( *t == '\0' )
1128|          break;
1129|
1130|        if ( nextBuf == NULL ) {
1131|          error2_1( IniFile_Err_FewData,
1132|            "%s 属性のデータのが少ない", attr_name );
1133|        }
1134|        buf = nextBuf + 1;
1135|        while ( *buf == ' ' )  buf ++;
1136|      }
1137|    }
1138|  }
1139|  if ( arr != NULL )
1140|    ArrX_init2( arr, pp_first, pp );
1141|}
1142|#endif
1143|#endif /* USES_ARRX */
1144|
1145|
1146| 
1147|/***********************************************************************
1148|*  36. <<< [IniFile_Read_getVars2] 同じ属性名の構造体を連続入力する2 >>> 
1149|*【引数】
1150|*  ・StrX_Mem*  mem;     文字列を格納する領域
1151|*  ・ArrX_Buf*  pmem;    構造体を格納する領域
1152|*  ・char*      attr_name;  属性名
1153|*  ・char*      types;   型リスト
1154|*  ・ArrX*      arr;     構造体配列(出力),または NULL
1155|*  ・int        size;    1つの構造体のメモリサイズ
1156|*  ・...                 オフセット値(Offset型)の羅列
1157|*【補足】
1158|*・IniFile_Read_getVars との違いは、構造体メンバへの格納先を任意に
1159|*  指定できることです。
1160|*・たとえば、types="si" で、以下の構造体(の配列)に格納する場合、
1161|*  ... の引数は次のように指定します。
1162|*  struct { int p1; char* p2; int p3; };
1163|*  引数= Offset_init2(s,p2), Offset_init2(s,p3)
1164|*  ただし s は、構造体への任意のポインタとする。
1165|*・arr == NULL の場合、pmem に格納するだけです。
1166|************************************************************************/
1167|#ifdef USES_ARRX
1168|#ifdef USES_STRX
1169|#ifdef USES_OFFSET
1170|void  IniFile_Read_getVars2( IniFile_Read* m, StrX_Mem* mem, ArrX_Buf* pmem,
1171|  const char* attr_name, const char* types, ArrX* arr, int size, ... )
1172|{
1173|  va_list  va;
1174|  int   i,n;
1175|  char*  pp_first;  /* 最初の構造体へのポインタ */
1176|  char*  pp;        /* 現在の構造体へのポインタ */
1177|  char*  pp2;       /* 構造体のメンバ変数へのポインタ */
1178|  char*  buf;       /* 行バッファ中の現在の値の位置 */
1179|  char*  nextBuf;   /* 次の buf */
1180|  const char* t;   /* types のポインタ */
1181|
1182|  ASSERT( sizeof(int) == sizeof(char*) );
1183|
1184|  n = IniFile_Read_getCount( m, attr_name );
1185|  pp_first = ArrX_Buf_allocs( pmem, char, n * size );
1186|  pp = pp_first;
1187|  for ( i = 0; i < n; i++ ) {
1188|    t = types;
1189|    buf = IniFile_Read_readAttrName( m, attr_name );
1190|    va_start( va, size );
1191|    if ( *t != '\0' ) {
1192|      for (;;) {
1193|
1194|        /* 値の文字列を作る */
1195|        nextBuf = StrX_meltCSV( buf, buf );
1196|
1197|        /* オフセット */
1198|        pp2 = &Offset_ref( va_arg(va,int), pp, char );
1199|
1200|        /* それぞれの型の値を読み込む */
1201|        switch ( *t ) {
1202|          case  's':
1203|            if ( (int)strlen( buf ) > m->string_sizeof - 1 )
1204|              buf[m->string_sizeof - 1] = '\0';
1205|            *(char**)pp2 = StrX_Mem_alloc( mem );
1206|            strcpy( *(char**)pp2, buf );
1207|            break;
1208|          case  'i':
1209|            *(int*)pp2 = atoi( buf );
1210|            break;
1211|          #ifdef USES_TIMEDATE
1212|            case 't':
1213|              TimeDate_loadStr( (time_t*)pp2, buf );
1214|              break;
1215|          #endif
1216|        }
1217|        t ++;
1218|        if ( *t == '\0' )
1219|          break;
1220|
1221|        if ( nextBuf == NULL ) {
1222|          error2_1( IniFile_Err_FewData,
1223|            "%s 属性のデータのが少ない", attr_name );
1224|        }
1225|        buf = nextBuf + 1;
1226|        while ( *buf == ' ' )  buf ++;
1227|      }
1228|    }
1229|    pp += size;
1230|    va_end( va );
1231|  }
1232|  if ( arr != NULL )
1233|    ArrX_init2( arr, pp_first, pp );
1234|}
1235|#endif /* USES_OFFSET */
1236|#endif
1237|#endif /* USES_ARRX */
1238|
1239|
1240| 
1241|/***********************************************************************
1242|*  37. <<< [IniFile_Read_getMultiLine] 複数行を入力する >>> 
1243|*【引数】
1244|*  ・char* attr_name;  属性名
1245|*  ・char* buf;        複数行バッファ
1246|*  ・int   buf_sizeof; buf の領域のサイズ
1247|*  ・char* retStr;     改行文字("\r\n"や"\n")
1248|*  ・char* 返り値;     buf
1249|*【補足】
1250|*・属性名が違っていると、例外を投げます。
1251|*・複数行は、初期化ファイル内では、次のように同じ属性が続きます。
1252|*  attr = text of line 1
1253|*  attr = text of line 2
1254|*  attr = text of line 3
1255|*・1行あたり IniFile_Read_setStringSize で設定した値のバイト数しか
1256|*  読み込むことができません。逆に、設定した値が大きすぎると、次の行を
1257|*  読み込むための buf の余裕がすぐに無くなります。
1258|************************************************************************/
1259|char*  IniFile_Read_getMultiLine( IniFile_Read* m, const char* attr_name,
1260|  char* retStr, char* buf, int buf_sizeof )
1261|{
1262|  int  i,n;
1263|  char*  p;
1264|  char*  p_over;
1265|
1266|  p = buf;
1267|  p_over = buf + buf_sizeof;
1268|
1269|  n = IniFile_Read_getCount( m, attr_name );
1270|  for ( i = 0; i < n; i++ ) {
1271|    if ( p_over - p < m->string_sizeof ) {
1272|      error2_1( IniFile_Err_FewBuf,
1273|        "バッファ領域が少ない(%s属性)", attr_name );
1274|    }
1275|    IniFile_Read_getVar( m, attr_name, "s", p );
1276|    p = strchr( p, '\0' );
1277|    strcat( p, retStr );
1278|    p = strchr( p, '\0' );
1279|  }
1280|  *p = '\0';
1281|
1282|  return  buf;
1283|}
1284|
1285|
1286| 
1287|/***********************************************************************
1288|*  38. <<< [IniFile_Read_getMultiLineM] 複数行を入力する >>> 
1289|*【引数】
1290|*  ・char*   attr_name;  属性名
1291|*  ・char*   mem;        文字列格納領域
1292|*  ・char**  var;        文字列(char*)のアドレス
1293|*  ・char*   retStr;     改行文字("\r\n"や"\n")
1294|*【補足】
1295|*・IniFile_Read_getMultiLine 関数の StrX_Mem を使用するタイプです。
1296|************************************************************************/
1297|#ifdef  USES_STRX
1298|void  IniFile_Read_getMultiLineM( IniFile_Read* m, StrX_Mem* mem,
1299|  const char* attr_name, char* retStr, char** var )
1300|{
1301|  *var = StrX_Mem_alloc( mem );
1302|  IniFile_Read_getMultiLine( m, attr_name, retStr,
1303|    *var, StrX_Mem_getLeftSize( mem ) );
1304|}
1305|#endif
1306|
1307| 
1308|/***********************************************************************
1309|*  39. <<< [IniFile_Read_getMultiStr] 複数行を複数の文字列として入力する(StrX_Set) >>> 
1310|*【引数】
1311|*  ・char*  attr_name;  属性名
1312|*  ・StrX_Mem*  buf;    文字列を格納する領域
1313|*  ・StrX_Set*  set;    文字列の部分集合
1314|*【補足】
1315|*・set は初期化されます。
1316|*・内部で、StrX_Mem_alloc を呼び出しています。
1317|*・複数行は、初期化ファイル内では、次のように同じ属性が続きます。
1318|*  attr = text of line 1
1319|*  attr = text of line 2
1320|*  attr = text of line 3
1321|************************************************************************/
1322|#ifdef  USES_STRX
1323|#ifdef  USES_ARRX
1324|void  IniFile_Read_getMultiStr( IniFile_Read* m, StrX_Mem* buf,
1325|  const char* attr_name, StrX_Set* set )
1326|{
1327|  StrX_Set_init1( set, buf );
1328|  IniFile_Read_getMultiStrM( m, attr_name, buf );
1329|  StrX_Set_init2( set, buf );
1330|}
1331|#endif
1332|#endif
1333|
1334|
1335| 
1336|/***********************************************************************
1337|*  40. <<< [IniFile_Read_getMultiStrM] 複数行を複数の文字列として入力する(StrX_Mem) >>> 
1338|*【引数】
1339|*  ・char*  attr_name;  属性名
1340|*  ・StrX_Mem*  buf;    文字列を格納する領域
1341|*【補足】
1342|*・IniFile_Read_getMultiStr との違いは、StrX_Set に格納しないこと
1343|*  だけです。
1344|************************************************************************/
1345|#ifdef  USES_STRX
1346|#ifdef  USES_ARRX
1347|void  IniFile_Read_getMultiStrM( IniFile_Read* m, const char* attr_name,
1348|  StrX_Mem* buf )
1349|{
1350|  int  i,n;
1351|  char*  s;
1352|
1353|  n = IniFile_Read_getCount( m, attr_name );
1354|  for ( i = 0; i < n; i++ ) {
1355|    s = StrX_Mem_alloc( buf );
1356|    if ( (int)StrX_Mem_getLeftSize( buf ) < m->string_sizeof ) {
1357|      error2_0( IniFile_Err_FewBuf, "文字列の格納領域が足りません" );
1358|    }
1359|    IniFile_Read_getVar( m, attr_name, "s", s );
1360|  }
1361|}
1362|#endif
1363|#endif
1364|
1365|
1366| 
1367|/***********************************************************************
1368|*  41. <<< [IniFile_Read_getMultiStrV] 複数行を複数の文字列として入力する(StrV_Set) >>> 
1369|*【引数】
1370|*  ・char*  attr_name;  属性名
1371|*  ・StrV_Mem*  buf;    文字列を格納する領域
1372|*  ・StrV_Set*  set;    文字列の部分集合
1373|*【補足】
1374|*・set は初期化されます。
1375|*・内部で、StrV_Mem_alloc を呼び出しています。
1376|*・複数行は、初期化ファイル内では、次のように同じ属性が続きます。
1377|*  attr = text of line 1
1378|*  attr = text of line 2
1379|*  attr = text of line 3
1380|************************************************************************/
1381|#ifdef  USES_STRV
1382|void  IniFile_Read_getMultiStrV( IniFile_Read* m, StrV_Mem* buf,
1383|  const char* attr_name, StrV_Set* set )
1384|{
1385|  int  i,n;
1386|  char*  s;
1387|
1388|  StrV_Set_init1( set, buf );
1389|  StrV_Mem_setMax( buf, m->string_sizeof );
1390|  n = IniFile_Read_getCount( m, attr_name );
1391|  for ( i = 0; i < n; i++ ) {
1392|    s = StrV_Mem_alloc( buf );
1393|    IniFile_Read_getVar( m, attr_name, "s", s );
1394|  }
1395|  StrV_Set_init2( set, buf );
1396|}
1397|#endif
1398|
1399| 
1400|/***********************************************************************
1401|*  42. <<< [IniFile_Read_getMultiStrAV] 複数行を複数の文字列として入力する(ArrX_Able) >>> 
1402|*【引数】
1403|*  ・char*  attr_name;  属性名
1404|*  ・ArrX_Able*  set;   StrV_AbleElem 型の文字列の集合
1405|*【補足】
1406|*・set は初期化されます。
1407|*・内部で、StrV_Mem_alloc を呼び出しています。
1408|*・複数行は、初期化ファイル内では、次のように同じ属性が続きます。
1409|*  attr = text of line 1
1410|*  attr = text of line 2
1411|*  attr = text of line 3
1412|************************************************************************/
1413|#ifdef  USES_BIGSTACK
1414|#ifdef  USES_STRV
1415|void  IniFile_Read_getMultiStrAV( IniFile_Read* m,
1416|  const char* attr_name, ArrX_Able* set )
1417|{
1418|  int  i,n;
1419|  char*  s;
1420|  StrV_AbleElem*  sv;
1421|  bool  bStack = false;
1422|
1423|  c_try {
1424|    BigStack_start();  bStack = true;
1425|    s = BigStack_alloc( m->string_sizeof );
1426|    n = IniFile_Read_getCount( m, attr_name );
1427|    for ( i = 0; i < n; i++ ) {
1428|      sv = ArrX_Able_getFirstDisabled( set, StrV_AbleElem );
1429|      if ( sv == NULL )  error();
1430|      IniFile_Read_getVar( m, attr_name, "s", s );
1431|      StrV_cpy( &sv->s, s );
1432|      ArrX_AbleElem_setAble( sv, true );
1433|    }
1434|  }
1435|  c_finally {
1436|    if ( bStack )  BigStack_end();
1437|  } c_end_finally;
1438|}
1439|#endif
1440|#endif
1441|
1442| 
1443|/***********************************************************************
1444|*  43. <<< [IniFile_Read_getPathes] ファイルパスの集合を入力する >>> 
1445|*【引数】
1446|*  ・StrX_Mem*  mem;     文字列を格納する領域
1447|*  ・ArrX_Buf*  pmem;    char* 型の配列を格納する領域
1448|*  ・char*      section_name;  セクション名
1449|*  ・ArrX*      arr;     ファイル・パス文字列の配列(出力)
1450|*【補足】
1451|*・属性名でなく、セクション名を指定することに注意してください。
1452|*  つまり、セクション全体が、ファイルパスの専用領域になります。
1453|*・型リストに関しては、IniFile_Read_getVar2 関数を参照してください。
1454|*・IniFile_Write_putPathes 関数によって出力されたデータを入力できます。
1455|*・取得するパスは、ini ファイルの内容によって相対パスか絶対パスか異なります。
1456|*・ini ファイルは、編集しやすいように、次のような構成になっているとします。
1457|*  [uses]
1458|*  Folder = .           ... フォルダパス
1459|*  File = main.c        ... ファイル名
1460|*  File = main2.c
1461|*  Folder = ..\inc
1462|*  File = sub1.h
1463|*  File = sub2.h
1464|*  File = sub3.h
1465|*  ただし、Folder 属性は、絶対パスでも構いません。
1466|************************************************************************/
1467|#ifdef  USES_ARRX
1468|#ifdef  USES_STRX
1469|void  IniFile_Read_getPathes( IniFile_Read* m, StrX_Mem* mem,
1470|  ArrX_Buf* pmem, const char* section_name, ArrX* arr )
1471|{
1472|  int    i;
1473|  int    nFolder;   /* Folder 属性の数 */
1474|  long   sect_top;  /* セクションの先頭のファイルアドレス */
1475|  char** pp = NULL;        /* ファイル名(char*型)へのポインタ */
1476|  char** pp_first = NULL;  /* pp の先頭 */
1477|  char*  buf = m->lineBuf;
1478|
1479|  /* セクションを検索する */
1480|  sect_top = IniFile_Read_searchSection( m, section_name, true );
1481|
1482|  /* Folder 属性がいくつあるか数える */
1483|  nFolder = 0;
1484|  for(;;) {
1485|    fgets( buf, m->lineBuf_sizeof, m->file );
1486|    if ( feof( m->file ) )
1487|      break;
1488|    if ( buf[0] == '[' )
1489|      break;
1490|    if ( strncmp( buf, "Folder", 6 ) == 0 &&
1491|         ( *(buf + 6) == ' ' || *(buf + 6) == '=' ) )
1492|      nFolder ++;
1493|  }
1494|
1495|  /* ファイルパスを入力する */
1496|  fseek( m->file, sect_top, SEEK_SET );
1497|  for ( i = 0; i < nFolder; i++ ) {
1498|    static char  folder[_MAX_PATH];
1499|    int  ii, nFile;
1500|
1501|    IniFile_Read_getVar( m, "Folder", "s", folder );
1502|    nFile = IniFile_Read_getCount( m, "File" );
1503|    pp = ArrX_Buf_allocs( pmem, char*, nFile );
1504|    if ( i == 0 )  pp_first = pp;
1505|
1506|    for ( ii = 0; ii < nFile; ii++ ) {
1507|      IniFile_Read_getVar2( m, mem, "File", "s", pp );
1508|      StrX_toAbsPath( *pp, StrX_Mem_getLeftSize(mem), folder );
1509|      pp++;
1510|    }
1511|  }
1512|  ArrX_init2( arr, pp_first, pp );
1513|}
1514|#endif
1515|#endif /* USES_ARRX */
1516|
1517|
1518| 
1519|/***********************************************************************
1520|*  44. <<< [IniFile_Read_getPathesS] ファイルパスの集合を入力する >>> 
1521|*【補足】
1522|*・IniFile_Read_getPathes との違いは、ArrX_Buf を使用しないで、
1523|*  StrX_Set を初期化することです。
1524|************************************************************************/
1525|#ifdef  USES_ARRX
1526|#ifdef  USES_STRX
1527|void  IniFile_Read_getPathesS( IniFile_Read* m, StrX_Mem* mem,
1528|  const char* section_name, StrX_Set* set )
1529|{
1530|  static char*  bufX[1024];
1531|  ArrX_Buf  buf;
1532|  ArrX      dummy;
1533|
1534|  ArrX_Buf_init( &buf, bufX, sizeof(bufX) );
1535|
1536|  StrX_Set_init1( set, mem );
1537|  IniFile_Read_getPathes( m, mem, &buf, section_name, &dummy );
1538|  StrX_Set_init2( set, mem );
1539|}
1540|#endif
1541|#endif
1542|
1543| 
1544|/* 45. <<< ------- 内部用 ------- >>> */ 
1545|
1546|
1547| 
1548|/***********************************************************************
1549|*  46. <<< [IniFile_Read_readAttrName] 属性名をチェックする >>> 
1550|*【引数】
1551|*  ・char* attr_name;  属性名
1552|*  ・char* 返り値;     行バッファ中の値の位置
1553|*【補足】
1554|*・内部用です。
1555|*【内部補足】
1556|*・属性名が違っていると、例外を投げます。
1557|*・行頭または空行の行頭にしてから、呼び出します。
1558|*・この関数から返ったとき、ファイルポインタは、属生行の次に移動しています。
1559|************************************************************************/
1560|char*  IniFile_Read_readAttrName( IniFile_Read* m, const char* attr_name )
1561|{
1562|  char*  buf = m->lineBuf;
1563|  char*  p;
1564|  int  c;
1565|
1566|  /* 空行を飛ばして、属性行を読み込む */
1567|  do {
1568|    fgets( buf, m->lineBuf_sizeof, m->file );
1569|    if ( buf[strlen(buf)-1] == '\n' )
1570|      buf[strlen(buf)-1] = '\0';
1571|
1572|    if ( feof( m->file ) ) {
1573|      error2_1( IniFile_Err_FewAttr, "属性が少ない", attr_name );
1574|    }
1575|  } while ( buf[0] == '\0' );
1576|
1577|  /* 属性名をチェックする */
1578|  while ( *buf == ' ' || *buf == '\t' ) {
1579|    if ( *buf == '\0' )  break;
1580|    buf++;
1581|  }
1582|  p = &buf[strlen(attr_name)];
1583|  c = *p;
1584|  *p = '\0';
1585|  if ( c != ' ' && c != '=' && c != ':' ) {
1586|    error2_3( IniFile_Err_BadAttr,
1587|      "属性名が異なる(アプリ=%s, ファイル=%s in %s)",
1588|      attr_name, buf, m->path );
1589|  }
1590|  if ( stricmp( buf, attr_name ) != 0 ) {
1591|    error2_3( IniFile_Err_BadAttr,
1592|      "属性名が異なる(アプリ=%s, ファイル=%s in %s)",
1593|      attr_name, buf, m->path );
1594|  }
1595|
1596|  /* 空白とイコール(コロン)をスキップする */
1597|  p++;
1598|  while ( *p == ' ' )  p++;
1599|  if ( *p == '=' || *p == ':' )  p++;
1600|  while ( *p == ' ' )  p++;
1601|
1602|  return  p;
1603|}
1604|
1605|
1606| 
1607|/***********************************************************************
1608|*  47. <<< [IniFile_Read_readVar] 属性値を読み込む >>> 
1609|*【引数】
1610|*  ・char* vars;   ファイル行バッファ中の値の位置
1611|*  ・char* types;  型リスト
1612|*【補足】
1613|*・内部用です。
1614|*【内部補足】
1615|*・詳細は、IniFile_Read_getVar 関数の解説を参照してください。
1616|************************************************************************/
1617|#ifdef  USES_STRX
1618|void  IniFile_Read_readVar( IniFile_Read* m, char* vars,
1619|  const char* types, va_list va )
1620|{
1621|  char*  nextVar;
1622|
1623|  if ( *types != '\0' ) {
1624|    for (;;) {
1625|
1626|      /* 値の文字列を作る */
1627|      nextVar = StrX_meltCSV( vars, vars );
1628|
1629|      /* それぞれの型のデフォルト値を設定する(途中) */
1630|      if ( *vars == '\0' ) {
1631|        switch( *types ) {
1632|          case 's':  strcpy( va_arg( va, char* ), "" );  break;
1633|          case 'i':  *va_arg( va, int* ) = 0;  break;
1634|          case 'b':  *va_arg( va, bool* ) = false;  break;
1635|        }
1636|      }
1637|      /* それぞれの型の値を読み込む */
1638|      else {
1639|        switch ( *types ) {
1640|          case  's':
1641|          case  'S':
1642|            if ( (int)strlen( vars ) > m->string_sizeof - 1 ) {
1643|              strncpy( va_arg( va, char* ), vars, m->string_sizeof - 1 );
1644|              vars[m->string_sizeof - 1] = '\0';
1645|            }
1646|            else
1647|              strcpy( va_arg( va, char* ), vars );
1648|            break;
1649|          case  'i':
1650|          case  'I':
1651|            *va_arg( va, int* ) = atoi( vars );
1652|            break;
1653|          case  'b':
1654|            *va_arg( va, bool* ) = ( *vars != '0' );
1655|            break;
1656|          case  'x':
1657|            sscanf( vars + 2, "%X", va_arg( va, int* ) );
1658|            break;
1659|          #ifdef USES_TIMEDATE
1660|            case 't':
1661|              TimeDate_loadStr( va_arg( va, time_t* ), vars );
1662|              break;
1663|          #endif
1664|          default:
1665|            ASSERT( *types == '-' );
1666|            break;
1667|        }
1668|      }
1669|
1670|      /* 次の項目への準備をする */
1671|      types ++;  if ( *types == '\0' )  break;
1672|
1673|      if ( nextVar == NULL )  break;
1674|      vars = nextVar;
1675|    }
1676|
1677|    /* それぞれの型のデフォルト値を設定する(末尾) */
1678|    while ( *types != '\0' ) {
1679|char*  aa;
1680|      switch ( *types ) {
1681|        case 's':  aa = va_arg( va, char* );  aa[0] = '\0';  break;
1682|        case 'i':  *va_arg( va, int* ) = 0;  break;
1683|        case 'b':  *va_arg( va, bool* ) = false;  break;
1684|      }
1685|      types ++;
1686|    }
1687|  }
1688|}
1689|#endif
1690|
1691| 
1692|/***********************************************************************
1693|*  48. <<< [IniFile_Read_readVar2] 属性値を読み込む(StrX_Mem使用) >>> 
1694|*【引数】
1695|*  ・char* vars;     ファイル行バッファ中の値の位置
1696|*  ・StrX_Mem* mem;  文字列を格納する領域
1697|*  ・char* types;    型リスト
1698|*【補足】
1699|*・内部用です。
1700|*【内部補足】
1701|*・詳細は、IniFile_Read_getVar2 関数の解説を参照してください。
1702|************************************************************************/
1703|#ifdef  USES_STRX
1704|void  IniFile_Read_readVar2( IniFile_Read* m, char* vars,
1705|  StrX_Mem* mem, const char* types, va_list va )
1706|{
1707|  char*  nextVar;
1708|  char** pp;
1709|
1710|  if ( *types != '\0' ) {
1711|    for (;;) {
1712|
1713|      /* 配列に行末までの要素を読み込む */
1714|      #ifdef USES_ARRX
1715|        if ( *types == '*' ) {
1716|          types++;
1717|          IniFile_Read_readVar2_subA( m, vars, mem, types, va );
1718|          return;
1719|        }
1720|      #endif
1721|
1722|      /* 値の文字列を作る */
1723|      nextVar = StrX_meltCSV( vars, vars );
1724|
1725|      /* それぞれの型の値を読み込む */
1726|      switch ( *types ) {
1727|        case  's':
1728|        case  'S':
1729|          pp = va_arg(va, char**);
1730|          if ( *vars == '\0' ) {
1731|            if ( *types == 'S' )  *pp = StrX_Mem_alloc(mem);
1732|            else   *pp = NULL;
1733|          }
1734|          else
1735|            IniFile_Read_readVar2_subS( m, vars, pp, mem );
1736|          break;
1737|
1738|        case  'i':
1739|          if ( *vars != '\0' ) {
1740|            *va_arg( va, int* ) = atoi( vars );
1741|          }
1742|          break;
1743|        case  'b':
1744|          if ( *vars != '\0' ) {
1745|            *va_arg( va, bool* ) = ( *vars != '0' );
1746|          }
1747|          break;
1748|        #ifdef USES_TIMEDATE
1749|          case 't':
1750|            if ( *vars != '\0' ) {
1751|              TimeDate_loadStr( va_arg( va, time_t* ), vars );
1752|            }
1753|            break;
1754|        #endif
1755|        default:
1756|          ASSERT( *types == '-' );
1757|          break;
1758|      }
1759|
1760|      /* 次の項目への準備をする */
1761|      types ++;  if ( *types == '\0' )  break;
1762|
1763|      if ( nextVar == NULL )  break;
1764|      vars = nextVar;
1765|    }
1766|  }
1767|}
1768|#endif
1769|
1770| 
1771|/***********************************************************************
1772|*  49. <<< [IniFile_Read_readVar2_subA] 属性値を配列に読み込む(StrX_Mem使用) >>> 
1773|*【補足】
1774|*・内部用です。
1775|*【内部補足】
1776|*・*types は、's' か 'i' のアドレスにしてください。
1777|*・詳細は、IniFile_Read_readVar2 関数を参照してください。
1778|************************************************************************/
1779|#ifdef  USES_ARRX
1780|#ifdef  USES_STRX
1781|void  IniFile_Read_readVar2_subA( IniFile_Read* m, char* vars,
1782|  StrX_Mem* mem, const char* types, va_list va )
1783|{
1784|  ArrX_Buf*  pmem = va_arg( va, ArrX_Buf* );
1785|  ArrX*      arr = va_arg( va, ArrX* );
1786|  void**  pp;        /* pmem から確保した領域のアドレス */
1787|  char*   ppX;       /* pmem == NULL のときの pp の参照先(ダミー)*/
1788|  void**  pp_first;  /* 最初の pp */
1789|  char*   nextVar;   /* 次の値 */
1790|
1791|
1792|  /* 1つも要素が無い場合 */
1793|  if ( StrX_isSpaceStr( vars ) ) {
1794|    if ( arr != NULL )  ArrX_init2( arr, NULL, NULL );
1795|    return;
1796|  }
1797|
1798|  /* 要素がある場合 */
1799|  if ( pmem != NULL )  pp_first = pp = ArrX_Buf_alloc( pmem, void* );
1800|  else  pp = &ppX;
1801|  for (;;) {
1802|
1803|    /* 値の文字列を作る */
1804|    nextVar = StrX_meltCSV( vars, vars );
1805|
1806|    /* それぞれの型の値を読み込む */
1807|    switch ( *types ) {
1808|      case  's':
1809|        IniFile_Read_readVar2_subS( m, vars, (char**)pp, mem );
1810|        break;
1811|      case  'i':
1812|        *(int*)pp = atoi( vars );
1813|        break;
1814|      case  'b':
1815|        *(bool*)pp = ( *vars != '0' );
1816|        break;
1817|    #ifdef USES_TIMEDATE
1818|      case  't':
1819|        TimeDate_loadStr( (time_t*)pp, vars );
1820|        break;
1821|    #endif
1822|      default:
1823|        ASSERT( *types == '-' );
1824|        break;
1825|    }
1826|
1827|    /* ループ終了判定 */
1828|    if ( nextVar == NULL )
1829|      break;
1830|
1831|    /* 次の値を格納する準備をする */
1832|    vars = nextVar;
1833|    while ( *vars == ' ' )  vars ++;
1834|    if ( pmem != NULL )
1835|      pp = ArrX_Buf_alloc( pmem, void* );
1836|  }
1837|
1838|  /* 配列を初期化する */
1839|  pp++;
1840|  if ( arr != NULL )  ArrX_init2( arr, pp_first, pp );
1841|}
1842|#endif
1843|#endif /* USES_ARRX */
1844| 
1845|