diff options
| author | Clecio Jung | 2023-03-23 17:10:31 -0300 |
|---|---|---|
| committer | Clecio Jung | 2023-03-23 17:10:31 -0300 |
| commit | 629d03b079b29f6d5178c407537aa15f9d109ec8 (patch) | |
| tree | 801cbdde1d0bce5181b99d0fa741f6134cd4981d /ini_file.c | |
| parent | 308925f81dcac05471e818865a94061f3421f217 (diff) | |
| download | libini-629d03b079b29f6d5178c407537aa15f9d109ec8.tar.xz libini-629d03b079b29f6d5178c407537aa15f9d109ec8.tar.zst | |
Sorting sections and properties
Diffstat (limited to 'ini_file.c')
| -rw-r--r-- | ini_file.c | 371 |
1 files changed, 223 insertions, 148 deletions
@@ -64,16 +64,22 @@ struct Ini_File *ini_file_new(void) { return NULL; } memset(ini_file, 0, sizeof(struct Ini_File)); - /* TODO: If we sort the sections, we shall think in another way to store - * and retrieve the data of this global section */ - /* The first section is global to the file */ - if (ini_file_add_section(ini_file, "global") != 0) { - ini_file_free(ini_file); - return NULL; - } + ini_file->current_section = &ini_file->global_section; return ini_file; } +static void ini_section_free(struct Ini_Section *const ini_section) { +#ifndef USE_CUSTOM_STRING_ALLOCATOR + size_t i; + free(ini_section->name); + for (i = 0; i < ini_section->properties_size; i++) { + free(ini_section->properties[i].key); + free(ini_section->properties[i].value); + } +#endif + free(ini_section->properties); +} + void ini_file_free(struct Ini_File *const ini_file) { size_t i; if (ini_file == NULL) { @@ -83,16 +89,9 @@ void ini_file_free(struct Ini_File *const ini_file) { string_buffer_free(ini_file->strings); #endif for (i = 0; i < ini_file->sections_size; i++) { -#ifndef USE_CUSTOM_STRING_ALLOCATOR - size_t j; - free(ini_file->sections[i].name); - for (j = 0; j < ini_file->sections[i].properties_size; j++) { - free(ini_file->sections[i].properties[j].key); - free(ini_file->sections[i].properties[j].value); - } -#endif - free(ini_file->sections[i].properties); + ini_section_free(&ini_file->sections[i]); } + ini_section_free(&ini_file->global_section); free(ini_file->sections); free(ini_file); } @@ -102,26 +101,25 @@ void ini_section_print_to(const struct Ini_Section *const ini_section, FILE *con if (ini_section == NULL) { return; } - fprintf(sink, "[%s]\n", ini_section->name); + if ((ini_section->name != NULL) && (ini_section->name[0] != '\0')) { + fprintf(sink, "[%s]\n", ini_section->name); + } for (property_index = 0; property_index < ini_section->properties_size; property_index++) { fprintf(sink, "%s = %s\n", ini_section->properties[property_index].key, ini_section->properties[property_index].value); } } void ini_file_print_to(const struct Ini_File *const ini_file, FILE *const sink) { - size_t section_index, property_index; + size_t section_index; if (ini_file == NULL) { return; } + if (ini_file->global_section.properties_size > 0) { + ini_section_print_to(&ini_file->global_section, sink); + putchar('\n'); + } for (section_index = 0; section_index < ini_file->sections_size; section_index++) { - if (section_index != 0) { - fprintf(sink, "[%s]\n", ini_file->sections[section_index].name); - } - for (property_index = 0; property_index < ini_file->sections[section_index].properties_size; property_index++) { - fprintf(sink, "%s = %s\n", - ini_file->sections[section_index].properties[property_index].key, - ini_file->sections[section_index].properties[property_index].value); - } + ini_section_print_to(&ini_file->sections[section_index], sink); putchar('\n'); } } @@ -137,6 +135,7 @@ char *ini_file_error_to_string(const enum Ini_File_Errors error) { "A section name was not provided", "A key was not provided", "A value was not provided", + "This key already exists", "Didn't found the requested section", "Didn't found the requested property", "The requested property is not a valid integer number", @@ -152,11 +151,25 @@ char *ini_file_error_to_string(const enum Ini_File_Errors error) { /* This function is usefull for debug purposes */ void ini_file_info(const struct Ini_File *const ini_file) { - size_t siz, i, allocs = 1, properties = 0; + size_t siz, i, allocs = 1, properties = 0, sections = ini_file->sections_size; if (ini_file == NULL) { return; } siz = sizeof(*ini_file) + sizeof(*ini_file->sections) * ini_file->sections_capacity; + if (ini_file->global_section.properties_size > 0) { +#ifndef USE_CUSTOM_STRING_ALLOCATOR + size_t j; + for (j = 0; j < ini_file->global_section.properties_size; j++) { + siz += 1 + strlen(ini_file->global_section.properties[j].key); + siz += 1 + strlen(ini_file->global_section.properties[j].value); + } + allocs += 2 * ini_file->global_section.properties_size; +#endif + properties += ini_file->global_section.properties_size; + siz += sizeof(*ini_file->global_section.properties) * ini_file->global_section.properties_capacity; + allocs++; + sections++; + } if (ini_file->sections_size > 0) { allocs++; } @@ -186,7 +199,7 @@ void ini_file_info(const struct Ini_File *const ini_file) { allocs++; } } - printf("Sections: %lu\n", ini_file->sections_size); + printf("Sections: %lu\n", sections); printf("Properties: %lu\n", properties); printf("Allocated chunks: %lu\n", allocs); printf("Memory used: %lu bytes\n", siz); @@ -246,9 +259,6 @@ static int ini_file_parse_handle_error(Ini_File_Error_Callback callback, const c return 0; } -/* TODO: Check for repeated section and key names? */ -/* TODO: Sort the sections and keys? This would allow us to use binary search */ - /* Remember to free the memory allocated for the returned ini file structure */ struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callback callback) { enum Ini_File_Errors error; @@ -286,7 +296,7 @@ struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callb name = cursor; advance_string_until(&cursor, "]#;\r\n"); if (*cursor != ']') { - if (ini_file_parse_handle_error(callback, filename, line_number, (size_t)(cursor-line+1), line, ini_expected_clocing_bracket) != 0) { + if (ini_file_parse_handle_error(callback, filename, line_number, (size_t)(cursor-line+1), line, ini_expected_closing_bracket) != 0) { goto ini_file_parse_error; } continue; @@ -346,152 +356,96 @@ ini_file_parse_error: return NULL; } -static size_t max_size(const size_t a, const size_t b) { - return ((a > b) ? a : b); -} - -enum Ini_File_Errors ini_file_add_section_sized(struct Ini_File *const ini_file, const char *const name, const size_t name_len) { - struct Ini_Section *ini_section; - if (ini_file == NULL) { - return ini_invalid_parameters; - } - if ((name == NULL) || (name_len == 0)) { - return ini_section_not_provided; - } - /* Check if we need expand our array of sections */ - if ((ini_file->sections_size + 1) >= ini_file->sections_capacity) { - const size_t new_cap = max_size(2 * ini_file->sections_capacity, INITIAL_SECTIONS_CAPACITY); - struct Ini_Section *const new_sections = realloc(ini_file->sections, new_cap * sizeof(struct Ini_Section)); - if (new_sections == NULL) { - return ini_allocation; - } - ini_file->sections = new_sections; - ini_file->sections_capacity = new_cap; - } - /* Insert a new section at the end of the array */ - ini_section = &ini_file->sections[ini_file->sections_size]; - memset(ini_section, 0, sizeof(struct Ini_Section)); - ini_section->name = copy_sized_string(ini_file, name, name_len); - if (ini_section->name == NULL) { - return ini_allocation; - } - ini_file->sections_size++; - return ini_no_error; -} - -enum Ini_File_Errors ini_file_add_section(struct Ini_File *const ini_file, const char *const name) { - if (name == NULL) { - return ini_section_not_provided; +/* This function compares a sized-string str1 with a null-terminated string str2 */ +static int compare_sized_str_to_cstr(const char* str1, const char* str2, size_t len1) { + const int comp = strncmp(str1, str2, len1); + /* If str1 is equal to the first len characters of str2, + * but str2 is longer than len characters, str1 is considered + * less than str2 */ + if ((comp == 0) && (str2[len1] > '\0')) { + return -1; } - return ini_file_add_section_sized(ini_file, name, strlen(name)); + return comp; } -enum Ini_File_Errors ini_file_add_property_sized(struct Ini_File *const ini_file, const char *const key, const size_t key_len, const char *const value, const size_t value_len) { - struct Ini_Section *ini_section; - struct Key_Value_Pair *property; - if (ini_file == NULL) { - return ini_invalid_parameters; - } - if ((key == NULL) || (key_len == 0)) { - return ini_key_not_provided; - } - if ((value == NULL) || (value_len == 0)) { - return ini_value_not_provided; - } - if (ini_file->sections_size == 0) { - return ini_allocation; - } - /* Insert the new property at the last section */ - ini_section = &ini_file->sections[ini_file->sections_size - 1]; - if ((ini_section->properties_size + 1) >= ini_section->properties_capacity) { - const size_t new_cap = max_size(2 * ini_section->properties_capacity, INITIAL_PROPERTIES_CAPACITY); - struct Key_Value_Pair *const new_properties = realloc(ini_section->properties, new_cap * sizeof(struct Key_Value_Pair)); - if (new_properties == NULL) { - return ini_allocation; +/* Binary search in the array of sections */ +static enum Ini_File_Errors ini_file_find_section_index(struct Ini_File *const ini_file, const char *const section, const size_t section_len, size_t *const index) { + size_t low = 0; + size_t high = ini_file->sections_size - 1; + while ((low <= high) && (high < ini_file->sections_size)) { + int comp; + *index = (low + high) / 2; + comp = compare_sized_str_to_cstr(section, ini_file->sections[*index].name, section_len); + if (comp < 0) { + high = *index - 1; + } else if (comp > 0) { + low = *index + 1; + } else { + return ini_no_error; } - ini_section->properties = new_properties; - ini_section->properties_capacity = new_cap; } - property = &ini_section->properties[ini_section->properties_size]; - property->key = copy_sized_string(ini_file, key, key_len); - if (property->key == NULL) { - return ini_allocation; - } - property->value = copy_sized_string(ini_file, value, value_len); - if (property->value == NULL) { -#ifndef USE_CUSTOM_STRING_ALLOCATOR - free(property->key); -#endif - return ini_allocation; - } - ini_section->properties_size++; - return ini_no_error; -} - -enum Ini_File_Errors ini_file_add_property(struct Ini_File *const ini_file, const char *const key, const char *const value) { - if (key == NULL) { - return ini_key_not_provided; - } - if (value == NULL) { - return ini_value_not_provided; - } - return ini_file_add_property_sized(ini_file, key, strlen(key), value, strlen(value)); + /* Didn't found the requested section, so return the correct index to insert the new element, keeping the order of the array */ + *index = low; + return ini_no_such_section; } -enum Ini_File_Errors ini_file_save(const struct Ini_File *const ini_file, const char *const filename) { - FILE *file; - if (ini_file == NULL) { - return ini_invalid_parameters; - } - file = fopen(filename, "wb"); - if (file == NULL) { - return ini_couldnt_open_file; +/* Binary search in the array of properties */ +static enum Ini_File_Errors ini_file_find_key_index(struct Ini_Section *const ini_section, const char *const key, const size_t key_len, size_t *const index) { + size_t low = 0; + size_t high = ini_section->properties_size - 1; + while ((low <= high) && (high < ini_section->properties_size)) { + int comp; + *index = (low + high) / 2; + comp = compare_sized_str_to_cstr(key, ini_section->properties[*index].key, key_len); + if (comp < 0) { + high = *index - 1; + } else if (comp > 0) { + low = *index + 1; + } else { + return ini_no_error; + } } - ini_file_print_to(ini_file, file); - fclose(file); - return ini_no_error; + /* Didn't found the requested key, so return the correct index to insert the new element, keeping the order of the array */ + *index = low; + return ini_no_such_section; } enum Ini_File_Errors ini_file_find_section(struct Ini_File *const ini_file, const char *const section, struct Ini_Section **ini_section) { + enum Ini_File_Errors error; size_t section_index; - if ((ini_file == NULL) || (section == NULL) || (ini_section == NULL)) { + if ((ini_file == NULL) || (ini_section == NULL)) { return ini_invalid_parameters; } - if (strlen(section) == 0) { - return ini_invalid_parameters; + if ((section == NULL) || (section[0] == '\0')) { + *ini_section = &ini_file->global_section; + return ini_no_error; } - for (section_index = 0; section_index < ini_file->sections_size; section_index++) { - if (strcmp(ini_file->sections[section_index].name, section) == 0) { - *ini_section = &ini_file->sections[section_index]; - return ini_no_error; - } + error = ini_file_find_section_index(ini_file, section, strlen(section), §ion_index); + if (error == ini_no_error) { + *ini_section = &ini_file->sections[section_index]; } - /* Didn't found the requested section */ - return ini_no_such_section; + return error; } enum Ini_File_Errors ini_file_find_property(struct Ini_File *const ini_file, const char *const section, const char *const key, char **value) { + enum Ini_File_Errors error; struct Ini_Section *ini_section; size_t property_index; - enum Ini_File_Errors error; - if ((ini_file == NULL) || (section == NULL) || (key == NULL) || (value == NULL)) { + if ((ini_file == NULL) || (key == NULL) || (value == NULL)) { return ini_invalid_parameters; } - if (strlen(key) == 0) { + if (key[0] == '\0') { return ini_invalid_parameters; } error = ini_file_find_section(ini_file, section, &ini_section); if (error != ini_no_error) { return error; } - for (property_index = 0; property_index < ini_section->properties_size; property_index++) { - if (strcmp(ini_section->properties[property_index].key, key) == 0) { - *value = ini_section->properties[property_index].value; - return ini_no_error; - } + error = ini_file_find_key_index(ini_section, key, strlen(key), &property_index); + if (error == ini_no_error) { + *value = ini_section->properties[property_index].value; } - /* Didn't found the requested property */ - return ini_no_such_property; + return error; } enum Ini_File_Errors ini_file_find_integer(struct Ini_File *const ini_file, const char *const section, const char *const key, long *integer) { @@ -551,6 +505,127 @@ enum Ini_File_Errors ini_file_find_float(struct Ini_File *const ini_file, const return ini_no_error; } +static size_t max_size(const size_t a, const size_t b) { + return ((a > b) ? a : b); +} + +enum Ini_File_Errors ini_file_add_section_sized(struct Ini_File *const ini_file, const char *const name, const size_t name_len) { + size_t section_index; + char *copied_name; + if (ini_file == NULL) { + return ini_invalid_parameters; + } + if ((name == NULL) || (name_len == 0)) { + return ini_section_not_provided; + } + if (ini_file_find_section_index(ini_file, name, name_len, §ion_index) == ini_no_error) { + /* There is already a section with that name so we just update the current section */ + ini_file->current_section = &ini_file->sections[section_index]; + return ini_no_error; + } + /* Check if we need expand our array of sections */ + if ((ini_file->sections_size + 1) >= ini_file->sections_capacity) { + const size_t new_cap = max_size(2 * ini_file->sections_capacity, INITIAL_SECTIONS_CAPACITY); + struct Ini_Section *const new_sections = realloc(ini_file->sections, new_cap * sizeof(struct Ini_Section)); + if (new_sections == NULL) { + return ini_allocation; + } + ini_file->sections = new_sections; + ini_file->sections_capacity = new_cap; + } + /* Allocates memory to store the section name */ + copied_name = copy_sized_string(ini_file, name, name_len); + if (copied_name == NULL) { + return ini_allocation; + } + /* Updates the current section */ + ini_file->current_section = &ini_file->sections[section_index]; + /* Moves the sections to insert the new section in the middle, keeping the array sorted by names */ + memmove((ini_file->current_section + 1), ini_file->current_section, (ini_file->sections_size - section_index)*sizeof(struct Ini_Section)); + memset(ini_file->current_section, 0, sizeof(struct Ini_Section)); + ini_file->current_section->name = copied_name; + ini_file->sections_size++; + return ini_no_error; +} + +enum Ini_File_Errors ini_file_add_section(struct Ini_File *const ini_file, const char *const name) { + if (name == NULL) { + return ini_section_not_provided; + } + return ini_file_add_section_sized(ini_file, name, strlen(name)); +} + +enum Ini_File_Errors ini_file_add_property_sized(struct Ini_File *const ini_file, const char *const key, const size_t key_len, const char *const value, const size_t value_len) { + size_t property_index; + struct Key_Value_Pair *property; + char *copied_key, *copied_value; + if (ini_file == NULL) { + return ini_invalid_parameters; + } + if ((key == NULL) || (key_len == 0)) { + return ini_key_not_provided; + } + if ((value == NULL) || (value_len == 0)) { + return ini_value_not_provided; + } + if (ini_file_find_key_index(ini_file->current_section, key, key_len, &property_index) == ini_no_error) { + /* There is already a property with that key name, which is not allowed */ + return ini_repeated_key; + } + if ((ini_file->current_section->properties_size + 1) >= ini_file->current_section->properties_capacity) { + const size_t new_cap = max_size(2 * ini_file->current_section->properties_capacity, INITIAL_PROPERTIES_CAPACITY); + struct Key_Value_Pair *const new_properties = realloc(ini_file->current_section->properties, new_cap * sizeof(struct Key_Value_Pair)); + if (new_properties == NULL) { + return ini_allocation; + } + ini_file->current_section->properties = new_properties; + ini_file->current_section->properties_capacity = new_cap; + } + copied_key = copy_sized_string(ini_file, key, key_len); + if (copied_key == NULL) { + return ini_allocation; + } + copied_value = copy_sized_string(ini_file, value, value_len); + if (copied_value == NULL) { +#ifndef USE_CUSTOM_STRING_ALLOCATOR + free(copied_key); +#endif + return ini_allocation; + } + property = &ini_file->current_section->properties[property_index]; + /* Moves the sections to insert the new section in the middle, keeping the array sorted by names */ + memmove((property + 1), property, (ini_file->current_section->properties_size - property_index)*sizeof(struct Key_Value_Pair)); + /* Update the values to the new property */ + property->key = copied_key; + property->value = copied_value; + ini_file->current_section->properties_size++; + return ini_no_error; +} + +enum Ini_File_Errors ini_file_add_property(struct Ini_File *const ini_file, const char *const key, const char *const value) { + if (key == NULL) { + return ini_key_not_provided; + } + if (value == NULL) { + return ini_value_not_provided; + } + return ini_file_add_property_sized(ini_file, key, strlen(key), value, strlen(value)); +} + +enum Ini_File_Errors ini_file_save(const struct Ini_File *const ini_file, const char *const filename) { + FILE *file; + if (ini_file == NULL) { + return ini_invalid_parameters; + } + file = fopen(filename, "wb"); + if (file == NULL) { + return ini_couldnt_open_file; + } + ini_file_print_to(ini_file, file); + fclose(file); + return ini_no_error; +} + /*------------------------------------------------------------------------------ * END *------------------------------------------------------------------------------ |
