tinifile.c 31.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
/**
    @file   tinifile.c
    @date   2009-04-09
    @author 장길석 jwjwmx@gmail.com
    @brief  Ver 0.0.6
            Delphi에서 제공하는 TIniFile을 생성한다.
    @brief  
        -# 2012-02-07 오재경
            -# get_identify() 함수에서 index() 함수 리턴값 처리

        -# 2010-09-14 장길석
            -# 파일 없이 IniFile을 생성했을 때에도 객체가 생성되고
            -# 파일로 저장할 수 있도록 수정
    @todo
        -# 섹션을 삭제하는 함수를 추가
        -# 구별자 정보를 삭제하는 함수를 추가
    @bug
    @remark
    @warning
        - 저작권 에프에이리눅스(주)
        - 외부공개 금지
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <tlist.h>
#include <tstrlist.h>
#include <tinifile.h>

/// INI에서 사용하는 버퍼 크기
#define MAX_BUFFSIZE        1024
/// 섹션 문자열 최대 크기
#define MAX_SECTION         128
/// 구분자 문자열 최대 크기
#define MAX_IDENTIFY        128

/// 에러 전역 코드
int     ini_error_code;                                                 // 에러코드

/// 섹션과 구분자를 위한 버퍼
char buf_text[MAX_BUFFSIZE+1];
/// 센션과 구분자에 대한 데이터를 구하기 위한 버퍼
char buf_data[MAX_BUFFSIZE+1];

static void trim( char *str)
// 설명: 인수로 받은 문자열에서 앞과 뒤의 화이트 문자를 제거한다.
{
    int     sz_str;
    int     ndx;

    sz_str = strlen( str);

    if ( 0 >= sz_str)   return;                                         // 정상적인 문자열이 아니면 바로 복귀

    for ( ndx = sz_str -1; 0 <= ndx; ndx--)                             // 문자열 뒤의 화이트 문자를 제거, -1: NULL 위치 제거
    {
        if ( ' ' < str[ndx])                                            // 한글이나 특수문자의 두번째 바이트일 경우를 대비하기 위해 MSB 비트를 확인한다.
        {
            break;
        }
        str[ndx]    = '\0';
    }

    sz_str  = strlen( str);                                             // 문자열 앞의 화이트 문자를 제거
    for( ndx = 0; ndx < sz_str; ndx++)
    {
        if ( ' ' != str[ndx])                                           // 한글이 있을 경우를 생각해서 ' ' 문자를 직접 비교한다.
            break;
    }
    memcpy( str, str+ndx, strlen( str));                                // 회이트 문자가 아닌 부분을 포인터의 시작 위치로 이동
}

static char *get_identify( char *str, char *identify)
// 설명: 인수 문자열에서 구별지 문자열을 구한다.
// 참고: '='까지의 문자열을 구하며, 앞과 뒤의 화이트 문자를 제거한다.
// 반환: 구별자 문자열
{
    char   *p_data;
    char   *p_branch;
    int     sz_data;
    int     pos;

    p_branch  = index( str, '=');
    if ( NULL == p_branch ) return NULL;
    
    pos = p_branch -str;

    memcpy( identify, str, pos);
    identify[pos]   = '\0';
    sz_data         = strlen( str) - pos;
    p_data          = malloc( sz_data +1);

    if ( NULL != p_data)
    {
        memcpy( p_data, p_branch+1, sz_data);
        p_data[sz_data] = '\0';                                         // 복사 문자열 끝을 '\0'으로 마무리

        trim( identify);
        trim( p_data);
    }
    return p_data;
}

static void get_section( char *str, char *sec)
// 설명: 인수 문자열에서 섹션 문자열을 구한다.
// 참고: '['와 ']' 사이의 문자를 구하며, 문자열 앞과 뒤의 화이트 문자는 제거한다.
// 반환: 섹션 문자열
{
    int     ndx_str;
    int     ndx_sec;

    ndx_str = 0;
    while( ('\0' != str[ndx_str]) && ( ndx_str < MAX_BUFFSIZE))
    {
        if ( '[' == str[ndx_str++])
            break;
    }

    ndx_sec = 0;
    while( ('\0' != str[ndx_str]) && ( ndx_sec < MAX_SECTION))
    {
        if ( ']' == str[ndx_str])
        {
            sec[ndx_sec]    = '\0';
            break;
        }
        sec[ndx_sec++] = str[ndx_str++];
    }
    trim( sec);
}

static int  is_remark( char *str)
// 설명: 문자열이 주석인지의 여부를 판단한다.
// 참고: '#' 문자 또는 ';' 문자로 문장이 시작하면 주석으로 판단
// 반환: INI_TRUE=주석
{
    char    ch_data;
    int     ndx;

    ndx = 0;
    while( ('\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE))                 // 문장의 끝 또는 버퍼의 크기 만큼 확인
    {
        ch_data = str[ndx++];
        if ( '#' == ch_data)                                            // 공백 이상의 문자 중 주석에 해당되는 '#' 문자를 만나면 주석으로 판단
        {
            return INI_TRUE;
        }
        else if ( ';' == ch_data)                                       // 공백 이상의 문자 중 주석에 해당되는 ';' 문자를 만나면 주석으로 판단
        {
            return INI_TRUE;
        }
        else if ( ' ' != str[ndx])                                      // 공백 키워드가 아닌 공백 이상의 문자를 만나면 주석 행이 아님으로 판단
        {
            return INI_FALSE;
        }
    }
    return INI_TRUE;
}

static int  is_identify( char *str)
// 설명: 문자열이 섹션 정보를 담고있는지의 여부를 반환
// 참고: '=' 문자가 있으면서 유효
// 반환: 0 = 구별자 정보가 없음, 1 = 구별자 정보가 있음
{
    int     ndx;

    ndx = 0;
    while( ('\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE))                 // 문장의 끝 또는 버퍼의 크기 만큼 확인
    {
        if ( ' ' < str[ndx++])                                          // 앞에 있는 공백 문자를 무시. 공백 이상의 문자가 오면
        {
            while( ('\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE))         // 공백 이상에 '=' 문자가 있는지 확인
            {
                if ( '=' == str[ndx++])
                {
                    return 1 == 1;
                }
            }
            return 1 == 0;
        }
    }
    return 1 == 0;
}

static int  is_section( char *str)
//설명: 문자열이 섹션 정보를 담고있는지의 여부를 반환
//참고: '[' 문자가 있으면서 '[' 앞에는 빈 문자열이거나 공백 문자만 유효
//      '[' 문자와 ']' 문자 사이에는 섹션에 대한 이름 문자열이 있어야 함
//반환: 0 = 섹션 정보가 없음, 1 = 섹션 정보가 있음
{
    char    ch_data;
    int     ndx;

    ndx = 0;
    while( ( '\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE))                // 문장의 끝 또는 버퍼의 크기 만큼 확인
    {
        ch_data = str[ndx++];                                           // 확인할 문자열
        if ( '[' == ch_data)                                            // 시작 [ 문자 있음
        {
            while( ('\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE))         // 문장의 끝 또는 버퍼의 크기 만큼 확인
            {
                if ( ' ' < str[ndx++])                                  // [ 문자 이후에 다른 문자가 있다면
                {
                    while( ('\0' != str[ndx]) && ( ndx < MAX_BUFFSIZE)) // 문장의 끝 또는 버퍼의 크기 만큼 확인
                    {
                        if ( ']' == str[ndx++])                         // ] 문자가 있다면 TRUE
                        {
                            return 1;
                        }
                    }
                    return 0;
                }
            }
            return 0;
        }
        else if ( ' ' != ch_data)                                       // [ 문자 앞에 공백 문자 외에 다른 문자가 있다면 FALSE
        {
            break;
        }
    }
    return 0;
}

static void read_inifile_data( FILE *fp, inifile_t *inifile)
//설명: ini 파일의 내용을 읽어들여 inifile_t 객체의 내용을 완성한다.
//인수: FILE *fp          : ini 파일에 대한 파일 포인터
//      ifile_t *inifile  : inifile_t 객체 포인터
//참고: 섹션에 따라 구별자 목록을 갖는 tstrlist를 생성하여
//      섹션 리스트의 아이템에 object로 추가한다.
{
    char        str_section [MAX_SECTION +1];
    char        str_identify[MAX_IDENTIFY+1];
    tstrlist   *lst_identify    = NULL;
    char       *p_data;

    while( NULL != fgets( buf_text, MAX_BUFFSIZE, fp))
    {
        if      ( INI_TRUE == is_remark( buf_text) )                    // 주석 행이면 다음 행으로
            ;
        else if ( NULL != index( buf_text, '['))
        {
            if ( is_section( buf_text))                                 // 파일에서 읽어들인 문자열이 섹션 정보로 유효하다면
            {
                lst_identify    = tstrlist_create();                    // 섹션에 포함된 모든 구별자 정보를 담을 수 있는 tstrlist를 생성
                if ( NULL == lst_identify)
                {
                    ini_error_code  = INIERR_CREATE_IDENTIFY_FAIL;      //  에러코드: 구별자 정보를 위한 메모리 할당 실패
                    return;
                }
                get_section( buf_text, str_section);
                tstrlist_add_object( inifile->lst_sections, str_section, lst_identify);
            }
            else
                lst_identify    = NULL;
        }
        else if (   ( NULL != lst_identify)
                &&  is_identify( buf_text))
        {
            p_data = get_identify( buf_text, str_identify);
            if ( NULL == p_data)
            {
                ini_error_code  = INIERR_READ_IDENTIFY_FAIL;            //  에러코드: 구별자의 문자열 정보를 메모리 할당 실패
                return;
            }
            tstrlist_add_object( lst_identify, str_identify, p_data);
        }
    }

    inifile->is_changed = 0;
}

static char *read_string( inifile_t *inifile, char *str_section, char *str_identify)
//설명: 섹션과 구별자의 문자열 데이터를 구한다.
//인수: inifile_t *inifile  : inifile_t 객체 포인터
//      char *str_section   : 섹션 문자열
//      char *str_identify  : 구별자 문자열
//반환: 섹션과 구별자의 문자열 데이터
//주의: 섹션과 구별자가 없다면 NULL을 반환
//      반환된 문자열 포인터로 메모리 소멸을 해서는 안 된다.
{
    tstrlist   *lst_section;
    int         index;


    index   = tstrlist_indexof( inifile->lst_sections, str_section);
    if ( 0 > index) return NULL;

    lst_section = ( tstrlist *)tstrlist_get_object( inifile->lst_sections, index);
    if ( NULL == lst_section) return NULL;

    index   = tstrlist_indexof( lst_section, str_identify);
    if ( 0 > index) return NULL;

    return (char *)tstrlist_get_object( lst_section, index);
}

static int write_string( inifile_t *inifile, char *str_section, char *str_identify, char *data)
//설명: 섹션과 구별자의 문자열 데이터를 변경 또는 추가한다.
//인수: inifile_t *inifile      : inifile_t 객체 포인터
//      char      *str_section  : 섹션 문자열
//      char      *str_identify : 구별자 문자열
//      char      *data         : 저장할 데이터
//반환: INI_TRUE= 변경 또는 추가 성공, INI_FALSE 변경 또는 추가 실패
//주의: 섹션과 구별자가 없다면 새로 추가한다.
{
    tstrlist   *lst_section;
    char       *p_data;
    int         index;

    inifile->is_changed = 1;                                            // 자료 변경 이나 추가가 있음

    index   = tstrlist_indexof( inifile->lst_sections, str_section);
    if ( 0 > index)                                                     // 섹션이 없다면 추가한다.
    {
        lst_section = tstrlist_create();                                // 섹션에 포함된 모든 구별자 정보를 담을 수 있는 tstrlist를 생성
        if ( NULL == lst_section)
        {
            ini_error_code  = INIERR_CREATE_IDENTIFY_FAIL;              //  에러코드: 구별자 정보를 위한 메모리 할당 실패
            return INI_FALSE;
        }
        tstrlist_add_object( inifile->lst_sections, str_section, lst_section);
    }
    else
    {
        lst_section = ( tstrlist *)tstrlist_get_object( inifile->lst_sections, index);
    }

    index   = tstrlist_indexof( lst_section, str_identify);
    if ( 0 > index)                                                     // 구별자가 없다면 추가한다.
    {
        tstrlist_add_object( lst_section, str_identify, data);
    }
    else
    {
        p_data  = (char *)tstrlist_get_object( lst_section, index);
        free( p_data);

        tstrlist_put_object( lst_section, index, data);
    }
    return INI_TRUE;
}

char  *ini_error_string( void)
/**
    @brief  ini_error_code에 대한 에러 설명 문자열을 반환
    @return 에러 코드에 대한 에러 설명 문자열 포인터
    @warning 절대 반환 받은 문자열을 소멸 시켜서는 안 된다!!
*/
{
   char *error_string[] ={ "에러 없음",                                 //  INIERR_NONE
                           "메모리 부족",                               //  INIERR_OUT_OF_MEMORY
                           "파일 이름 지정 오류",                       //  INIERR_FILENAME_FAIL
                           "자료 없음",                                 //  INIERR_NO_DATA
                           "IniFile 없음",                              //  INIERR_NO_FILE
                           "IniFile을 읽을 수 없음",                    //  INIERR_ACCESS_FAIL
                           "섹션 리스트 생성 실패",                     //  INIERR_CREATE_SECTION_FAIL
                           "구별자 생성 실패",                          //  INIERR_CREATE_IDENTIFY_FAIL
                           "인수의 객체가 NULL"                         //  INIERR_NULL_POINTER
                        };
   return( error_string[ini_error_code]);
}

int ini_print_error( char *remark)
/**
    @brief  ini_error_code에 대한 에러 설명 문자열을 화면에 출력
    @param  remark : 에러 설명 문자열 끝에 첨부하여 출력할 문자열
    @return 에러 코드
*/
{
   printf( "[ini error:%d]%s %s\n", ini_error_code, ini_error_string(), remark);
   return ini_error_code;
}

int ini_write_bool( inifile_t *inifile, char *str_section, char *str_identify, int value)
/**
    @brief  섹션과 구별자가 지정하는 데이터의 값을 변경한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  value : boolean 값
    @return\n
        INI_TRUE  - 변경 또는 추가 성공\n
        INI_FALSE - 변경 또는 추가 실패
*/
{
    char    *data;

    data        = malloc( 2);
    if ( value) sprintf( data, "1");
    else        sprintf( data, "0");

    return write_string( inifile, str_section, str_identify, data);
}

int ini_write_real( inifile_t *inifile, char *str_section, char *str_identify, double value)
/**
    @brief  섹션과 구별자가 지정하는 실수 데이터를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  value : 실수 값
    @return
        - INI_TRUE  - 변경 또는 추가 성공
        - INI_FALSE - 변경 또는 추가 실패
*/
{
    char    *data;

    data        = malloc( 30);
    sprintf( data, "%lf", value);
    trim( data);
    return write_string( inifile, str_section, str_identify, data);
}

int ini_write_integer( inifile_t *inifile, char *str_section, char *str_identify, int value)
/**
    @brief  섹션과 구별자가 지정하는 정수 데이터를 지정한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열\n
    @param  str_identify : 구별자 문자열\n
    @param  value : 정수 데이터
    @return
        - INI_TRUE  - 변경 또는 추가 성공
        - INI_FALSE - 변경 또는 추가 실패
*/
{
    char    *data;

    data        = malloc( 20);
    sprintf( data, "%d", value);
    trim( data);

    return write_string( inifile, str_section, str_identify, data);
}

int ini_write_string( inifile_t *inifile, char *str_section, char *str_identify, char *value)
/**
    @brief  섹션과 구별자의 문자열을 변경
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열\n
    @param  str_identify : 구별자 문자열\n
    @param  value : 문자열 데이터
    @return
        - INI_TRUE  - 변경 또는 추가 성공
        - INI_FALSE - 변경 또는 추가 실패
*/
{
    char    *data;

    data        = malloc( strlen( value)+1);
    memcpy( data, value, strlen( value));
    data[strlen(value)]    = '\0';
    return write_string( inifile, str_section, str_identify, data);
}

int ini_write_char( inifile_t *inifile, char *str_section, char *str_identify, char value)
/**
    @brief  섹션과 구별자의 문자를 변경
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열\n
    @param  str_identify : 구별자 문자열\n
    @param  value : 문자
    @return
        - INI_TRUE  - 변경 또는 추가 성공
        - INI_FALSE - 변경 또는 추가 실패
*/
{
    char  str[16];

    sprintf( str, "%c", value );
    return ini_write_string( inifile, str_section, str_identify, str );
}

int ini_read_bool( inifile_t *inifile, char *str_section, char *str_identify, int default_value)
/**
    @brief  섹션과 구별자가 지정하는 Boolean 데이터를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열\n
    @param  str_identify : 구별자 문자열\n
    @param  default_value : 값이 없다면 대신 반환될 기본값
    @warning    ini 파일에 저장한 문자열이 0 이면 FALSE로 반환하며\n
                이외는 무조건 TRUE로 반환한다.\n
                즉, 섹션과 구별자가 가지고 있는 문자열 정보가\n
                '0' 인지 아닌지의 여부를 반환한다.
*/
{
    char   *data;
    int     int_data;

    data    = read_string( inifile, str_section, str_identify);                         // 먼저 문자열로 데이터를 읽어 들인다.
    if      ( NULL == data)                             return default_value;           // 찾는 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == strlen( data) )                      return default_value;           // 문자열 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == sscanf( data, "%d", &int_data))      return default_value;           // 정수로 변환된 값이 없다면 기본값을 반환
    else                                                return 0 != int_data;           // 정수 값이 0이면 FALSE로 반환한다.
}

double ini_read_real( inifile_t *inifile, char *str_section, char *str_identify, double default_value)
/**
    @brief  섹션과 구별자가 지정하는 실수 데이터를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section  : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  default_value: 값이 없다면 대신 반환될 기본값
    @return\n
        섹션과 구별자에 해당하는 실수 값\n
        저정한 섹션이나 구별자에 대한 실수 값이 없다면 기본값을 반환
*/
{
    char       *data;
    double      float_data;


    data    = read_string( inifile, str_section, str_identify);                         // 먼저 문자열로 데이터를 읽어 들인다.
    if      ( NULL == data)                             return default_value;           // 찾는 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == strlen( data) )                      return default_value;           // 문자열 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == sscanf( data, "%lf", &float_data))   return default_value;           // 실수로 변환된 값이 없다면 기본값을 반환
    else                                                return float_data;              // 실수 값을 반환한다.
}

int ini_read_integer( inifile_t *inifile, char *str_section, char *str_identify, int default_value)
/**
    @brief  섹션과 구별자가 지정하는 정수 데이터를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  default_value : 값이 없다면 대신 반환될 기본값
    @return\n
        섹션과 구별자에 해당하는 정수 값\n
        저정한 섹션이나 구별자에 대한 정수 값이 없다면 기본값을 반환
*/
{
    char   *data;
    int     int_data;

    data    = read_string( inifile, str_section, str_identify);                         // 먼저 문자열로 데이터를 읽어 들인다.
    
    if      ( NULL == data)                             return default_value;           // 찾아진 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == strlen( data) )                      return default_value;           // 문자열 데이터가 없다면 기본값을 반환한다.
    else if ( 0 == sscanf( data, "%d", &int_data))      return default_value;           // 정수로 변환된 값이 없다면 기본값을 반환
    else                                                return int_data;                // 정수값을 반환한다.
}

char *ini_read_string( inifile_t *inifile, char *str_section, char *str_identify, char *default_value)
/**
    @brief  섹션과 구별자가 지정하는 문자열 데이터를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  default_value : 값이 없다면 대신 반환될 기본값
    @return\n
        섹션과 구별자의 문자열 정보\n
        저정한 섹션이나 구별자에 대한 문자열이 없다면 기본값을 반환
    @warning 절대 반환 받은 문자열을 소멸 시켜서는 안 된다!!
*/
{
    char    *data;

    data    = read_string( inifile, str_section, str_identify);
    if ( NULL == data)                                  return default_value;
    else if ( 0 == strlen( data) )                      return default_value;           // 문자열 데이터가 없다면 기본값을 반환한다.
    else                                                return data;
}

char         ini_read_char( inifile_t *inifile, char *str_section, char *str_identify, char default_value)
/**
    @brief  섹션과 구별자가 지정하는 케릭터 데이타를 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @param  str_identify : 구별자 문자열
    @param  default_value : 값이 없다면 대신 반환될 기본값
    @return\n
        섹션과 구별자의 문자열 정보\n
        저정한 섹션이나 구별자에 대한 문자열이 없다면 기본값을 반환
    @warning 절대 반환 받은 문자열을 소멸 시켜서는 안 된다!!
*/
{
	char *cc;

	cc = ini_read_string( inifile, str_section, str_identify, NULL );
	if ( cc ) return cc[0];
	else      return default_value;
}

tstrlist *ini_read_section( inifile_t *inifile, char *str_section)
/**
    @brief  섹션이 가지고 있는 모든 구별자 문자열의 정보를 tstrlist 형식으로 구한다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 섹션 문자열
    @return\n
        섹션이 가지고 있는 모든 구별자 문자열을 가지고 있는 tstrlist 객체\n
        저정한 섹션이 없다면 NULL을 반환
    @warning 절대 반환 받은 객체를 소멸 시켜서는 안 된다!!
*/
{
    tstrlist   *lst_section;
    int         index;

    if ( NULL == inifile)   return  NULL;
    index   = tstrlist_indexof( inifile->lst_sections, str_section);

    if ( 0 > index)         return  NULL;
    lst_section = ( tstrlist *)tstrlist_get_object( inifile->lst_sections, index);

    return lst_section;
}

tstrlist *ini_read_sections( inifile_t *inifile)
/**
    @brief  inifile_t 객체가 가지고 있는 모든 섹션 정보를 tstrlist 형식으로 구한다.
    @param  inifile : inifile_t 객체 포인터
    @return 모든 섹션 문자열을 가지고 있는 tstrlist 객체
    @warning 절대 반환 받은 객체를 소멸 시켜서는 안 된다!!
*/
{
    if ( NULL == inifile)
    {
        return  NULL;
    }
    return inifile->lst_sections;
}

int ini_save_to_file( inifile_t *inifile, char *filename){

    /**
        @brief  inifile_t 객체 내용이 변경되어 있다면 파일로 저장한다.
        @param  inifile : inifile_t 객체 포인터
        @return  INI_TRUE - 작업 중 오류 없음
    */
    FILE       *fp_inifile;             // ini 파일 객체
    tstrlist   *lst_sections;           // inifile_t가 가지고 있는 모든 섹션 리스트
    tstrlist   *lst_identifies;         // 섹션이 가지고 있는 구별자 리스트
    char       *str_section;            // 섹션 문자열
    char       *str_identify;           // 구별자 문자열
    char       *str_data;               // 섹션과 구별자의 문자열 데이터
    int         ndx_sec;                // 모든 섹션을 처리하기 위한 루프 인데스
    int         ndx_idn;                // 모든 구별자를 처리하기 위한 루프 인덱스

    if ( NULL == inifile)                                               // ini 파일 개체가 없음
    {
        ini_error_code  = INIERR_NULL_POINTER;
        return INI_FALSE;
    }
    if ( NULL == inifile->lst_sections)
    {
        ini_error_code  = INIERR_NULL_POINTER;
        return INI_FALSE;
    }
    fp_inifile = fopen( filename, "w");                    //  ini 파일을 쓰기 전용으로 열기
    if ( NULL == fp_inifile)                                        //  파일 열기 실패
    {
        ini_error_code  = INIERR_ACCESS_FAIL;                       //  에러코드: 파일 열기 실패 지정
        return INI_FALSE;
    }
    lst_sections = ini_read_sections( inifile);                     // ini 객체에서 모든 섹션 리스트를 구한다.
    for( ndx_sec = 0; ndx_sec < tstrlist_getcount( lst_sections); ndx_sec++)// 모든 섹션에 대해서
    {
        str_section     = tstrlist_get_string( lst_sections, ndx_sec);      // 섹션 문자열을 구한다.
        fprintf( fp_inifile, "[%s]\n", str_section);                // 파일에 섹션 문자열을 쓰기

        lst_identifies  = ini_read_section( inifile, str_section);  // 섹션의 모든 구별자 리스트를 구한다.
        for( ndx_idn = 0; ndx_idn < tstrlist_getcount( lst_identifies); ndx_idn++)
        {
            str_identify    = tstrlist_get_string( lst_identifies, ndx_idn);
            str_data        = ini_read_string( inifile, str_section, str_identify, "");
            fprintf( fp_inifile, "%s=%s\n", str_identify, str_data);         // 구별자와 데이터를 쓰기
        }
    }
    fclose( fp_inifile);
    sync();

    return INI_TRUE;
}

int ini_flush( inifile_t *inifile)
/**
    @brief  inifile_t 객체 내용이 변경되어 있다면 파일로 저장한다.
    @param  inifile : inifile_t 객체 포인터
    @return  INI_TRUE - 작업 중 오류 없음
*/
{
    int     rst = INIERR_NONE;

    if ( INI_TRUE == inifile->is_changed){                              // 데이터가 변경되었거나 추가되었음
        inifile->is_changed = INI_FALSE;
        rst = ini_save_to_file( inifile, inifile->filename);
    }
    return rst;
}

void ini_remove_section( inifile_t *inifile, char *str_section)
/**
    @brief  지정된 섹션을 제거합니다.
    @param  inifile : inifile_t 객체 포인터
    @param  str_section : 삭제할 섹션
    @warning 변경이나 추가된 자료를 위해 ini_flush()를 호출한다.
*/
{
    tstrlist   *psection;
    char       *pdata;
    int         index;
    int         ndx_idn;

    if ( NULL != inifile)
    {
        if ( NULL != inifile->lst_sections)
        {
            index   = tstrlist_indexof( inifile->lst_sections, str_section);
            if ( 0 <= index)
            {
                psection = ( tstrlist *)tstrlist_get_object( inifile->lst_sections, index);
                for ( ndx_idn = 0; ndx_idn < tstrlist_getcount( psection); ndx_idn++)
                {
                    pdata   = ( char *)tstrlist_get_object( psection, ndx_idn);         // identify에 지정된 데이터 문자열 메모리를 제거
                    free( pdata);
                }
                tstrlist_free( psection);
                tstrlist_delete( inifile->lst_sections, index);

		        inifile->is_changed = INI_TRUE;	// [KTG] section 삭제에 대해서도 파일에 반영되도록 하기 위함.
            }
        }
    }
}

void ini_free( inifile_t *inifile)
/**
    @brief  inifile_t 객체를 소멸
    @param  inifile : inifile_t 객체 포인터
    @warning 변경이나 추가된 자료를 위해 ini_flush()를 호출한다.
*/
{
    tstrlist   *psection;
    char       *pdata;
    int         ndx_sec;
    int         ndx_idn;

    if ( NULL != inifile)
    {
        ini_flush( inifile);                                            // 변경된 내용이 있다면 파일로 저장
        if ( NULL != inifile->filename) free( inifile->filename);       // 파일 이름의 메모리 소멸
        if ( NULL != inifile->lst_sections)
        {
            for ( ndx_sec = 0; ndx_sec < tstrlist_getcount( inifile->lst_sections); ndx_sec++)
            {
                psection    = tstrlist_get_object( inifile->lst_sections, ndx_sec);
                for ( ndx_idn = 0; ndx_idn < tstrlist_getcount( psection); ndx_idn++)
                {
                    pdata   = ( char *)tstrlist_get_object( psection, ndx_idn);         // identify에 지정된 데이터 문자열 메모리를 제거
                    free( pdata);
                }
                tstrlist_free( psection);
            }
            tstrlist_free( inifile->lst_sections);
        }
        free( inifile);
    }
}

inifile_t *ini_create( char *filename)
/**
    @brief  filename의 내용을 섹션과 구별자로 구분하여 읽어 들인다.\n
    file에서 자료를 읽어 들인 후에는 데이터가 변경되어 저장이 필요하기 전까지는 \n
    다시 파일을 억세스할 필요가 없다.
    @param  filename : ini 파일의 이름
    @return\n
        ini 파일의 모든 내용을 담은 inifile_t 객체 포인터
        읽기에 실패했다면 NULL을 반환
*/
{
    inifile_t  *inifile;                // ini 파일 객체
    FILE       *fp_inifile;             // ini 파일을 읽기 위한 파일 포인터
    int         sz_filename;            // ini 파일의 전체 이름 크기

    if ( 0 != access( filename, F_OK | R_OK))                           //  IniFile 존재 확인 및 읽기가 가능한지 확인
    {
        fp_inifile = fopen( filename, "w");                             //  파일이 없다면 파일 생성이 가능한지를 확인한다.
        if ( NULL == fp_inifile)                                        //  파일 생성에 실패했다면 에러코드 반환
        {
            ini_error_code  = INIERR_ACCESS_FAIL;                       //  에러코드: 파일 열기 실패 지정
            return  NULL;
        }
        fclose( fp_inifile);
    }
    
    inifile = malloc( sizeof( inifile_t));                              //  ini 파일 객체 생성
    if ( NULL == inifile)                                               //  객체를 위한 메모리 할당에 실패하면
    {
        ini_error_code  = INIERR_OUT_OF_MEMORY;                         //  에러코드 지정
        return  NULL;
    }
    inifile->filename       = NULL;                                     //  기본값 지정: 파일 이름 지정이 아직 안 되어 있음
    inifile->lst_sections   = NULL;                                     //  
    inifile->is_changed     = INI_FALSE;                                //  기본값 지정: 변경된 내용이 없음

    sz_filename             = strlen( filename);                        //  파일 이름을 객체 정보에 저장한다.
    if ( 0 == sz_filename)
    {
        ini_free( inifile);
        ini_error_code  = INIERR_FILENAME_FAIL;
        return  NULL;
    }
    inifile->filename = malloc( sz_filename+1);
    if ( NULL == inifile->filename)                                     //  메모리 할당에 실패했다면 에러 처리한다.
    {
        ini_free( inifile);
        ini_error_code  = INIERR_OUT_OF_MEMORY;
        return  NULL;
    }
    memcpy( inifile->filename, filename, sz_filename+1);
    inifile->lst_sections = tstrlist_create();                          //  루트 섹션을 생성
    if ( NULL == inifile->lst_sections)                                 //  루트 섹션을 위한 스트링 리스트를 생성하지 못했다면
    {
        ini_free( inifile);
        ini_error_code  = INIERR_CREATE_SECTION_FAIL;                   //  에러코드: 루트 섹션 리스트 생성
        return  NULL;
    }

    fp_inifile = fopen( filename, "r");                                 //  IniFile의 파일을 열기를 함
    if ( NULL == fp_inifile)                                            //  파일 열기 실패
    {                                                                   
        ini_free( inifile);                                             
        ini_error_code  = INIERR_ACCESS_FAIL;                           //  에러코드: 파일 열기 실패 지정
        return  NULL;                                                   
    }                                                                   
    read_inifile_data( fp_inifile, inifile);                            // IniFile 내용을 모두 읽어 들임
    fclose( fp_inifile);
    
    return inifile;
}