#include "config_store.h" #include #include #include #include static void trim_whitespace(char *s) { char *start; char *end; if (s == NULL || *s == '\0') return; start = s; while (*start != '\0' && isspace((unsigned char)*start)) start++; if (start != s) memmove(s, start, strlen(start) + 1U); if (*s == '\0') return; end = s + strlen(s) - 1; while (end >= s && isspace((unsigned char)*end)) { *end = '\0'; end--; } } static bool str_ieq(const char *a, const char *b) { unsigned char ca, cb; if (a == NULL || b == NULL) return false; while (*a != '\0' && *b != '\0') { ca = (unsigned char)tolower((unsigned char)*a); cb = (unsigned char)tolower((unsigned char)*b); if (ca != cb) return false; a++; b++; } return (*a == '\0' && *b == '\0'); } static bool parse_bool(const char *s, bool *out) { if (s == NULL || out == NULL) return false; if (str_ieq(s, "true") || str_ieq(s, "yes") || strcmp(s, "1") == 0) { *out = true; return true; } if (str_ieq(s, "false") || str_ieq(s, "no") || strcmp(s, "0") == 0) { *out = false; return true; } return false; } static bool parse_u32(const char *s, uint32_t *out) { char *endptr = NULL; unsigned long v; if (s == NULL || out == NULL || *s == '\0') return false; v = strtoul(s, &endptr, 10); if (endptr == NULL || *endptr != '\0') return false; *out = (uint32_t)v; return true; } static void copy_str(char *dst, size_t dst_size, const char *src) { if (dst == NULL || dst_size == 0U) return; if (src == NULL) src = ""; strncpy(dst, src, dst_size - 1U); dst[dst_size - 1U] = '\0'; } static bool copy_dyn_str(char **dst, const char *src) { char *new_value; size_t len; if (dst == NULL) return false; if (src == NULL) src = ""; len = strlen(src); new_value = malloc(len + 1U); if (new_value == NULL) return false; memcpy(new_value, src, len + 1U); if (*dst != NULL) free(*dst); *dst = new_value; return true; } static int parse_device_section_index(const char *section) { unsigned long idx; char *endptr = NULL; if (section == NULL) return -1; if (strlen(section) < 8U) return -1; if (tolower((unsigned char)section[0]) != 'd' || tolower((unsigned char)section[1]) != 'e' || tolower((unsigned char)section[2]) != 'v' || tolower((unsigned char)section[3]) != 'i' || tolower((unsigned char)section[4]) != 'c' || tolower((unsigned char)section[5]) != 'e') { return -1; } if (section[6] != ' ') return -1; idx = strtoul(§ion[7], &endptr, 10); if (endptr == NULL || *endptr != '\0') return -1; if (idx == 0UL || idx > MAX_VIRTUAL_DEVICES) return -1; return (int)(idx - 1UL); } static void set_device_defaults(virtual_device_settings_t *dev, uint8_t index) { char default_name[DEVICE_NAME_MAX_LEN]; if (dev == NULL) return; memset(dev, 0, sizeof(*dev)); dev->enabled = false; dev->unit_id = (uint8_t)(index + 1U); snprintf(default_name, sizeof(default_name), "Device_%u", (unsigned)(index + 1U)); copy_str(dev->name, sizeof(dev->name), default_name); dev->csv = NULL; } void config_store_set_defaults(device_config_t *cfg) { size_t i; if (cfg == NULL) return; memset(cfg, 0, sizeof(*cfg)); copy_str(cfg->wifi.ssid, sizeof(cfg->wifi.ssid), "YOUR_WIFI_SSID"); copy_str(cfg->wifi.password, sizeof(cfg->wifi.password), "YOUR_WIFI_PASSWORD"); cfg->wifi.dhcp = true; copy_str(cfg->wifi.static_ip, sizeof(cfg->wifi.static_ip), "192.168.1.50"); copy_str(cfg->wifi.gateway, sizeof(cfg->wifi.gateway), "192.168.1.1"); copy_str(cfg->wifi.netmask, sizeof(cfg->wifi.netmask), "255.255.255.0"); cfg->modbus.port = 502; cfg->modbus.max_clients = 4; cfg->modbus.device_count = 1; cfg->modbus.max_points = 150; for (i = 0; i < MAX_VIRTUAL_DEVICES; i++) set_device_defaults(&cfg->devices[i], (uint8_t)i); } void config_store_free(device_config_t *cfg) { size_t i; if (cfg == NULL) return; for (i = 0; i < MAX_VIRTUAL_DEVICES; i++) { if (cfg->devices[i].csv != NULL) { free(cfg->devices[i].csv); cfg->devices[i].csv = NULL; } } } static bool read_dynamic_line(FILE *fp, char **out_line) { char *line = NULL; size_t capacity = 128U; size_t length = 0U; int ch; if (fp == NULL || out_line == NULL) return false; *out_line = NULL; line = malloc(capacity); if (line == NULL) return false; while ((ch = fgetc(fp)) != EOF) { char *new_line; if (length + 1U >= capacity) { size_t new_capacity = capacity * 2U; new_line = realloc(line, new_capacity); if (new_line == NULL) { free(line); return false; } line = new_line; capacity = new_capacity; } line[length++] = (char)ch; if (ch == '\n') break; } if (length == 0U && ch == EOF) { free(line); return false; } line[length] = '\0'; *out_line = line; return true; } bool config_store_load(const char *filename, device_config_t *cfg) { FILE *fp; char *line = NULL; char section[32]; if (filename == NULL || cfg == NULL) return false; config_store_free(cfg); config_store_set_defaults(cfg); memset(section, 0, sizeof(section)); fp = fopen(filename, "r"); if (fp == NULL) return false; while (read_dynamic_line(fp, &line)) { char *eq; char *key; char *value; line[strcspn(line, "\r\n")] = '\0'; trim_whitespace(line); if (line[0] == '\0') { free(line); line = NULL; continue; } if (line[0] == ';' || line[0] == '#') { free(line); line = NULL; continue; } if (line[0] == '[') { size_t len = strlen(line); if (len >= 3U && line[len - 1U] == ']') { line[len - 1U] = '\0'; copy_str(section, sizeof(section), &line[1]); trim_whitespace(section); } free(line); line = NULL; continue; } eq = strchr(line, '='); if (eq == NULL) { free(line); line = NULL; continue; } *eq = '\0'; key = line; value = eq + 1; trim_whitespace(key); trim_whitespace(value); if (str_ieq(section, "wifi")) { if (str_ieq(key, "ssid")) copy_str(cfg->wifi.ssid, sizeof(cfg->wifi.ssid), value); else if (str_ieq(key, "password")) copy_str(cfg->wifi.password, sizeof(cfg->wifi.password), value); else if (str_ieq(key, "dhcp")) { bool b; if (parse_bool(value, &b)) cfg->wifi.dhcp = b; } else if (str_ieq(key, "static_ip")) copy_str(cfg->wifi.static_ip, sizeof(cfg->wifi.static_ip), value); else if (str_ieq(key, "gateway")) copy_str(cfg->wifi.gateway, sizeof(cfg->wifi.gateway), value); else if (str_ieq(key, "netmask")) copy_str(cfg->wifi.netmask, sizeof(cfg->wifi.netmask), value); } else if (str_ieq(section, "modbus")) { uint32_t v; if (str_ieq(key, "port")) { if (parse_u32(value, &v) && v <= 65535U) cfg->modbus.port = (uint16_t)v; } else if (str_ieq(key, "max_clients")) { if (parse_u32(value, &v) && v > 0U && v <= 16U) cfg->modbus.max_clients = (uint8_t)v; } else if (str_ieq(key, "device_count")) { if (parse_u32(value, &v) && v > 0U) { if (v > MAX_VIRTUAL_DEVICES) cfg->modbus.device_count = MAX_VIRTUAL_DEVICES; else cfg->modbus.device_count = (uint8_t)v; } } else if (str_ieq(key, "max_points")) { if (parse_u32(value, &v) && v > 0U) { cfg->modbus.max_points = (size_t)v; } } } else { int dev_index = parse_device_section_index(section); if (dev_index >= 0) { virtual_device_settings_t *dev = &cfg->devices[dev_index]; uint32_t v; if (str_ieq(key, "enabled")) { bool b; if (parse_bool(value, &b)) dev->enabled = b; } else if (str_ieq(key, "unit_id")) { if (parse_u32(value, &v) && v > 0U && v <= 255U) dev->unit_id = (uint8_t)v; } else if (str_ieq(key, "name")) { copy_str(dev->name, sizeof(dev->name), value); } else if (str_ieq(key, "csv")) { if (!copy_dyn_str(&dev->csv, value)) { free(line); line = NULL; fclose(fp); config_store_free(cfg); return false; } } } } free(line); line = NULL; } fclose(fp); if (cfg->wifi.ssid[0] == '\0') { printf("CONFIG ERROR: wifi.ssid is empty\n"); return false; } if (cfg->modbus.port == 0U) return false; if (cfg->modbus.max_clients == 0U) return false; if (cfg->modbus.max_points == 0U) return false; if (cfg->modbus.device_count == 0U || cfg->modbus.device_count > MAX_VIRTUAL_DEVICES) return false; for (uint8_t i = 0; i < cfg->modbus.device_count; i++) { uint8_t j; virtual_device_settings_t *dev = &cfg->devices[i]; if (!dev->enabled) continue; if (dev->unit_id == 0U) { printf("CONFIG ERROR: enable device %u has invalid unit_id 0\n", (unsigned)(i + 1U)); return false; } if (dev->csv == NULL || dev->csv[0] == '\0') { printf("CONFIG ERROR: enabled device %u has empty csv\n", (unsigned)(i + 1U)); return false; } if (dev->name[0] == '\0') { char default_name[DEVICE_NAME_MAX_LEN]; snprintf(default_name, sizeof(default_name), "Device_%u", (unsigned)(i + 1U)); copy_str(dev->name, sizeof(dev->name), default_name); } for (j = (uint8_t)(i + 1U); j < cfg->modbus.device_count; j++) { if (!cfg->devices[j].enabled) continue; if (dev->unit_id == cfg->devices[j].unit_id) { printf("CONFIG ERROR: duplicate unit_id %u between device %u and device %u\n", (unsigned)dev->unit_id, (unsigned)(i + 1U), (unsigned)(j + 1U)); } } } return true; }