diff --git a/libs/ini.h b/libs/ini.h new file mode 100644 index 0000000..07c6aac --- /dev/null +++ b/libs/ini.h @@ -0,0 +1,1065 @@ +/* +------------------------------------------------------------------------------ + 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 */ + + +/** + +ini.h +===== + +Simple ini-file reader for C/C++. + + +Examples +-------- + +#### Loading an ini file and retrieving values + + #define INI_IMPLEMENTATION + #include "ini.h" + + #include + #include + + 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, NULL ); + free( data ); + int second_index = ini_find_property( ini, INI_GLOBAL_SECTION, "SecondSetting", 0 ); + 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", 0 ); + int third_index = ini_find_property( ini, section, "ThirdSetting", 0 ); + 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 + #include + + int main() + { + ini_t* ini = ini_create( NULL ); + ini_property_add( ini, INI_GLOBAL_SECTION, "FirstSetting", 0, "Test", 0 ); + ini_property_add( ini, INI_GLOBAL_SECTION, "SecondSetting", 0, "2", 0 ); + int section = ini_section_add( ini, "MySection", 0 ); + ini_property_add( ini, section, "ThirdSetting", 0, "Three", 0 ); + + 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 - 1, 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 + +#ifndef INI_MALLOC + #include + #define INI_MALLOC( ctx, size ) ( malloc( size ) ) + #define INI_FREE( ctx, ptr ) ( free( ptr ) ) +#endif + +#ifndef INI_MEMCPY + #include + #define INI_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) +#endif + +#ifndef INI_STRLEN + #include + #define INI_STRLEN( s ) ( strlen( s ) ) +#endif + +#ifndef INI_STRNICMP + #ifdef _WIN32 + #include + #define INI_STRNICMP( s1, s2, cnt ) ( strnicmp( s1, s2, cnt ) ) + #else + #include + #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 ] ) ); + 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 >= start2 && *(--ptr) <= ' ' ) + (void)ptr; + ptr++; + if( ptr == start2 ) + ini_property_add( ini, s, start, l, "", 1 ); + else + 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( strlen( other ) == name_length && INI_STRNICMP( name, other, (size_t)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_count; ++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( strlen( other ) == name_length && INI_STRNICMP( name, other, (size_t) 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( (size_t) 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( (size_t) 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( (size_t) 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( (size_t) 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( (size_t) 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( (size_t) 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. + +------------------------------------------------------------------------------ +*/