240 lines
6.3 KiB
C
240 lines
6.3 KiB
C
#include <voxula/internals/utility/ini.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
char * vx_parse_ini_section_name(
|
|
vx_arena_s *arena,
|
|
const char *source,
|
|
uint32_t offset,
|
|
uint32_t *len_name
|
|
) {
|
|
++offset; // Jump over the starting square bracket
|
|
uint32_t start_offset = offset;
|
|
|
|
while(source[offset] != 0)
|
|
{
|
|
if(source[offset] == ']')
|
|
{
|
|
++offset;
|
|
}
|
|
++offset;
|
|
}
|
|
uint32_t name_word_length = offset - start_offset;
|
|
char *name = malloc(name_word_length + 1);
|
|
memcpy(name, &source[start_offset], name_word_length);
|
|
name[name_word_length] = 0;
|
|
|
|
if(len_name)
|
|
{
|
|
*len_name = name_word_length;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
vx_ini_value_s vx_parse_ini_value(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
|
{
|
|
// For string values
|
|
if(source[offset] == '"')
|
|
{
|
|
vx_ini_value_s value;
|
|
uint32_t start_offset = offset;
|
|
|
|
if(source[offset + 1] == '"')
|
|
{
|
|
// If this is an empty string
|
|
if(source[offset + 2] != '"')
|
|
{
|
|
value.type = VX_INI_VALUE_STRING;
|
|
value.specifics.string = vx_arena_alloc(ini->string_arena, 1);
|
|
value.specifics.string[0] = 0;
|
|
return value;
|
|
}
|
|
// If this is a multi-line string
|
|
offset += 3; // Jump over the quotation marks so they can't be picked up as the ending ones.
|
|
uint32_t string_data_start = offset;
|
|
while(source[offset != 0])
|
|
{
|
|
if(source[offset] != '"')
|
|
{
|
|
++offset;
|
|
continue;
|
|
}
|
|
if(source[offset + 1] != '"')
|
|
{
|
|
++offset;
|
|
continue;
|
|
}
|
|
if(source[offset + 2] != '"')
|
|
{
|
|
++offset;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
value.type = VX_INI_VALUE_STRING;
|
|
value.specifics.string = vx_arena_dupe_string_up_to(ini->string_arena, &source[string_data_start], offset - string_data_start);
|
|
return value;
|
|
}
|
|
// A regular, single-line string
|
|
++offset;
|
|
while(source[offset] != 0)
|
|
{
|
|
if(source[offset] == '\n')
|
|
{
|
|
break;
|
|
}
|
|
if(source[offset] == '"')
|
|
{
|
|
break;
|
|
}
|
|
++offset;
|
|
}
|
|
value.type = VX_INI_VALUE_STRING;
|
|
value.specifics.string = vx_arena_dupe_string_up_to(ini->string_arena, &source[start_offset], offset - start_offset);
|
|
return value;
|
|
}
|
|
if(vx_is_ascii_digit(source[offset]))
|
|
{
|
|
vx_ini_value_s value;
|
|
uint32_t start_offset = offset;
|
|
while(source[offset] != 0)
|
|
{
|
|
if( ! vx_is_ascii_digit(source[offset]))
|
|
{
|
|
break;
|
|
}
|
|
++offset;
|
|
}
|
|
// todo(delayed): Add floating-point numbers
|
|
|
|
char *integer_string = vx_arena_dupe_string_up_to(ini->string_arena, &source[start_offset], offset - start_offset);
|
|
|
|
value.type = VX_INI_VALUE_INTEGER;
|
|
value.specifics.integer = atol(integer_string);
|
|
return value;
|
|
}
|
|
vx_ini_value_s value;
|
|
value.type = VX_INI_VALUE_NULL;
|
|
return value;
|
|
}
|
|
|
|
vx_ini_field_s vx_parse_ini_field(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
|
{
|
|
uint32_t start_offset = offset;
|
|
|
|
// Get the field's name
|
|
while(source[offset] != 0)
|
|
{
|
|
if( ! vx_is_ascii_letter(source[offset])
|
|
&& (source[offset] != '_'))
|
|
{
|
|
break;
|
|
}
|
|
++offset;
|
|
}
|
|
vx_ini_field_s field;
|
|
field.len_name = offset - start_offset;
|
|
field.name = vx_arena_dupe_string_up_to(ini->string_arena, &source[offset], field.len_name);
|
|
|
|
uint32_t len_value;
|
|
field.value = vx_parse_ini_value(ini, source, offset, &len_value);
|
|
if(out_length)
|
|
{
|
|
*out_length = (offset - start_offset) + len_value;
|
|
}
|
|
return field;
|
|
}
|
|
|
|
vx_ini_section_s vx_parse_ini_section(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
|
{
|
|
uint32_t start_offset = offset;
|
|
vx_ini_section_s section;
|
|
|
|
section.name = vx_parse_ini_section_name(ini->string_arena, source, offset, §ion.len_name);
|
|
offset += section.len_name + 2;
|
|
section.num_fields = 0;
|
|
section.fields_capacity = 32;
|
|
section.fields = malloc(section.fields_capacity * sizeof(vx_ini_field_s));
|
|
|
|
while(source[offset] != 0)
|
|
{
|
|
// Skip comments
|
|
if(source[offset] == '#')
|
|
{
|
|
while(source[offset] != 0)
|
|
{
|
|
if(source[offset] == '\n')
|
|
{
|
|
break;
|
|
}
|
|
++offset;
|
|
}
|
|
++offset;
|
|
}
|
|
// Get all the fields of this section
|
|
while(source[offset] != 0)
|
|
{
|
|
if(vx_is_ascii_letter(source[offset]
|
|
|| (source[offset] == '_'))
|
|
) {
|
|
break;
|
|
}
|
|
if(source[offset] == '[')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if(source[offset] == '[')
|
|
{
|
|
break;
|
|
}
|
|
uint32_t bytes_to_jump = 0;
|
|
vx_parse_ini_field(ini, source, offset, &bytes_to_jump);
|
|
offset += bytes_to_jump;
|
|
}
|
|
++offset;
|
|
return section;
|
|
}
|
|
|
|
vx_ini_s vx_parse_ini(const char *source)
|
|
{
|
|
uint32_t len_source = strlen(source);
|
|
|
|
vx_ini_s ini;
|
|
ini.num_sections = 0;
|
|
ini.sections_capacity = 64;
|
|
ini.sections = malloc(ini.sections_capacity * sizeof(vx_ini_section_s));
|
|
ini.string_arena = vx_new_arena(len_source * 2);
|
|
|
|
uint32_t offset = 0;
|
|
while(offset < len_source)
|
|
{
|
|
if(source[offset] == '[')
|
|
{
|
|
uint32_t len_section;
|
|
ini.sections[ini.num_sections] = vx_parse_ini_section(&ini, source, offset, &len_section);
|
|
offset += len_section;
|
|
}
|
|
}
|
|
return ini;
|
|
}
|
|
|
|
void vx_delete_ini(vx_ini_s *ini)
|
|
{
|
|
|
|
}
|
|
|
|
vx_ini_value_s vx_get_ini(vx_ini_s *ini, const char *field)
|
|
{
|
|
uint32_t splitter = 0;
|
|
while(field[splitter] != 0)
|
|
{
|
|
if(field[splitter] == '.')
|
|
{
|
|
break;
|
|
}
|
|
++splitter;
|
|
}
|
|
|
|
}
|