src/properties.c

changeset 928
d2d42cb1d59e
parent 924
3c90dfc35f06
child 929
192a440b99df
--- a/src/properties.c	Sun Oct 13 15:19:12 2024 +0200
+++ b/src/properties.c	Sun Oct 13 16:44:29 2024 +0200
@@ -92,6 +92,7 @@
     char *dest = prop->buf + prop->buf_size;
     memcpy(dest, src, len);
     prop->buf_size += len;
+    prop->text_pos += len;
     return 0;
 }
 
@@ -137,7 +138,7 @@
     prop->flags |= CX_PROPERTIES_FLAG_USE_STACK;
 }
 
-enum cx_properties_status cxPropertiesNext(
+CxPropertiesStatus cxPropertiesNext(
         CxProperties *prop,
         cxstring *key,
         cxstring *value
@@ -171,7 +172,7 @@
             prop->text_pos = 0;
             prop->buf_size = 0;
 
-            enum cx_properties_status result;
+            CxPropertiesStatus result;
             result = cxPropertiesNext(prop, key, value);
 
             // restore original buffer
@@ -199,6 +200,9 @@
             }
         } else {
             // still not enough data
+            if (cx_properties_rescue_input(prop)) {
+                return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+            }
             return CX_PROPERTIES_INCOMPLETE_DATA;
         }
     }
@@ -241,6 +245,9 @@
 
         if (c != '\n') {
             // we don't have enough data for a line
+            if (cx_properties_rescue_input(prop)) {
+                return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+            }
             return CX_PROPERTIES_INCOMPLETE_DATA;
         }
 
@@ -284,3 +291,167 @@
     assert(prop->text_pos == prop->text_size);
     return CX_PROPERTIES_NO_DATA;
 }
+
+static int cx_properties_sink_map(
+        __attribute__((__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 = cx_map_put_cxstr(map, key, v.ptr);
+    if (r != 0) cx_strfree_a(alloc, &v);
+    return r;
+}
+
+CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
+    CxPropertiesSink sink;
+    sink.sink = map;
+    sink.data = cxDefaultAllocator;
+    sink.sink_func = cx_properties_sink_map;
+    return sink;
+}
+
+static int cx_properties_read_string(
+        CxProperties *prop,
+        CxPropertiesSource *src,
+        cxstring *target
+) {
+    if (prop->text == 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;
+    }
+    return 0;
+}
+
+static int cx_properties_read_file(
+        __attribute__((__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(src->src);
+}
+
+static int cx_properties_read_init_file(
+        __attribute__((__unused__)) CxProperties *prop,
+        CxPropertiesSource *src
+) {
+    src->data_ptr = malloc(src->data_size);
+    if (src->data_ptr == NULL) return 1;
+    return 0;
+}
+
+static void cx_properties_read_clean_file(
+        __attribute__((__unused__)) CxProperties *prop,
+        CxPropertiesSource *src
+) {
+    free(src->data_ptr);
+}
+
+CxPropertiesSource cxPropertiesStringSource(cxstring str) {
+    CxPropertiesSource src;
+    src.src = (void*) str.ptr;
+    src.data_size = str.length;
+    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.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.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.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;
+        }
+    }
+
+    // transfer the data from the source to the sink
+    CxPropertiesStatus status;
+    bool found = false;
+    while (true) {
+        // read input
+        cxstring input;
+        if (source.read_func(prop, &source, &input)) {
+            status = CX_PROPERTIES_READ_FAILED;
+            break;
+        }
+
+        // no more data - break
+        if (input.length == 0) {
+            status = found ? CX_PROPERTIES_NO_ERROR : CX_PROPERTIES_NO_DATA;
+            break;
+        }
+
+        // set the input buffer and read the k/v-pairs
+        cxPropertiesInput(prop, input.ptr, input.length);
+
+        CxPropertiesStatus kv_status;
+        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;
+                }
+            }
+        } 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);
+    }
+
+    return status;
+}

mercurial