text: /*
  ------------------------------------------------------------------------------
            Licensing information can be found at the end of the file.
  ------------------------------------------------------------------------------
  
  ini.h - v1.2 - Simple ini-file reader for C/C++.
  
  Do this:
      #define INI_IMPLEMENTATION
  before you include this file in *one* C/C++ file to create the implementation.
  */
  
  #ifndef ini_h
  #define ini_h
  
  #define INI_GLOBAL_SECTION ( 0 )
  #define INI_NOT_FOUND ( -1 )
  
  typedef struct ini_t ini_t;
  
  ini_t* ini_create( void* memctx );
  ini_t* ini_load( char const* data, void* memctx );
  
  int ini_save( ini_t const* ini, char* data, int size );
  void ini_destroy( ini_t* ini );
  
  int ini_section_count( ini_t const* ini );
  char const* ini_section_name( ini_t const* ini, int section );
  
  int ini_property_count( ini_t const* ini, int section );
  char const* ini_property_name( ini_t const* ini, int section, int property );
  char const* ini_property_value( ini_t const* ini, int section, int property );
  
  int ini_find_section( ini_t const* ini, char const* name, int name_length );
  int ini_find_property( ini_t const* ini, int section, char const* name, int name_length );
  
  int ini_section_add( ini_t* ini, char const* name, int length );
  void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length );
  void ini_section_remove( ini_t* ini, int section );
  void ini_property_remove( ini_t* ini, int section, int property );
  
  void ini_section_name_set( ini_t* ini, int section, char const* name, int length );
  void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length );
  void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length  );
  
  #endif /* ini_h */
  
  
  /**
  
  Examples
  ========
  
  Loading an ini file and retrieving values
  -----------------------------------------
  
      #define INI_IMPLEMENTATION
      #include "ini.h"
  
      #include <stdio.h>
      #include <stdlib.h>
  
      int main()
          {
          FILE* fp = fopen( "test.ini", "r" );
          fseek( fp, 0, SEEK_END );
          int size = ftell( fp );
          fseek( fp, 0, SEEK_SET );
          char* data = (char*) malloc( size + 1 );
          fread( data, 1, size, fp );
          data[ size ] = '\0';
          fclose( fp );
  
          ini_t* ini = ini_load( data );
          free( data );
          int second_index = ini_find_property( ini, INI_GLOBAL_SECTION, "SecondSetting" );
          char const* second = ini_property_value( ini, INI_GLOBAL_SECTION, second_index );
          printf( "%s=%s\n", "SecondSetting", second );
          int section = ini_find_section( ini, "MySection" );
          int third_index = ini_find_property( ini, section, "ThirdSetting" );
          char const* third = ini_property_value( ini, section, third_index );
          printf( "%s=%s\n", "ThirdSetting", third );
          ini_destroy( ini );
  
          return 0;
          }
  
  
  Creating a new ini file
  -----------------------
  
      #define INI_IMPLEMENTATION
      #include "ini.h"
  
      #include <stdio.h>
      #include <stdlib.h>
  
      int main()
          {       
          ini_t* ini = ini_create();
          ini_property_add( ini, INI_GLOBAL_SECTION, "FirstSetting", "Test" );
          ini_property_add( ini, INI_GLOBAL_SECTION, "SecondSetting", "2" );
          int section = ini_section_add( ini, "MySection" );
          ini_property_add( ini, section, "ThirdSetting", "Three" );
  
          int size = ini_save( ini, NULL, 0 ); // Find the size needed
          char* data = (char*) malloc( size );
          size = ini_save( ini, data, size ); // Actually save the file
          ini_destroy( ini );
  
          FILE* fp = fopen( "test.ini", "w" );
          fwrite( data, 1, size, fp );
          fclose( fp );
          free( data );
  
          return 0;
          }
  
  
  
  API Documentation
  =================
  
  ini.h is a small library for reading classic .ini files. It is a single-header library, and does not need any .lib files 
  or other binaries, or any build scripts. To use it, you just include ini.h to get the API declarations. To get the 
  definitions, you must include ini.h from *one* single C or C++ file, and #define the symbol `INI_IMPLEMENTATION` before 
  you do. 
  
  
  Customization
  -------------
  There are a few different things in ini.h which are configurable by #defines. The customizations only affect the 
  implementation, so will only need to be defined in the file where you have the #define INI_IMPLEMENTATION.
  
  Note that if all customizations are utilized, ini.h will include no external files whatsoever, which might be useful
  if you need full control over what code is being built.
  
  
  ### Custom memory allocators
  
  To store the internal data structures, ini.h needs to do dynamic allocation by calling `malloc`. Programs might want to 
  keep track of allocations done, or use custom defined pools to allocate memory from. ini.h allows for specifying custom 
  memory allocation functions for `malloc` and `free`.
  This is done with the following code:
  
      #define INI_IMPLEMENTATION
      #define INI_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) )
      #define INI_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) )
      #include "ini.h"
  
  where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter
  is an optional parameter of type `void*`. When `ini_create` or `ini_load` is called, you can pass in a `memctx` 
  parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every 
  `INI_MALLOC`/`INI_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your tracking 
  data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the 
  right type, and access the tracking data.
  
  If no custom allocator is defined, ini.h will default to `malloc` and `free` from the C runtime library.
  
  
  ### Custom C runtime function
  
  The library makes use of three additional functions from the C runtime library, and for full flexibility, it allows you 
  to substitute them for your own. Here's an example:
  
      #define INI_IMPLEMENTATION
      #define INI_MEMCPY( dst, src, cnt ) ( my_memcpy_func( dst, src, cnt ) )
      #define INI_STRLEN( s ) ( my_strlen_func( s ) )
      #define INI_STRNICMP( s1, s2, cnt ) ( my_strnicmp_func( s1, s2, cnt ) )
      #include "ini.h"
  
  If no custom function is defined, ini.h will default to the C runtime library equivalent.
  
  
  ini_create
  ----------
      
      ini_t* ini_create( void* memctx )
  
  Instantiates a new, empty ini structure, which can be manipulated with other API calls, to fill it with data. To save it
  out to an ini-file string, use `ini_save`. When no longer needed, it can be destroyed by calling `ini_destroy`.
  `memctx` is a pointer to user defined data which will be passed through to the custom INI_MALLOC/INI_FREE calls. It can 
  be NULL if no user defined data is needed.
  
  
  ini_load
  --------
  
      ini_t* ini_load( char const* data, void* memctx )
  
  Parse the zero-terminated string `data` containing an ini-file, and create a new ini_t instance containing the data. 
  The instance can be manipulated with other API calls to enumerate sections/properties and retrieve values. When no 
  longer needed, it can be destroyed by calling `ini_destroy`. `memctx` is a pointer to user defined data which will be 
  passed through to the custom INI_MALLOC/INI_FREE calls. It can be NULL if no user defined data is needed.
  
  
  ini_save
  --------
      
      int ini_save( ini_t const* ini, char* data, int size )
  
  Saves an ini structure as a zero-terminated ini-file string, into the specified buffer. Returns the number of bytes 
  written, including the zero terminator. If `data` is NULL, nothing is written, but `ini_save` still returns the number
  of bytes it would have written. If the size of `data`, as specified in the `size` parameter, is smaller than that 
  required, only part of the ini-file string will be written. `ini_save` still returns the number of bytes it would have
  written had the buffer been large enough.
  
  
  ini_destroy
  -----------
  
      void ini_destroy( ini_t* ini )
  
  Destroy an `ini_t` instance created by calling `ini_load` or `ini_create`, releasing the memory allocated by it. No
  further API calls are valid on an `ini_t` instance after calling `ini_destroy` on it.
  
  
  ini_section_count
  -----------------
  
      int ini_section_count( ini_t const* ini )
  
  Returns the number of sections in an ini file. There's at least one section in an ini file (the global section), but 
  there can be many more, each specified in the file by the section name wrapped in square brackets [ ].
  
  
  ini_section_name
  ----------------
  
      char const* ini_section_name( ini_t const* ini, int section )
  
  Returns the name of the section with the specified index. `section` must be non-negative and less than the value 
  returned by `ini_section_count`, or `ini_section_name` will return NULL. The defined constant `INI_GLOBAL_SECTION` can
  be used to indicate the global section.
  
  
  ini_property_count
  ------------------
  
      int ini_property_count( ini_t const* ini, int section )
  
  Returns the number of properties belonging to the section with the specified index. `section` must be non-negative and 
  less than the value returned by `ini_section_count`, or `ini_section_name` will return 0. The defined constant 
  `INI_GLOBAL_SECTION` can be used to indicate the global section. Properties are declared in the ini-file on he format
  `name=value`.
  
  
  ini_property_name
  -----------------
  
      char const* ini_property_name( ini_t const* ini, int section, int property )
  
  Returns the name of the property with the specified index `property` in the section with the specified index `section`.
  `section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be 
  non-negative and less than the value returned by `ini_property_count`, or `ini_property_name` will return NULL. The 
  defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section.
  
  
  ini_property_value
  ------------------
  
      char const* ini_property_value( ini_t const* ini, int section, int property )
  
  Returns the value of the property with the specified index `property` in the section with the specified index `section`.
  `section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be 
  non-negative and less than the value returned by `ini_property_count`, or `ini_property_value` will return NULL. The 
  defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section.
  
  
  ini_find_section
  ----------------
  
      int ini_find_section( ini_t const* ini, char const* name, int name_length )
  
  Finds the section with the specified name, and returns its index. `name_length` specifies the number of characters in
  `name`, which does not have to be zero-terminated. If `name_length` is zero, the length is determined automatically, but
  in this case `name` has to be zero-terminated. If no section with the specified name could be found, the value
  `INI_NOT_FOUND` is returned.
  
  
  ini_find_property
  -----------------
  
      int ini_find_property( ini_t const* ini, int section, char const* name, int name_length )
  
  Finds the property with the specified name, within the section with the specified index, and returns the index of the 
  property. `name_length` specifies the number of characters in `name`, which does not have to be zero-terminated. If 
  `name_length` is zero, the length is determined automatically, but in this case `name` has to be zero-terminated. If no 
  property with the specified name could be found within the specified section, the value `INI_NOT_FOUND` is  returned.
  `section` must be non-negative and less than the value returned by `ini_section_count`, or `ini_find_property` will 
  return `INI_NOT_FOUND`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section.
  
  
  ini_section_add
  ---------------
  
      int ini_section_add( ini_t* ini, char const* name, int length )
  
  Adds a section with the specified name, and returns the index it was added at. There is no check done to see if a 
  section with the specified name already exists - multiple sections of the same name are allowed. `length` specifies the 
  number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length is determined 
  automatically, but in this case `name` has to be zero-terminated.
  
  
  ini_property_add
  ----------------
      
      void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length )
  
  Adds a property with the specified name and value to the specified section, and returns the index it was added at. There 
  is no check done to see if a property with the specified name already exists - multiple properties of the same name are 
  allowed. `name_length` and `value_length` specifies the number of characters in `name` and `value`, which does not have 
  to be zero-terminated. If `name_length` or `value_length` is zero, the length is determined automatically, but in this 
  case `name`/`value` has to be zero-terminated. `section` must be non-negative and less than the value returned by
  `ini_section_count`, or the property will not be added. The defined constant `INI_GLOBAL_SECTION` can be used to 
  indicate the global section.
  
  
  ini_section_remove
  ------------------
  
      void ini_section_remove( ini_t* ini, int section )
  
  Removes the section with the specified index, and all properties within it. `section` must be non-negative and less than 
  the value returned by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global 
  section. Note that removing a section will shuffle section indices, so that section indices you may have stored will no 
  longer indicate the same section as it did before the remove. Use the find functions to update your indices.
  
  
  ini_property_remove
  -------------------
  
      void ini_property_remove( ini_t* ini, int section, int property )
  
  Removes the property with the specified index from the specified section. `section` must be non-negative and less than 
  the value returned by `ini_section_count`, and `property` must be non-negative and less than the value returned by 
  `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. Note that 
  removing a property will shuffle property indices within the specified section, so that property indices you may have 
  stored will no longer indicate the same property as it did before the remove. Use the find functions to update your 
  indices.
  
  
  ini_section_name_set
  --------------------
  
      void ini_section_name_set( ini_t* ini, int section, char const* name, int length )
  
  Change the name of the section with the specified index. `section` must be non-negative and less than the value returned 
  by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. `length` 
  specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length 
  is determined automatically, but in this case `name` has to be zero-terminated.
  
  
  ini_property_name_set
  ---------------------
  
      void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length )
  
  Change the name of the property with the specified index in the specified section. `section` must be non-negative and 
  less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value 
  returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section.
  `length` specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, 
  the length is determined automatically, but in this case `name` has to be zero-terminated.
  
  
  ini_property_value_set
  ----------------------
  
      void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length  )
  
  Change the value of the property with the specified index in the specified section. `section` must be non-negative and 
  less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value 
  returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section.
  `length` specifies the number of characters in `value`, which does not have to be zero-terminated. If `length` is zero, 
  the length is determined automatically, but in this case `value` has to be zero-terminated.
  
  **/
  
  
  /*
  ----------------------
      IMPLEMENTATION
  ----------------------
  */
  
  #ifdef INI_IMPLEMENTATION
  #undef INI_IMPLEMENTATION
  
  #define INITIAL_CAPACITY ( 256 )
  
  #undef _CRT_NONSTDC_NO_DEPRECATE 
  #define _CRT_NONSTDC_NO_DEPRECATE 
  #undef _CRT_SECURE_NO_WARNINGS
  #define _CRT_SECURE_NO_WARNINGS
  #include <stddef.h>
  
  #ifndef INI_MALLOC
      #include <stdlib.h>
      #define INI_MALLOC( ctx, size ) ( malloc( size ) )
      #define INI_FREE( ctx, ptr ) ( free( ptr ) )
  #endif
  
  #ifndef INI_MEMCPY
      #include <string.h>
      #define INI_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) )
  #endif 
  
  #ifndef INI_STRLEN
      #include <string.h>
      #define INI_STRLEN( s ) ( strlen( s ) )
  #endif 
  
  #ifndef INI_STRNICMP
      #ifdef _WIN32
          #include <string.h>
          #define INI_STRNICMP( s1, s2, cnt ) ( strnicmp( s1, s2, cnt ) )
      #else                           
          #include <string.h>         
          #define INI_STRNICMP( s1, s2, cnt ) ( strncasecmp( s1, s2, cnt ) )        
      #endif
  #endif 
  
  
  struct ini_internal_section_t
      {
      char name[ 32 ];
      char* name_large;
      };
  
  
  struct ini_internal_property_t
      {
      int section;
      char name[ 32 ];
      char* name_large;
      char value[ 64 ];
      char* value_large;
      };
  
  
  struct ini_t
      {
      struct ini_internal_section_t* sections;
      int section_capacity;
      int section_count;
  
      struct ini_internal_property_t* properties;
      int property_capacity;
      int property_count;
  
      void* memctx;
      };
  
  
  static int ini_internal_property_index( ini_t const* ini, int section, int property )
      {
      int i;
      int p;
  
      if( ini && section >= 0 && section < ini->section_count )
          {
          p = 0;
          for( i = 0; i < ini->property_count; ++i )
              {
              if( ini->properties[ i ].section == section )
                  {
                  if( p == property ) return i;
                  ++p;
                  }
              }
          }
  
      return INI_NOT_FOUND;
      }
  
  
  ini_t* ini_create( void* memctx )
      {
      ini_t* ini;
  
      ini = (ini_t*) INI_MALLOC( memctx, sizeof( ini_t ) );
      ini->memctx = memctx;
      ini->sections = (struct ini_internal_section_t*) INI_MALLOC( ini->memctx, INITIAL_CAPACITY * sizeof( ini->sections[ 0 ] ) );
      ini->section_capacity = INITIAL_CAPACITY;
      ini->section_count = 1; /* global section */
      ini->sections[ 0 ].name[ 0 ] = '\0'; 
      ini->sections[ 0 ].name_large = 0;
      ini->properties = (struct ini_internal_property_t*) INI_MALLOC( ini->memctx, INITIAL_CAPACITY * sizeof( ini->properties[ 0 ] ) );
  	memset(ini->properties, 0, INITIAL_CAPACITY * sizeof(ini->properties[0]));
      ini->property_capacity = INITIAL_CAPACITY;
      ini->property_count = 0;
      return ini;
      }
  
  
  ini_t* ini_load( char const* data, void* memctx )
      {
      ini_t* ini;
      char const* ptr;
      int s;
      char const* start;
      char const* start2;
      int l;
  
      ini = ini_create( memctx );
  
      ptr = data;
      if( ptr )
          {
          s = 0;
          while( *ptr )
              {
              /* trim leading whitespace */
              while( *ptr && *ptr <=' ' )
                  ++ptr;
              
              /* done? */
              if( !*ptr ) break;
  
              /* comment */
              else if( *ptr == ';' )
                  {
                  while( *ptr && *ptr !='\n' )
                      ++ptr;
                  }
              /* section */
              else if( *ptr == '[' )
                  {
                  ++ptr;
                  start = ptr;
                  while( *ptr && *ptr !=']' && *ptr != '\n' )
                      ++ptr;
  
                  if( *ptr == ']' )
                      {
                      s = ini_section_add( ini, start, (int)( ptr - start) );
                      ++ptr;
                      }
                  }
              /* property */
              else
                  {
                  start = ptr;
                  while( *ptr && *ptr !='=' && *ptr != '\n' )
                      ++ptr;
  
                  if( *ptr == '=' )
                      {
                      l = (int)( ptr - start);
                      ++ptr;
                      while( *ptr && *ptr <= ' ' && *ptr != '\n' ) 
                          ptr++;
                      start2 = ptr;
                      while( *ptr && *ptr != '\n' )
                          ++ptr;
                      while( *(--ptr) <= ' ' ) 
                          (void)ptr;
                      ptr++;
                      ini_property_add( ini, s, start, l, start2, (int)( ptr - start2) );
                      }
                  }
              }
          }   
  
      return ini;
      }
  
  
  int ini_save( ini_t const* ini, char* data, int size )
      {
      int s;
      int p;
      int i;
      int l;
      char* n;
      int pos;
  
      if( ini )
          {
          pos = 0;
          for( s = 0; s < ini->section_count; ++s )
              {
              n = ini->sections[ s ].name_large ? ini->sections[ s ].name_large : ini->sections[ s ].name;
              l = (int) INI_STRLEN( n );
              if( l > 0 )
                  {
                  if( data && pos < size ) data[ pos ] = '[';
                  ++pos;
                  for( i = 0; i < l; ++i )
                      {
                      if( data && pos < size ) data[ pos ] = n[ i ];
                      ++pos;
                      }
                  if( data && pos < size ) data[ pos ] = ']';
                  ++pos;
                  if( data && pos < size ) data[ pos ] = '\n';
                  ++pos;
                  }
  
              for( p = 0; p < ini->property_count; ++p )
                  {
                  if( ini->properties[ p ].section == s )
                      {
                      n = ini->properties[ p ].name_large ? ini->properties[ p ].name_large : ini->properties[ p ].name;
                      l = (int) INI_STRLEN( n );
                      for( i = 0; i < l; ++i )
                          {
                          if( data && pos < size ) data[ pos ] = n[ i ];
                          ++pos;
                          }
                      if( data && pos < size ) data[ pos ] = '=';
                      ++pos;
                      n = ini->properties[ p ].value_large ? ini->properties[ p ].value_large : ini->properties[ p ].value;
                      l = (int) INI_STRLEN( n );
                      for( i = 0; i < l; ++i )
                          {
                          if( data && pos < size ) data[ pos ] = n[ i ];
                          ++pos;
                          }
                      if( data && pos < size ) data[ pos ] = '\n';
                      ++pos;
                      }
                  }
  
              if( pos > 0 )
                  {
                  if( data && pos < size ) data[ pos ] = '\n';
                  ++pos;
                  }
              }
  
          if( data && pos < size ) data[ pos ] = '\0';
          ++pos;
  
          return pos;
          }
  
      return 0;
      }
  
  
  void ini_destroy( ini_t* ini )
      {
      int i;
  
      if( ini )
          {
          for( i = 0; i < ini->property_count; ++i )
              {
              if( ini->properties[ i ].value_large ) INI_FREE( ini->memctx, ini->properties[ i ].value_large );
              if( ini->properties[ i ].name_large ) INI_FREE( ini->memctx, ini->properties[ i ].name_large );
              }
          for( i = 0; i < ini->section_count; ++i )
              if( ini->sections[ i ].name_large ) INI_FREE( ini->memctx, ini->sections[ i ].name_large );
          INI_FREE( ini->memctx, ini->properties );
          INI_FREE( ini->memctx, ini->sections );
          INI_FREE( ini->memctx, ini );
          }
      }
  
  
  int ini_section_count( ini_t const* ini )
      {
      if( ini ) return ini->section_count;
      return 0;
      }
  
  
  char const* ini_section_name( ini_t const* ini, int section )
      {
      if( ini && section >= 0 && section < ini->section_count )
          return ini->sections[ section ].name_large ? ini->sections[ section ].name_large : ini->sections[ section ].name;
  
      return NULL;
      }
  
  
  int ini_property_count( ini_t const* ini, int section )
      {
      int i;
      int count;
  
      if( ini )
          {
          count = 0;
          for( i = 0; i < ini->property_count; ++i )
              {
              if( ini->properties[ i ].section == section ) ++count;
              }
          return count;
          }
  
      return 0;
      }
  
  
  char const* ini_property_name( ini_t const* ini, int section, int property )
      {
      int p;
  
      if( ini && section >= 0 && section < ini->section_count )
          {
          p = ini_internal_property_index( ini, section, property );
          if( p != INI_NOT_FOUND )
              return ini->properties[ p ].name_large ? ini->properties[ p ].name_large : ini->properties[ p ].name;
          }
  
      return NULL;
      }
  
  
  char const* ini_property_value( ini_t const* ini, int section, int property )
      {
      int p;
  
      if( ini && section >= 0 && section < ini->section_count )
          {
          p = ini_internal_property_index( ini, section, property );
          if( p != INI_NOT_FOUND )
              return ini->properties[ p ].value_large ? ini->properties[ p ].value_large : ini->properties[ p ].value;
          }
  
      return NULL;
      }
  
  
  int ini_find_section( ini_t const* ini, char const* name, int name_length )
      {
      int i;
  
      if( ini && name )
          {
          if( name_length <= 0 ) name_length = (int) INI_STRLEN( name );
          for( i = 0; i < ini->section_count; ++i )
              {
              char const* const other = 
                  ini->sections[ i ].name_large ? ini->sections[ i ].name_large : ini->sections[ i ].name;
              if( INI_STRNICMP( name, other, name_length ) == 0 )
                  return i;
              }
          }
  
      return INI_NOT_FOUND;
      }
  
  
  int ini_find_property( ini_t const* ini, int section, char const* name, int name_length )
      {
      int i;
      int c;
  
      if( ini && name && section >= 0 && section < ini->section_count)
          {
          if( name_length <= 0 ) name_length = (int) INI_STRLEN( name );
          c = 0;
          for( i = 0; i < ini->property_capacity; ++i )
              {
              if( ini->properties[ i ].section == section )
                  {
                  char const* const other = 
                      ini->properties[ i ].name_large ? ini->properties[ i ].name_large : ini->properties[ i ].name;
                  if( INI_STRNICMP( name, other, name_length ) == 0 )
                      return c;
                  ++c;
                  }
              }
          }
  
      return INI_NOT_FOUND;
      }
  
  
  int ini_section_add( ini_t* ini, char const* name, int length )
      {
      struct ini_internal_section_t* new_sections;
      
      if( ini && name )
          {
          if( length <= 0 ) length = (int) INI_STRLEN( name );
          if( ini->section_count >= ini->section_capacity )
              {
              ini->section_capacity *= 2;
              new_sections = (struct ini_internal_section_t*) INI_MALLOC( ini->memctx, 
                  ini->section_capacity * sizeof( ini->sections[ 0 ] ) );
              INI_MEMCPY( new_sections, ini->sections, ini->section_count * sizeof( ini->sections[ 0 ] ) );
              INI_FREE( ini->memctx, ini->sections );
              ini->sections = new_sections;
              }
  
          ini->sections[ ini->section_count ].name_large = 0;
          if( length + 1 >= sizeof( ini->sections[ 0 ].name ) )
              {
              ini->sections[ ini->section_count ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 );
              INI_MEMCPY( ini->sections[ ini->section_count ].name_large, name, (size_t) length );
              ini->sections[ ini->section_count ].name_large[ length ] = '\0';
              }
          else
              {
              INI_MEMCPY( ini->sections[ ini->section_count ].name, name, (size_t) length );
              ini->sections[ ini->section_count ].name[ length ] = '\0';
              }
  
          return ini->section_count++;
          }
      return INI_NOT_FOUND;
      }
  
  
  void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length )
      {
      struct ini_internal_property_t* new_properties;
  
      if( ini && name && section >= 0 && section < ini->section_count )
          {
          if( name_length <= 0 ) name_length = (int) INI_STRLEN( name );
          if( value_length <= 0 ) value_length = (int) INI_STRLEN( value );
  
          if( ini->property_count >= ini->property_capacity )
              {
  
              ini->property_capacity *= 2;
              new_properties = (struct ini_internal_property_t*) INI_MALLOC( ini->memctx, 
                  ini->property_capacity * sizeof( ini->properties[ 0 ] ) );
              INI_MEMCPY( new_properties, ini->properties, ini->property_count * sizeof( ini->properties[ 0 ] ) );
              INI_FREE( ini->memctx, ini->properties );
              ini->properties = new_properties;
              }
          
          ini->properties[ ini->property_count ].section = section;
          ini->properties[ ini->property_count ].name_large = 0;
          ini->properties[ ini->property_count ].value_large = 0;
  
          if( name_length + 1 >= sizeof( ini->properties[ 0 ].name ) )
              {
              ini->properties[ ini->property_count ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) name_length + 1 );
              INI_MEMCPY( ini->properties[ ini->property_count ].name_large, name, (size_t) name_length );
              ini->properties[ ini->property_count ].name_large[ name_length ] = '\0';
              }
          else
              {
              INI_MEMCPY( ini->properties[ ini->property_count ].name, name, (size_t) name_length );
              ini->properties[ ini->property_count ].name[ name_length ] = '\0';
              }
  
          if( value_length + 1 >= sizeof( ini->properties[ 0 ].value ) )
              {
              ini->properties[ ini->property_count ].value_large = (char*) INI_MALLOC( ini->memctx, (size_t) value_length + 1 );
              INI_MEMCPY( ini->properties[ ini->property_count ].value_large, value, (size_t) value_length );
              ini->properties[ ini->property_count ].value_large[ value_length ] = '\0';
              }
          else
              {
              INI_MEMCPY( ini->properties[ ini->property_count ].value, value, (size_t) value_length );
              ini->properties[ ini->property_count ].value[ value_length ] = '\0';
              }
  
          ++ini->property_count;
          }
      }
  
  
  void ini_section_remove( ini_t* ini, int section )
      {
      int p;
  
      if( ini && section >= 0 && section < ini->section_count )
          {
          if( ini->sections[ section ].name_large ) INI_FREE( ini->memctx, ini->sections[ section ].name_large );
          for( p = ini->property_count - 1; p >= 0; --p ) 
              {
              if( ini->properties[ p ].section == section )
                  {
                  if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large );
                  if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large );
                  ini->properties[ p ] = ini->properties[ --ini->property_count ];
                  }
              }
  
          ini->sections[ section ] = ini->sections[ --ini->section_count  ];
          
          for( p = 0; p < ini->property_count; ++p ) 
              {
              if( ini->properties[ p ].section == ini->section_count )
                  ini->properties[ p ].section = section;
              }
          }
      }
  
  
  void ini_property_remove( ini_t* ini, int section, int property )
      {
      int p;
  
      if( ini && section >= 0 && section < ini->section_count )
          {
          p = ini_internal_property_index( ini, section, property );
          if( p != INI_NOT_FOUND )
              {
              if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large );
              if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large );
              ini->properties[ p ] = ini->properties[ --ini->property_count  ];
              return;
              }
          }
      }
  
  
  void ini_section_name_set( ini_t* ini, int section, char const* name, int length )
      {
      if( ini && name && section >= 0 && section < ini->section_count )
          {
          if( length <= 0 ) length = (int) INI_STRLEN( name );
          if( ini->sections[ section ].name_large ) INI_FREE( ini->memctx, ini->sections[ section ].name_large );
          ini->sections[ section ].name_large = 0;
          
          if( length + 1 >= sizeof( ini->sections[ 0 ].name ) )
              {
              ini->sections[ section ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 );
              INI_MEMCPY( ini->sections[ section ].name_large, name, (size_t) length );
              ini->sections[ section ].name_large[ length ] = '\0';
              }
          else
              {
              INI_MEMCPY( ini->sections[ section ].name, name, (size_t) length );
              ini->sections[ section ].name[ length ] = '\0';
              }
          }
      }
  
  
  void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length )
      {
      int p;
  
      if( ini && name && section >= 0 && section < ini->section_count )
          {
          if( length <= 0 ) length = (int) INI_STRLEN( name );
          p = ini_internal_property_index( ini, section, property );
          if( p != INI_NOT_FOUND )
              {
              if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large );
              ini->properties[ ini->property_count ].name_large = 0;
  
              if( length + 1 >= sizeof( ini->properties[ 0 ].name ) )
                  {
                  ini->properties[ p ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 );
                  INI_MEMCPY( ini->properties[ p ].name_large, name, (size_t) length );
                  ini->properties[ p ].name_large[ length ] = '\0';
                  }
              else
                  {
                  INI_MEMCPY( ini->properties[ p ].name, name, (size_t) length );
                  ini->properties[ p ].name[ length ] = '\0';
                  }
              }
          }
      }
  
  
  void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length )
      {
      int p;
  
      if( ini && value && section >= 0 && section < ini->section_count )
          {
          if( length <= 0 ) length = (int) INI_STRLEN( value );
          p = ini_internal_property_index( ini, section, property );
          if( p != INI_NOT_FOUND )
              {
              if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large );
              ini->properties[ ini->property_count ].value_large = 0;
  
              if( length + 1 >= sizeof( ini->properties[ 0 ].value ) )
                  {
                  ini->properties[ p ].value_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 );
                  INI_MEMCPY( ini->properties[ p ].value_large, value, (size_t) length );
                  ini->properties[ p ].value_large[ length ] = '\0';
                  }
              else
                  {
                  INI_MEMCPY( ini->properties[ p ].value, value, (size_t) length );
                  ini->properties[ p ].value[ length ] = '\0';
                  }
              }
          }
      }
  
  
  #endif /* INI_IMPLEMENTATION */
  
  /*
  
  contributors:
      Randy Gaul (copy-paste bug in ini_property_value_set)
      Branimir Karadzic (INI_STRNICMP bugfix)
  
  revision history:
      1.2     using strnicmp for correct length compares, fixed copy-paste bug in ini_property_value_set
      1.1     customization, added documentation, cleanup
      1.0     first publicly released version
  
  */
  
  /*
  ------------------------------------------------------------------------------
  
  This software is available under 2 licenses - you may choose the one you like.
  
  ------------------------------------------------------------------------------
  
  ALTERNATIVE A - MIT License
  
  Copyright (c) 2015 Mattias Gustavsson
  
  Permission is hereby granted, free of charge, to any person obtaining a copy of 
  this software and associated documentation files (the "Software"), to deal in 
  the Software without restriction, including without limitation the rights to 
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
  of the Software, and to permit persons to whom the Software is furnished to do 
  so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all 
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  ------------------------------------------------------------------------------
  
  ALTERNATIVE B - Public Domain (www.unlicense.org)
  
  This is free and unencumbered software released into the public domain.
  
  Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
  software, either in source code form or as a compiled binary, for any purpose, 
  commercial or non-commercial, and by any means.
  
  In jurisdictions that recognize copyright laws, the author or authors of this 
  software dedicate any and all copyright interest in the software to the public 
  domain. We make this dedication for the benefit of the public at large and to 
  the detriment of our heirs and successors. We intend this dedication to be an 
  overt act of relinquishment in perpetuity of all present and future rights to 
  this software under copyright law.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  ------------------------------------------------------------------------------
  */
  