From f5590c7dc85a53f8b9ffdb5ad5ba8e3834cffbd4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:17:33 +0000 Subject: [PATCH 1/3] Initial plan From eae43be8404456692d3dfb447d41aca814420442 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:28:44 +0000 Subject: [PATCH 2/3] Fix configure_section_drivers to use dmini_generate_string instead of direct file I/O Replace Dmod_FileOpen/Dmod_FileReadLine with dmini_generate_string so that section enumeration works from the already-parsed in-memory dmini context rather than re-reading the raw config file from disk. This is the intended usage as described in the issue: use the dmini API (dmini_set_active_section) to restrict context scope, not a hand-rolled file parser. - Remove config_path parameter from configure_section_drivers (no longer needed) - Remove unused INI_LINE_BUFFER_SIZE define - Add whitespace trimming for robustness when parsing section names - Use dmini_generate_string to obtain INI content from in-memory context Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 151 ++++++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 65 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 34b6438..4f8e1a2 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -23,7 +23,6 @@ #define DMDEVFS_CONTEXT_MAGIC 0x444D4456 // 'DMDV' #define ROOT_DIRECTORY_NAME "/" #define MAX_PATH_LENGTH (DMOD_MAX_MODULE_NAME_LENGTH + 20) -#define INI_LINE_BUFFER_SIZE 256 #define INI_MAIN_SECTION "main" /** @@ -75,7 +74,7 @@ struct dmfsi_context // ============================================================================ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const char* config_path); static driver_node_t* configure_driver(const char* driver_name, dmini_context_t config_ctx); -static int configure_section_drivers(dmfsi_context_t ctx, dmini_context_t config_ctx, const char* config_path); +static int configure_section_drivers(dmfsi_context_t ctx, dmini_context_t config_ctx); static int unconfigure_drivers(dmfsi_context_t ctx); static bool is_file(const char* path); static bool is_driver( const char* name); @@ -857,7 +856,7 @@ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const // Section-specific driver_name entries take priority over the file/directory // derived driver name. Only configure the main driver when no section-level // drivers are present in the file. - int section_drivers_added = configure_section_drivers(ctx, config_ctx, full_path); + int section_drivers_added = configure_section_drivers(ctx, config_ctx); if (section_drivers_added == 0) { driver_node_t* driver_node = configure_driver(module_name, config_ctx); @@ -967,90 +966,112 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t /** * @brief Configure drivers for non-main sections that contain a driver_name key * - * Scans the config file line by line for INI section headers. For each section - * other than "main" that contains a driver_name key, a new driver is configured - * with the INI context restricted to that section via dmini_set_active_section, - * so the driver only sees the keys belonging to its own section. + * Uses dmini_generate_string to obtain the full INI content from the + * already-parsed context (avoiding a second read of the config file from + * disk). Each non-main section that contains a driver_name key is configured + * as a separate driver with the INI context restricted to that section via + * dmini_set_active_section, so the driver only sees the keys belonging to its + * own section. * * Returns the number of section-specific drivers that were successfully added. * A non-zero return value signals to the caller that the file is a multi-driver * config and no fallback main driver should be configured. */ -static int configure_section_drivers(dmfsi_context_t ctx, dmini_context_t config_ctx, const char* config_path) +static int configure_section_drivers(dmfsi_context_t ctx, dmini_context_t config_ctx) { - void* file = Dmod_FileOpen(config_path, "r"); - if (file == NULL) + // Generate the full INI content from the already-parsed dmini context. + // This avoids re-reading the file from disk; we work entirely with the + // in-memory representation that dmini built during dmini_parse_file. + int content_size = dmini_generate_string(config_ctx, NULL, 0); + if (content_size <= 0) { - DMOD_LOG_ERROR("Failed to open config file for section scan: %s\n", config_path); return 0; } - int num_added = 0; - char line[INI_LINE_BUFFER_SIZE]; - while (Dmod_FileReadLine(line, sizeof(line), file) != NULL) + char* ini_content = (char*)Dmod_Malloc((size_t)content_size); + if (ini_content == NULL) { - // Skip lines that don't start with '[' (ignore leading whitespace) - char* p = line; - while (*p == ' ' || *p == '\t') p++; - if (*p != '[') continue; - - // Find closing bracket - char* end = strchr(p, ']'); - if (end == NULL || end <= p) continue; + DMOD_LOG_ERROR("Failed to allocate buffer for INI content scan\n"); + return 0; + } - // Extract section name - size_t name_len = (size_t)(end - p - 1); - if (name_len == 0 || name_len >= DMOD_MAX_MODULE_NAME_LENGTH) continue; + if (dmini_generate_string(config_ctx, ini_content, (size_t)content_size) <= 0) + { + Dmod_Free(ini_content); + return 0; + } - char section_name[DMOD_MAX_MODULE_NAME_LENGTH]; - strncpy(section_name, p + 1, name_len); - section_name[name_len] = '\0'; + int num_added = 0; + const char* p = ini_content; + while (*p != '\0') + { + // Skip leading whitespace on the line + while (*p == ' ' || *p == '\t') p++; - // Trim trailing whitespace from section name - size_t slen = strlen(section_name); - while (slen > 0 && (section_name[slen - 1] == ' ' || section_name[slen - 1] == '\t')) + if (*p == '[') { - slen--; - } - section_name[slen] = '\0'; - - // Skip empty section names and the "main" section (handled by existing logic) - if (slen == 0 || strcmp(section_name, INI_MAIN_SECTION) == 0) continue; - - // Check if this section has a driver_name key - if (!dmini_has_key(config_ctx, section_name, "driver_name")) continue; + // Extract section name between '[' and ']' + const char* name_start = p + 1; + const char* name_end = name_start; + while (*name_end != '\0' && *name_end != ']' && *name_end != '\n') name_end++; - const char* drv_name = dmini_get_string(config_ctx, section_name, "driver_name", NULL); - if (drv_name == NULL) continue; - - char module_name[DMOD_MAX_MODULE_NAME_LENGTH]; - strncpy(module_name, drv_name, sizeof(module_name)); - module_name[sizeof(module_name) - 1] = '\0'; + if (*name_end == ']') + { + // Trim leading whitespace inside the brackets + while (name_start < name_end && (*name_start == ' ' || *name_start == '\t')) name_start++; + // Trim trailing whitespace inside the brackets + while (name_end > name_start && (*(name_end - 1) == ' ' || *(name_end - 1) == '\t')) name_end--; - // Restrict the INI context to this section and configure the driver. - // Token 0 means no owner-token protection (context was created with dmini_create). - dmini_set_active_section(config_ctx, section_name, 0); - driver_node_t* driver_node = configure_driver(module_name, config_ctx); - dmini_clear_active_section(config_ctx, 0); + size_t name_len = (size_t)(name_end - name_start); + if (name_len > 0 && name_len < DMOD_MAX_MODULE_NAME_LENGTH) + { + char section_name[DMOD_MAX_MODULE_NAME_LENGTH]; + strncpy(section_name, name_start, name_len); + section_name[name_len] = '\0'; - if (driver_node == NULL) - { - DMOD_LOG_ERROR("Failed to configure driver for section [%s]: %s\n", section_name, module_name); - continue; + // Skip the [main] section; it is handled by existing logic + if (strcmp(section_name, INI_MAIN_SECTION) != 0 && + dmini_has_key(config_ctx, section_name, "driver_name")) + { + const char* drv_name = dmini_get_string(config_ctx, section_name, "driver_name", NULL); + if (drv_name != NULL) + { + char module_name[DMOD_MAX_MODULE_NAME_LENGTH]; + strncpy(module_name, drv_name, sizeof(module_name)); + module_name[sizeof(module_name) - 1] = '\0'; + + // Restrict the INI context to this section and configure the driver. + // Token 0 means no owner-token protection (context was created with dmini_create). + dmini_set_active_section(config_ctx, section_name, 0); + driver_node_t* driver_node = configure_driver(module_name, config_ctx); + dmini_clear_active_section(config_ctx, 0); + + if (driver_node == NULL) + { + DMOD_LOG_ERROR("Failed to configure driver for section [%s]: %s\n", + section_name, module_name); + } + else if (!dmlist_push_back(ctx->drivers, driver_node)) + { + DMOD_LOG_ERROR("Failed to add driver to list: %s\n", module_name); + Dmod_Free(driver_node); + } + else + { + num_added++; + } + } + } + } + } } - if (!dmlist_push_back(ctx->drivers, driver_node)) - { - DMOD_LOG_ERROR("Failed to add driver to list: %s\n", module_name); - Dmod_Free(driver_node); - } - else - { - num_added++; - } + // Advance to the next line + while (*p != '\0' && *p != '\n') p++; + if (*p == '\n') p++; } - Dmod_FileClose(file); + Dmod_Free(ini_content); return num_added; } From 97c9a6947c095d6c5719eb42117df9657b6b7c79 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:31:54 +0000 Subject: [PATCH 3/3] Use dmini_section_count/dmini_section_name for section iteration Replace dmini_generate_string + manual string parsing with the proper dmini@0.5 section iteration API: dmini_section_count() and dmini_section_name(index). This gives direct access to section names from the in-memory context without allocating a serialised string or parsing it manually. The dmini_set_active_section / dmini_clear_active_section usage for scoping the context per driver is unchanged. Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 123 ++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 88 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 4f8e1a2..cc4063f 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -966,12 +966,10 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t /** * @brief Configure drivers for non-main sections that contain a driver_name key * - * Uses dmini_generate_string to obtain the full INI content from the - * already-parsed context (avoiding a second read of the config file from - * disk). Each non-main section that contains a driver_name key is configured - * as a separate driver with the INI context restricted to that section via - * dmini_set_active_section, so the driver only sees the keys belonging to its - * own section. + * Iterates over all sections in the dmini context using dmini_section_count + * and dmini_section_name. For each non-main section that contains a driver_name + * key, the INI context is restricted to that section via dmini_set_active_section + * so the driver only sees the keys belonging to its own section. * * Returns the number of section-specific drivers that were successfully added. * A non-zero return value signals to the caller that the file is a multi-driver @@ -979,99 +977,48 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t */ static int configure_section_drivers(dmfsi_context_t ctx, dmini_context_t config_ctx) { - // Generate the full INI content from the already-parsed dmini context. - // This avoids re-reading the file from disk; we work entirely with the - // in-memory representation that dmini built during dmini_parse_file. - int content_size = dmini_generate_string(config_ctx, NULL, 0); - if (content_size <= 0) - { - return 0; - } + int num_added = 0; + int section_count = dmini_section_count(config_ctx); - char* ini_content = (char*)Dmod_Malloc((size_t)content_size); - if (ini_content == NULL) + for (int i = 0; i < section_count; i++) { - DMOD_LOG_ERROR("Failed to allocate buffer for INI content scan\n"); - return 0; - } + const char* section_name = dmini_section_name(config_ctx, i); - if (dmini_generate_string(config_ctx, ini_content, (size_t)content_size) <= 0) - { - Dmod_Free(ini_content); - return 0; - } + // Skip the global (unnamed) section and the [main] section + if (section_name == NULL || strcmp(section_name, INI_MAIN_SECTION) == 0) continue; - int num_added = 0; - const char* p = ini_content; - while (*p != '\0') - { - // Skip leading whitespace on the line - while (*p == ' ' || *p == '\t') p++; + // Only process sections that declare a driver_name + if (!dmini_has_key(config_ctx, section_name, "driver_name")) continue; - if (*p == '[') - { - // Extract section name between '[' and ']' - const char* name_start = p + 1; - const char* name_end = name_start; - while (*name_end != '\0' && *name_end != ']' && *name_end != '\n') name_end++; + const char* drv_name = dmini_get_string(config_ctx, section_name, "driver_name", NULL); + if (drv_name == NULL) continue; - if (*name_end == ']') - { - // Trim leading whitespace inside the brackets - while (name_start < name_end && (*name_start == ' ' || *name_start == '\t')) name_start++; - // Trim trailing whitespace inside the brackets - while (name_end > name_start && (*(name_end - 1) == ' ' || *(name_end - 1) == '\t')) name_end--; + char module_name[DMOD_MAX_MODULE_NAME_LENGTH]; + strncpy(module_name, drv_name, sizeof(module_name)); + module_name[sizeof(module_name) - 1] = '\0'; - size_t name_len = (size_t)(name_end - name_start); - if (name_len > 0 && name_len < DMOD_MAX_MODULE_NAME_LENGTH) - { - char section_name[DMOD_MAX_MODULE_NAME_LENGTH]; - strncpy(section_name, name_start, name_len); - section_name[name_len] = '\0'; + // Restrict the INI context to this section and configure the driver. + // Token 0 means no owner-token protection (context was created with dmini_create). + dmini_set_active_section(config_ctx, section_name, 0); + driver_node_t* driver_node = configure_driver(module_name, config_ctx); + dmini_clear_active_section(config_ctx, 0); - // Skip the [main] section; it is handled by existing logic - if (strcmp(section_name, INI_MAIN_SECTION) != 0 && - dmini_has_key(config_ctx, section_name, "driver_name")) - { - const char* drv_name = dmini_get_string(config_ctx, section_name, "driver_name", NULL); - if (drv_name != NULL) - { - char module_name[DMOD_MAX_MODULE_NAME_LENGTH]; - strncpy(module_name, drv_name, sizeof(module_name)); - module_name[sizeof(module_name) - 1] = '\0'; - - // Restrict the INI context to this section and configure the driver. - // Token 0 means no owner-token protection (context was created with dmini_create). - dmini_set_active_section(config_ctx, section_name, 0); - driver_node_t* driver_node = configure_driver(module_name, config_ctx); - dmini_clear_active_section(config_ctx, 0); - - if (driver_node == NULL) - { - DMOD_LOG_ERROR("Failed to configure driver for section [%s]: %s\n", - section_name, module_name); - } - else if (!dmlist_push_back(ctx->drivers, driver_node)) - { - DMOD_LOG_ERROR("Failed to add driver to list: %s\n", module_name); - Dmod_Free(driver_node); - } - else - { - num_added++; - } - } - } - } - } + if (driver_node == NULL) + { + DMOD_LOG_ERROR("Failed to configure driver for section [%s]: %s\n", + section_name, module_name); + } + else if (!dmlist_push_back(ctx->drivers, driver_node)) + { + DMOD_LOG_ERROR("Failed to add driver to list: %s\n", module_name); + Dmod_Free(driver_node); + } + else + { + num_added++; } - - // Advance to the next line - while (*p != '\0' && *p != '\n') p++; - if (*p == '\n') p++; } - Dmod_Free(ini_content); return num_added; }