--- a/src/properties.c Sun Dec 07 15:33:16 2025 +0100 +++ b/src/properties.c Sun Dec 07 15:34:46 2025 +0100 @@ -29,6 +29,8 @@ #include "cx/properties.h" #include <assert.h> +#include <stdio.h> +#include <string.h> const CxPropertiesConfig cx_properties_config_default = { '=', @@ -241,180 +243,89 @@ return CX_PROPERTIES_NO_DATA; } -static int cx_properties_sink_map( - cx_attr_unused CxProperties *prop, - CxPropertiesSink *sink, - cxstring key, - cxstring value -) { - CxMap *map = sink->sink; - CxAllocator *alloc = sink->data; - cxmutstr v = cx_strdup_a(alloc, value); - int r = cxMapPut(map, key, v.ptr); - if (r != 0) cx_strfree_a(alloc, &v); - return r; -} +#ifndef CX_PROPERTIES_LOAD_FILL_SIZE +#define CX_PROPERTIES_LOAD_FILL_SIZE 1024 +#endif +const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE; +#ifndef CX_PROPERTIES_LOAD_BUF_SIZE +#define CX_PROPERTIES_LOAD_BUF_SIZE 256 +#endif +const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE; -CxPropertiesSink cxPropertiesMapSink(CxMap *map) { - CxPropertiesSink sink; - sink.sink = map; - sink.data = (void*) cxDefaultAllocator; - sink.sink_func = cx_properties_sink_map; - return sink; -} +CxPropertiesStatus cx_properties_load(CxPropertiesConfig config, + cxstring filename, CxMap *target) { + // sanity check for the map + const bool use_cstring = cxCollectionStoresPointers(target); + if (!use_cstring && cxCollectionElementSize(target) != sizeof(cxmutstr)) { + return CX_PROPERTIES_MAP_ERROR; + } -static int cx_properties_read_string( - CxProperties *prop, - CxPropertiesSource *src, - cxstring *target -) { - if (prop->input.space == src->src) { - // when the input buffer already contains the string - // we have nothing more to provide - target->length = 0; - } else { - target->ptr = src->src; - target->length = src->data_size; + // create a duplicate to guarantee zero-termination + cxmutstr fname = cx_strdup(filename); + if (fname.ptr == NULL) { + return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } - return 0; -} -static int cx_properties_read_file( - cx_attr_unused CxProperties *prop, - CxPropertiesSource *src, - cxstring *target -) { - target->ptr = src->data_ptr; - target->length = fread(src->data_ptr, 1, src->data_size, src->src); - return ferror((FILE*)src->src); -} + // open the file + FILE *f = fopen(fname.ptr, "r"); + if (f == NULL) { + cx_strfree(&fname); + return CX_PROPERTIES_FILE_ERROR; + } -static int cx_properties_read_init_file( - cx_attr_unused CxProperties *prop, - CxPropertiesSource *src -) { - src->data_ptr = cxMallocDefault(src->data_size); - if (src->data_ptr == NULL) return 1; - return 0; -} + // initialize the parser + char linebuf[cx_properties_load_buf_size]; + char fillbuf[cx_properties_load_fill_size]; + CxPropertiesStatus status; + CxProperties parser; + cxPropertiesInit(&parser, config); + cxPropertiesUseStack(&parser, linebuf, cx_properties_load_buf_size); -static void cx_properties_read_clean_file( - cx_attr_unused CxProperties *prop, - CxPropertiesSource *src -) { - cxFreeDefault(src->data_ptr); -} - -CxPropertiesSource cxPropertiesStringSource(cxstring str) { - CxPropertiesSource src; - src.src = (void*) str.ptr; - src.data_size = str.length; - src.data_ptr = NULL; - src.read_func = cx_properties_read_string; - src.read_init_func = NULL; - src.read_clean_func = NULL; - return src; -} - -CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) { - CxPropertiesSource src; - src.src = (void*) str; - src.data_size = len; - src.data_ptr = NULL; - src.read_func = cx_properties_read_string; - src.read_init_func = NULL; - src.read_clean_func = NULL; - return src; -} - -CxPropertiesSource cxPropertiesCstrSource(const char *str) { - CxPropertiesSource src; - src.src = (void*) str; - src.data_size = strlen(str); - src.data_ptr = NULL; - src.read_func = cx_properties_read_string; - src.read_init_func = NULL; - src.read_clean_func = NULL; - return src; -} - -CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) { - CxPropertiesSource src; - src.src = file; - src.data_size = chunk_size; - src.data_ptr = NULL; - src.read_func = cx_properties_read_file; - src.read_init_func = cx_properties_read_init_file; - src.read_clean_func = cx_properties_read_clean_file; - return src; -} - -CxPropertiesStatus cxPropertiesLoad( - CxProperties *prop, - CxPropertiesSink sink, - CxPropertiesSource source -) { - assert(source.read_func != NULL); - assert(sink.sink_func != NULL); - - // initialize reader - if (source.read_init_func != NULL) { - if (source.read_init_func(prop, &source)) { - return CX_PROPERTIES_READ_INIT_FAILED; // LCOV_EXCL_LINE + // read/fill/parse loop + status = CX_PROPERTIES_NO_DATA; + while (true) { + size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f); + if (ferror(f)) { + status = CX_PROPERTIES_FILE_ERROR; + break; + } + if (r == 0) { + break; + } + if (cxPropertiesFilln(&parser, fillbuf, r)) { + status = CX_PROPERTIES_BUFFER_ALLOC_FAILED; + break; + } + cxstring key, value; + while (true) { + status = cxPropertiesNext(&parser, &key, &value); + if (status != CX_PROPERTIES_NO_ERROR) { + break; + } else { + cxmutstr v = cx_strdup(value); + if (v.ptr == NULL) { + status = CX_PROPERTIES_MAP_ERROR; + break; + } + void *mv = use_cstring ? (void*)v.ptr : &v; + if (cxMapPut(target, key, mv)) { + cx_strfree(&v); + status = CX_PROPERTIES_MAP_ERROR; + break; + } + } + } + if (status > CX_PROPERTIES_OK) { + break; + } else if (status == CX_PROPERTIES_NO_DATA) { + // we want to report this case differently in this function + status = CX_PROPERTIES_NO_ERROR; } } - // transfer the data from the source to the sink - CxPropertiesStatus status; - CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA; - bool found = false; - while (true) { - // read input - cxstring input; - if (source.read_func(prop, &source, &input)) { // LCOV_EXCL_START - status = CX_PROPERTIES_READ_FAILED; - break; - } // LCOV_EXCL_STOP - - // no more data - break - if (input.length == 0) { - if (found) { - // something was found, check the last kv_status - if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) { - status = CX_PROPERTIES_INCOMPLETE_DATA; - } else { - status = CX_PROPERTIES_NO_ERROR; - } - } else { - // nothing found - status = CX_PROPERTIES_NO_DATA; - } - break; - } - - // set the input buffer and read the k/v-pairs - cxPropertiesFill(prop, input); - - do { - cxstring key, value; - kv_status = cxPropertiesNext(prop, &key, &value); - if (kv_status == CX_PROPERTIES_NO_ERROR) { - found = true; - if (sink.sink_func(prop, &sink, key, value)) { - kv_status = CX_PROPERTIES_SINK_FAILED; // LCOV_EXCL_LINE - } - } - } while (kv_status == CX_PROPERTIES_NO_ERROR); - - if (kv_status > CX_PROPERTIES_OK) { - status = kv_status; - break; - } - } - - if (source.read_clean_func != NULL) { - source.read_clean_func(prop, &source); - } - + // cleanup and exit + fclose(f); + cxPropertiesDestroy(&parser); + cx_strfree(&fname); return status; }