mirror of
https://github.com/bkaradzic/bx.git
synced 2026-02-17 20:52:37 +01:00
Added .ini settings utility.
This commit is contained in:
1
3rdparty/ini/README.md
vendored
Normal file
1
3rdparty/ini/README.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
https://github.com/mattiasgustavsson/libs/
|
||||
1056
3rdparty/ini/ini.h
vendored
Normal file
1056
3rdparty/ini/ini.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
333
3rdparty/ini/ini.md
vendored
Normal file
333
3rdparty/ini/ini.md
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
ini.h
|
||||
=====
|
||||
|
||||
Library: [ini.h](../ini.h)
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Loading an ini file and retrieving values
|
||||
-----------------------------------------
|
||||
|
||||
```cpp
|
||||
#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
|
||||
-----------------------
|
||||
|
||||
```cpp
|
||||
#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_STRICMP( s1, s2 ) ( my_stricmp_func( s1, s2 ) )
|
||||
#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.
|
||||
@@ -200,10 +200,10 @@ namespace bx
|
||||
|
||||
inline MemoryWriter::MemoryWriter(MemoryBlockI* _memBlock)
|
||||
: m_memBlock(_memBlock)
|
||||
, m_data(NULL)
|
||||
, m_pos(0)
|
||||
, m_top(0)
|
||||
, m_size(0)
|
||||
, m_data(NULL)
|
||||
, m_pos(0)
|
||||
, m_top(0)
|
||||
, m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -391,11 +391,19 @@ namespace bx
|
||||
inline int64_t getSize(SeekerI* _seeker)
|
||||
{
|
||||
int64_t offset = _seeker->seek();
|
||||
int64_t size = _seeker->seek(0, Whence::End);
|
||||
int64_t size = _seeker->seek(0, Whence::End);
|
||||
_seeker->seek(offset, Whence::Begin);
|
||||
return size;
|
||||
}
|
||||
|
||||
inline int64_t getRemain(SeekerI* _seeker)
|
||||
{
|
||||
int64_t offset = _seeker->seek();
|
||||
int64_t size = _seeker->seek(0, Whence::End);
|
||||
_seeker->seek(offset, Whence::Begin);
|
||||
return size-offset;
|
||||
}
|
||||
|
||||
inline int32_t peek(ReaderSeekerI* _reader, void* _data, int32_t _size, Error* _err)
|
||||
{
|
||||
BX_ERROR_SCOPE(_err);
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
namespace bx
|
||||
{
|
||||
///
|
||||
struct EncodingType
|
||||
{
|
||||
enum Enum
|
||||
enum Enum ///
|
||||
{
|
||||
Unorm,
|
||||
Int,
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace bx
|
||||
uint32_t m_size;
|
||||
};
|
||||
|
||||
///
|
||||
/// Sizer writer. Dummy writter that only counts number of bytes written into it.
|
||||
class SizerWriter : public WriterSeekerI
|
||||
{
|
||||
public:
|
||||
@@ -226,7 +226,7 @@ namespace bx
|
||||
int64_t m_size;
|
||||
};
|
||||
|
||||
///
|
||||
/// Static (fixed size) memory block writer.
|
||||
class StaticMemoryBlockWriter : public MemoryWriter
|
||||
{
|
||||
public:
|
||||
@@ -288,6 +288,9 @@ namespace bx
|
||||
/// Returns size of file.
|
||||
int64_t getSize(SeekerI* _seeker);
|
||||
|
||||
/// Returns remaining size from current offset of file.
|
||||
int64_t getRemain(SeekerI* _seeker);
|
||||
|
||||
/// Peek data.
|
||||
int32_t peek(ReaderSeekerI* _reader, void* _data, int32_t _size, Error* _err = NULL);
|
||||
|
||||
@@ -301,16 +304,16 @@ namespace bx
|
||||
/// Align writer stream (pads stream with zeros).
|
||||
int32_t align(WriterSeekerI* _writer, uint32_t _alignment, Error* _err = NULL);
|
||||
|
||||
///
|
||||
/// Open for read.
|
||||
bool open(ReaderOpenI* _reader, const FilePath& _filePath, Error* _err = NULL);
|
||||
|
||||
///
|
||||
/// Open fro write.
|
||||
bool open(WriterOpenI* _writer, const FilePath& _filePath, bool _append = false, Error* _err = NULL);
|
||||
|
||||
///
|
||||
/// Open process.
|
||||
bool open(ProcessOpenI* _process, const FilePath& _filePath, const StringView& _args, Error* _err = NULL);
|
||||
|
||||
///
|
||||
/// Close.
|
||||
void close(CloserI* _reader);
|
||||
|
||||
} // namespace bx
|
||||
|
||||
61
include/bx/settings.h
Normal file
61
include/bx/settings.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2011-2017 Branimir Karadzic. All rights reserved.
|
||||
* License: https://github.com/bkaradzic/bx#license-bsd-2-clause
|
||||
*/
|
||||
|
||||
#ifndef BX_SETTINGS_H_HEADER_GUARD
|
||||
#define BX_SETTINGS_H_HEADER_GUARD
|
||||
|
||||
#include "allocator.h"
|
||||
#include "readerwriter.h"
|
||||
#include "string.h"
|
||||
|
||||
namespace bx
|
||||
{
|
||||
///
|
||||
class Settings
|
||||
{
|
||||
public:
|
||||
///
|
||||
Settings(AllocatorI* _allocator, const void* _data = NULL, uint32_t _len = 0);
|
||||
|
||||
///
|
||||
~Settings();
|
||||
|
||||
///
|
||||
void clear();
|
||||
|
||||
///
|
||||
void load(const void* _data, uint32_t _len);
|
||||
|
||||
///
|
||||
const char* get(const StringView& _name) const;
|
||||
|
||||
///
|
||||
void set(const StringView& _name, const StringView& _value = "");
|
||||
|
||||
///
|
||||
void remove(const StringView& _name) const;
|
||||
|
||||
///
|
||||
int32_t read(ReaderSeekerI* _reader, Error* _err);
|
||||
|
||||
///
|
||||
int32_t write(WriterI* _writer, Error* _err) const;
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
||||
AllocatorI* m_allocator;
|
||||
void* m_ini;
|
||||
};
|
||||
|
||||
///
|
||||
int32_t read(ReaderSeekerI* _reader, Settings& _settings, Error* _err = NULL);
|
||||
|
||||
///
|
||||
int32_t write(WriterI* _writer, const Settings& _settings, Error* _err = NULL);
|
||||
|
||||
} // namespace bx
|
||||
|
||||
#endif // BX_SETTINGS_H_HEADER_GUARD
|
||||
@@ -200,6 +200,15 @@ namespace bx
|
||||
/// Find substring in string. Case insensitive. Limit search to _max characters.
|
||||
const char* strFindI(const StringView& _str, const StringView& _find, int32_t _num = INT32_MAX);
|
||||
|
||||
///
|
||||
StringView strLTrim(const StringView& _str, const StringView& _chars);
|
||||
|
||||
///
|
||||
StringView strRTrim(const StringView& _str, const StringView& _chars);
|
||||
|
||||
///
|
||||
StringView strTrim(const StringView& _str, const StringView& _chars);
|
||||
|
||||
/// Find new line. Returns pointer after new line terminator.
|
||||
const char* strnl(const char* _str);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ project "bx"
|
||||
|
||||
includedirs {
|
||||
path.join(BX_DIR, "include"),
|
||||
path.join(BX_DIR, "3rdparty"),
|
||||
}
|
||||
|
||||
files {
|
||||
@@ -40,6 +41,7 @@ project "bx"
|
||||
path.join(BX_DIR, "src/os.cpp"),
|
||||
path.join(BX_DIR, "src/process.cpp"),
|
||||
path.join(BX_DIR, "src/semaphore.cpp"),
|
||||
path.join(BX_DIR, "src/settings.cpp"),
|
||||
path.join(BX_DIR, "src/sort.cpp"),
|
||||
path.join(BX_DIR, "src/string.cpp"),
|
||||
path.join(BX_DIR, "src/thread.cpp"),
|
||||
|
||||
@@ -28,8 +28,8 @@ solution "bx"
|
||||
language "C++"
|
||||
|
||||
BX_DIR = path.getabsolute("..")
|
||||
local BX_BUILD_DIR = path.join(BX_DIR, ".build")
|
||||
local BX_THIRD_PARTY_DIR = path.join(BX_DIR, "3rdparty")
|
||||
BX_BUILD_DIR = path.join(BX_DIR, ".build")
|
||||
BX_THIRD_PARTY_DIR = path.join(BX_DIR, "3rdparty")
|
||||
|
||||
dofile "toolchain.lua"
|
||||
toolchain(BX_BUILD_DIR, BX_THIRD_PARTY_DIR)
|
||||
|
||||
213
src/settings.cpp
Normal file
213
src/settings.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright 2011-2017 Branimir Karadzic. All rights reserved.
|
||||
* License: https://github.com/bkaradzic/bx#license-bsd-2-clause
|
||||
*/
|
||||
|
||||
#include <bx/settings.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
#define INI_MALLOC(_ctx, _size) (BX_ALLOC(reinterpret_cast<bx::AllocatorI*>(_ctx), _size) )
|
||||
#define INI_FREE(_ctx, _ptr) (BX_FREE(reinterpret_cast<bx::AllocatorI*>(_ctx), _ptr) )
|
||||
#define INI_MEMCPY(_dst, _src, _count) (bx::memCopy(_dst, _src, _count) )
|
||||
#define INI_STRLEN(_str) (bx::strLen(_str) )
|
||||
#define INI_STRICMP(_s1, _s2, _len) (bx::strCmpI(_s1, _s2, _len) )
|
||||
|
||||
#define INI_IMPLEMENTATION
|
||||
|
||||
BX_PRAGMA_DIAGNOSTIC_PUSH();
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wsign-compare");
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function");
|
||||
#include <ini/ini.h>
|
||||
BX_PRAGMA_DIAGNOSTIC_POP();
|
||||
}
|
||||
|
||||
namespace bx
|
||||
{
|
||||
|
||||
Settings::Settings(AllocatorI* _allocator, const void* _data, uint32_t _len)
|
||||
: m_allocator(_allocator)
|
||||
, m_ini(NULL)
|
||||
{
|
||||
load(_data, _len);
|
||||
}
|
||||
|
||||
#define INI_T(_ptr) reinterpret_cast<ini_t*>(_ptr)
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
ini_destroy(INI_T(m_ini) );
|
||||
}
|
||||
|
||||
void Settings::clear()
|
||||
{
|
||||
load(NULL, 0);
|
||||
}
|
||||
|
||||
void Settings::load(const void* _data, uint32_t _len)
|
||||
{
|
||||
if (NULL != m_ini)
|
||||
{
|
||||
ini_destroy(INI_T(m_ini) );
|
||||
}
|
||||
|
||||
if (NULL == _data)
|
||||
{
|
||||
m_ini = ini_create(m_allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
BX_UNUSED(_len);
|
||||
m_ini = ini_load( (const char*)_data, m_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
const char* Settings::get(const StringView& _name) const
|
||||
{
|
||||
ini_t* ini = INI_T(m_ini);
|
||||
|
||||
FilePath uri(_name);
|
||||
const StringView path = strTrim(uri.getPath(), "/");
|
||||
const StringView& fileName = uri.getFileName();
|
||||
|
||||
int32_t section = INI_GLOBAL_SECTION;
|
||||
|
||||
if (!path.isEmpty() )
|
||||
{
|
||||
section = ini_find_section(ini, path.getPtr(), path.getLength()-1);
|
||||
if (INI_NOT_FOUND == section)
|
||||
{
|
||||
section = INI_GLOBAL_SECTION;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ini_property_value(ini, section, property);
|
||||
}
|
||||
|
||||
void Settings::set(const StringView& _name, const StringView& _value)
|
||||
{
|
||||
ini_t* ini = INI_T(m_ini);
|
||||
|
||||
FilePath uri(_name);
|
||||
const StringView path = strTrim(uri.getPath(), "/");
|
||||
const StringView& fileName = uri.getFileName();
|
||||
|
||||
int32_t section = INI_GLOBAL_SECTION;
|
||||
|
||||
if (!path.isEmpty() )
|
||||
{
|
||||
section = ini_find_section(ini, path.getPtr(), path.getLength()-1);
|
||||
if (INI_NOT_FOUND == section)
|
||||
{
|
||||
section = ini_section_add(ini, path.getPtr(), path.getLength()-1);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
{
|
||||
ini_property_add(
|
||||
ini
|
||||
, section
|
||||
, fileName.getPtr()
|
||||
, fileName.getLength()
|
||||
, _value.getPtr()
|
||||
, _value.getLength()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ini_property_value_set(
|
||||
ini
|
||||
, section
|
||||
, property
|
||||
, _value.getPtr()
|
||||
, _value.getLength()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::remove(const StringView& _name) const
|
||||
{
|
||||
ini_t* ini = INI_T(m_ini);
|
||||
|
||||
FilePath uri(_name);
|
||||
const StringView path = strTrim(uri.getPath(), "/");
|
||||
const StringView& fileName = uri.getFileName();
|
||||
|
||||
int32_t section = INI_GLOBAL_SECTION;
|
||||
|
||||
if (!path.isEmpty() )
|
||||
{
|
||||
section = ini_find_section(ini, path.getPtr(), path.getLength()-1);
|
||||
if (INI_NOT_FOUND == section)
|
||||
{
|
||||
section = INI_GLOBAL_SECTION;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ini_property_remove(ini, section, property);
|
||||
|
||||
if (INI_GLOBAL_SECTION != section
|
||||
&& 0 == ini_property_count(ini, section) )
|
||||
{
|
||||
ini_section_remove(ini, section);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Settings::read(ReaderSeekerI* _reader, Error* _err)
|
||||
{
|
||||
int32_t size = int32_t(getRemain(_reader) );
|
||||
|
||||
void* data = BX_ALLOC(m_allocator, size);
|
||||
|
||||
int32_t total = bx::read(_reader, data, size, _err);
|
||||
load(data, size);
|
||||
|
||||
BX_FREE(m_allocator, data);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
int32_t Settings::write(WriterI* _writer, Error* _err) const
|
||||
{
|
||||
ini_t* ini = INI_T(m_ini);
|
||||
|
||||
int32_t size = ini_save(ini, NULL, 0);
|
||||
void* data = BX_ALLOC(m_allocator, size);
|
||||
|
||||
ini_save(ini, (char*)data, size);
|
||||
int32_t total = bx::write(_writer, data, size-1, _err);
|
||||
|
||||
BX_FREE(m_allocator, data);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#undef INI_T
|
||||
|
||||
int32_t read(ReaderSeekerI* _reader, Settings& _settings, Error* _err)
|
||||
{
|
||||
BX_ERROR_SCOPE(_err);
|
||||
return _settings.read(_reader, _err);
|
||||
}
|
||||
|
||||
int32_t write(WriterI* _writer, const Settings& _settings, Error* _err)
|
||||
{
|
||||
BX_ERROR_SCOPE(_err);
|
||||
return _settings.write(_writer, _err);
|
||||
}
|
||||
|
||||
} // namespace bx
|
||||
@@ -333,9 +333,9 @@ namespace bx
|
||||
return strCat(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) );
|
||||
}
|
||||
|
||||
inline const char* strFind(const char* _str, int32_t _max, char _ch)
|
||||
inline const char* strFindUnsafe(const char* _str, int32_t _len, char _ch)
|
||||
{
|
||||
for (int32_t ii = 0, len = strLen(_str, _max); ii < len; ++ii)
|
||||
for (int32_t ii = 0; ii < _len; ++ii)
|
||||
{
|
||||
if (_str[ii] == _ch)
|
||||
{
|
||||
@@ -346,14 +346,19 @@ namespace bx
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const char* strFind(const char* _str, int32_t _max, char _ch)
|
||||
{
|
||||
return strFindUnsafe(_str, strLen(_str, _max), _ch);
|
||||
}
|
||||
|
||||
const char* strFind(const StringView& _str, char _ch)
|
||||
{
|
||||
return strFind(_str.getPtr(), _str.getLength(), _ch);
|
||||
}
|
||||
|
||||
inline const char* strRFind(const char* _str, int32_t _max, char _ch)
|
||||
inline const char* strRFindUnsafe(const char* _str, int32_t _len, char _ch)
|
||||
{
|
||||
for (int32_t ii = strLen(_str, _max); 0 <= ii; --ii)
|
||||
for (int32_t ii = _len; 0 <= ii; --ii)
|
||||
{
|
||||
if (_str[ii] == _ch)
|
||||
{
|
||||
@@ -364,6 +369,11 @@ namespace bx
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const char* strRFind(const char* _str, int32_t _max, char _ch)
|
||||
{
|
||||
return strRFindUnsafe(_str, strLen(_str, _max), _ch);
|
||||
}
|
||||
|
||||
const char* strRFind(const StringView& _str, char _ch)
|
||||
{
|
||||
return strRFind(_str.getPtr(), _str.getLength(), _ch);
|
||||
@@ -430,6 +440,44 @@ namespace bx
|
||||
);
|
||||
}
|
||||
|
||||
StringView strLTrim(const StringView& _str, const StringView& _chars)
|
||||
{
|
||||
const char* ptr = _str.getPtr();
|
||||
const char* chars = _chars.getPtr();
|
||||
const uint32_t charsLen = _chars.getLength();
|
||||
for (uint32_t ii = 0, len = _str.getLength(); ii < len; ++ii)
|
||||
{
|
||||
if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) )
|
||||
{
|
||||
return StringView(ptr + ii, len-ii);
|
||||
}
|
||||
}
|
||||
|
||||
return StringView();
|
||||
}
|
||||
|
||||
StringView strRTrim(const StringView& _str, const StringView& _chars)
|
||||
{
|
||||
const char* ptr = _str.getPtr();
|
||||
const char* chars = _chars.getPtr();
|
||||
const uint32_t charsLen = _chars.getLength();
|
||||
|
||||
for (int32_t len = _str.getLength(), ii = len-1; 0 <= ii; --ii)
|
||||
{
|
||||
if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) )
|
||||
{
|
||||
return StringView(ptr, ii);
|
||||
}
|
||||
}
|
||||
|
||||
return StringView();
|
||||
}
|
||||
|
||||
StringView strTrim(const StringView& _str, const StringView& _chars)
|
||||
{
|
||||
return strLTrim(strRTrim(_str, _chars), _chars);
|
||||
}
|
||||
|
||||
const char* strnl(const char* _str)
|
||||
{
|
||||
for (; '\0' != *_str; _str += strLen(_str, 1024) )
|
||||
|
||||
@@ -80,10 +80,10 @@ static const FilePathSplit s_filePathSplit[] =
|
||||
{
|
||||
{ "\\abc/def\\../..\\../test.txt", true, "/", "test.txt", "test", ".txt" },
|
||||
{ "/abv/gd/555/333/pod.mac", true, "/abv/gd/555/333/", "pod.mac", "pod", ".mac" },
|
||||
{ "archive.tar.gz", false, "", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "tmp/archive.tar.gz", false, "tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "/tmp/archive.tar.gz", true, "/tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "d:/tmp/archive.tar.gz", true, "D:/tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "archive.tar.gz", false, "", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "tmp/archive.tar.gz", false, "tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "/tmp/archive.tar.gz", true, "/tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
{ "d:/tmp/archive.tar.gz", true, "D:/tmp/", "archive.tar.gz", "archive", ".tar.gz" },
|
||||
};
|
||||
|
||||
TEST_CASE("FilePath", "")
|
||||
|
||||
49
tests/settings_test.cpp
Normal file
49
tests/settings_test.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2017 Branimir Karadzic. All rights reserved.
|
||||
* License: https://github.com/bkaradzic/bx#license-bsd-2-clause
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
#include <bx/settings.h>
|
||||
#include <bx/file.h>
|
||||
|
||||
TEST_CASE("Settings", "")
|
||||
{
|
||||
bx::FilePath filePath;
|
||||
filePath.set(bx::TempDir::Tag);
|
||||
filePath.join("settings.ini");
|
||||
|
||||
bx::DefaultAllocator allocator;
|
||||
|
||||
bx::Settings settings(&allocator);
|
||||
|
||||
settings.set("meh/podmac", "true");
|
||||
settings.set("test/foo/bar/abvgd", "1389");
|
||||
|
||||
bx::FileWriter writer;
|
||||
if (bx::open(&writer, filePath) )
|
||||
{
|
||||
bx::write(&writer, settings);
|
||||
bx::close(&writer);
|
||||
}
|
||||
|
||||
REQUIRE(NULL == settings.get("meh") );
|
||||
REQUIRE(0 == bx::strCmp(settings.get("meh/podmac"), "true") );
|
||||
REQUIRE(0 == bx::strCmp(settings.get("test/foo/bar/abvgd"), "1389") );
|
||||
|
||||
settings.remove("meh/podmac");
|
||||
REQUIRE(NULL == settings.get("meh/podmac") );
|
||||
|
||||
settings.clear();
|
||||
|
||||
bx::FileReader reader;
|
||||
if (bx::open(&reader, filePath) )
|
||||
{
|
||||
bx::read(&reader, settings);
|
||||
bx::close(&writer);
|
||||
}
|
||||
|
||||
REQUIRE(NULL == settings.get("meh") );
|
||||
REQUIRE(0 == bx::strCmp(settings.get("meh/podmac"), "true") );
|
||||
REQUIRE(0 == bx::strCmp(settings.get("test/foo/bar/abvgd"), "1389") );
|
||||
}
|
||||
@@ -196,6 +196,8 @@ TEST_CASE("strFind", "")
|
||||
REQUIRE(NULL == bx::strFind(test, "Quick1") );
|
||||
REQUIRE(&test[4] == bx::strFind(bx::StringView(test, 9), "Quick") );
|
||||
REQUIRE(&test[4] == bx::strFind(test, "Quick") );
|
||||
|
||||
REQUIRE(NULL == bx::strFind("vgd", 'a') );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,3 +363,19 @@ TEST_CASE("StringView", "")
|
||||
sv.clear();
|
||||
REQUIRE(0 == sv.getLength() );
|
||||
}
|
||||
|
||||
TEST_CASE("Trim", "")
|
||||
{
|
||||
REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "ab"), "vgd") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "vagbd"), "") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "vgd"), "abvgd") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strLTrim("/555333/podmac/", "/"), "555333/podmac/") );
|
||||
|
||||
REQUIRE(0 == bx::strCmp(bx::strRTrim("abvgd", "vagbd"), "") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strRTrim("abvgd", "abv"), "abvgd") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strRTrim("/555333/podmac/", "/"), "/555333/podmac") );
|
||||
|
||||
REQUIRE(0 == bx::strCmp(bx::strTrim("abvgd", "da"), "bvg") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strTrim("<1389>", "<>"), "1389") );
|
||||
REQUIRE(0 == bx::strCmp(bx::strTrim("/555333/podmac/", "/"), "555333/podmac") );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user