From fcf98acf8d3a5269dc0b4ec93a1e1ba815b88c7b Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sat, 30 May 2026 19:34:27 +0200 Subject: [PATCH] fix(portability+format): Windows pkgmap stat; clang-format distilled passes - pass_pkgmap: lstat/S_ISLNK are POSIX-only and undeclared on the MSYS2 Windows build. Guard them under #ifndef _WIN32 (plain stat on Windows, where symlinks aren't a concern here). Fixes the Windows build break introduced by the workspace-manifest walker. - Run clang-format-20 over str_util.c, store.c, mcp.c, pipeline.c, pass_route_nodes.c, pass_pkgmap.c so the recently-landed pkgmap / route / security passes satisfy the lint gate. --- src/foundation/str_util.c | 12 ++++++++---- src/mcp/mcp.c | 3 ++- src/pipeline/pass_pkgmap.c | 15 +++++++++++---- src/pipeline/pass_route_nodes.c | 12 ++++++------ src/pipeline/pipeline.c | 5 ++--- src/store/store.c | 9 ++++++--- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/foundation/str_util.c b/src/foundation/str_util.c index f20079f52..6275ab592 100644 --- a/src/foundation/str_util.c +++ b/src/foundation/str_util.c @@ -271,13 +271,17 @@ bool cbm_validate_shell_arg(const char *s) { } bool cbm_validate_project_name(const char *name) { - if (!name || !*name) return false; + if (!name || !*name) + return false; /* Reject directory traversal */ - if (strcmp(name, "..") == 0 || strstr(name, "..") != NULL) return false; + if (strcmp(name, "..") == 0 || strstr(name, "..") != NULL) + return false; /* Reject path separators */ - if (strchr(name, '/') || strchr(name, '\\')) return false; + if (strchr(name, '/') || strchr(name, '\\')) + return false; /* Reject leading dot (hidden files / relative refs) */ - if (name[0] == '.') return false; + if (name[0] == '.') + return false; /* Allow only alphanumeric, dash, underscore, dot */ for (const char *p = name; *p; p++) { if (!(((*p >= 'a') && (*p <= 'z')) || ((*p >= 'A') && (*p <= 'Z')) || diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index 203a53769..c7d20dbb9 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -822,7 +822,8 @@ static int collect_db_project_names(const char *dir_path, char *out, size_t out_ if (!is_project_db_file(n, len)) { continue; } - if ((size_t)offset >= out_sz) break; /* bounds check before write */ + if ((size_t)offset >= out_sz) + break; /* bounds check before write */ if (count > 0 && offset < (int)out_sz - MCP_SEPARATOR) { out[offset++] = ','; } diff --git a/src/pipeline/pass_pkgmap.c b/src/pipeline/pass_pkgmap.c index 12befff68..225c06d36 100644 --- a/src/pipeline/pass_pkgmap.c +++ b/src/pipeline/pass_pkgmap.c @@ -775,8 +775,7 @@ static bool is_pkgmap_manifest_basename(const char *basename) { * matched by the shared cbm_should_skip_dir helper so we don't walk * node_modules, .git, build, etc. Returns the number of manifests * parsed, accumulated across the whole walk. */ -static int pkgmap_walk_dir(const char *abs_dir, const char *rel_dir, - cbm_pkg_entries_t *entries) { +static int pkgmap_walk_dir(const char *abs_dir, const char *rel_dir, cbm_pkg_entries_t *entries) { DIR *dir = opendir(abs_dir); if (!dir) { return 0; @@ -797,12 +796,20 @@ static int pkgmap_walk_dir(const char *abs_dir, const char *rel_dir, snprintf(rel_path, sizeof(rel_path), "%s", name); } struct stat st; +#ifdef _WIN32 + /* Windows has no lstat/S_ISLNK; symlinks aren't a concern for the + * MSYS2 build, so a plain stat is sufficient here. */ + if (stat(abs_path, &st) != 0) { + continue; + } +#else if (lstat(abs_path, &st) != 0) { continue; } if (S_ISLNK(st.st_mode)) { continue; } +#endif if (S_ISDIR(st.st_mode)) { if (cbm_should_skip_dir(name, CBM_MODE_FULL)) { continue; @@ -902,8 +909,8 @@ CBMHashTable *cbm_pkgmap_build_from_repo(const char *repo_path, const cbm_file_i int from_walk = cbm_pkgmap_scan_repo(repo_path, &entries); cbm_log_info("pkgmap.scan", "manifests_from_files", pkgmap_itoa(from_files), - "manifests_from_walk", pkgmap_itoa(from_walk), - "entries", pkgmap_itoa(entries.count)); + "manifests_from_walk", pkgmap_itoa(from_walk), "entries", + pkgmap_itoa(entries.count)); CBMHashTable *map = cbm_pkgmap_build(&entries, SKIP_ONE, project_name); cbm_pkg_entries_free(&entries); return map; diff --git a/src/pipeline/pass_route_nodes.c b/src/pipeline/pass_route_nodes.c index b436a2796..68f469e0f 100644 --- a/src/pipeline/pass_route_nodes.c +++ b/src/pipeline/pass_route_nodes.c @@ -974,8 +974,9 @@ static void sveltekit_file_visitor(const cbm_gbuf_node_t *node, void *userdata) /* Find every DEFINES edge from this file to a Function or Variable. */ const cbm_gbuf_edge_t **edges = NULL; int edge_count = 0; - if (cbm_gbuf_find_edges_by_source_type(ctx->gb, node->id, "DEFINES", &edges, &edge_count) != 0 - || edge_count == 0) { + if (cbm_gbuf_find_edges_by_source_type(ctx->gb, node->id, "DEFINES", &edges, &edge_count) != + 0 || + edge_count == 0) { return; } @@ -1018,16 +1019,15 @@ static void sveltekit_file_visitor(const cbm_gbuf_node_t *node, void *userdata) char route_props[CBM_SZ_256]; snprintf(route_props, sizeof(route_props), "{\"method\":\"%s\",\"framework\":\"sveltekit\"}", method); - int64_t route_id = cbm_gbuf_upsert_node(ctx->gb, "Route", route_path, route_qn, "", 0, 0, - route_props); + int64_t route_id = + cbm_gbuf_upsert_node(ctx->gb, "Route", route_path, route_qn, "", 0, 0, route_props); if (route_id == 0) { continue; } ctx->routes_created++; char hprops[CBM_SZ_256]; - snprintf(hprops, sizeof(hprops), - "{\"handler\":\"%s\",\"framework\":\"sveltekit\"%s}", + snprintf(hprops, sizeof(hprops), "{\"handler\":\"%s\",\"framework\":\"sveltekit\"%s}", child->qualified_name ? child->qualified_name : child->name, is_actions ? ",\"via\":\"actions_object\"" : ""); cbm_gbuf_insert_edge(ctx->gb, child->id, route_id, "HANDLES", hprops); diff --git a/src/pipeline/pipeline.c b/src/pipeline/pipeline.c index cdc63014d..7062eb87c 100644 --- a/src/pipeline/pipeline.c +++ b/src/pipeline/pipeline.c @@ -598,9 +598,8 @@ static int run_parallel_pipeline(cbm_pipeline_t *p, cbm_pipeline_ctx_t *ctx, * stays NULL and the fused resolver simply no-ops cross-file resolution * (per-file LSP already ran during extract). */ char cbm_lsp_cross_env[CBM_SZ_16]; - const bool run_cross_lsp = - cbm_safe_getenv("CBM_DISABLE_LSP_CROSS", cbm_lsp_cross_env, sizeof(cbm_lsp_cross_env), - NULL) == NULL; + const bool run_cross_lsp = cbm_safe_getenv("CBM_DISABLE_LSP_CROSS", cbm_lsp_cross_env, + sizeof(cbm_lsp_cross_env), NULL) == NULL; if (!run_cross_lsp) { cbm_log_info("lsp_cross.skipped", "reason", "CBM_DISABLE_LSP_CROSS env set"); } diff --git a/src/store/store.c b/src/store/store.c index d6ae9c277..34d624928 100644 --- a/src/store/store.c +++ b/src/store/store.c @@ -1772,8 +1772,10 @@ int cbm_store_find_nodes_by_qn_suffix(cbm_store_t *s, const char *project, const void cbm_store_node_degree(cbm_store_t *s, int64_t node_id, int *in_deg, int *out_deg) { if (!s) { - if (in_deg) *in_deg = 0; - if (out_deg) *out_deg = 0; + if (in_deg) + *in_deg = 0; + if (out_deg) + *out_deg = 0; return; } *in_deg = 0; @@ -2319,7 +2321,8 @@ static void where_add_like_hints(const char *column, const char *pattern, char * continue; int pool_was_full = (pool->count >= ST_LIKE_POOL_MAX); like_pool_add(pool, lp); - if (pool_was_full) continue; /* lp was freed — skip bind */ + if (pool_was_full) + continue; /* lp was freed — skip bind */ snprintf(bind_buf, sizeof(bind_buf), "%s LIKE ?%d", column, *bind_idx + SKIP_ONE); *wlen = where_append(where, where_sz, *wlen, nparams, bind_buf); where_bind_text(binds, bind_idx, lp);