From 4da5e4b8bd5e3af8192040e18148905a84106008 Mon Sep 17 00:00:00 2001
From: Andreas Gattringer <andreas.gattringer@univie.ac.at>
Date: Tue, 30 Jan 2024 18:15:07 +0100
Subject: [PATCH] refactoring: added libbdc/bdc_io, bdc_strings, bdc_hashtable
 and bdc_logging

---
 libbdc/CMakeLists.txt                         |   8 +
 libbdc/bdc_hashtable/CMakeLists.txt           |  10 +
 libbdc/bdc_hashtable/bdc_hashtable.c          | 476 ++++++++++++++++++
 libbdc/bdc_hashtable/bdc_hashtable.h          |  88 ++++
 .../bdc_hashtable/tests/test_bdc_hashtable.c  |  52 ++
 libbdc/bdc_io/CMakeLists.txt                  |  13 +
 .../io_text.c => libbdc/bdc_io/bdc_io.c       |  25 +-
 libbdc/bdc_io/bdc_io.h                        |  56 +++
 libbdc/bdc_io/bdc_paths.c                     | 136 +++++
 libbdc/bdc_logging/CMakeLists.txt             |  12 +
 libbdc/bdc_logging/README.md                  |  10 +
 {src/logging => libbdc/bdc_logging}/logging.c | 114 ++---
 {src/logging => libbdc/bdc_logging}/logging.h |  27 +-
 libbdc/bdc_strings/CMakeLists.txt             |  18 +
 libbdc/bdc_strings/README.md                  |   9 +
 .../bdc_strings/bdc_strings.c                 | 113 ++++-
 .../bdc_strings/bdc_strings.h                 | 127 +++--
 .../bdc_strings}/pretty_print.c               |   9 +-
 .../bdc_strings}/string_converters.c          |  16 +-
 .../bdc_strings}/string_helpers.c             |  28 +-
 libbdc/bdc_strings/string_substitution.c      | 158 ++++++
 libbdc/bdc_time/bdc_time.c                    |  42 +-
 libbdc/bdc_time/bdc_time.h                    |  42 +-
 libbdc/bdc_time/bdc_timer.c                   |  21 +
 src/CMakeLists.txt                            |   3 +-
 src/cats/CMakeLists.txt                       |  10 +-
 src/cats/actions/setup_actions.c              |   2 +-
 src/cats/cats.c                               |   2 +-
 src/cats/command_line/command_line_info.c     |   2 +-
 src/cats/command_line/command_line_options.h  |   4 +-
 src/cats/configuration/load_config_spatial.c  |  10 +-
 src/cats/configuration/load_configuration.c   |   3 +-
 .../load_configuration_dispersal.c            |  25 +-
 .../load_configuration_environments.c         |  14 +-
 .../configuration/load_configuration_glm.c    |  18 +-
 .../configuration/load_configuration_helper.c |  10 +-
 .../load_configuration_modules.c              |   4 +-
 .../load_configuration_overlays.c             |  10 +-
 .../load_configuration_species_params.c       |  18 +-
 src/cats/data/cats_datatypes.c                |   4 +-
 src/cats/data/cats_datatypes.h                |   2 +-
 src/cats/data/cats_global.c                   |   2 +-
 src/cats/data/simulation_geometry.c           |   2 +-
 src/cats/debug/debug.c                        |   3 +-
 src/cats/debug/debug_vital_rates.c            |   1 +
 src/cats/environment/environment.c            |   2 +-
 src/cats/environment/environment_registry.c   |   8 +-
 src/cats/environment/environment_registry.h   |   2 +-
 src/cats/environment/environment_set.c        |  10 +-
 src/cats/environment/load_environment.c       |   2 +-
 src/cats/grids/cats_grid.c                    |   1 +
 src/cats/grids/gdal_helper.c                  |   4 +-
 src/cats/hybrid/scalefactor.c                 |   2 +-
 src/cats/inline_population.h                  |   2 +-
 src/cats/lambda/eigen.c                       |   2 +-
 src/cats/lambda/leslie_matrix.c               |   2 +-
 src/cats/lambda/matrix_helpers.c              |   2 +-
 src/cats/misc/debug.c                         |   1 +
 src/cats/misc/filesystem.c                    |   2 +-
 src/cats/misc/misc.h                          |  10 +-
 src/cats/modules/load_module.c                |   2 +-
 src/cats/modules/modules.c                    |   2 +-
 src/cats/mpi/mpi_debug.c                      |   1 +
 src/cats/overlays/overlays.c                  |   4 +-
 src/cats/paths/directory_helper.c             |   8 +-
 src/cats/paths/output_paths.c                 |   3 +-
 src/cats/paths/paths.c                        |   4 +-
 src/cats/paths/paths.h                        |   2 +-
 src/cats/paths/paths_suitability.c            |   1 +
 src/cats/stats/global_stats.c                 |   1 +
 src/cats/stats/lambda_stats.c                 |   1 +
 src/cats/stats/statistics.c                   |   2 +-
 src/cats/threading/threading-helpers.c        |   2 +
 src/cats/vital_ages/default_vital_ages.c      |   2 +-
 src/cats/vital_rates/setup_rates.c            |   2 +-
 src/cats/vital_rates/vital_rate_ranges.c      |   2 +-
 src/cats_csv/CMakeLists.txt                   |   2 +-
 src/cats_csv/cats_csv.c                       |  21 +-
 src/cats_csv/cats_csv.h                       |   2 +-
 src/cats_ini/CMakeLists.txt                   |   2 +-
 src/cats_ini/cats_ini.c                       |   4 +-
 src/cats_ini/cats_ini_helpers.c               |   2 +-
 src/cats_ini/cats_ini_read_values.c           |   6 +-
 src/cats_ini/cats_ini_write_values.c          |   5 +-
 src/cats_strings/CMakeLists.txt               |  17 -
 src/cats_strings/io_text.h                    |  26 -
 src/cats_strings/pretty_print.h               |  25 -
 src/cats_strings/string_converters.h          |  39 --
 src/cats_strings/string_helpers.h             |  26 -
 src/logging/CMakeLists.txt                    |  12 -
 src/modules/butterflies/CMakeLists.txt        |   2 +-
 src/modules/butterflies/butterflies_main.c    |   1 +
 src/modules/butterflies/butterflies_paths.c   |   2 +
 src/modules/butterflies/butterflies_stats.c   |   1 +
 src/modules/cats_test_module/CMakeLists.txt   |   2 +-
 src/tests/CMakeLists.txt                      |   4 +-
 src/tests/csv_test.c                          |   3 +-
 97 files changed, 1547 insertions(+), 507 deletions(-)
 create mode 100644 libbdc/bdc_hashtable/CMakeLists.txt
 create mode 100644 libbdc/bdc_hashtable/bdc_hashtable.c
 create mode 100644 libbdc/bdc_hashtable/bdc_hashtable.h
 create mode 100644 libbdc/bdc_hashtable/tests/test_bdc_hashtable.c
 create mode 100644 libbdc/bdc_io/CMakeLists.txt
 rename src/cats_strings/io_text.c => libbdc/bdc_io/bdc_io.c (75%)
 create mode 100644 libbdc/bdc_io/bdc_io.h
 create mode 100644 libbdc/bdc_io/bdc_paths.c
 create mode 100644 libbdc/bdc_logging/CMakeLists.txt
 create mode 100644 libbdc/bdc_logging/README.md
 rename {src/logging => libbdc/bdc_logging}/logging.c (58%)
 rename {src/logging => libbdc/bdc_logging}/logging.h (66%)
 create mode 100644 libbdc/bdc_strings/CMakeLists.txt
 create mode 100644 libbdc/bdc_strings/README.md
 rename src/cats_strings/cats_strings.c => libbdc/bdc_strings/bdc_strings.c (80%)
 rename src/cats_strings/cats_strings.h => libbdc/bdc_strings/bdc_strings.h (52%)
 rename {src/cats_strings => libbdc/bdc_strings}/pretty_print.c (95%)
 rename {src/cats_strings => libbdc/bdc_strings}/string_converters.c (94%)
 rename {src/cats_strings => libbdc/bdc_strings}/string_helpers.c (90%)
 create mode 100644 libbdc/bdc_strings/string_substitution.c
 delete mode 100644 src/cats_strings/CMakeLists.txt
 delete mode 100644 src/cats_strings/io_text.h
 delete mode 100644 src/cats_strings/pretty_print.h
 delete mode 100644 src/cats_strings/string_converters.h
 delete mode 100644 src/cats_strings/string_helpers.h
 delete mode 100644 src/logging/CMakeLists.txt

diff --git a/libbdc/CMakeLists.txt b/libbdc/CMakeLists.txt
index 3a0a1b2..0ecb214 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 0000000..5e11b83
--- /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 0000000..74975d3
--- /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 0000000..9b7f9ec
--- /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 0000000..9ebc916
--- /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 0000000..8436cfb
--- /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 19cac9b..1a0c931 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 0000000..54ade85
--- /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 0000000..586572d
--- /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 0000000..3391d98
--- /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 0000000..32e219e
--- /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 a7c0ed5..73e5f82 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 52eefe7..2a5ac64 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 0000000..78d25f7
--- /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 0000000..2a86721
--- /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 4843f1b..64a6733 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 d0031c2..7e64d17 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 fb94e70..a3797c3 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 5578c1e..c7b8b74 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 eb6553f..9c46586 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 0000000..2ee87c1
--- /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 41bc4a6..2d7908c 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 68ff826..d8f8227 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 321951d..2a147c2 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 ee838a3..423f4c7 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 66572ef..ff9fab8 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 e3a1539..274d8a6 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 a0d5a4f..d2c14b3 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 779d32e..fb3b699 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 df6924e..35f4287 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 6fed729..fa9ae72 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 e312c92..7b768ad 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 045df97..d180881 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 08f0f70..94cc25e 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 d5d1745..3c234a3 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 2e93757..ee3c452 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 281d1eb..bd3c3d7 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 31fe1d7..f48163d 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 a7418dd..ec3ce4e 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 c311e40..06624c3 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 d2bba54..eea7454 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 d3a5749..977f3e3 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 ed9e0b1..9959d15 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 ebe381a..b924064 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 ca9da40..996e31e 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 353a797..04d0ae3 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 fa1f606..d2208b7 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 1d4680f..e5a810d 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 7e5f6b0..c0d34ea 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 c1324c5..f9119cf 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 9665728..882fe5f 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 403df56..3ac7221 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 040be9f..f1c8563 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 20937d8..d52ced9 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 5933034..360d208 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 e1a8582..491e7ed 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 c054e99..e5694b8 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 f86e57b..5a92ff6 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 88de2fc..94b6313 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 2d377fd..6194c37 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 6e6d9bf..7859124 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 b18d538..92ee7f9 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 1570c2c..b6b423a 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 a89b27a..c08d061 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 80d8c5b..6c33840 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 08e0fc9..47ad4df 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 53310ab..56b95de 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 b2cfdbc..583ada1 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 d3360fa..37739e6 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 a412a82..c7aee12 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 7f02b8b..ee5f5a3 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 c1ed496..1834bfd 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 0877f6b..cdf3e7a 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 bbff02c..f9ce195 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 7b738d9..02883ad 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 44451ee..82635a8 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 d3afbbe..5f055d7 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 db8cf59..c3d4c2e 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 08e79fc..774183f 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 8b03aec..3aa7adc 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 96f1de8..abc937a 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 89c1643..4a14df0 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 b12be68..fcc777a 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 b99d1be..aa461bb 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 1414340..0000000
--- 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 224a71a..0000000
--- 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 be12b63..0000000
--- 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 b2a8082..0000000
--- 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 10671ce..0000000
--- 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 872a70e..0000000
--- 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 c20a250..538918b 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 e8e0b29..28b3fb1 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 ba749c2..7cc158d 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 567a20f..d711dcc 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 6ebb9e8..68caf04 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 237b15d..b205a80 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 1375ec1..467a7a4 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 {
-- 
GitLab