diff --git a/libbdc/CMakeLists.txt b/libbdc/CMakeLists.txt
index 3a0a1b2d8eceab000d421bb17aabdc60a278f7d5..0ecb214949674ba5f44d8fbb8ab7623700999306 100644
--- a/libbdc/CMakeLists.txt
+++ b/libbdc/CMakeLists.txt
@@ -32,7 +32,11 @@ endif ()
 
 
 add_subdirectory(bdc_cross_platform)
+add_subdirectory(bdc_hashtable)
+add_subdirectory(bdc_io)
+add_subdirectory(bdc_logging)
 add_subdirectory(bdc_memory)
+add_subdirectory(bdc_strings)
 add_subdirectory(bdc_time)
 
 add_library(bdc STATIC "")
@@ -40,6 +44,10 @@ add_library(bdc STATIC "")
 set_target_properties(bdc PROPERTIES LINKER_LANGUAGE C)
 set_property(TARGET bdc PROPERTY POSITION_INDEPENDENT_CODE ON)
 target_link_libraries(bdc
+                      bdc_hashtable
+                      bdc_io
+                      bdc_logging
                       bdc_memory
+                      bdc_strings
                       bdc_time)
 
diff --git a/libbdc/bdc_hashtable/CMakeLists.txt b/libbdc/bdc_hashtable/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5e11b831162c1c676de5c24a1a349ad2112d71aa
--- /dev/null
+++ b/libbdc/bdc_hashtable/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_library(bdc_hashtable STATIC bdc_hashtable.c bdc_hashtable.h)
+set_property(TARGET bdc_hashtable PROPERTY POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(bdc_hashtable bdc_logging)
+
+
+
+add_executable(bdc_hashtable_test tests/test_bdc_hashtable.c)
+
+
+target_link_libraries(bdc_hashtable_test bdc_strings)
\ No newline at end of file
diff --git a/libbdc/bdc_hashtable/bdc_hashtable.c b/libbdc/bdc_hashtable/bdc_hashtable.c
new file mode 100644
index 0000000000000000000000000000000000000000..74975d3090ff32860e0bb7fa204364a6e5a7022d
--- /dev/null
+++ b/libbdc/bdc_hashtable/bdc_hashtable.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_hashtable.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "bdc_hashtable.h"
+#include "bdc_logging/logging.h"
+#include "bdc_memory/bdc_memory.h"
+
+
+struct bdc_value {
+        union {
+                char *string;
+                int64_t int64;
+                double double_value;
+                void *pointer;
+        };
+        enum bdc_value_type type;
+};
+struct bdc_item {
+        char *key;
+        struct bdc_value *value;
+        struct bdc_item *next;
+};
+struct bdc_hash_table {
+        size_t item_count;
+        size_t bucket_count;
+        size_t *items_per_bucket;
+        //size_t max_items_pro_bucket;
+        struct bdc_item **items;
+
+};
+
+
+
+// FNV-1a hash, see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
+#define OFFSET 14695981039346656037UL
+#define PRIME 1099511628211UL
+
+#define DEBUG(...)
+
+// FIXME: add function to return keys
+
+uint64_t hash_function(const char *key)
+{
+        uint64_t hash = OFFSET;
+        while (*key != '\0') {
+                hash ^= (uint64_t) (unsigned char) *key;
+                hash *= PRIME;
+                key++;
+        }
+        return hash;
+}
+
+
+__attribute__((unused)) size_t hash_index(const char *key, size_t buckets)
+{
+        return (size_t) hash_function(key) & (buckets - 1);
+}
+
+
+size_t hash_idx_from_hash(uint64_t hash, size_t buckets)
+{
+        return hash & (buckets - 1);
+}
+
+
+
+struct bdc_hash_table *bdc_intern_new_hash_table(size_t buckets)
+{
+        struct bdc_hash_table *table = calloc_or_die(1, sizeof(struct bdc_hash_table));
+        table->bucket_count = buckets;
+        table->items = calloc_or_die(table->bucket_count, sizeof(struct bdc_item *));
+        table->item_count = 0;
+        //table->max_items_pro_bucket = 0;
+
+        table->items_per_bucket = calloc_or_die(table->bucket_count, sizeof(size_t));
+        DEBUG("Creating hash table with %lu buckets\n", table->bucket_count)
+
+        for (size_t i = 0; i < table->bucket_count; i++) {
+                table->items_per_bucket[i] = 0;
+                table->items[i] = NULL;
+        }
+
+        return table;
+}
+
+
+__attribute__((unused)) struct bdc_hash_table *new_hash_table(void)
+{
+        return bdc_intern_new_hash_table(MIN_SIZE);
+}
+
+
+void destroy_value(struct bdc_value **value)
+{
+        if (value == NULL || *value == NULL) return;
+        struct bdc_value *this = *value;
+        if(this->type == BDC_STRING) {
+                log_message(LOG_DEBUG, "freeing string value '%s'", this->string);
+                free(this->string);
+                this->string = NULL;
+        }
+        log_message(LOG_DEBUG, "freeing value");
+        free(this);
+        *value = NULL;
+
+}
+
+struct bdc_item *bdc_intern_get_entry(struct bdc_hash_table *table, const char *key, uint64_t hash)
+{
+        size_t hash_idx = hash_idx_from_hash(hash, table->bucket_count);
+        if (table->items[hash_idx] == NULL) return NULL;
+
+        struct bdc_item *entry = table->items[hash_idx];
+
+        if (0 == strcmp(entry->key, key)) return entry;
+
+        while (entry->next != NULL) {
+                entry = entry->next;
+                if (0 == strcmp(entry->key, key)) return entry;
+        }
+
+        return NULL;
+}
+
+
+struct bdc_item *get_entry(struct bdc_hash_table *table, const char *key)
+{
+        uint64_t hash = hash_function(key);
+
+        size_t hash_idx = hash & (table->bucket_count - 1);
+        if (table->items[hash_idx] == NULL) return NULL;
+
+        struct bdc_item *entry = table->items[hash_idx];
+        if (0 == strcmp(entry->key, key)) return entry;
+        while (entry->next != NULL) {
+                entry = entry->next;
+                if (0 == strcmp(entry->key, key)) return entry;
+        }
+
+        return NULL;
+}
+
+void destroy_item(struct bdc_item **item)
+{
+        if(item == NULL || *item == NULL) return;
+        struct bdc_item *this = *item;
+        destroy_value(&this->value);
+        free(this->key);
+        free(this);
+        *item = NULL;
+}
+
+bool remove_key(struct bdc_hash_table *table, const char *key)
+{
+        if (!bdc_key_in_hash_table(table, key)) return false;
+
+        uint64_t hash = hash_function(key);
+        size_t hash_idx = hash & (table->bucket_count - 1);
+
+        struct bdc_item *target = NULL;
+        struct bdc_item *entry = table->items[hash_idx];
+        if (0 == strcmp(entry->key, key)) {
+                target = entry;
+                table->items[hash_idx] = target->next;
+                table->items_per_bucket[hash_idx]--;
+                destroy_item(&target);
+                return true;
+        }
+        struct bdc_item *prev = NULL;
+        while (entry->next != NULL) {
+                prev = entry,
+                        entry = entry->next;
+                if (0 == strcmp(entry->key, key)) {
+                        target = entry;
+                        prev->next = target->next;
+                        table->items_per_bucket[hash_idx]--;
+                        free(entry);
+                        return true;
+                }
+        }
+        return false;
+}
+
+
+__attribute__((unused)) bool bdc_key_in_hash_table(struct bdc_hash_table *table, const char *key)
+{
+        return get_entry(table, key) != NULL;
+
+}
+
+
+struct bdc_value *bdc_get_value_from_key(struct bdc_hash_table *table, const char *key)
+{
+        struct bdc_item *entry = get_entry(table, key);
+        if (entry) {
+                return entry->value;
+        } else {
+                return NULL;
+        }
+}
+
+
+struct bdc_item *new_entry(const char *key, struct bdc_value *value)
+{
+        struct bdc_item *entry = calloc_or_die(1, sizeof(struct bdc_item));
+
+
+        entry->value = value;
+        log_message(LOG_DEBUG, "duplicating key '%s'", key);
+        entry->key = strdup(key);
+        entry->next = NULL;
+
+        return entry;
+}
+
+
+void print_key_val(struct bdc_item *entry)
+{
+        struct bdc_value *value = entry->value;
+        switch (value->type) {
+
+                case BDC_STRING:
+                        printf("\t\t%s -> '%s'\n", entry->key, value->string);
+                        break;
+                case BDC_INT64:
+                        printf("\t\t%s -> %ld\n", entry->key, value->int64);
+                        break;
+                case BDC_DOUBLE:
+                        printf("\t\t%s -> %f\n", entry->key, value->double_value);
+                        break;
+                case BDC_CUSTOM:
+                        printf("\t\t%s -> %p\n", entry->key, value->pointer);
+                        break;
+        }
+
+}
+
+
+__attribute__((unused)) void print_hash_table(const struct bdc_hash_table *table)
+{
+        printf("\n");
+        printf("Hash table: %lu buckets with %lu items\n", table->bucket_count,
+               table->item_count);
+        for (size_t i = 0; i < table->bucket_count; i++) {
+                if (table->items[i] == NULL) continue;
+                printf("\tentry %lu: %lu entries\n", i, table->items_per_bucket[i]);
+                size_t count = 1;
+                struct bdc_item *entry = table->items[i];
+                print_key_val(entry);
+                while (entry->next != NULL) {
+                        entry = entry->next;
+                        count++;
+                        print_key_val(entry);
+                }
+
+                if (count != table->items_per_bucket[i]) {
+                        printf("Error: mismatch between expected (%lu) and found entries (%lu)\n",
+                               table->items_per_bucket[i], i);
+                }
+        }
+        printf("\n");
+}
+
+
+
+
+
+struct bdc_item *bdc_intern_add_entry(struct bdc_hash_table *table, struct bdc_item *entry, uint64_t hash)
+{
+        size_t hash_idx = hash_idx_from_hash(hash, table->bucket_count);
+        entry->next = table->items[hash_idx];
+        table->items[hash_idx] = entry;
+        table->items_per_bucket[hash_idx] += 1;
+        table->item_count += 1;
+
+        return entry;
+}
+
+
+void rehash_table(struct bdc_hash_table *table)
+{
+        log_message(LOG_INFO, "%s: rehashing!", __func__);
+        struct bdc_hash_table *tmp = bdc_intern_new_hash_table(table->bucket_count * 2);
+
+        for (size_t i = 0; i < table->bucket_count; i++) {
+                if (table->items[i] == NULL) continue;
+                struct bdc_item *entry = table->items[i];
+                struct bdc_item *next = entry->next;
+                uint64_t hash = hash_function(entry->key);
+                bdc_intern_add_entry(tmp, entry, hash);
+
+                while (next) {
+                        entry = next;
+                        next = entry->next;
+                        hash = hash_function(entry->key);
+                        bdc_intern_add_entry(tmp, entry, hash);
+                }
+
+        }
+
+        free(table->items);
+        free(table->items_per_bucket);
+        table->items = tmp->items;
+        table->items_per_bucket = tmp->items_per_bucket;
+        table->bucket_count = tmp->bucket_count;
+
+        free(tmp);
+        tmp = NULL;
+
+}
+
+
+struct bdc_item *bdc_ht_add_string(struct bdc_hash_table *table, const char *key, const char *value)
+{
+        struct bdc_value *val = calloc(1, sizeof(struct bdc_value));
+        log_message(LOG_DEBUG, "duplicating value '%s", value);
+        val->string = strdup(value);
+        val->type = BDC_STRING;
+        return intern_bdc_add_entry(table, key, val);
+}
+
+
+struct bdc_item *bdc_ht_add_custom(struct bdc_hash_table *table, const char *key, void *value)
+{
+        struct bdc_value *val = calloc(1, sizeof(struct bdc_value));
+        val->pointer = value;
+        val->type = BDC_CUSTOM;
+        return intern_bdc_add_entry(table, key, val);
+}
+
+
+struct bdc_item *bdc_ht_add_int64(struct bdc_hash_table *table, const char *key, int64_t value)
+{
+        struct bdc_value *val = calloc(1, sizeof(struct bdc_value));
+        val->int64 = value;
+        val->type = BDC_INT64;
+        return intern_bdc_add_entry(table, key, val);
+}
+
+
+struct bdc_item *bdc_ht_add_double(struct bdc_hash_table *table, const char *key, double value)
+{
+        struct bdc_value *val = calloc(1, sizeof(struct bdc_value));
+        val->double_value = value;
+        val->type = BDC_DOUBLE;
+        return intern_bdc_add_entry(table, key, val);
+}
+
+
+struct bdc_item *intern_bdc_add_entry(struct bdc_hash_table *table, const char *key, struct bdc_value *value)
+{
+        uint64_t hash = hash_function(key);
+        size_t hash_idx = hash_idx_from_hash(hash, table->bucket_count);
+        switch (value->type) {
+                case BDC_STRING:
+                        log_message(LOG_DEBUG, "adding entry %s %s with hash %lu to bucket %lu", key, value->string,
+                                    hash_function(key), hash_idx);
+                        break;
+                case BDC_INT64:
+                        log_message(LOG_DEBUG, "adding entry %s %ld with hash %lu to bucket %lu", key, value->int64,
+                                    hash_function(key), hash_idx);
+                        break;
+                case BDC_DOUBLE:
+                        log_message(LOG_DEBUG, "adding entry %s %f with hash %lu to bucket %lu", key,
+                                    value->double_value,
+                                    hash_function(key), hash_idx);
+                        break;
+                case BDC_CUSTOM:
+                        log_message(LOG_DEBUG, "adding entry %s %p with hash %lu to bucket %lu", key, value->pointer,
+                                    hash_function(key), hash_idx);
+                        break;
+        }
+
+
+        struct bdc_item *entry = bdc_intern_get_entry(table, key, hash);
+
+        if (entry != NULL) {
+                log_message(LOG_DEBUG, "replacing key '%s'", entry->key);
+                destroy_value(&entry->value);
+                entry->value = value;
+
+                if (entry->value->type == BDC_STRING) {
+                        log_message(LOG_DEBUG, "setting value of '%s' to '%s'", key, entry->value->string);
+                }
+                return entry;
+        }
+        assert(entry == NULL);
+        entry = new_entry(key, value);
+        bdc_intern_add_entry(table, entry, hash);
+
+        if (table->items_per_bucket[hash_idx] > 2) {
+                rehash_table(table);
+        }
+
+        log_message(LOG_DEBUG, "added key '%s'\n", key);
+        return entry;
+}
+
+
+bool bdc_ht_get_int64_from_key(struct bdc_hash_table *table, const char *key, int64_t *result)
+{
+        struct bdc_value *value = bdc_get_value_from_key(table, key);
+        if (!value) return false;
+        if (value->type != BDC_INT64) return false;
+        *result = value->int64;
+        return true;
+}
+
+
+__attribute__((unused)) bool bdc_ht_get_double_from_key(struct bdc_hash_table *table, const char *key, double *result)
+{
+        struct bdc_value *value = bdc_get_value_from_key(table, key);
+        if (!value) return false;
+        if (value->type != BDC_DOUBLE) return false;
+        *result = value->double_value;
+        return true;
+}
+
+
+bool bdc_ht_get_string_from_key(struct bdc_hash_table *table, const char *key, char **result)
+{
+        struct bdc_value *value = bdc_get_value_from_key(table, key);
+        if (!value) return false;
+        if (value->type != BDC_STRING) return false;
+        *result = strdup(value->string);
+        return true;
+}
+
+void destroy_hash_table(struct bdc_hash_table **ht)
+{
+        if (ht == NULL || *ht == NULL) return;
+        struct bdc_hash_table *t = *ht;
+        size_t count = t->bucket_count;
+        for (size_t i = 0; i < count; i++){
+                struct bdc_item *item = t->items[i];
+                while(item){
+                        struct bdc_item *this = item;
+                        item = item->next;
+                        if (this->value->type == BDC_STRING) {
+                                log_message(LOG_DEBUG, "freeing item '%s' -> '%s'", this->key, this->value->string);
+                                free(this->value->string);
+                        }
+                        free(this->value);
+                        free(this->key);
+                        free(this);
+                        this = NULL;
+                }
+                free(item);
+        }
+        free(t->items_per_bucket);
+        free(t->items);
+        free(t);
+        *ht = NULL;
+}
\ No newline at end of file
diff --git a/libbdc/bdc_hashtable/bdc_hashtable.h b/libbdc/bdc_hashtable/bdc_hashtable.h
new file mode 100644
index 0000000000000000000000000000000000000000..9b7f9ecbde64d3f96bf361b60e451690a210b0da
--- /dev/null
+++ b/libbdc/bdc_hashtable/bdc_hashtable.h
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_hashtable.h
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#ifndef BDC_HASHTABLE_H
+#define BDC_HASHTABLE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#define MIN_SIZE 1024
+
+
+enum bdc_value_type {
+        BDC_STRING = 0,
+        BDC_INT64 = 1,
+        BDC_DOUBLE = 2,
+        BDC_CUSTOM
+};
+
+
+
+
+struct bdc_hash_table;
+struct bdc_value;
+
+bool remove_key(struct bdc_hash_table *table, const char *key);
+struct bdc_item *intern_bdc_add_entry(struct bdc_hash_table *table, const char *key, struct bdc_value *value);
+
+struct bdc_item *bdc_ht_add_string(struct bdc_hash_table *table, const char *key, const char *value);
+
+struct bdc_item *bdc_ht_add_custom(struct bdc_hash_table *table, const char *key, void *custom);
+
+struct bdc_item *bdc_ht_add_int64(struct bdc_hash_table *table, const char *key, int64_t value);
+
+struct bdc_item *bdc_ht_add_double(struct bdc_hash_table *table, const char *key, double value);
+
+__attribute__((unused)) struct bdc_hash_table *new_hash_table(void);
+
+__attribute__((unused)) void print_hash_table(const struct bdc_hash_table *table);
+
+struct bdc_value *bdc_get_value_from_key(struct bdc_hash_table *table, const char *key);
+
+__attribute__((unused)) bool bdc_key_in_hash_table(struct bdc_hash_table *table, const char *key);
+
+bool bdc_ht_get_int64_from_key(struct bdc_hash_table *table, const char *key, int64_t *result);
+
+bool bdc_ht_get_string_from_key(struct bdc_hash_table *table, const char *key, char **result);
+
+__attribute__((unused)) bool bdc_ht_get_double_from_key(struct bdc_hash_table *table, const char *key, double *result);
+void destroy_hash_table(struct bdc_hash_table **ht);
+
+#define bdc_ht_get_data_from_key(TABLE, KEY, RESULT) _GENERIC((RESULT), \
+        int64_t *: bdc_ht_get_int64_from_key,                           \
+        char **:   bdc_ht_get_string_from_key,                          \
+        double *:  bdc_ht_get_value_from_key,                           \
+)(TABLE, KEY, (RESULT))
+
+#define bdc_ht_add_entry(table, key, value) \
+   _Generic((value),\
+        int64_t: bdc_ht_add_int64,    \
+        int: bdc_ht_add_int64,              \
+        double: bdc_ht_add_double,          \
+        void *:  bdc_ht_add_custom, \
+        char *: bdc_ht_add_string)(table, key, value)
+
+
+// move to private later // FIXME
+void rehash_table(struct bdc_hash_table *table);
+#endif //BDC_HASHTABLE_H
diff --git a/libbdc/bdc_hashtable/tests/test_bdc_hashtable.c b/libbdc/bdc_hashtable/tests/test_bdc_hashtable.c
new file mode 100644
index 0000000000000000000000000000000000000000..9ebc91628a8d9e6117c8a2013d9d90966e28fa76
--- /dev/null
+++ b/libbdc/bdc_hashtable/tests/test_bdc_hashtable.c
@@ -0,0 +1,52 @@
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// test_bdc_hashtable.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include <stdlib.h>
+#include "bdc_hashtable/bdc_hashtable.h"
+#include "bdc_strings/bdc_strings.h"
+
+
+int main(int argc, char **argv)
+{
+        //logging_initialize(LOG_DEBUG, NULL, NULL, false);
+        struct bdc_hash_table *t = new_hash_table();
+        bdc_ht_add_string(t, "a", "b12345");
+        bdc_ht_add_string(t, "a", "c123123");
+        bdc_ht_add_string(t, "b", "d21542");
+        bdc_ht_add_string(t, "c", "d316315");
+        bdc_ht_add_string(t, "d", "d1326536");
+        bdc_ht_add_string(t, "a", "d316136");
+        remove_key(t, "a");
+        bdc_ht_add_string(t, "a", "e13616136136");
+        destroy_hash_table(&t);
+
+        struct string_substitution *s = new_substituter();
+        add_substitution(s, "%a", "<was a>");
+        add_substitution(s, "%b", "<was b>");
+        add_substitution(s, "%c", "<was c>");
+        const char *orig = "%c-%a-%b-%c-aa-%d";
+        char *x = substitute_string(orig, s);
+        printf("%s -> %s\n", orig, x);
+        destroy_substituter(&s);
+        free(x);
+        return 0;
+}
\ No newline at end of file
diff --git a/libbdc/bdc_io/CMakeLists.txt b/libbdc/bdc_io/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8436cfbab4603543f88f0eb6b673db7f9d84e809
--- /dev/null
+++ b/libbdc/bdc_io/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_library(bdc_io STATIC ""   ../bdc_cross_platform/bdc_cross_platform.h )
+
+target_sources(bdc_io
+               PRIVATE
+               bdc_io.c
+               bdc_paths.c
+               PUBLIC
+               bdc_io.h
+)
+
+
+set_property(TARGET bdc_io PROPERTY POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(bdc_io bdc_strings)
diff --git a/src/cats_strings/io_text.c b/libbdc/bdc_io/bdc_io.c
similarity index 75%
rename from src/cats_strings/io_text.c
rename to libbdc/bdc_io/bdc_io.c
index 19cac9ba58ad60e2a038d0c0089dc7f534cb79f4..1a0c9313c44f045183b9dddb64cfc12c66adeecf 100644
--- a/src/cats_strings/io_text.c
+++ b/libbdc/bdc_io/bdc_io.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 //
-// io_text.c
+// bdc_io.c
 //
 // Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
 //
@@ -19,13 +19,12 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 
-#include "cats_global.h"
+#include <stdio.h>
 #include <assert.h>
 #include <string.h>
-#include <stdlib.h>
-#include <logging/logging.h>
-#include "cats_strings.h"
 
+#include "bdc_memory/bdc_memory.h"
+#include "bdc_io.h"
 
 char *read_single_line(FILE *file)
 {
@@ -33,9 +32,9 @@ char *read_single_line(FILE *file)
         char buffer[MAX_STRING_BUFF_LENGTH] = {0};
         fgets(buffer, MAX_STRING_BUFF_LENGTH, file);
         fflush(stdout);
-        char *result = right_trim(buffer);
 
-        if (strlen(result)) return strdup(result);
+
+        if (strlen(buffer)) return strdup(buffer);
         return NULL;
 }
 
@@ -47,7 +46,7 @@ char *slurp_file(const char *filename)
         FILE *file = fopen(filename, "r");
 
         if (!file) {
-                log_message(LOG_ERROR, "%s: File '%s' does not exist or could not be opened.", __func__, filename);
+                fprintf(stderr, "%s: File '%s' does not exist or could not be opened.\n", __func__, filename);
                 return NULL;
         }
 
@@ -56,15 +55,9 @@ char *slurp_file(const char *filename)
         long size = ftell(file);
         fseek(file, 0L, SEEK_SET);
 
-        char *buffer = malloc(size + 1);
-
-        if (!buffer) {
-                log_message(LOG_ERROR, "%s: failed to allocate memory", __func__);
-                exit(EXIT_FAILURE);
-        }
-
+        char *buffer = malloc_or_die(size + 1);
         fread(buffer, sizeof(char), size, file);
         fclose(file);
         buffer[size] = '\0';
         return buffer;
-}
+}
\ No newline at end of file
diff --git a/libbdc/bdc_io/bdc_io.h b/libbdc/bdc_io/bdc_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..54ade855606b54c1c5b6e0c02f1fe0ea8d3eeee3
--- /dev/null
+++ b/libbdc/bdc_io/bdc_io.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_io.h
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#ifndef BDC_IO_H
+#define BDC_IO_H
+
+#define MAX_STRING_BUFF_LENGTH 4096
+#if defined(WIN32) || defined (_WIN32)
+#define DIRECTORY_SEPARATOR "\\"
+#else
+#define DIRECTORY_SEPARATOR "/"
+#endif
+
+
+#include "bdc_strings/bdc_strings.h"
+
+char *slurp_file(const char *filename);
+char *read_single_line(FILE *file);
+
+#define ENSURE_FILE_OPENED(FILE_HANDLE, FILENAME)\
+if (! (FILE_HANDLE))\
+{\
+        fprintf(stderr, "ERROR: Couldn't open file %s in %s: %d (%s)\n", FILENAME, __FILE__, __LINE__, __func__);\
+        exit(EXIT_FAILURE);\
+}
+
+char *assemble_path(const struct string_array *path);
+
+char *assemble_filename(struct string_array *path, struct string_array *filename, char *field_separator, char *extension);
+
+bool check_and_create_directory_if_needed(const struct string_array *directory_path);
+
+bool directory_exists(const char *path);
+
+bool directory_writable(const char *path);
+
+void make_directory(const char *path);
+#endif //BDC_IO_H
diff --git a/libbdc/bdc_io/bdc_paths.c b/libbdc/bdc_io/bdc_paths.c
new file mode 100644
index 0000000000000000000000000000000000000000..586572d0bb15272d145522d3e30344f77843d605
--- /dev/null
+++ b/libbdc/bdc_io/bdc_paths.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_paths.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include <string.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "bdc_io.h"
+#include "bdc_cross_platform/bdc_cross_platform.h"
+
+#ifdef BDC_ON_WINDOWS
+#include <direct.h>
+#else
+#include <unistd.h>
+#endif
+
+
+
+bool directory_exists(const char *path)
+{
+        struct stat info;
+        int rc = stat(path, &info);
+        if (rc == 0 && info.st_mode & S_IFDIR) return true;
+
+        if (rc == 0) {
+                fprintf(stderr, "Path '%s' is not a directory. Exiting.\n", path);
+                exit(EXIT_FAILURE);
+        }
+
+        fprintf(stderr, "Directory '%s' does not exist\n", path);
+        return false;
+}
+
+
+bool directory_writable(const char *path)
+{
+        int rc = access(path, F_OK);
+
+        if (rc == 0) {
+                return true;
+        }
+
+        fprintf(stderr, "directory %s exists but cannot be accessed or written to.\n", path);
+        return false;
+}
+
+
+void make_directory(const char *path)
+{
+#ifndef BDC_ON_WINDOWS
+        int rc = mkdir(path, S_IRWXU);
+#else
+        int rc = _mkdir(path);
+#endif
+        if (rc == 0) return;
+
+        int error = errno;
+        fprintf(stderr, "unable to create directory %s: %s\n", path, strerror(error));
+        exit(EXIT_FAILURE);
+}
+
+
+char *assemble_path(const struct string_array *path)
+{
+        return string_array_paste(path, DIRECTORY_SEPARATOR);
+}
+
+
+char *
+assemble_filename(struct string_array *path, struct string_array *filename, char *field_separator, char *extension)
+{
+        char *name_string = string_array_paste(filename, field_separator);
+        char *result = NULL;
+
+        if (path && get_string_count(path) > 0) {
+                char *path_string = string_array_paste(path, DIRECTORY_SEPARATOR);
+                if (extension && strlen(extension)) {
+                        char *fn = compound_string(name_string, extension, ".");
+                        result = compound_string(path_string, fn, DIRECTORY_SEPARATOR);
+                        free(fn);
+                } else {
+                        result = compound_string(path_string, name_string, DIRECTORY_SEPARATOR);
+                }
+                free(path_string);
+        } else {
+
+                if (extension && strlen(extension)) {
+                        result = compound_string(name_string, extension, ".");
+                } else {
+                        result = strdup(name_string);
+                }
+        }
+
+        free(name_string);
+        return result;
+}
+
+
+bool check_and_create_directory_if_needed(const struct string_array *directory_path)
+{
+        if (directory_path == NULL || get_string_count(directory_path) == 0) {
+                fprintf(stderr, "Unable to check directory - no path specified\n");
+                exit(EXIT_FAILURE);
+        }
+
+        char *dir = assemble_path(directory_path);
+
+
+        if (!directory_exists(dir)) {
+                fprintf(stderr, "directory '%s' does not exist. Trying to create.\n", dir);
+                make_directory(dir);
+        }
+
+        bool writeable = directory_writable(dir);
+        free(dir);
+        return writeable;
+}
\ No newline at end of file
diff --git a/libbdc/bdc_logging/CMakeLists.txt b/libbdc/bdc_logging/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3391d9862697e0cd9c94b604e9546daecbbfdb0e
--- /dev/null
+++ b/libbdc/bdc_logging/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library(bdc_logging STATIC "")
+
+target_sources(bdc_logging
+               PRIVATE
+               logging.c
+               PUBLIC
+               logging.h
+               )
+
+target_include_directories(bdc_logging PUBLIC ".")
+target_link_libraries(bdc_logging bdc_memory bdc_time bdc_cross_platform)
+set_property(TARGET bdc_logging PROPERTY POSITION_INDEPENDENT_CODE ON)
\ No newline at end of file
diff --git a/libbdc/bdc_logging/README.md b/libbdc/bdc_logging/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..32e219e21dfd07c71180c652f6d5d2dac42b617b
--- /dev/null
+++ b/libbdc/bdc_logging/README.md
@@ -0,0 +1,10 @@
+# bdc_logging
+
+This is a library to handle logging.
+
+## Dependencies
+* bdc_memory
+* bdc_time 
+* bdc_cross_platform (header only)
+
+
diff --git a/src/logging/logging.c b/libbdc/bdc_logging/logging.c
similarity index 58%
rename from src/logging/logging.c
rename to libbdc/bdc_logging/logging.c
index a7c0ed515b4fce7d30b8e52093d057f932008926..73e5f822cbdc4302732d1a129280aa2bd665e70c 100644
--- a/src/logging/logging.c
+++ b/libbdc/bdc_logging/logging.c
@@ -21,27 +21,26 @@
 
 #include <stdbool.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <string.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include "logging.h"
+
 #include "bdc_memory/bdc_memory.h"
 #include "bdc_time/bdc_time.h"
-#include "misc/misc.h"
-
+#include "bdc_io/bdc_io.h"
+#include "bdc_logging/logging.h"
 
-enum cats_log_level internal_current_log_level = LOG_INFO;
+enum bdc_log_level internal_current_log_level = LOG_INFO;
 int32_t logging_mpi_rank = -1;
-struct bdc_time cats_logging_start_time;
-FILE *cats_log_file = NULL;
-char *cats_log_file_name = NULL;
+struct bdc_time bdc_logging_start_time;
+FILE *bdc_log_file = NULL;
+char *bdc_log_file_name = NULL;
 const char *logging_module_name = NULL;
 bool time_initialized = false;
 bool logging_quiet = false;
 
 
-const char *get_log_level_name(enum cats_log_level level)
+const char *get_log_level_name(enum bdc_log_level level)
 {
         switch (level) {
                 case LOG_DEBUG:
@@ -50,7 +49,6 @@ const char *get_log_level_name(enum cats_log_level level)
                         return "INFO";
                 case LOG_RAW:
                 case LOG_EMPTY:
-                        return "";
                 case LOG_MARK:
                         return "";
                 case LOG_IMPORTANT:
@@ -70,58 +68,52 @@ const char *get_log_level_name(enum cats_log_level level)
 }
 
 
-const char *get_log_level_color(enum cats_log_level level)
+const char *get_log_level_color(enum bdc_log_level level)
 {
         switch (level) {
                 case LOG_RAW:
                 case LOG_DEBUG:
-                        return C_RESET;
                 case LOG_INFO:
-                        return C_RESET;
                 case LOG_EMPTY:
-                        return C_RESET;
                 case LOG_MARK:
                         return C_RESET;
                 case LOG_IMPORTANT:
-                        return C_YELLOW;
                 case LOG_WARNING:
                         return C_YELLOW;
                 case LOG_ERROR:
-                        return C_RED;
                 case LOG_MPI:
-                        return C_RED;
                 case LOG_UNIMPLEMENTED:
-                        return C_RED;
                 case LOG_UNKNOWN:
                         return C_RED;
+
         }
 
         return C_RESET;
 }
 
 
-void set_log_level(enum cats_log_level new_level)
+void set_log_level(enum bdc_log_level new_level)
 {
         internal_current_log_level = new_level;
 }
 
 
-
 // - mpi world rank can be added later
 // FIXME: if start_time is NULL, generate
-void logging_initialize(enum cats_log_level level, const struct bdc_time *start_time, const char *log_file_name, bool quiet)
+void
+logging_initialize(enum bdc_log_level level, const struct bdc_time *start_time, const char *log_file_name, bool quiet)
 {
         set_log_level(level);
         logging_quiet = quiet;
         if (start_time != NULL) {
-                cats_logging_start_time = *start_time;
+                bdc_logging_start_time = *start_time;
                 time_initialized = true;
         }
 
         if (log_file_name != NULL) {
-                cats_log_file_name = strdup(log_file_name);
-                cats_log_file = fopen(log_file_name, "w");
-                ENSURE_FILE_OPENED(cats_log_file, cats_log_file_name)
+                bdc_log_file_name = strdup(log_file_name);
+                bdc_log_file = fopen(log_file_name, "w");
+                ENSURE_FILE_OPENED(bdc_log_file, bdc_log_file_name)
         }
 
 }
@@ -138,13 +130,16 @@ void logging_set_mpi_rank(int mpi_world_rank)
         logging_mpi_rank = mpi_world_rank;
 }
 
+
 // actual logging function(s), only to be called from log_message
-void log_msg(const char *msg, enum cats_log_level loglevel);
-void log_msg_simple(const char *msg, enum cats_log_level loglevel);
+void log_msg(const char *msg, enum bdc_log_level loglevel);
+
+void log_msg_simple(const char *msg, enum bdc_log_level loglevel);
+
 
 // logging wrapper function
 __attribute__ ((__format__ (__printf__, 2, 3)))
-void log_message(enum cats_log_level level, const char *fmt, ...)
+void log_message(enum bdc_log_level level, const char *fmt, ...)
 {
         if (level < internal_current_log_level) return;
 
@@ -165,13 +160,13 @@ void log_message(enum cats_log_level level, const char *fmt, ...)
 }
 
 
-enum cats_log_level get_log_level(void)
+enum bdc_log_level get_log_level(void)
 {
         return internal_current_log_level;
 }
 
 
-void log_message_simple(enum cats_log_level level, const char *fmt, ...)
+__attribute__((unused)) void log_message_simple(enum bdc_log_level level, const char *fmt, ...)
 {
         if (level < internal_current_log_level) return;
         char *message = NULL;
@@ -190,26 +185,26 @@ void log_message_simple(enum cats_log_level level, const char *fmt, ...)
 }
 
 
-void log_msg(const char *msg, enum cats_log_level loglevel)
+void log_msg(const char *msg, enum bdc_log_level loglevel)
 {
         if (loglevel < internal_current_log_level) return;
 
         if (loglevel == LOG_RAW) {
-                if (! logging_quiet) fprintf(stdout, "%s", msg);
-                if (cats_log_file != NULL) fprintf(cats_log_file, "%s", msg);
+                if (!logging_quiet) fprintf(stdout, "%s", msg);
+                if (bdc_log_file != NULL) fprintf(bdc_log_file, "%s", msg);
                 return;
         }
 
         if (loglevel == LOG_EMPTY) {
-                if (! logging_quiet) fprintf(stdout, "\n");
-                if (cats_log_file != NULL) fprintf(cats_log_file, "\n");
+                if (!logging_quiet) fprintf(stdout, "\n");
+                if (bdc_log_file != NULL) fprintf(bdc_log_file, "\n");
                 return;
         }
 
         const char *color = get_log_level_color(loglevel);
         if (loglevel == LOG_MARK) {
-                if (! logging_quiet)  fprintf(stdout, "%s%s%s\n", color, msg, C_RESET);
-                if (cats_log_file != NULL)  fprintf(cats_log_file, "%s%s%s\n", color, msg, C_RESET);
+                if (!logging_quiet) fprintf(stdout, "%s%s%s\n", color, msg, C_RESET);
+                if (bdc_log_file != NULL) fprintf(bdc_log_file, "%s%s%s\n", color, msg, C_RESET);
                 return;
         }
         const char *log_default = "unknown";
@@ -222,55 +217,60 @@ void log_msg(const char *msg, enum cats_log_level loglevel)
 
 
         if (logging_mpi_rank >= 0) {
-                if (! logging_quiet) fprintf(stdout, "(%02d)::", logging_mpi_rank);
-                if (cats_log_file != NULL) fprintf(cats_log_file, "(%02d)::", logging_mpi_rank);
+                if (!logging_quiet) fprintf(stdout, "(%02d)::", logging_mpi_rank);
+                if (bdc_log_file != NULL) fprintf(bdc_log_file, "(%02d)::", logging_mpi_rank);
         }
 
         if (time_initialized) {
-                double secs = seconds_monotonic_since(&cats_logging_start_time);
-                if (cats_log_file != NULL) {
-                        fprintf(cats_log_file, "% 16.4f::", secs);
+                double secs = seconds_monotonic_since(&bdc_logging_start_time);
+                if (bdc_log_file != NULL) {
+                        fprintf(bdc_log_file, "% 16.4f::", secs);
                 }
-                if (! logging_quiet) fprintf(stdout, "% 16.4f::", secs);
+                if (!logging_quiet) fprintf(stdout, "% 16.4f::", secs);
         } else {
-                if (cats_log_file != NULL) {
-                        fprintf(cats_log_file, "                ::");
+                if (bdc_log_file != NULL) {
+                        fprintf(bdc_log_file, "                ::");
                 }
-                if (! logging_quiet)  fprintf(stdout, "                ::");
+                if (!logging_quiet) fprintf(stdout, "                ::");
         }
 
         if (logging_module_name != NULL) {
-                if (! logging_quiet) fprintf(stdout, "%s%s::[%s] %s%s\n", color, loglevel_name, logging_module_name, msg, C_RESET);
-                if (cats_log_file) fprintf(cats_log_file, "%s%s::[%s] %s%s\n", color, loglevel_name, logging_module_name, msg, C_RESET);
+                if (!logging_quiet)
+                        fprintf(stdout, "%s%s::[%s] %s%s\n", color, loglevel_name, logging_module_name, msg, C_RESET);
+                if (bdc_log_file)
+                        fprintf(bdc_log_file, "%s%s::[%s] %s%s\n", color, loglevel_name, logging_module_name, msg,
+                                C_RESET);
 
         } else {
-                if (! logging_quiet) fprintf(stdout, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
-                if (cats_log_file)   fprintf(cats_log_file, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
+                if (!logging_quiet) fprintf(stdout, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
+                if (bdc_log_file) fprintf(bdc_log_file, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
 
         }
 
 }
 
 
-void log_msg_simple(const char *msg, enum cats_log_level loglevel)
+void log_msg_simple(const char *msg, enum bdc_log_level loglevel)
 {
         if (loglevel < internal_current_log_level) return;
         const char *log_default = "unknown";
         const char *loglevel_name = log_default;
 
-        if (loglevel <= LOG_UNKNOWN && loglevel >= LOG_DEBUG) {
+        if (loglevel <= LOG_UNKNOWN) {
                 loglevel_name = get_log_level_name(loglevel);
         }
 
         const char *color = get_log_level_color(loglevel);
         if (logging_mpi_rank >= 0) {
-                if (! logging_quiet)        fprintf(stdout, "(%02d)%s%s: %s%s", logging_mpi_rank, color, loglevel_name, msg, C_RESET);
-                if (cats_log_file != NULL)  fprintf(cats_log_file, "(%02d)%s%s: %s%s", logging_mpi_rank, color, loglevel_name, msg, C_RESET);
+                if (!logging_quiet)
+                        fprintf(stdout, "(%02d)%s%s: %s%s", logging_mpi_rank, color, loglevel_name, msg, C_RESET);
+                if (bdc_log_file != NULL)
+                        fprintf(bdc_log_file, "(%02d)%s%s: %s%s", logging_mpi_rank, color, loglevel_name, msg,
+                                C_RESET);
 
         } else {
-                if (! logging_quiet)        fprintf(stdout, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
-                if (cats_log_file != NULL)  fprintf(cats_log_file, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
+                if (!logging_quiet) fprintf(stdout, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
+                if (bdc_log_file != NULL) fprintf(bdc_log_file, "%s%s::%s%s\n", color, loglevel_name, msg, C_RESET);
 
         }
-
 }
diff --git a/src/logging/logging.h b/libbdc/bdc_logging/logging.h
similarity index 66%
rename from src/logging/logging.h
rename to libbdc/bdc_logging/logging.h
index 52eefe767e846a4ffef06e4853a7241b5c901e65..2a5ac642429a296017353904a527205c606c936f 100644
--- a/src/logging/logging.h
+++ b/libbdc/bdc_logging/logging.h
@@ -19,14 +19,15 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 
-#ifndef CATS_LOGGING_H_
-#define CATS_LOGGING_H_
+#ifndef BDC_LOGGING_H_
+#define BDC_LOGGING_H_
+
 #include <stdbool.h>
-#include "cats_defs.h"
 #include <time.h>
+
 #include "bdc_time/bdc_time.h"
 
-#if defined(__MINGW32__) || defined(CATS_ON_WINDOWS)
+#if defined(__MINGW32__) || defined(WIN32)
 // no color codes
 #define C_RED    ""
 #define C_GREEN  ""
@@ -41,7 +42,7 @@
 #define C_WHITE  "\x1B[37m"
 #endif
 
-enum cats_log_level {
+enum bdc_log_level {
         LOG_DEBUG,
         LOG_INFO,
         LOG_EMPTY,
@@ -55,19 +56,25 @@ enum cats_log_level {
         LOG_UNKNOWN
 };
 
-void logging_initialize(enum cats_log_level level, const struct bdc_time *start_time, const char *log_file_name, bool quiet);
+void
+logging_initialize(enum bdc_log_level level, const struct bdc_time *start_time, const char *log_file_name, bool quiet);
 
 void logging_set_mpi_rank(int mpi_world_rank);
 
 void logging_set_module_name(const char *name);
 
 __attribute__ ((__format__ (__printf__, 2, 3)))
-void log_message(enum cats_log_level level, const char *fmt, ...);
+void log_message(enum bdc_log_level level, const char *fmt, ...);
+
+enum bdc_log_level get_log_level(void);
+
+__attribute__((unused)) void log_message_simple(enum bdc_log_level level, const char *fmt, ...);
 
-enum cats_log_level get_log_level(void);
+void set_log_level(enum bdc_log_level new_level);
 
-void log_message_simple(enum cats_log_level level, const char *fmt, ...);
+#define INCOMPLETE_IMPLEMENTATION() log_message(LOG_ERROR, "%s: not yet implemented", __func__)
+#define MISSING_IMPLEMENTATION(X) log_message(LOG_ERROR, "%s: missing functionality %s", __func__, (X))
+#define ABORT_IMPLEMENTATION() log_message(LOG_ERROR, "%s:%s:%d: not yet implemented", __FILE__, __func__, __LINE__); exit(EXIT_FAILURE)
 
-void set_log_level(enum cats_log_level new_level);
 
 #endif
\ No newline at end of file
diff --git a/libbdc/bdc_strings/CMakeLists.txt b/libbdc/bdc_strings/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..78d25f74c5bf3c3f9b29ae31125e98b2646eadf4
--- /dev/null
+++ b/libbdc/bdc_strings/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_library(bdc_strings STATIC ""  ../bdc_cross_platform/bdc_cross_platform.h
+
+)
+
+target_sources(bdc_strings
+               PRIVATE
+               bdc_strings.c
+               pretty_print.c
+               string_helpers.c
+               string_substitution.c
+               string_converters.c
+               PUBLIC
+               bdc_strings.h
+
+               )
+
+set_property(TARGET bdc_strings PROPERTY POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(bdc_strings bdc_logging bdc_hashtable)
\ No newline at end of file
diff --git a/libbdc/bdc_strings/README.md b/libbdc/bdc_strings/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a86721af7f78b5a20dfe18c4e18a8254a00e6b0
--- /dev/null
+++ b/libbdc/bdc_strings/README.md
@@ -0,0 +1,9 @@
+# bdc_strings
+
+
+This is a library to handle strings.
+
+## Dependencies
+* bdc_logging 
+* bdc_hashtable
+
diff --git a/src/cats_strings/cats_strings.c b/libbdc/bdc_strings/bdc_strings.c
similarity index 80%
rename from src/cats_strings/cats_strings.c
rename to libbdc/bdc_strings/bdc_strings.c
index 4843f1b104b68ebf2937b421383ce1208db3d059..64a67339550d6b5b83ed4cfc53a1847c5e6f2a29 100644
--- a/src/cats_strings/cats_strings.c
+++ b/libbdc/bdc_strings/bdc_strings.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 //
-// cats_strings.c
+// bdc_strings.c
 //
 // Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
 //
@@ -19,24 +19,57 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 
-#include "cats_global.h"
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "cats_strings.h"
-#include <logging/logging.h>
+#include "bdc_strings.h"
+#include "bdc_logging/logging.h"
 #include "bdc_memory/bdc_memory.h"
-#include <assert.h>
+
+struct string_array {
+        int count;
+        char **string;
+        enum JSON_HINT *typehint;
+};
 
 
 void asprintf_check(int rc)
 {
         if (rc >= 0) return;
-        log_message(LOG_ERROR, "error in asprintf_ could not allocate memory or other error. rc: %d", rc);
+        log_message(LOG_ERROR, "error in asprintf - could not allocate memory or other error. rc: %d", rc);
         exit(EXIT_FAILURE);
 }
 
 
+void snprintf_check(int rc, size_t expected)
+{
+        if (rc >= 0 && (rc == expected || rc + 1 == expected)) return;
+        if (rc < 0) {
+                log_message(LOG_ERROR, "error in snprintf - error : %d", rc);
+                exit(EXIT_FAILURE);
+        }
+        if (rc < expected - 1) {
+                log_message(LOG_DEBUG, "error in snprintf - output truncated: %d of %ld bytes written", rc, expected);
+        }
+}
+
+int get_string_count(const struct string_array *array)
+{
+        assert(array != NULL);
+        return array->count;
+}
+
+
+const char *get_string(const struct string_array *array, int count)
+{
+        assert(array != NULL);
+        assert(count >= 0);
+        assert(count < array->count);
+        return array->string[count];
+
+}
+
 void string_array_add(struct string_array *array, const char *string)
 {
         if (!array) {
@@ -51,6 +84,8 @@ void string_array_add(struct string_array *array, const char *string)
         DBG_STRING(log_message(LOG_RAW, "%s: address:        %p\n", __func__, array);)
         DBG_STRING(log_message(LOG_RAW, "%s: count:          %d\n", __func__, array->count);)
         DBG_STRING(log_message(LOG_RAW, "%s: trying to add:  %s\n", __func__, string);)
+        DBG_STRING(log_message(LOG_RAW, "%p: string address: %p\n", __func__, array->string);)
+        DBG_STRING(log_message(LOG_RAW, "%s: new size:       %zu\n", __func__, new_size);)
 
         array->string = realloc_or_die(array->string, new_size);
         array->typehint = realloc_or_die(array->typehint, sizeof(enum JSON_HINT) * (array->count + 1));
@@ -66,7 +101,7 @@ void string_array_add(struct string_array *array, const char *string)
 }
 
 
-void string_array_add_conditional(struct string_array *array, char *string, bool condition)
+__attribute__((unused)) void string_array_add_conditional(struct string_array *array, char *string, bool condition)
 {
         if (!condition) return;
         string_array_add(array, string);
@@ -79,8 +114,10 @@ void string_array_add_int(struct string_array *array, int32_t number, const char
         if (!format_string || !strlen(format_string)) f = "%d";
 
         int count = snprintf(NULL, 0, f, number) + 1;
+        snprintf_check(count, -1);
         char *tmp = malloc_or_die(count);
-        snprintf(tmp, count, f, number);
+        int rc = snprintf(tmp, count, f, number);
+        snprintf_check(rc, count);
 
         string_array_add(array, tmp);
         array->typehint[array->count - 1] = JSON_NUMERIC;
@@ -94,6 +131,7 @@ void string_array_add_int64(struct string_array *array, int64_t number, char *fo
         if (!format_string || !strlen(format_string)) f = "%ld";
 
         int count = snprintf(NULL, 0, f, number) + 1;
+        snprintf_check(count, -1);
         char *tmp = malloc_or_die(count);
         snprintf(tmp, count, f, number);
 
@@ -103,20 +141,22 @@ void string_array_add_int64(struct string_array *array, int64_t number, char *fo
 }
 
 
-void string_array_add_double(struct string_array *array, double number, const char *format_string)
+__attribute__((unused)) void string_array_add_double(struct string_array *array, double number, const char *format_string)
 {
         const char *f = format_string;
         if (!format_string || !strlen(format_string)) f = "%f";
         int count = snprintf(NULL, 0, f, number) + 1;
+        snprintf_check(count, -1);
         char *tmp = malloc_or_die(count);
-        snprintf(tmp, count, f, number);
+        int rc = snprintf(tmp, count, f, number);
+        snprintf_check(rc, count);
         string_array_add(array, tmp);
         array->typehint[array->count - 1] = JSON_NUMERIC;
         free(tmp);
 }
 
 
-void
+__attribute__((unused)) void
 string_array_add_int_conditional(struct string_array *array, int32_t number, const char *format_string, bool condition)
 {
         if (!condition) return;
@@ -134,11 +174,18 @@ struct string_array *new_string_array()
         return result;
 }
 
-const char *get_nth_string_from_array(const struct string_array *array, int n)
+
+__attribute__((unused)) const char *string_array_get_string(const struct string_array *array, int n)
 {
+
+        if (array == NULL || array->string == NULL) {
+                log_message(LOG_ERROR, "%s: string array or contents are NULL", __func__);
+                exit(EXIT_FAILURE);
+        }
         assert(array != NULL);
+
         if (n < 0 && n >= array->count) {
-                log_message(LOG_ERROR, "%s: requested string index %d out of range [0, %d]", __func__ , n, array->count);
+                log_message(LOG_ERROR, "%s: requested string index %d out of range [0, %d]", __func__, n, array->count);
                 exit(EXIT_FAILURE);
         }
 
@@ -146,6 +193,7 @@ const char *get_nth_string_from_array(const struct string_array *array, int n)
 
 }
 
+
 struct string_array *new_string_array_init(const char *entry)
 {
         struct string_array *result = new_string_array();
@@ -207,16 +255,16 @@ int32_t count_fields(const char *line, const char *sep)
 
 
 // untested trivial
-void abort_on_null_token(const char *token, char *orig, int count)
+void abort_on_null_token(const char *token, const char *orig, int count)
 {
         if (token != NULL) return;
         log_message(LOG_WARNING, "%s: string '%s' has not enough (%d) tokens", __func__, orig, count);
         abort();
-        exit(EXIT_FAILURE);
+        //exit(EXIT_FAILURE);
 }
 
 
-struct string_array *get_all_tokens(char *line, const char *sep)
+struct string_array *get_all_tokens(const char *line, const char *sep)
 {
         const int32_t max_count = count_fields(line, sep);
 
@@ -229,6 +277,7 @@ struct string_array *get_all_tokens(char *line, const char *sep)
 
         char *token = strtok_r(copy, sep, &saveptr);
         abort_on_null_token(token, line, max_count);
+
         char *to_add = strdup(token);
         to_add = left_trim(right_trim(to_add));
         string_array_add(result, to_add);
@@ -253,7 +302,7 @@ struct string_array *get_all_tokens(char *line, const char *sep)
 }
 
 
-char *get_nth_token(char *line, const char *sep, int32_t n)
+__attribute__((unused)) char *get_nth_token(char *line, const char *sep, int32_t n)
 {
         char *result = NULL;
         struct string_array *tokens = get_all_tokens(line, sep);
@@ -265,7 +314,7 @@ char *get_nth_token(char *line, const char *sep, int32_t n)
 }
 
 
-char *remove_0th_token(char *line, const char *sep)
+char *remove_0th_token(const char *line, const char *sep)
 {
         char *result = NULL;
         struct string_array *tokens = get_all_tokens(line, sep);
@@ -302,7 +351,7 @@ char *json_dict(const struct string_array *header, const struct string_array *da
 
 
         char *result = calloc_or_die(1, len);
-        size_t written = 0;
+        size_t written; // = 0;
 
         result[0] = '{'; // written: 1
         written = 1;
@@ -361,7 +410,7 @@ char *string_array_paste(const struct string_array *array, const char *sep)
         if (length == 0) {
                 log_message(LOG_ERROR, "%s: empty string, exiting", __func__);
                 abort();
-                exit(EXIT_FAILURE);
+                // exit(EXIT_FAILURE);
         }
 
         length += (array->count - 1) * strlen(sep);  // space for separator
@@ -382,7 +431,7 @@ char *string_array_paste(const struct string_array *array, const char *sep)
 }
 
 
-void print_string_array(const struct string_array *array)
+__attribute__((unused)) void print_string_array(const struct string_array *array)
 {
         log_message(LOG_RAW, "string array with %d entries:\n", array->count);
         for (int32_t i = 0; i < array->count; i++) {
@@ -392,7 +441,7 @@ void print_string_array(const struct string_array *array)
 }
 
 
-int32_t string_array_index(const struct string_array *array, const char *needle)
+__attribute__((unused)) int32_t string_array_string_index(const struct string_array *array, const char *needle)
 {
         if (array == NULL) {
                 log_message(LOG_ERROR, "%s: called with empty parameter array", __func__);
@@ -407,7 +456,7 @@ int32_t string_array_index(const struct string_array *array, const char *needle)
 }
 
 
-bool in_string_array(const struct string_array *array, const char *needle)
+__attribute__((unused)) bool string_array_contains_string(const struct string_array *array, const char *needle)
 {
         for (int32_t i = 0; i < array->count; i++) {
                 if (strcmp(needle, array->string[i]) == 0) return true;
@@ -442,6 +491,20 @@ char *compound_string(const char *s1, const char *s2, const char *sep)
 
         size_t len = strlen(s1) + strlen(s2) + strlen(sep) + 1;
         result = malloc_or_die(len);
-        snprintf(result, len, "%s%s%s", s1, sep, s2);
+        //printf("s1:  %s (%ld)\n", s1, strlen(s1));
+        //printf("sep: %s (%ld)\n", sep, strlen(sep));
+        //printf("s2:  %s (%ld)\n", s2, strlen(s2));
+        //printf("len: %ld\n", len);
+        int rc = snprintf(result, len, "%s%s%s", s1, sep, s2);
+        //printf("res: %s (%ld)\n", result, strlen(result));
+        snprintf_check(rc, len);
         return result;
-}
\ No newline at end of file
+}
+
+
+bool string_is_empty_or_null(const char *name)
+{
+        if (name == NULL) return true;
+        if (strlen(name) == 0) return true; // FIXME CHECK ONLY FIRST X BYTES
+        return false;
+}
diff --git a/src/cats_strings/cats_strings.h b/libbdc/bdc_strings/bdc_strings.h
similarity index 52%
rename from src/cats_strings/cats_strings.h
rename to libbdc/bdc_strings/bdc_strings.h
index d0031c202ea42da7c25c62db078eecee8d852754..7e64d1790fadb9bf2fb7458cf8dd08b3a4bbff00 100644
--- a/src/cats_strings/cats_strings.h
+++ b/libbdc/bdc_strings/bdc_strings.h
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 //
-// cats_strings.h
+// bdc_strings.h
 //
 // Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
 //
@@ -21,51 +21,44 @@
 
 #ifndef CATS_STRING_H_
 #define CATS_STRING_H_
+
 #include <stdlib.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include "data/error.h"
+#include <stdio.h>
+
+struct string_array;
+struct string_substitution;
 enum JSON_HINT {
         JSON_STRING = 0,
         JSON_QUOTED = 0,
         JSON_UNQUOTED = 1,
         JSON_NUMERIC = 1
 };
+// constants
+#define TEXT_DIVIDER "=================================================================================================="
+#define PAD_NAME 55
+#define PAD_VALUE 35
 
-struct string_array {
-        int count;
-        char **string;
-        enum JSON_HINT *typehint;
-};
-
-
-
-
-#include <stdio.h>
-
-#define MAX_STRING_BUFF_LENGTH 2048
-
-char *read_single_line(FILE *file);
-
-char *slurp_file(const char *filename);
+//#define DEBUG_STRING
+#ifdef DEBUG_STRING
+#define DBG_STRING(X) {X;}while(0);
+#else
+#define DBG_STRING(X)
+#endif
 
-#define ENSURE_FILE_OPENED(FILE_HANDLE, FILENAME)\
-if (! (FILE_HANDLE))\
-{\
-        fprintf(stderr, "ERROR: Couldn't open file %s in %s: %d (%s)\n", FILENAME, __FILE__, __LINE__, __func__);\
-        exit(E_UNREADABLE_FILE);\
-}
 
+// string array functions
+struct string_array *new_string_array(void);
 
-void asprintf_check(int rc);
+const char *get_string(const struct string_array *array, int count);
 
-struct string_array *new_string_array(void);
+int get_string_count(const struct string_array *array);
 
 void free_string_array(struct string_array **array);
 
-int32_t count_fields(const char *line, const char *sep);
+struct string_array *get_all_tokens(const char *line, const char *sep);
 
-struct string_array *get_all_tokens(char *line, const char *seperator);
 
 void string_array_add(struct string_array *array, const char *string);
 
@@ -73,33 +66,42 @@ void string_array_add_int64(struct string_array *array, int64_t number, char *fo
 
 char *string_array_paste(const struct string_array *array, const char *sep);
 
-char *assemble_filename(struct string_array *path, struct string_array *filename, char *filesep, char *extension);
 
 void string_array_add_int(struct string_array *array, int32_t number, const char *format_string);
 
 struct string_array *new_string_array_init(const char *entry);
 
 
-void string_array_add_conditional(struct string_array *array, char *string, bool conditon);
+__attribute__((unused)) void string_array_add_conditional(struct string_array *array, char *string, bool conditon);
 
-void string_array_add_double(struct string_array *array, double number, const char *format_string);
+__attribute__((unused)) void string_array_add_double(struct string_array *array, double number, const char *format_string);
 
-void
+__attribute__((unused)) void
 string_array_add_int_conditional(struct string_array *array, int32_t number, const char *format_string, bool condition);
-const char *get_nth_string_from_array(const struct string_array *array, int n);
-void print_string_array(const struct string_array *array);
 
-char *remove_0th_token(char *line, const char *sep);
+__attribute__((unused)) const char *string_array_get_string(const struct string_array *array, int n);
+
+__attribute__((unused)) void print_string_array(const struct string_array *array);
 
-char *get_nth_token(char *line, const char *sep, int32_t n);
 
 struct string_array *copy_string_array(const struct string_array *src);
 
 char *json_dict(const struct string_array *header, const struct string_array *data);
 
-int32_t string_array_index(const struct string_array *array, const char *needle);
+int32_t string_array_string_index(const struct string_array *array, const char *needle);
+
+__attribute__((unused)) bool string_array_contains_string(const struct string_array *array, const char *needle);
+
+
+// string functions
+char *trim_both(char *line);
+
+int32_t count_fields(const char *line, const char *sep);
+
 
-bool in_string_array(const struct string_array *array, const char *needle);
+char *remove_0th_token(const char *line, const char *sep);
+
+__attribute__((unused)) char *get_nth_token(char *line, const char *sep, int32_t n);
 
 size_t substring_count(const char *haystack, const char *needle);
 
@@ -115,14 +117,20 @@ char *right_trim(char *line);
 
 char *left_trim(char *line);
 
-void abort_on_null_token(const char *token, char *orig, int count);
+bool string_is_empty_or_null(const char *name);
 
-#define PAD_NAME 55
-#define PAD_VALUE 35
+void abort_on_null_token(const char *token, const char *orig, int count);
+// helper functions
+
+void asprintf_check(int rc);
 
+void snprintf_check(int rc, size_t expected);
+
+
+// pretty print functions
 void print_string(int indent, const char *name, const char *value);
 
-void print_subcategory(int indent, const char *category_name, int64_t category_number, char *name);
+void print_subcategory(int indent, const char *category_name, int64_t category_number, const char *name);
 
 void print_category(int indent, const char *category, const char *name);
 
@@ -130,15 +138,38 @@ void print_integer(int indent, const char *name, int64_t value);
 
 void print_rate(int indent, const char *name, long double value);
 
+
+
+// string substitution
+
+void add_substitution(struct string_substitution *substituter, const char *key, const char *initial_value);
+
+void update_substitution(struct string_substitution *substituter, const char *key, const char *initial_value);
+
+struct string_substitution *new_substituter(void);
+
+char *substitute_string(const char *string, const struct string_substitution *substituter);
+
+void destroy_substituter(struct string_substitution **substituter);
+
+struct string_substitution *copy_substituter(const struct string_substitution *from);
+
+void add_substitution_integer(struct string_substitution *substituter, const char *key, int32_t initial_value);
+
+void set_integer_pattern(struct string_substitution *substituter, const char *key, const char *pattern);
+
+
+// conversion function
 const char *bool_to_string(bool b);
 
-#ifdef DEBUG_STRING
-#define DBG_STRING(X) {X;}while(0);
-#else
-#define DBG_STRING(X)
-#endif
+bool string_to_float(char *string, float *value);
+
+bool string_to_bool(char *string, bool *value);
+
+bool string_to_double(const char *string, double *value);
 
-// implemented in string_helpers.h
+bool string_to_integer(const char *string, int32_t *value);
 
+bool string_to_long_double(const char *string, long double *value);
 
 #endif
diff --git a/src/cats_strings/pretty_print.c b/libbdc/bdc_strings/pretty_print.c
similarity index 95%
rename from src/cats_strings/pretty_print.c
rename to libbdc/bdc_strings/pretty_print.c
index fb94e70e609bae74089f2ce9c4f6242b5ea68e3a..a3797c3aa733638c488de17c2aafa3728d209e60 100644
--- a/src/cats_strings/pretty_print.c
+++ b/libbdc/bdc_strings/pretty_print.c
@@ -19,10 +19,11 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 
-#include "cats_global.h"
+
 #include <inttypes.h>
-#include "cats_strings.h"
-#include "logging/logging.h"
+#include "bdc_strings.h"
+#include "bdc_logging/logging.h"
+
 
 void print_string(int indent, const char *name, const char *value)
 {
@@ -32,7 +33,7 @@ void print_string(int indent, const char *name, const char *value)
 }
 
 
-void print_subcategory(int indent, const char *category_name, int64_t category_number, char *name)
+void print_subcategory(int indent, const char *category_name, int64_t category_number, const char *name)
 {
         indent *= 2;
         if (indent > 0) log_message(LOG_RAW, "%*s", indent, "");
diff --git a/src/cats_strings/string_converters.c b/libbdc/bdc_strings/string_converters.c
similarity index 94%
rename from src/cats_strings/string_converters.c
rename to libbdc/bdc_strings/string_converters.c
index 5578c1ecc2e2e69ccc64aad52035ea6bb56a2bf5..c7b8b74926cebc4eb5870316b49c16d2e3b863ba 100644
--- a/src/cats_strings/string_converters.c
+++ b/libbdc/bdc_strings/string_converters.c
@@ -25,8 +25,10 @@
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
-#include "string_converters.h"
-#include "logging/logging.h"
+
+
+#include "bdc_logging/logging.h"
+
 
 const char *true_values[] = {"1", "y", "t"};
 const char *false_values[] = {"0", "n", "f"};
@@ -34,7 +36,7 @@ const int true_counts = (int) (sizeof(true_values) / sizeof(char *));
 const int false_counts = (int) (sizeof(false_values) / sizeof(char *));
 
 
-bool string_to_double(char *string, double *value)
+bool string_to_double(const char *string, double *value)
 {
         if (string == NULL || strlen(string) == 0) return false;
 
@@ -82,7 +84,7 @@ bool string_to_float(char *string, float *value)
 }
 
 
-bool string_to_long_double(char *string, long double *value)
+bool string_to_long_double(const char *string, long double *value)
 {
         if (string == NULL || strlen(string) == 0) return false;
 
@@ -127,7 +129,7 @@ bool string_to_bool(char *string, bool *value)
 }
 
 
-bool string_to_integer(char *string, int32_t *value)
+bool string_to_integer(const char *string, int32_t *value)
 {
         if (string == NULL || strlen(string) == 0) return false;
 
@@ -148,3 +150,7 @@ bool string_to_integer(char *string, int32_t *value)
         return false;
 }
 
+
+
+
+
diff --git a/src/cats_strings/string_helpers.c b/libbdc/bdc_strings/string_helpers.c
similarity index 90%
rename from src/cats_strings/string_helpers.c
rename to libbdc/bdc_strings/string_helpers.c
index eb6553fa7543bd54b55214b74d4e7504debddf74..9c46586d411f2519804df9b364a37a00e23b6196 100644
--- a/src/cats_strings/string_helpers.c
+++ b/libbdc/bdc_strings/string_helpers.c
@@ -19,12 +19,11 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 
-#include "cats_global.h"
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <ctype.h>
-#include <logging/logging.h>
+#include "bdc_logging/logging.h"
 #include "bdc_memory/bdc_memory.h"
 
 
@@ -78,6 +77,13 @@ char *left_trim(char *line)
 }
 
 
+char *trim_both(char *line)
+{
+        return left_trim(right_trim(line));
+
+}
+
+
 size_t substring_count(const char *haystack, const char *needle)
 {
         size_t count = 0;
@@ -134,8 +140,8 @@ char *replace_substring(const char *original, const char *search, const char *re
                 exit(EXIT_FAILURE);
         }
 
-        size_t occurrences = substring_count(original, search);
-        size_t result_len = strlen(original) + strlen(replacement) * occurrences - strlen(search) * occurrences + 1;
+        size_t n_occurrences = substring_count(original, search);
+        size_t result_len = strlen(original) + strlen(replacement) * n_occurrences - strlen(search) * n_occurrences + 1;
 
         char *result = calloc_or_die(1, result_len);
 
@@ -144,20 +150,24 @@ char *replace_substring(const char *original, const char *search, const char *re
 
         size_t bytes_copied = 0;
 
-        for (size_t i = 0; i < occurrences; i++) {
+        for (size_t i = 0; i < n_occurrences; i++) {
                 char *next_occurrence = strstr(orig_loc, search);
-                if (next_occurrence == NULL) { break; }
+                if (next_occurrence == NULL || strlen(next_occurrence) == 0) { break; }
+
+                // how many bytes are before our first hit?
                 size_t bytes_to_copy = strlen(orig_loc) - strlen(next_occurrence);
+
+
                 if (bytes_to_copy) {
                         bytes_copied += bytes_to_copy;
                         strncpy(result_loc, orig_loc, bytes_to_copy);
                         result_loc += bytes_to_copy;
-                        orig_loc += bytes_to_copy + search_len;
+                        orig_loc += bytes_to_copy;
                 }
-
-
                 strncpy(result_loc, replacement, replacement_len);
                 result_loc += replacement_len;
+                orig_loc += search_len;
+
         }
 
         if (bytes_copied < original_len) {
diff --git a/libbdc/bdc_strings/string_substitution.c b/libbdc/bdc_strings/string_substitution.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ee87c138c2c97a7de2df7e3ae82bfbdf709b83a
--- /dev/null
+++ b/libbdc/bdc_strings/string_substitution.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// string_substitution.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include <string.h>
+
+#include "bdc_memory/bdc_memory.h"
+#include "bdc_logging/logging.h"
+#include "bdc_hashtable/bdc_hashtable.h"
+#include "bdc_strings.h"
+
+struct string_substitution {
+        struct bdc_hash_table *substitution_strings;
+        struct bdc_hash_table *integer_patterns;
+        struct string_array *keys;
+};
+
+struct string_substitution *new_substituter()
+{
+        struct string_substitution *new = calloc_or_die(1, sizeof(struct string_substitution));
+        new->substitution_strings = new_hash_table();
+        new->integer_patterns = new_hash_table();
+        new->keys = new_string_array();
+        return new;
+}
+
+
+void set_integer_pattern(struct string_substitution *substituter, const char *key, const char *pattern)
+{
+        if (substituter == NULL) {
+                log_message(LOG_ERROR, "%s: substituter == NULL", __func__);
+                exit(EXIT_FAILURE);
+        }
+        bdc_ht_add_string(substituter->integer_patterns, key, pattern);
+}
+
+void add_substitution_integer(struct string_substitution *substituter, const char *key, int32_t initial_value)
+{
+        if (substituter == NULL) {
+                log_message(LOG_ERROR, "%s: substituter == NULL", __func__);
+                exit(EXIT_FAILURE);
+        }
+
+        char *default_pattern = "%d";
+        char *pattern = NULL;
+        char *loaded_pattern = NULL;
+        bdc_ht_get_string_from_key(substituter->integer_patterns, key, &loaded_pattern);
+        if (!loaded_pattern) {
+                pattern = default_pattern;
+
+        } else {
+                pattern = loaded_pattern;
+        }
+
+        char *value = NULL;
+        int rc = asprintf(&value, pattern, initial_value);
+        asprintf_check(rc);
+        add_substitution(substituter, key, value);
+        free(value);
+        free(loaded_pattern);
+}
+
+
+
+
+
+void add_substitution(struct string_substitution *substituter, const char *key, const char *initial_value)
+{
+        if (!bdc_key_in_hash_table(substituter->substitution_strings, key)) {
+                string_array_add(substituter->keys, key);
+        }
+
+        bdc_ht_add_string(substituter->substitution_strings, key, initial_value);
+}
+
+char *substitute_string(const char *string, const struct string_substitution *substituter)
+{
+        if (substituter == NULL) {
+                log_message(LOG_ERROR, "%s: substituter == NULL", __func__);
+                exit(EXIT_FAILURE);
+        }
+        if (substituter->keys == NULL) {
+                log_message(LOG_ERROR, "%s: substituter invalid, keys = NULL", __func__);
+                exit(EXIT_FAILURE);
+        }
+        log_message(LOG_INFO, "substituting string '%s' with %d potential substitutions", string, get_string_count(substituter->keys));
+        const int count = get_string_count(substituter->keys);
+        char *copy = strdup(string);
+        char *result = strdup(string);
+        for (int i = 0; i < count; i++) {
+                const char *key = get_string(substituter->keys, i);
+                char *value = NULL;
+                bool found = bdc_ht_get_string_from_key(substituter->substitution_strings, key, &value);
+                if (! found) {
+                        continue;
+                }
+                free(result);
+                result = replace_substring(copy, key, value);
+                free(copy);
+                copy = strdup(result);
+
+
+        }
+        free(copy);
+        return result;
+}
+
+void destroy_substituter(struct string_substitution **substituter)
+{
+        if (substituter == NULL || *substituter == NULL) return;
+        struct string_substitution *s = *substituter;
+        free_string_array(&s->keys);
+        destroy_hash_table(&s->substitution_strings);
+        destroy_hash_table(&s->integer_patterns);
+        free(s);
+        *substituter = NULL;
+
+}
+
+struct string_substitution *copy_substituter(const struct string_substitution *from)
+{
+        if (from == NULL) return NULL;
+        struct string_substitution *to = new_substituter();
+        const struct string_array *keys = from->keys;
+        const int32_t count = get_string_count(keys);
+        for (int32_t i = 0; i< count; i++) {
+                const char *key = get_string(keys, i);
+                char *value = NULL;
+                bdc_ht_get_string_from_key(from->substitution_strings, key, &value);
+                if (value) {
+                        add_substitution(to, key, value);
+                }
+                bdc_ht_get_string_from_key(from->integer_patterns, key, &value);
+                if (value) {
+                        set_integer_pattern(to, key, value);
+                }
+
+        }
+
+        return to;
+}
\ No newline at end of file
diff --git a/libbdc/bdc_time/bdc_time.c b/libbdc/bdc_time/bdc_time.c
index 41bc4a6da3f07010050648f2a3d1563b80a99b31..2d7908c6dc53cef1a10ab80f0350dbcc0faa5862 100644
--- a/libbdc/bdc_time/bdc_time.c
+++ b/libbdc/bdc_time/bdc_time.c
@@ -1,25 +1,23 @@
-/*
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- * bdc_time.c
- *
- * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_time.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
 
 #include <time.h>
 #include <stdio.h>
diff --git a/libbdc/bdc_time/bdc_time.h b/libbdc/bdc_time/bdc_time.h
index 68ff82626a726d3e74142093c81ecad47158de97..d8f822726f0224b3f3a1c2ccb828ee4c67d8f60a 100644
--- a/libbdc/bdc_time/bdc_time.h
+++ b/libbdc/bdc_time/bdc_time.h
@@ -1,25 +1,23 @@
-/*
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- * bdc_time.h
- *
- * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_time.h
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
 
 #ifndef BDC_TIME_H
 #define BDC_TIME_H
diff --git a/libbdc/bdc_time/bdc_timer.c b/libbdc/bdc_time/bdc_timer.c
index 321951d39041ea12dcb20500357f0fa5261b037c..2a147c211b09f2e11fbc794ed1ecb6d4851adc8c 100644
--- a/libbdc/bdc_time/bdc_timer.c
+++ b/libbdc/bdc_time/bdc_timer.c
@@ -1,3 +1,24 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// bdc_timer.c
+//
+// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
 #include <assert.h>
 #include <stdlib.h>
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ee838a3dbcd34a8adf2d1919a0445bdf80e93a0e..423f4c71f61cc0965ab519536d3f2b765ba76ed5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,5 @@
-add_subdirectory(logging)
+
 add_subdirectory(cats)
-add_subdirectory(cats_strings)
 add_subdirectory(cats_ini)
 add_subdirectory(cats_csv)
 add_subdirectory(modules/cats_test_module)
diff --git a/src/cats/CMakeLists.txt b/src/cats/CMakeLists.txt
index 66572ef47e0c98811b0a72b89bc8b53880826e49..ff9fab8605001c6f6591178ab168dc1a5b98d451 100644
--- a/src/cats/CMakeLists.txt
+++ b/src/cats/CMakeLists.txt
@@ -318,15 +318,15 @@ set(CATS_SOURCES_MPI
     )
 
 add_executable(cats cats.c)
-target_link_libraries(libcats cats_logging bdc cats_strings cats_ini cats_csv)
-target_link_libraries(cats libcats cats_logging bdc cats_strings cats_ini cats_csv)
+target_link_libraries(libcats bdc_logging bdc bdc_strings cats_ini cats_csv)
+target_link_libraries(cats libcats bdc_logging bdc bdc_strings cats_ini cats_csv)
 
 
 if (MSVC)
     # target_include_directories(cats PRIVATE ${GDAL_INCLUDE_DIRS})
-    # target_link_libraries(cats cats_logging cats_memory cats_strings cats_ini cats_csv  ${GDAL_LIBRARIES})
+    # target_link_libraries(cats bdc_logging bdc_memory bdc_strings cats_ini cats_csv  ${GDAL_LIBRARIES})
 else ()
-    target_link_libraries(cats cats_logging bdc cats_strings cats_ini cats_csv)
+    target_link_libraries(cats bdc_logging bdc bdc_strings cats_ini cats_csv)
 
 endif ()
 
@@ -334,7 +334,7 @@ if (MPI_FOUND)
 
 
     add_executable(cats-mpi cats.c ${CATS_SOURCES_MPI})
-    target_link_libraries(cats-mpi ${MPI_C_LIBRARIES} cats_logging bdc_memory cats_strings cats_ini cats_csv libcats)
+    target_link_libraries(cats-mpi ${MPI_C_LIBRARIES} bdc_logging bdc_memory bdc_strings cats_ini cats_csv libcats)
 
 
     target_compile_options(cats-mpi PRIVATE -DUSEMPI)
diff --git a/src/cats/actions/setup_actions.c b/src/cats/actions/setup_actions.c
index e3a1539c9cd1e45246f03c3c12a57cdc5b0e260e..274d8a68c078bf0619680be0d09b1afa97fea9d8 100644
--- a/src/cats/actions/setup_actions.c
+++ b/src/cats/actions/setup_actions.c
@@ -28,7 +28,7 @@
 #include "actions/setup_actions.h"
 #include "actions/cats_actions.h"
 #include "actions/process_inter_period_survival.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 #include "dispersal/dispersal.h"
 #include "bdc_memory/bdc_memory.h"
 
diff --git a/src/cats/cats.c b/src/cats/cats.c
index a0d5a4fbacaf4079b1d73695362a0363356697ee..d2c14b367a7d5115dc935fba55af221cef26622b 100644
--- a/src/cats/cats.c
+++ b/src/cats/cats.c
@@ -31,7 +31,7 @@
 #include "cats_global.h"
 
 #include <cats_ini/cats_ini.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #include "cats/command_line/command_line_info.h"
 #include "cats/command_line/command_line_options.h"
diff --git a/src/cats/command_line/command_line_info.c b/src/cats/command_line/command_line_info.c
index 779d32ea3c3ef7067eae1e9263b902cb4df88c7b..fb3b6995b5e2f70f84f270e7b4217e03f209a646 100644
--- a/src/cats/command_line/command_line_info.c
+++ b/src/cats/command_line/command_line_info.c
@@ -24,7 +24,7 @@
 #include "vital_rates/vital_rates_helper.h"
 #include "command_line_options.h"
 #include "command_line_info.h"
-#include "logging/logging.h"
+#include "bdc_logging/logging.h"
 #include "cats_ini/cats_ini.h"
 #include "defaults.h"
 #include "cats_global.h"
diff --git a/src/cats/command_line/command_line_options.h b/src/cats/command_line/command_line_options.h
index df6924e7e08482f941b3135f174bc7f0406caf17..35f4287da9304452f03f24b72b53e79f473712c3 100644
--- a/src/cats/command_line/command_line_options.h
+++ b/src/cats/command_line/command_line_options.h
@@ -25,7 +25,7 @@
 #include <getopt.h>
 #include <stdlib.h>
 #include <stdbool.h>
-#include "logging/logging.h"
+#include "bdc_logging/logging.h"
 #include "cats_global.h"
 
 
@@ -114,7 +114,7 @@ struct program_options {
         bool lambda_test;
         enum cats_debug_flags debug_flags;
 
-        enum cats_log_level default_log_level;
+        enum bdc_log_level default_log_level;
         bool show_unused_config_values;
         bool show_unspecified_config_values;
         bool have_poisson_dampening_factor;
diff --git a/src/cats/configuration/load_config_spatial.c b/src/cats/configuration/load_config_spatial.c
index 6fed7296b5057f9b9b1ae0ccffa249a0c5fc2277..fa9ae72e37a55c2df8ebd3c415bf59500b4cac6b 100644
--- a/src/cats/configuration/load_config_spatial.c
+++ b/src/cats/configuration/load_config_spatial.c
@@ -25,7 +25,7 @@
 #include "misc/misc.h"
 #include "logging.h"
 #include "cats_ini/cats_ini.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 #include "load_configuration_temporal.h"
 #include "paths/paths.h"
 #include "grids/gdal_helper.h"
@@ -34,7 +34,7 @@
 void load_simulation_geometry_from_initial_population(struct cats_configuration *conf, struct cats_ini *ini)
 {
         struct string_array *species_sections = get_sections_with_prefix(ini, "species");
-        if (species_sections->count == 0) {
+        if (get_string_count(species_sections) == 0) {
                 log_message(LOG_ERROR, "%s: could not load any species information", __func__);
                 exit_cats(EXIT_FAILURE);
         }
@@ -53,9 +53,9 @@ void load_simulation_geometry_from_initial_population(struct cats_configuration
         }
         char *file_name = NULL;
         bool load_ini_pop = ! conf->command_line_options.no_input_rasters_required;
-        for (int32_t i = 0; i < species_sections->count; i++) {
-                char *species_section = species_sections->string[i];
-
+        size_t count =  get_string_count(species_sections);
+        for (int32_t i = 0; i < count; i++) {
+                const char *species_section = get_string(species_sections, i);
                 load_conf_value(load_ini_pop, ini, species_section, "initial population filename", &file_name); // VR::ignored::
                 break;
         }
diff --git a/src/cats/configuration/load_configuration.c b/src/cats/configuration/load_configuration.c
index e312c92b70dfe218f895318fc46ce6d950ca75c0..7b768adc9af1a909ae37ce70c73d465a8b4c236f 100644
--- a/src/cats/configuration/load_configuration.c
+++ b/src/cats/configuration/load_configuration.c
@@ -25,7 +25,7 @@
 #include <assert.h>
 
 #include <cats_ini/cats_ini.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #ifdef USEMPI
 #include "mpi/mpi_cats.h"
@@ -48,6 +48,7 @@
 #include "load_configuration_modules.h"
 #include "test/test_ini.h"
 #include "debug/debug_vital_rates.h"
+#include "bdc_io/bdc_io.h"
 
 void load_configuration_elements(struct cats_configuration *conf, struct cats_ini *ini);
 
diff --git a/src/cats/configuration/load_configuration_dispersal.c b/src/cats/configuration/load_configuration_dispersal.c
index 045df971bb8ea10e2017ed6801136c05d2bdc80b..d1808817077f889ac2dde754454e7f66b061a0ee 100644
--- a/src/cats/configuration/load_configuration_dispersal.c
+++ b/src/cats/configuration/load_configuration_dispersal.c
@@ -26,8 +26,8 @@
 #include <string.h>
 #include <stdbool.h>
 
-#include <logging/logging.h>
-#include <cats_strings/cats_strings.h>
+#include <bdc_logging/logging.h>
+#include <bdc_strings/bdc_strings.h>
 
 #include "data/species_parameters.h"
 #include "load_configuration_helper.h"
@@ -42,10 +42,13 @@ bool load_dispersal_info(struct cats_configuration *conf, struct cats_ini *ini,
         struct string_array *prob_min = get_char_array_from_conf2(ini, dispersal_section, "minimum weights");
         struct string_array *prob_max = get_char_array_from_conf2(ini, dispersal_section, "maximum weights");
 
-        if (!((file_names->count == prob_min->count) && (file_names->count == prob_max->count))) {
+        if (!((get_string_count(file_names) == get_string_count(prob_min)) &&
+              (get_string_count(file_names) == get_string_count(prob_max)))) {
                 log_message(LOG_ERROR, "CONF: count mismatch: %d file names, %d minimum weights, "
-                                       "%d maximum weights. Expected all counts to be equal.", file_names->count,
-                            prob_min->count, prob_max->count);
+                                       "%d maximum weights. Expected all counts to be equal.",
+                            get_string_count(file_names),
+                            get_string_count(prob_min),
+                            get_string_count(prob_max));
                 exit(EXIT_FAILURE);
         }
 
@@ -64,20 +67,20 @@ bool load_dispersal_info(struct cats_configuration *conf, struct cats_ini *ini,
                 dispersal->long_range_enabled = true;
         }
 
-        for (int32_t i = 0; i < file_names->count; i++) {
+        for (int32_t i = 0; i <get_string_count(file_names); i++) {
                 dispersal->types[i] = DISPERSAL_KERNEL;
-                bool pmin_succ = string_to_long_double(prob_min->string[i], &pmin);
-                bool pmax_succ = string_to_long_double(prob_max->string[i], &pmax);
+                bool pmin_succ = string_to_long_double(get_string(prob_min, i), &pmin);
+                bool pmax_succ = string_to_long_double(get_string(prob_min, i), &pmax);
 
                 if (pmin_succ == false || pmax_succ == false) {
                         log_message(LOG_ERROR, "CONF: error reading minimum and maximum probability: "
                                                "'%s' and '%s'",
-                                    prob_min->string[i], prob_max->string[i]);
+                                    get_string(prob_min, i), get_string(prob_min, i));
                         exit(EXIT_FAILURE);
                 }
 
-                if (file_names->count > i && file_names->string[i]) {
-                        dispersal->filenames[i] = strdup(file_names->string[i]);
+                if (get_string_count(file_names) > i && get_string(file_names, i)) {
+                        dispersal->filenames[i] = strdup(get_string(file_names, i));
                 }
 
                 if ((pmax >= 0 && pmax <= 1.0 && pmin >= 0 && pmin <= 1.0 && pmax >= pmin) ||
diff --git a/src/cats/configuration/load_configuration_environments.c b/src/cats/configuration/load_configuration_environments.c
index 08f0f70ff73125cee770102111959888435992ca..94cc25eb4a5c89e4dd9d7ff11f81f73199eac759 100644
--- a/src/cats/configuration/load_configuration_environments.c
+++ b/src/cats/configuration/load_configuration_environments.c
@@ -42,7 +42,7 @@ enum environment_variable_type get_environment_variable_type_from_string(const c
 }
 
 
-void add_environment_variable_from_conf(struct cats_configuration *conf, struct cats_ini *ini, char *environment_section)
+void add_environment_variable_from_conf(struct cats_configuration *conf, struct cats_ini *ini, const char *environment_section)
 {
         struct array_dimension *dimension = &conf->geometry.dimension;
         char *name = remove_0th_token(environment_section, ":");
@@ -126,14 +126,16 @@ void load_environments_configuration(struct cats_configuration *conf, struct cat
         //struct string_array *suitability_variables = get_sections_with_prefix(ini, "suitability");
         struct string_array *glms = get_sections_with_prefix(ini, "glm");
 
+        size_t count = get_string_count(env_variables);
+        for (int32_t i = 0; i < count; i++) {
 
-        for (int32_t i = 0; i < env_variables->count; i++) {
-                add_environment_variable_from_conf(conf, ini, env_variables->string[i]);
+                add_environment_variable_from_conf(conf, ini, get_string(env_variables, i));
         }
 
-
-        for (int32_t i = 0; i < glms->count; i++) {
-                add_glm_from_conf(conf, ini, glms->string[i]);
+        count = get_string_count(glms);
+        for (int32_t i = 0; i < count; i++) {
+                char *name = strdup(get_string(glms, i)); // FIXME, make add_glm_from_conf argument const
+                add_glm_from_conf(conf, ini, name);
         }
 
 
diff --git a/src/cats/configuration/load_configuration_glm.c b/src/cats/configuration/load_configuration_glm.c
index d5d1745ab743f81fc19ec3901e548c046915af03..3c234a32a83e6c2ddcc0ae3604d63b182829eb7a 100644
--- a/src/cats/configuration/load_configuration_glm.c
+++ b/src/cats/configuration/load_configuration_glm.c
@@ -115,14 +115,14 @@ void add_glm_from_conf(struct cats_configuration *conf, struct cats_ini *ini, ch
         }
         struct glm_params glm = {0};
         struct string_array *predictors = get_char_array_from_conf2(ini, environment_section, "predictors");
-        if (predictors->count == 0) {
+        if (get_string_count(predictors) == 0) {
                 log_message(LOG_ERROR, "%s: section [%s] does not contain any predictor names", __func__, environment_section);
                 exit_cats(EXIT_FAILURE);
         }
-        if (predictors->count > MAX_ENVIRONMENTS) {
+        if (get_string_count(predictors) > MAX_ENVIRONMENTS) {
                 log_message(LOG_ERROR, "%s: section [%s]: more predictors (%d) found than the allowed maximum %d",
                             __func__, environment_section,
-                            predictors->count, MAX_ENVIRONMENTS);
+                            get_string_count(predictors), MAX_ENVIRONMENTS);
                 exit_cats(EXIT_FAILURE);
         }
         enum environment_type type = get_environment_glm_type(type_name);
@@ -142,15 +142,15 @@ void add_glm_from_conf(struct cats_configuration *conf, struct cats_ini *ini, ch
         free(family_name);
 
 
-        for (int32_t i = 0; i < predictors->count; i++) {
-                get_environment_from_registry(&conf->environment_registry, predictors->string[i]);
-                char *predictor_linear = compound_string(predictors->string[i], "linear", " ");
+        for (int32_t i = 0; i < get_string_count(predictors); i++) {
+                get_environment_from_registry(&conf->environment_registry, get_string(predictors,i));
+                char *predictor_linear = compound_string(get_string(predictors, i), "linear", " ");
                 load_conf_value(true, ini, environment_section, predictor_linear, &glm.linear[i]);
                 free(predictor_linear);
                 glm.count += 1;
 
                 if (glm.type == GLM_QUADRATIC) {
-                        char *predictor_quadratic = compound_string(predictors->string[i], "quadratic", " ");
+                        char *predictor_quadratic = compound_string(get_string(predictors, i), "quadratic", " ");
                         load_conf_value(true, ini, environment_section, predictor_quadratic, &glm.quadratic[i]);
                         free(predictor_quadratic);
                 }
@@ -160,9 +160,9 @@ void add_glm_from_conf(struct cats_configuration *conf, struct cats_ini *ini, ch
         struct cats_environment *set = add_environment(conf, name, type, &glm);
         load_conf_value(false, ini, environment_section, "save environment", &set->save_environment);
 
-        for (int32_t i = 0; i < predictors->count; i++) {
+        for (int32_t i = 0; i < get_string_count(predictors); i++) {
                 struct cats_environment_variable *env = get_environment_from_registry(&conf->environment_registry,
-                                                                                      predictors->string[i]);
+                                                                                      get_string(predictors, i));
                 add_to_environment(set, env);
         }
 
diff --git a/src/cats/configuration/load_configuration_helper.c b/src/cats/configuration/load_configuration_helper.c
index 2e9375712b0aae95a2e40057c6f957a48ec965ff..ee3c4527710f651f9377dbfa03fe1a95bf616544 100644
--- a/src/cats/configuration/load_configuration_helper.c
+++ b/src/cats/configuration/load_configuration_helper.c
@@ -60,7 +60,7 @@ void reset_same_dispersals(bool all_same, struct cats_configuration_counts *coun
 }
 
 
-int32_t count_kernel_dispersal_for_species(struct cats_ini *ini, char *species_section)
+int32_t count_kernel_dispersal_for_species(struct cats_ini *ini, const char *species_section)
 {
         int32_t dispersal_count = 0;
         char *dispersal_name = NULL;
@@ -77,14 +77,14 @@ int32_t count_kernel_dispersal_for_species(struct cats_ini *ini, char *species_s
 
         struct string_array *dispersal_kernel_filenames = get_char_array_from_conf2(ini, dispersal_section_name,
                                                                                     "kernel filenames");
-        if (!dispersal_kernel_filenames->count) {
+        if (!get_string_count(dispersal_kernel_filenames)) {
                 log_message(LOG_ERROR,
                             "Error creating configuration structures: could not load dispersal information for species '%s': '%s' [%s]",
                             species_section, dispersal_name, dispersal_section_name);
                 exit_cats(EXIT_FAILURE);
         }
 
-        dispersal_count = dispersal_kernel_filenames->count;
+        dispersal_count = get_string_count(dispersal_kernel_filenames);
 
         free(dispersal_section_name);
         free_string_array(&dispersal_kernel_filenames);
@@ -100,13 +100,13 @@ struct cats_configuration_counts get_counts_from_config(struct cats_ini *ini)
 
         // required
         struct string_array *species = get_sections_with_prefix(ini, "species");
-        counts.species = species->count;
+        counts.species = get_string_count(species);
 
 
         counts.dispersals = new_raw_1d_array(counts.species, sizeof(int));
 
         for (int i = 0; i < counts.species; i++) {
-                counts.dispersals[i] = count_kernel_dispersal_for_species(ini, species->string[i]);
+                counts.dispersals[i] = count_kernel_dispersal_for_species(ini, get_string(species, i));
         }
 
         free_string_array(&species);
diff --git a/src/cats/configuration/load_configuration_modules.c b/src/cats/configuration/load_configuration_modules.c
index 281d1ebe2d5951d187c2b8c16fcaa159bdeb2f50..bd3c3d7b0291968f2cda02ca97aaedc25d23ceaf 100644
--- a/src/cats/configuration/load_configuration_modules.c
+++ b/src/cats/configuration/load_configuration_modules.c
@@ -29,9 +29,9 @@
 void load_configuration_modules(struct cats_configuration *conf, struct cats_ini *ini)
 {
         struct string_array *modules = get_sections_with_prefix(ini, "module");
-        for (int32_t i = 0; i < modules->count; i++) {
+        for (int32_t i = 0; i < get_string_count(modules); i++) {
 
-                char *module_section = modules->string[i];
+                const char *module_section = get_string(modules, i);
                 char *name = remove_0th_token(module_section, ":");
                 if (i >= MAX_MODULES) {
                         log_message(LOG_ERROR, "too many modules, limit is %d", MAX_MODULES);
diff --git a/src/cats/configuration/load_configuration_overlays.c b/src/cats/configuration/load_configuration_overlays.c
index 31fe1d7e9878273c70ccd1f6e1645dba8f2b26e6..f48163d2bbf498b9f81884343205b84a7f9655fd 100644
--- a/src/cats/configuration/load_configuration_overlays.c
+++ b/src/cats/configuration/load_configuration_overlays.c
@@ -24,8 +24,8 @@
 #include <string.h>
 #include <math.h>
 
-#include <logging/logging.h>
-#include <cats_strings/cats_strings.h>
+#include <bdc_logging/logging.h>
+#include <bdc_strings/bdc_strings.h>
 #include "load_configuration_overlays.h"
 #include "overlays/overlay_habitat_type_cc.h"
 #include "grids/grid_setup.h"
@@ -36,15 +36,15 @@ void load_overlay_configuration(struct cats_configuration *conf, struct cats_ini
         if (conf->command_line_options.no_input_rasters_required) return;
         struct string_array *overlay_names = get_sections_with_prefix(ini, "overlay");
 
-        for (int32_t i = 0; i < overlay_names->count; i++) {
-                char *overlay_section = overlay_names->string[i];
+        for (int32_t i = 0; i < get_string_count(overlay_names); i++) {
+                const char *overlay_section = get_string(overlay_names, i);
                 bool enabled = true;
                 load_conf_value(false, ini, overlay_section, "enabled", &enabled);
                 if (enabled == false) {
                         continue;
                 }
 
-                char *overlay_name = remove_0th_token(overlay_names->string[i], ":");
+                char *overlay_name = remove_0th_token(get_string(overlay_names, i), ":");
 
 
                 char *type_name = NULL;
diff --git a/src/cats/configuration/load_configuration_species_params.c b/src/cats/configuration/load_configuration_species_params.c
index a7418dde57b2746150b0e7dbf16f2dd21bbe6807..ec3ce4edb5cf392e76ed3fd109d3fd9d0c584d7d 100644
--- a/src/cats/configuration/load_configuration_species_params.c
+++ b/src/cats/configuration/load_configuration_species_params.c
@@ -30,7 +30,7 @@
 
 #include "bdc_memory/bdc_memory.h"
 #include <cats_ini/cats_ini.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #include "configuration/configuration.h"
 #include "load_configuration_dispersal.h"
@@ -46,7 +46,7 @@ enum sexuality_type get_sexuality_from_string(const char *string)
 }
 
 
-void preload_default_vital_rates_for_presets(struct cats_configuration *conf, struct cats_ini *ini, char *species_section,
+void preload_default_vital_rates_for_presets(struct cats_configuration *conf, struct cats_ini *ini, const char *species_section,
                                              struct cats_species_param *p)
 {
         // load values that could be used for presets -- e.g. some presets could depend on these values
@@ -62,7 +62,7 @@ void preload_default_vital_rates_for_presets(struct cats_configuration *conf, st
 }
 
 
-void preload_default_vital_ages_for_presets(struct cats_configuration *conf, struct cats_ini *ini, char *species_section,
+void preload_default_vital_ages_for_presets(struct cats_configuration *conf, struct cats_ini *ini, const char *species_section,
                                             struct cats_species_param *p)
 {
         for (enum cats_vital_age_id age_id = VA_MIN + 1; age_id < VA_MAX; age_id++) {
@@ -73,7 +73,7 @@ void preload_default_vital_ages_for_presets(struct cats_configuration *conf, str
 }
 
 
-void load_conf_vital_ages(struct cats_configuration *conf, struct cats_ini *ini, char *species_section,
+void load_conf_vital_ages(struct cats_configuration *conf, struct cats_ini *ini, const char *species_section,
                           struct cats_species_param *p)
 {
         for (enum cats_vital_age_id age_id = VA_MIN + 1; age_id < VA_MAX; age_id++) {
@@ -125,7 +125,7 @@ void load_conf_vital_rate(struct cats_vital_rate *vr, struct cats_configuration
 
 
 void load_plant_species_parameter(struct cats_configuration *conf, struct cats_ini *ini,
-                                  char *species_name, struct cats_species_param *p)
+                                  const char *species_name, struct cats_species_param *p)
 {
 
         char *preset_string = NULL;
@@ -182,7 +182,7 @@ void load_plant_species_parameter(struct cats_configuration *conf, struct cats_i
 
 
 void load_config_species_parameter(struct cats_configuration *conf, struct cats_ini *ini, int species_idx,
-                                   char *species_section)
+                                   const char *species_section)
 {
         struct cats_species_param *p = &conf->param[species_idx];
 
@@ -290,7 +290,7 @@ void load_config_species_parameter(struct cats_configuration *conf, struct cats_
 }
 
 
-void load_parameter_sets_all_same(struct cats_configuration *conf, struct cats_ini *ini, int species_idx, char *name)
+void load_parameter_sets_all_same(struct cats_configuration *conf, struct cats_ini *ini, int species_idx, const char *name)
 {
         assert(species_idx >= 0);
         char *species_name = strdup(conf->param[0].species_name);
@@ -313,8 +313,8 @@ void load_all_species(struct cats_configuration *conf, struct cats_ini *ini)
 {
         struct string_array *species = get_sections_with_prefix(ini, "species");
 
-        for (int32_t i = 0; i < species->count; i++) {
-                char *species_section = species->string[i];
+        for (int32_t i = 0; i < get_string_count(species); i++) {
+                const char *species_section = get_string(species, i);
                 load_config_species_parameter(conf, ini, i, species_section);
 
                 if (conf->all_species_same && i == 0) {
diff --git a/src/cats/data/cats_datatypes.c b/src/cats/data/cats_datatypes.c
index c311e402cf4b5f2158e07de5fb190bcb2c7fb0ad..06624c3f9415516e8ead6192a02a471f4b72a335 100644
--- a/src/cats/data/cats_datatypes.c
+++ b/src/cats/data/cats_datatypes.c
@@ -35,7 +35,7 @@ const char *false_values[] = {"0", "n", "f"};
 const int true_counts = (int) (sizeof(true_values) / sizeof(char *));
 const int false_counts = (int) (sizeof(false_values) / sizeof(char *));
 
-
+/*
 bool string_to_double(char *string, double *value)
 {
         if (string == NULL || strlen(string) == 0) return false;
@@ -149,5 +149,5 @@ bool string_to_integer(char *string, int32_t *value)
         }
         return false;
 }
-
+*/
 
diff --git a/src/cats/data/cats_datatypes.h b/src/cats/data/cats_datatypes.h
index d2bba5466e47223b70793c29c3c30a8da695e390..eea745469db26eb2c80f128ffd223b5811db2c23 100644
--- a/src/cats/data/cats_datatypes.h
+++ b/src/cats/data/cats_datatypes.h
@@ -58,6 +58,6 @@ struct cats_coordinates {
 
 
 // conversion functions
-#include <cats_strings/string_converters.h>
+#include <bdc_strings/bdc_strings.h>
 
 #endif
\ No newline at end of file
diff --git a/src/cats/data/cats_global.c b/src/cats/data/cats_global.c
index d3a574978bda439c226640c80d2ed34b690a8f0b..977f3e3296a1209e7e20a1e5041e701f458dabb1 100644
--- a/src/cats/data/cats_global.c
+++ b/src/cats/data/cats_global.c
@@ -24,7 +24,7 @@
 #include "misc/misc.h"
 #include "defaults.h"
 #include "bdc_memory/bdc_memory.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include <stdlib.h>
 
 
diff --git a/src/cats/data/simulation_geometry.c b/src/cats/data/simulation_geometry.c
index ed9e0b14fe8659c2876789622156f3cdd0ef6555..9959d15cadfbd2679c41ee2475caced2740b611c 100644
--- a/src/cats/data/simulation_geometry.c
+++ b/src/cats/data/simulation_geometry.c
@@ -23,7 +23,7 @@
 #include "cats_global.h"
 #include <math.h>
 #include "simulation_geometry.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 void init_simulation_geometry(struct simulation_geometry *geo)
 {
diff --git a/src/cats/debug/debug.c b/src/cats/debug/debug.c
index ebe381a57c45c402db821cb9d3a75fa58db646f9..b924064d8307e46b61885504b95e7849eadf5bbb 100644
--- a/src/cats/debug/debug.c
+++ b/src/cats/debug/debug.c
@@ -24,8 +24,9 @@
 #include "bdc_memory/bdc_memory.h"
 #include "debug/debug.h"
 #include "threading/threading-helpers.h"
-#include <cats_strings/cats_strings.h>
+#include <bdc_strings/bdc_strings.h>
 #include "misc/misc.h"
+#include "bdc_io/bdc_io.h"
 
 
 #define DEBUG_DIR "debug"
diff --git a/src/cats/debug/debug_vital_rates.c b/src/cats/debug/debug_vital_rates.c
index ca9da40274bbe54f90f692b0b6daea0a60d91f7e..996e31e8b6ddf2509b067f924446760599a56651 100644
--- a/src/cats/debug/debug_vital_rates.c
+++ b/src/cats/debug/debug_vital_rates.c
@@ -26,6 +26,7 @@
 #include "inline_carrying_capacity.h"
 #include "grids/grid_setup.h"
 #include "paths/directory_helper.h"
+#include "bdc_io/bdc_io.h"
 
 
 struct cats_environment *minimal_suitability_environment(void)
diff --git a/src/cats/environment/environment.c b/src/cats/environment/environment.c
index 353a79783c1cd82b153359f0680f06f7ff19be5e..04d0ae375dfdd8e5299a07bd387baecd6a209649 100644
--- a/src/cats/environment/environment.c
+++ b/src/cats/environment/environment.c
@@ -26,7 +26,7 @@
 #endif
 
 #include <assert.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #include "configuration/configuration.h"
 #include "memory.h"
diff --git a/src/cats/environment/environment_registry.c b/src/cats/environment/environment_registry.c
index fa1f606830009413903de358d4d29dedf73d1a14..d2208b75dca4ba6e9dc09ebd4259ac0de6e0c09e 100644
--- a/src/cats/environment/environment_registry.c
+++ b/src/cats/environment/environment_registry.c
@@ -25,7 +25,7 @@
 #include <limits.h>
 #include "environment/environment_structures.h"
 #include "bdc_memory/bdc_memory.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 void reset_environment_registry(struct cats_environment_registry *reg)
 {
@@ -125,10 +125,10 @@ add_environment_to_registry(struct cats_environment_registry *registry, const ch
 }
 
 
-struct cats_environment_variable *get_environment_from_registry(struct cats_environment_registry *reg, char *name)
+struct cats_environment_variable *get_environment_from_registry(struct cats_environment_registry *reg, const char *name)
 {
 
-        int32_t idx = string_array_index(reg->name, name);
+        int32_t idx = string_array_string_index(reg->name, name);
         if (idx < 0) {
                 log_message(LOG_ERROR, "%s: could not find environment '%s' in registry.", __func__, name);
                 exit(EXIT_FAILURE);
@@ -148,7 +148,7 @@ void cleanup_environment_registry(struct cats_environment_registry *reg)
 {
 
         for (int32_t i = 0; i < reg->count; i++) {
-                log_message(LOG_INFO, "%s: cleaning up %s", __func__, reg->name->string[i]);
+                log_message(LOG_INFO, "%s: cleaning up %s", __func__, get_string(reg->name, i));
                 cleanup_cats_environment_variable(&reg->environment[i]);
         }
 
diff --git a/src/cats/environment/environment_registry.h b/src/cats/environment/environment_registry.h
index 1d4680f2160cc46b354b6d2dd67ebf41671ad086..e5a810dc25cf48dc24bfdd6da175df5e4050d87b 100644
--- a/src/cats/environment/environment_registry.h
+++ b/src/cats/environment/environment_registry.h
@@ -38,7 +38,7 @@ add_environment_to_registry(struct cats_environment_registry *registry, const ch
                             enum environment_variable_type type, const char *file_name_pattern, int32_t reload_interval,
                             int32_t interpolation, const struct array_dimension *dimension);
 
-struct cats_environment_variable *get_environment_from_registry(struct cats_environment_registry *reg, char *name);
+struct cats_environment_variable *get_environment_from_registry(struct cats_environment_registry *reg, const char *name);
 
 void cleanup_environment_registry(struct cats_environment_registry *reg);
 
diff --git a/src/cats/environment/environment_set.c b/src/cats/environment/environment_set.c
index 7e5f6b0662ebd9f4e8fb14169b6220304fc7ca51..c0d34ea4f12ea5e20cc1afbd76cc7f87f7c5e172 100644
--- a/src/cats/environment/environment_set.c
+++ b/src/cats/environment/environment_set.c
@@ -177,12 +177,12 @@ void add_to_environment(struct cats_environment *environment, struct cats_enviro
 void failed_to_find_environment(const struct cats_configuration *conf, const char *name)
 {
         log_message(LOG_ERROR, "%s: could not find environment set '%s' in registry", __func__, name);
-        if (conf->environment.names == NULL || conf->environment.names->count == 0) {
+        if (conf->environment.names == NULL || get_string_count(conf->environment.names) == 0) {
                 log_message(LOG_ERROR, "%s: registry empty ('%s')", __func__, name);
         } else {
-                for (int32_t i = 0; i < conf->environment.names->count; i++) {
+                for (int32_t i = 0; i < get_string_count(conf->environment.names); i++) {
                         log_message(LOG_INFO, "%s: have registry entry: '%s'", __func__,
-                                    conf->environment.names->string[i]);
+                                    get_string(conf->environment.names, i));
 
                 }
         }
@@ -193,10 +193,10 @@ void failed_to_find_environment(const struct cats_configuration *conf, const cha
 
 struct cats_environment *get_environment(const struct cats_configuration *conf, const char *name)
 {
-        if (conf->environment.names == NULL || conf->environment.names->count == 0) {
+        if (conf->environment.names == NULL || get_string_count(conf->environment.names) == 0) {
                 failed_to_find_environment(conf, name);
         }
-        int32_t idx = string_array_index(conf->environment.names, name);
+        int32_t idx = string_array_string_index(conf->environment.names, name);
         if (idx < 0) failed_to_find_environment(conf, name);
 
 
diff --git a/src/cats/environment/load_environment.c b/src/cats/environment/load_environment.c
index c1324c5583a68fa3d4859be34a9db7bab54c862b..f9119cfd94ec26852f0fcf1e26aae176435cb32b 100644
--- a/src/cats/environment/load_environment.c
+++ b/src/cats/environment/load_environment.c
@@ -76,7 +76,7 @@ enum action_status update_environment_warmup(struct cats_configuration *conf, in
                                                       time);
                 } else if (time == start) {
                         log_message(LOG_INFO, " ENV: loading environment %s for year %d as static predictor",
-                                    conf->environment_registry.name->string[i],
+                                    get_string(conf->environment_registry.name, i),
                                     time);
                         load_environment_raster(conf, &conf->environment_registry.environment[i], INTERPOLATION_CURRENT,
                                                 time);
diff --git a/src/cats/grids/cats_grid.c b/src/cats/grids/cats_grid.c
index 9665728c33dee67cfa59c6909ff9b2d944d3d439..882fe5f855fb7c108c7f9fbe2db2bcdc96571041 100644
--- a/src/cats/grids/cats_grid.c
+++ b/src/cats/grids/cats_grid.c
@@ -55,6 +55,7 @@
 #include "paths/output_paths.h"
 #include "stats/global_stats.h"
 #include "actions/setup.h"
+#include "bdc_io/bdc_io.h"
 
 
 struct cats_grid **create_and_initialize_grids(struct cats_configuration *conf, const int32_t count)
diff --git a/src/cats/grids/gdal_helper.c b/src/cats/grids/gdal_helper.c
index 403df5601e4fe85ec1911094ce27f14dc515e69d..3ac72218a910e1ce56d5429ac8acbff79f55af4f 100644
--- a/src/cats/grids/gdal_helper.c
+++ b/src/cats/grids/gdal_helper.c
@@ -26,7 +26,7 @@
 #include <cpl_string.h>
 #include <assert.h>
 
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "gdal_load.h"
 #include "configuration/configuration.h"
 #include "misc/misc.h"
@@ -167,7 +167,7 @@ void verify_raster_geometry(GDALDatasetH dataset, struct simulation_geometry *ge
                 log_message(LOG_INFO, "GEOM: global projection string not set, ignoring projection '%s'", projection);
         } else {
                 if (strcmp(projection, geometry->projection_string) != 0) {
-                        enum cats_log_level ll;
+                        enum bdc_log_level ll;
                         if (geometry->ignore_raster_projection) {
                                 ll = LOG_INFO;
                         } else {
diff --git a/src/cats/hybrid/scalefactor.c b/src/cats/hybrid/scalefactor.c
index 040be9f7dbd824be3c3c3fafad4801e064e177fc..f1c8563617f8e1973c06366b3aa8daeb8a4a1098 100644
--- a/src/cats/hybrid/scalefactor.c
+++ b/src/cats/hybrid/scalefactor.c
@@ -29,7 +29,7 @@
 #endif
 
 #include <cats_ini/cats_ini.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #include "cats/command_line/command_line_options.h"
 #include "configuration/load_vital_rates.h"
diff --git a/src/cats/inline_population.h b/src/cats/inline_population.h
index 20937d8d3b768e025fd73c42f91a824d3b5ba9ab..d52ced908656ef1804655e811a71d2b09cecfad8 100644
--- a/src/cats/inline_population.h
+++ b/src/cats/inline_population.h
@@ -26,7 +26,7 @@
 #include "cats_global.h"
 #include <math.h>
 #include "data/cats_datatypes.h"
-#include "logging/logging.h"
+#include "bdc_logging/logging.h"
 #include "assert.h"
 #include "configuration/configuration.h"
 #include "inline_overlays.h"
diff --git a/src/cats/lambda/eigen.c b/src/cats/lambda/eigen.c
index 5933034471c6befbd7ff66ba0bb039804957b185..360d20865dd9efbe3cffe22d19cd82e8ab6cb954 100644
--- a/src/cats/lambda/eigen.c
+++ b/src/cats/lambda/eigen.c
@@ -25,7 +25,7 @@
 #include <stdbool.h>
 #include <math.h>
 
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "eigen.h"
 #include "matrix_helpers.h"
 #include "inline.h"
diff --git a/src/cats/lambda/leslie_matrix.c b/src/cats/lambda/leslie_matrix.c
index e1a858201b7a2c4c927c5d514f6cf04775b04911..491e7ede62044e9d89b5455e1b8bdba299ed38af 100644
--- a/src/cats/lambda/leslie_matrix.c
+++ b/src/cats/lambda/leslie_matrix.c
@@ -24,7 +24,7 @@
 #include "dispersal/dispersal.h"
 #include "leslie_matrix.h"
 #include "bdc_memory/bdc_memory.h"
-#include "logging/logging.h"
+#include "bdc_logging/logging.h"
 #include "configuration/configuration.h"
 #include "inline.h"
 #include "matrix_helpers.h"
diff --git a/src/cats/lambda/matrix_helpers.c b/src/cats/lambda/matrix_helpers.c
index c054e992f0a9c06fb3b675d4f1f0ce60360e22d1..e5694b85d4eb0d3caf56f724969000f7a1c200a3 100644
--- a/src/cats/lambda/matrix_helpers.c
+++ b/src/cats/lambda/matrix_helpers.c
@@ -21,7 +21,7 @@
 //
 
 #include <assert.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "bdc_memory/bdc_memory.h"
 #include "cats_global.h"
 
diff --git a/src/cats/misc/debug.c b/src/cats/misc/debug.c
index f86e57bcf73aae439cb3d99d0265bdff5a830d02..5a92ff63ea3f73df41bd448e359f3cacf2c98672 100644
--- a/src/cats/misc/debug.c
+++ b/src/cats/misc/debug.c
@@ -24,6 +24,7 @@
 #include "misc.h"
 #include "inline.h"
 #include "bdc_memory/bdc_memory.h"
+#include "bdc_io/bdc_io.h"
 
 
 void
diff --git a/src/cats/misc/filesystem.c b/src/cats/misc/filesystem.c
index 88de2fcba682fb2c06ca392398d495fdc30d4897..94b63130d3ee68626ec32246080eb4839c85ce96 100644
--- a/src/cats/misc/filesystem.c
+++ b/src/cats/misc/filesystem.c
@@ -25,7 +25,7 @@
 #include <errno.h>
 #include <string.h>
 
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "filesystem.h"
 #include "data/error.h"
 #ifndef CATS_ON_WINDOWS
diff --git a/src/cats/misc/misc.h b/src/cats/misc/misc.h
index 2d377fd57a67fc6534b6925fa67b9cb25f81fa8a..6194c37b356a7d9804b6c58078e859b11ff45f35 100644
--- a/src/cats/misc/misc.h
+++ b/src/cats/misc/misc.h
@@ -24,20 +24,12 @@
 
 #include <string.h>
 #include "data/error.h"
+
 #define TEXT_DIVIDER "=================================================================================================="
 
 const char *cats_version(void);
 
 
-// this is kept as a macro, so we can print calling function
-#define ENSURE_FILE_OPENED(FILE_HANDLE, FILENAME)\
-if (! (FILE_HANDLE))\
-{\
-        fprintf(stderr, "ERROR: Couldn't open file %s in %s: %d (%s)\n", FILENAME, __FILE__, __LINE__, __func__);\
-        exit(E_UNREADABLE_FILE);\
-}
-
-
 void set_program_name(const char *name);
 
 #endif
\ No newline at end of file
diff --git a/src/cats/modules/load_module.c b/src/cats/modules/load_module.c
index 6e6d9bf421e15ff1b371d8ff599b715ae5dad27b..7859124baa4749dded0bb5189c33ce33667cf60f 100644
--- a/src/cats/modules/load_module.c
+++ b/src/cats/modules/load_module.c
@@ -20,7 +20,7 @@
 //
 
 #include <dlfcn.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include <string.h>
 #include "configuration/configuration.h"
 #include "load_module.h"
diff --git a/src/cats/modules/modules.c b/src/cats/modules/modules.c
index b18d538ea6453f595a0799a9d1bc9a3141b5565d..92ee7f9bb2b3f8e477d2ec20f76665956da8a147 100644
--- a/src/cats/modules/modules.c
+++ b/src/cats/modules/modules.c
@@ -24,7 +24,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include "modules.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 
 void init_cats_module(struct cats_module *module, int32_t id)
 {
diff --git a/src/cats/mpi/mpi_debug.c b/src/cats/mpi/mpi_debug.c
index 1570c2c220b55aaf1aeb217d04b3a96c51279dea..b6b423a13921c928173896aad45c81029042ccdc 100644
--- a/src/cats/mpi/mpi_debug.c
+++ b/src/cats/mpi/mpi_debug.c
@@ -26,6 +26,7 @@
 #include "mpi_debug.h"
 #include "mpi_grid_helpers.h"
 #include "paths/output_paths.h"
+#include "bdc_io/bdc_io.h"
 
 
 char *get_node_name(const struct cats_configuration *conf)
diff --git a/src/cats/overlays/overlays.c b/src/cats/overlays/overlays.c
index a89b27aa75a005edb3ebb38d9f766912aeddb6cb..c08d061cf482626a35a744b921ba6e0c9e7d322a 100644
--- a/src/cats/overlays/overlays.c
+++ b/src/cats/overlays/overlays.c
@@ -93,8 +93,8 @@ enum overlay_type get_overlay_type_from_name(const char *name, const struct stri
         if (!strcmp(name, "resources")) return OL_RESOURCE;
 
         if (registered_names != NULL) {
-                for (int32_t i = 0; i < registered_names->count; i++) {
-                        if (!strcmp(name, registered_names->string[i])) {
+                for (int32_t i = 0; i < get_string_count(registered_names); i++) {
+                        if (!strcmp(name, get_string(registered_names,i))) {
                                 return OL_CUSTOM;
                         }
                 }
diff --git a/src/cats/paths/directory_helper.c b/src/cats/paths/directory_helper.c
index 80d8c5b4e0aed4626d2dcf5013600fb423860555..6c338405828760d874a60a04127b586bf10c233a 100644
--- a/src/cats/paths/directory_helper.c
+++ b/src/cats/paths/directory_helper.c
@@ -22,7 +22,7 @@
 #include "cats_global.h"
 
 #include <string.h>
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "misc/filesystem.h"
 #include "paths/paths.h"
 #include "paths/output_paths.h"
@@ -32,7 +32,7 @@
 
 bool check_and_create_directory_if_needed(const struct string_array *path)
 {
-        if (path == NULL || path->count == 0) {
+        if (path == NULL || get_string_count(path) == 0) {
                 fprintf(stderr, "Unable to check directory - no path specified\n");
                 log_message(LOG_ERROR, "\tunable to check directory - no path specified");
                 exit_cats(E_SYSTEM_ERROR);
@@ -94,8 +94,8 @@ void ensure_needed_directories_exist(const struct cats_configuration *conf, cons
         struct string_array *output = new_string_array_init(conf->output.output_directory);
         check_and_create_directory_if_needed(output);
         free_string_array(&output);
-        for (int32_t i = 0; i < output_dirs->count; i++) {
-                struct string_array *path = get_output_directory(conf, output_dirs->string[i]);
+        for (int32_t i = 0; i < get_string_count(output_dirs); i++) {
+                struct string_array *path = get_output_directory(conf, get_string(output_dirs,i));
                 check_and_create_directory_if_needed(path);
                 free_string_array(&path);
 
diff --git a/src/cats/paths/output_paths.c b/src/cats/paths/output_paths.c
index 08e0fc9c398959da4661ad25a63997fb77736944..47ad4df0936015681e2449f552c5de85d3bb44b2 100644
--- a/src/cats/paths/output_paths.c
+++ b/src/cats/paths/output_paths.c
@@ -22,7 +22,7 @@
 #include <string.h>
 #include <assert.h>
 
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 #include "data/cats_grid.h"
 #include "temporal/phase_names.h"
@@ -30,6 +30,7 @@
 
 #include "output_paths.h"
 #include "paths.h"
+#include "bdc_io/bdc_io.h"
 
 
 const char *get_base_output_directory(const struct cats_configuration *conf)
diff --git a/src/cats/paths/paths.c b/src/cats/paths/paths.c
index 53310ab453a5e7b765296ef5f080108ea660e9a8..56b95deb5e2a2f32c74fc766cbb2994d822fb4d1 100644
--- a/src/cats/paths/paths.c
+++ b/src/cats/paths/paths.c
@@ -28,7 +28,7 @@
 #include "cats_global.h"
 #include "data/cats_grid.h"
 #include "configuration/configuration.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 
 
 #include "temporal/phase_names.h"
@@ -60,7 +60,7 @@ char *assemble_filename(struct string_array *path, struct string_array *filename
         char *name_string = string_array_paste(filename, filesep);
         char *result = NULL;
 
-        if (path && path->count > 0) {
+        if (path && get_string_count(path) > 0) {
                 char *path_string = string_array_paste(path, "/");
                 if (extension && strlen(extension)) {
                         char *fn = compound_string(name_string, extension, ".");
diff --git a/src/cats/paths/paths.h b/src/cats/paths/paths.h
index b2cfdbc40f1ed4ccfa07a05b6b9ec4c9507ac39e..583ada17fc000a72ea4c8e3ab6598b7b846728b2 100644
--- a/src/cats/paths/paths.h
+++ b/src/cats/paths/paths.h
@@ -22,7 +22,7 @@
 #pragma once
 
 #include <stdint.h>
-#include <cats_strings/cats_strings.h>
+#include <bdc_strings/bdc_strings.h>
 
 struct cats_configuration;
 struct cats_environment_variable;
diff --git a/src/cats/paths/paths_suitability.c b/src/cats/paths/paths_suitability.c
index d3360fa00fd5f2cecdbf0b81596f43ac0f678b53..37739e6cf13982486dd385b8e1c9e89f911f7a1e 100644
--- a/src/cats/paths/paths_suitability.c
+++ b/src/cats/paths/paths_suitability.c
@@ -26,6 +26,7 @@
 #include "temporal/timeformat.h"
 #include "data/species_parameters.h"
 #include "path_patterns.h"
+#include "bdc_io/bdc_io.h"
 
 
 char *
diff --git a/src/cats/stats/global_stats.c b/src/cats/stats/global_stats.c
index a412a8250249b7e44b8a0f4dd089875b85669f04..c7aee12914706c807cb5dfeb2453de498f33369e 100644
--- a/src/cats/stats/global_stats.c
+++ b/src/cats/stats/global_stats.c
@@ -30,6 +30,7 @@
 #include "stats/grid_stats.h"
 #include "paths/output_paths.h"
 #include "bdc_memory/bdc_memory.h"
+#include "bdc_io/bdc_io.h"
 
 #ifdef USEMPI
 #include "mpi/mpi_stats.h"
diff --git a/src/cats/stats/lambda_stats.c b/src/cats/stats/lambda_stats.c
index 7f02b8b7a229db96732cec6d3e4c411577f317c6..ee5f5a34368a8086bacaaf29749885ac2fea709d 100644
--- a/src/cats/stats/lambda_stats.c
+++ b/src/cats/stats/lambda_stats.c
@@ -25,6 +25,7 @@
 #include "inline.h"
 #include "metadata.h"
 #include "inline_vital_rates.h"
+#include "bdc_io/bdc_io.h"
 
 
 FILE *
diff --git a/src/cats/stats/statistics.c b/src/cats/stats/statistics.c
index c1ed496cbcd5deeafe2bfc58b0744db5231f40d5..1834bfdaa54da16b4155c214d9b8ac6828fb6536 100644
--- a/src/cats/stats/statistics.c
+++ b/src/cats/stats/statistics.c
@@ -23,7 +23,7 @@
 #include <assert.h>
 #include <stdlib.h>
 #include "statistics.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 #include "bdc_memory/bdc_memory.h"
 #include "configuration/configuration.h"
 
diff --git a/src/cats/threading/threading-helpers.c b/src/cats/threading/threading-helpers.c
index 0877f6b54d05e7716d0752321462248a68be6850..cdf3e7ad20bd17675a129be72cb49aa77cf4fc68 100644
--- a/src/cats/threading/threading-helpers.c
+++ b/src/cats/threading/threading-helpers.c
@@ -27,6 +27,8 @@
 #include "misc/cats_random.h"
 #include "stats/grid_stats.h"
 #include "bdc_memory/bdc_memory.h"
+#include "data/error.h"
+
 
 void create_custom_stats_for_thread(struct statistics *thread_stats, struct statistics *grid_stats)
 {
diff --git a/src/cats/vital_ages/default_vital_ages.c b/src/cats/vital_ages/default_vital_ages.c
index bbff02ce684f6ca29ecd2d4e1de0d3849b6fc0c7..f9ce195723cdcc2abed8a8503591ad493a465af6 100644
--- a/src/cats/vital_ages/default_vital_ages.c
+++ b/src/cats/vital_ages/default_vital_ages.c
@@ -20,7 +20,7 @@
 //
 
 #include "default_vital_ages.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 const char *get_vital_age_name(enum cats_vital_age_id idx)
 {
diff --git a/src/cats/vital_rates/setup_rates.c b/src/cats/vital_rates/setup_rates.c
index 7b738d9ffc6fdfb485bbea8d84e7572c001a52ad..02883ad28173c2d87b8c964f33ec599a0cdf3dfe 100644
--- a/src/cats/vital_rates/setup_rates.c
+++ b/src/cats/vital_rates/setup_rates.c
@@ -21,7 +21,7 @@
 
 #include <math.h>
 #include <assert.h>
-#include <cats_strings/cats_strings.h>
+#include <bdc_strings/bdc_strings.h>
 #include "logging.h"
 #include "setup_rates.h"
 #include "string.h"
diff --git a/src/cats/vital_rates/vital_rate_ranges.c b/src/cats/vital_rates/vital_rate_ranges.c
index 44451ee91776abd948937deeec0d47a9fb7a6afc..82635a88d03740ec63b7b1865335f6e5293d7bb5 100644
--- a/src/cats/vital_rates/vital_rate_ranges.c
+++ b/src/cats/vital_rates/vital_rate_ranges.c
@@ -21,7 +21,7 @@
 
 #include <math.h>
 #include "vital_rate_ranges.h"
-#include "logging/logging.h"
+#include "bdc_logging/logging.h"
 
 
 int check_vital_rate_hybrid(struct cats_vital_rate *vr)
diff --git a/src/cats_csv/CMakeLists.txt b/src/cats_csv/CMakeLists.txt
index d3afbbe76e17a53b45f8fd9363045c61138fb5d6..5f055d7c04cd350d447de75f9ce17480ad5a1600 100644
--- a/src/cats_csv/CMakeLists.txt
+++ b/src/cats_csv/CMakeLists.txt
@@ -13,4 +13,4 @@ target_sources(cats_csv
 
 
 set_property(TARGET cats_csv PROPERTY POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(cats_logging bdc cats_strings)
\ No newline at end of file
+target_link_libraries(bdc_logging bdc bdc_strings)
\ No newline at end of file
diff --git a/src/cats_csv/cats_csv.c b/src/cats_csv/cats_csv.c
index db8cf5940b54461a97ad6fe45a81bbeec08925db..c3d4c2eeb3025b912688ae29b9097bb7a1381e05 100644
--- a/src/cats_csv/cats_csv.c
+++ b/src/cats_csv/cats_csv.c
@@ -26,8 +26,9 @@
 #include <string.h>
 
 #include "cats_csv.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 #include "bdc_memory/bdc_memory.h"
+#include "bdc_io/bdc_io.h"
 
 
 struct cats_csv *csv_new(char *header_line, int32_t expected_fields)
@@ -42,21 +43,21 @@ struct cats_csv *csv_new(char *header_line, int32_t expected_fields)
 
         struct string_array *headers = get_all_tokens(header_line, ",");
 
-        if (headers->count != expected_fields) {
-                log_message(LOG_ERROR, "%s: disparity in field count: got %d expected %d\n", __func__, headers->count,
+        if (get_string_count(headers) != expected_fields) {
+                log_message(LOG_ERROR, "%s: disparity in field count: got %d expected %d\n", __func__, get_string_count(headers),
                             expected_fields);
                 exit(EXIT_FAILURE);
         }
 
         struct cats_csv *result = malloc_or_die_trace(sizeof(struct cats_csv), __func__);
 
-        result->column_count = headers->count;
+        result->column_count = get_string_count(headers);
         result->data_row_count = 0;
         result->data = NULL;
         result->headers = malloc_or_die_trace(result->column_count * sizeof(char *), __func__);
 
-        for (int32_t i = 0; i < headers->count; i++) {
-                result->headers[i] = strdup(headers->string[i]);
+        for (int32_t i = 0; i < get_string_count(headers); i++) {
+                result->headers[i] = strdup(get_string(headers, i));
         }
 
         free_string_array(&headers);
@@ -224,8 +225,8 @@ void csv_add_row(struct cats_csv *csv, char *line)
 
         struct string_array *data = get_all_tokens(line, ",");
 
-        if (csv->column_count != data->count) {
-                log_message(LOG_ERROR, "%s: disparity in field count: got %d expected %d\n", __func__, data->count,
+        if (csv->column_count != get_string_count(data)) {
+                log_message(LOG_ERROR, "%s: disparity in field count: got %d expected %d\n", __func__, get_string_count(data),
                             csv->column_count);
                 exit(EXIT_FAILURE);
         }
@@ -233,14 +234,14 @@ void csv_add_row(struct cats_csv *csv, char *line)
 
         csv->data_row_count = csv->data_row_count + 1;
 
-        csv->data = realloc(csv->data, csv->data_row_count * sizeof(char **));
+        csv->data = realloc_or_die(csv->data, csv->data_row_count * sizeof(char **));
 
         int32_t row = csv->data_row_count - 1;
 
         csv->data[row] = malloc_or_die_trace(fields * sizeof(char *), __func__);
 
         for (int32_t i = 0; i < csv->column_count; i++) {
-                csv->data[row][i] = strdup(data->string[i]);
+                csv->data[row][i] = strdup(get_string(data,i));
 
         }
 
diff --git a/src/cats_csv/cats_csv.h b/src/cats_csv/cats_csv.h
index 08e79fc66fef2023c3e6852124274ebe229085d7..774183fc93c47fbeb03266a8d9e7d66fbb94764a 100644
--- a/src/cats_csv/cats_csv.h
+++ b/src/cats_csv/cats_csv.h
@@ -24,7 +24,7 @@
 
 #include <stdint.h>
 #include <stdio.h>
-#include <cats_strings/cats_strings.h>
+#include <bdc_strings/bdc_strings.h>
 
 ///@brief Data structure for simple csv files
 struct cats_csv {
diff --git a/src/cats_ini/CMakeLists.txt b/src/cats_ini/CMakeLists.txt
index 8b03aec39edbde74a99e212b9b403c7e09033d1e..3aa7adc109faf55147e34375e43b33f88dd9d7c4 100644
--- a/src/cats_ini/CMakeLists.txt
+++ b/src/cats_ini/CMakeLists.txt
@@ -18,4 +18,4 @@ target_sources(cats_ini
 
 
 set_property(TARGET cats_ini PROPERTY POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(cats_logging cats_strings bdc)
+target_link_libraries(bdc_strings bdc_logging bdc)
diff --git a/src/cats_ini/cats_ini.c b/src/cats_ini/cats_ini.c
index 96f1de8de6735176ad6fc52b36c1b55819561a81..abc937a636a9e26cfd8b1f6adc053c7459031151 100644
--- a/src/cats_ini/cats_ini.c
+++ b/src/cats_ini/cats_ini.c
@@ -28,8 +28,8 @@
 #include <string.h>
 #include <stddef.h>
 
-#include <cats_strings/cats_strings.h>
-#include <logging/logging.h>
+#include <bdc_strings/bdc_strings.h>
+#include <bdc_logging/logging.h>
 #include "cats_ini.h"
 
 
diff --git a/src/cats_ini/cats_ini_helpers.c b/src/cats_ini/cats_ini_helpers.c
index 89c1643d46c9c5800ddec28cac50ced189fc4757..4a14df026b77edc04b8603905db6d34b200baa72 100644
--- a/src/cats_ini/cats_ini_helpers.c
+++ b/src/cats_ini/cats_ini_helpers.c
@@ -25,7 +25,7 @@
 #include "cats_ini_helpers.h"
 #include "cats_ini_write_values.h"
 #include "misc/misc.h"
-#include <logging/logging.h>
+#include <bdc_logging/logging.h>
 
 void add_complement_value_read(struct cats_ini *ini, char *section, char *key)
 {
diff --git a/src/cats_ini/cats_ini_read_values.c b/src/cats_ini/cats_ini_read_values.c
index b12be6878cac1471a74da089492cfadb7a9e1b56..fcc777ad65473a97798b7b77903296052847bea2 100644
--- a/src/cats_ini/cats_ini_read_values.c
+++ b/src/cats_ini/cats_ini_read_values.c
@@ -22,12 +22,10 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
-#include "../logging/logging.h"
+#include "bdc_logging/logging.h"
 #include "cats_ini.h"
-#include "../cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 #include "cats_ini_read_values.h"
-
-#include "../cats_strings/string_helpers.h"
 #include "cats_ini_helpers.h"
 #include <stdio.h>
 
diff --git a/src/cats_ini/cats_ini_write_values.c b/src/cats_ini/cats_ini_write_values.c
index b99d1be269d89ac4e500e4ece6bbac1fad0d9db2..aa461bb51e6b9bd7043050072ac6baedb11f133f 100644
--- a/src/cats_ini/cats_ini_write_values.c
+++ b/src/cats_ini/cats_ini_write_values.c
@@ -20,10 +20,9 @@
 //
 
 #include "cats_ini_helpers.h"
-#include "../cats_strings/string_helpers.h"
 #include "cats_ini.h"
-#include "../logging/logging.h"
-#include "../cats_strings/cats_strings.h"
+#include "bdc_logging/logging.h"
+#include "bdc_strings/bdc_strings.h"
 #include <stddef.h>
 #include <string.h>
 #include <stdlib.h>
diff --git a/src/cats_strings/CMakeLists.txt b/src/cats_strings/CMakeLists.txt
deleted file mode 100644
index 1414340bcf846d1fb3daec3b9570fc1b58f5df98..0000000000000000000000000000000000000000
--- a/src/cats_strings/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-add_library(cats_strings STATIC "" ../cats_defs.h ../cats_windows.h string_converters.c string_converters.h)
-
-target_sources(cats_strings
-               PRIVATE
-               cats_strings.c
-               io_text.c
-               string_helpers.c
-               pretty_print.c
-               PUBLIC
-               cats_strings.h
-               io_text.h
-               pretty_print.h
-               string_helpers.h
-               )
-
-set_property(TARGET cats_strings PROPERTY POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(cats_logging bdc_time)
\ No newline at end of file
diff --git a/src/cats_strings/io_text.h b/src/cats_strings/io_text.h
deleted file mode 100644
index 224a71ae574159b2274fae8d0f04a7d671235d15..0000000000000000000000000000000000000000
--- a/src/cats_strings/io_text.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// io_text.h
-//
-// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 3 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-
-#ifndef CATS_IO_TEXT_H
-#define CATS_IO_TEXT_H
-
-
-#endif
diff --git a/src/cats_strings/pretty_print.h b/src/cats_strings/pretty_print.h
deleted file mode 100644
index be12b63a789ab371dcd60ff9971b7504b2bad7cf..0000000000000000000000000000000000000000
--- a/src/cats_strings/pretty_print.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// pretty_print.h
-//
-// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 3 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-
-#ifndef CATS_PRETTY_PRINT_H
-#define CATS_PRETTY_PRINT_H
-
-#endif //CATS_PRETTY_PRINT_H
diff --git a/src/cats_strings/string_converters.h b/src/cats_strings/string_converters.h
deleted file mode 100644
index b2a8082bfe4693d0656c7a35c9552d9417daee60..0000000000000000000000000000000000000000
--- a/src/cats_strings/string_converters.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// string_converters.h
-//
-// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 3 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-
-
-#include <stdint.h>
-
-
-
-
-#ifndef CATS_STRING_CONVERTERS_H
-#define CATS_STRING_CONVERTERS_H
-bool string_to_float(char *string, float *value);
-
-bool string_to_bool(char *string, bool *value);
-
-bool string_to_double(char *string, double *value);
-
-bool string_to_integer(char *string, int32_t *value);
-
-bool string_to_long_double(char *string, long double *value);
-#endif //CATS_STRING_CONVERTERS_H
diff --git a/src/cats_strings/string_helpers.h b/src/cats_strings/string_helpers.h
deleted file mode 100644
index 10671ce4bddd214ca68a777d3d835a4f57d4a9c9..0000000000000000000000000000000000000000
--- a/src/cats_strings/string_helpers.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// string_helpers.h
-//
-// Copyright (C) 2011-2024, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 3 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-
-#ifndef CATS_STRING_HELPERS_H
-#define CATS_STRING_HELPERS_H
-
-
-#endif //CATS_STRING_HELPERS_H
diff --git a/src/logging/CMakeLists.txt b/src/logging/CMakeLists.txt
deleted file mode 100644
index 872a70e10aab6cd48ce39fe8e43d472669e4dcce..0000000000000000000000000000000000000000
--- a/src/logging/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-add_library(cats_logging STATIC "" ../cats_windows.h)
-
-target_sources(cats_logging
-               PRIVATE
-               ../cats_defs.h
-               logging.c
-               PUBLIC
-               logging.h
-               )
-
-target_include_directories(cats_logging PUBLIC ".")
-set_property(TARGET cats_logging PROPERTY POSITION_INDEPENDENT_CODE ON)
\ No newline at end of file
diff --git a/src/modules/butterflies/CMakeLists.txt b/src/modules/butterflies/CMakeLists.txt
index c20a250845e6cfd490a47a0312602fc2f7ded1c1..538918b46ccf4b4136a7b81ff1d117be37439c33 100644
--- a/src/modules/butterflies/CMakeLists.txt
+++ b/src/modules/butterflies/CMakeLists.txt
@@ -24,4 +24,4 @@ target_sources(cats-butterflies
 
 
 set_property(TARGET cats-butterflies PROPERTY POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(cats-butterflies cats_logging libcats)
+target_link_libraries(cats-butterflies bdc_logging libcats)
diff --git a/src/modules/butterflies/butterflies_main.c b/src/modules/butterflies/butterflies_main.c
index e8e0b29088f4b9c5d9c516ef879ec9a6eca33e91..28b3fb18a4bb54deba517ea775f1360c57d3a031 100644
--- a/src/modules/butterflies/butterflies_main.c
+++ b/src/modules/butterflies/butterflies_main.c
@@ -39,6 +39,7 @@ struct cats_debug_options cats_debug;
 #include "lambda/leslie_matrix.h"
 #include "actions/setup_actions.h"
 #include "butterflies_scale.h"
+#include "bdc_io/bdc_io.h"
 
 
 cats_dt_rates bf_expected_local_eggs_per_female(struct cats_configuration *conf, struct cats_grid *grid, cats_dt_coord row, cats_dt_coord col)
diff --git a/src/modules/butterflies/butterflies_paths.c b/src/modules/butterflies/butterflies_paths.c
index ba749c26edc9f085135fc2cffcc0faedadae4b73..7cc158d23c1e746830e46473048d0be74792f8db 100644
--- a/src/modules/butterflies/butterflies_paths.c
+++ b/src/modules/butterflies/butterflies_paths.c
@@ -30,6 +30,8 @@
 #include "actions/cats_actions.h"
 #include "butterflies_paths.h"
 #include "module.h"
+#include "bdc_io/bdc_io.h"
+
 
 void bf_add_directories(struct cats_configuration *conf)
 {
diff --git a/src/modules/butterflies/butterflies_stats.c b/src/modules/butterflies/butterflies_stats.c
index 567a20fcd77b48951736851d621058630633321b..d711dcc8ea85fca4723ceac62665c2dddeaba2e8 100644
--- a/src/modules/butterflies/butterflies_stats.c
+++ b/src/modules/butterflies/butterflies_stats.c
@@ -32,6 +32,7 @@
 #include "butterflies_paths.h"
 #include "temporal/phase_names.h"
 #include "temporal/years.h"
+#include "bdc_io/bdc_io.h"
 
 
 const char *bf_get_stats_field_name(enum butterfly_stats which)
diff --git a/src/modules/cats_test_module/CMakeLists.txt b/src/modules/cats_test_module/CMakeLists.txt
index 6ebb9e80daed1eb2b058350a690dea82a2b1d098..68caf04a210f05943e7d41801d3554431e717209 100644
--- a/src/modules/cats_test_module/CMakeLists.txt
+++ b/src/modules/cats_test_module/CMakeLists.txt
@@ -8,4 +8,4 @@ target_sources(cats-test-module PRIVATE
 
 target_include_directories(cats-test-module PUBLIC ".")
 set_property(TARGET cats-test-module PROPERTY POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(cats-test-module cats_logging libcats)
+target_link_libraries(cats-test-module bdc_logging libcats)
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 237b15de3e760cd8c2182d7e35745afd44b8f878..b205a80d7b7822ff3f876118c20538575a06e5db 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -9,5 +9,5 @@ add_executable(vital_rate_test vital_rate_test.c)
 add_executable(csv_test csv_test.c )
 configure_file("test.csv" "test.csv")
 
-target_link_libraries(vital_rate_test cmocka cats_logging bdc cats_strings cats_ini cats_csv libcats)
-target_link_libraries(csv_test cmocka cats_strings cats_csv cats_strings cats_logging bdc)
+target_link_libraries(vital_rate_test cmocka bdc_logging bdc bdc_strings cats_ini cats_csv libcats)
+target_link_libraries(csv_test cmocka bdc_strings cats_csv bdc_strings bdc_logging bdc)
diff --git a/src/tests/csv_test.c b/src/tests/csv_test.c
index 1375ec10c656e6a942ef1817fd61ebe621e0469f..467a7a488288729afe9161f93083d7aab7d72ce1 100644
--- a/src/tests/csv_test.c
+++ b/src/tests/csv_test.c
@@ -24,8 +24,9 @@
 #include <cmocka.h>
 #include <stdio.h>
 #include "bdc_memory/bdc_memory.h"
-#include "cats_strings/cats_strings.h"
+#include "bdc_strings/bdc_strings.h"
 #include "cats_csv/cats_csv.h"
+#include "bdc_io/bdc_io.h"
 
 
 struct csv_test {