diff --git a/src/cli/cli.c b/src/cli/cli.c index 124341c..e687417 100644 --- a/src/cli/cli.c +++ b/src/cli/cli.c @@ -6,6 +6,7 @@ */ #include "cli/cli.h" #include "foundation/compat.h" +#include "foundation/platform.h" // the correct standard headers are included below but clang-tidy doesn't map them. #include @@ -1613,7 +1614,7 @@ unsigned char *cbm_extract_binary_from_targz(const unsigned char *data, int data static const char *get_cache_dir(const char *home_dir) { static char buf[1024]; if (!home_dir) { - home_dir = getenv("HOME"); + home_dir = cbm_get_home_dir(); } if (!home_dir) { return NULL; @@ -1837,10 +1838,9 @@ int cbm_cmd_config(int argc, char **argv) { return 0; } - // NOLINTNEXTLINE(concurrency-mt-unsafe) - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - fprintf(stderr, "error: HOME not set\n"); + fprintf(stderr, "error: HOME not set (use USERPROFILE on Windows)\n"); return 1; } @@ -1968,9 +1968,9 @@ int cbm_cmd_install(int argc, char **argv) { } } - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - fprintf(stderr, "error: HOME not set\n"); + fprintf(stderr, "error: HOME not set (use USERPROFILE on Windows)\n"); return 1; } @@ -2232,9 +2232,9 @@ int cbm_cmd_uninstall(int argc, char **argv) { } } - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - fprintf(stderr, "error: HOME not set\n"); + fprintf(stderr, "error: HOME not set (use USERPROFILE on Windows)\n"); return 1; } @@ -2425,9 +2425,9 @@ int cbm_cmd_uninstall(int argc, char **argv) { int cbm_cmd_update(int argc, char **argv) { parse_auto_answer(argc, argv); - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - fprintf(stderr, "error: HOME not set\n"); + fprintf(stderr, "error: HOME not set (use USERPROFILE on Windows)\n"); return 1; } diff --git a/src/foundation/platform.c b/src/foundation/platform.c index 2c33e4f..18e08ae 100644 --- a/src/foundation/platform.c +++ b/src/foundation/platform.c @@ -6,7 +6,8 @@ #include "platform.h" #include "compat.h" -#include // uint64_t, int64_t +#include // uint64_t, int64_t +#include // getenv #ifdef _WIN32 @@ -216,3 +217,19 @@ int64_t cbm_file_size(const char *path) { } #endif /* _WIN32 */ + +/* ── Home directory (cross-platform) ──────────────────────────── */ + +const char *cbm_get_home_dir(void) { + // NOLINTNEXTLINE(concurrency-mt-unsafe) + const char *h = getenv("HOME"); + if (h && h[0]) { + return h; + } + // NOLINTNEXTLINE(concurrency-mt-unsafe) + h = getenv("USERPROFILE"); + if (h && h[0]) { + return h; + } + return NULL; +} diff --git a/src/foundation/platform.h b/src/foundation/platform.h index ffa57fc..e88ab06 100644 --- a/src/foundation/platform.h +++ b/src/foundation/platform.h @@ -64,6 +64,12 @@ cbm_system_info_t cbm_system_info(void); * initial=false: max(1, perf_cores-1) (leave headroom for user apps) */ int cbm_default_worker_count(bool initial); +/* ── Home directory ─────────────────────────────────────────────── */ + +/* Cross-platform home directory: tries HOME first, then USERPROFILE (Windows). + * Returns NULL when neither is set. */ +const char *cbm_get_home_dir(void); + /* ── File system ───────────────────────────────────────────────── */ /* Check if a path exists. */ diff --git a/src/main.c b/src/main.c index 79618fa..1070d36 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ #include "store/store.h" #include "cli/cli.h" #include "foundation/log.h" +#include "foundation/platform.h" #include "foundation/compat_thread.h" #include "foundation/mem.h" #include "ui/config.h" @@ -221,7 +222,7 @@ int main(int argc, char **argv) { /* Open config store for runtime settings */ char config_dir[1024]; - const char *cfg_home = getenv("HOME"); + const char *cfg_home = cbm_get_home_dir(); cbm_config_t *runtime_config = NULL; if (cfg_home) { snprintf(config_dir, sizeof(config_dir), "%s/.cache/codebase-memory-mcp", cfg_home); diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index 55b99b6..796bd2f 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -564,10 +564,9 @@ bool cbm_mcp_server_has_cached_store(cbm_mcp_server_t *srv) { /* Returns the platform cache directory: ~/.cache/codebase-memory-mcp * Writes to buf, returns buf for convenience. */ static const char *cache_dir(char *buf, size_t bufsz) { - // NOLINTNEXTLINE(concurrency-mt-unsafe) - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - home = "/tmp"; + home = cbm_tmpdir(); } snprintf(buf, bufsz, "%s/.cache/codebase-memory-mcp", home); return buf; @@ -1995,8 +1994,7 @@ static void detect_session(cbm_mcp_server_t *srv) { /* 1. Try CWD */ char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) { - // NOLINTNEXTLINE(concurrency-mt-unsafe) - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); /* Skip useless roots: / and $HOME */ if (strcmp(cwd, "/") != 0 && (home == NULL || strcmp(cwd, home) != 0)) { snprintf(srv->session_root, sizeof(srv->session_root), "%s", cwd); @@ -2067,8 +2065,7 @@ static void maybe_auto_index(cbm_mcp_server_t *srv) { } /* Check if project already has a DB */ - // NOLINTNEXTLINE(concurrency-mt-unsafe) - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (home) { char db_check[1024]; snprintf(db_check, sizeof(db_check), "%s/.cache/codebase-memory-mcp/%s.db", home, diff --git a/src/pipeline/pipeline.c b/src/pipeline/pipeline.c index f5b7510..20c82e1 100644 --- a/src/pipeline/pipeline.c +++ b/src/pipeline/pipeline.c @@ -127,10 +127,9 @@ static char *resolve_db_path(const cbm_pipeline_t *p) { if (p->db_path) { snprintf(path, 1024, "%s", p->db_path); } else { - // NOLINTNEXTLINE(concurrency-mt-unsafe) - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) { - home = "/tmp"; + home = cbm_tmpdir(); } snprintf(path, 1024, "%s/.cache/codebase-memory-mcp/%s.db", home, p->project_name); } @@ -680,14 +679,13 @@ int cbm_pipeline_run(cbm_pipeline_t *p) { if (!check_cancel(p)) { cbm_clock_gettime(CLOCK_MONOTONIC, &t); - // NOLINTNEXTLINE(concurrency-mt-unsafe) — called once during single-threaded dump - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); char db_path[1024]; if (p->db_path) { snprintf(db_path, sizeof(db_path), "%s", p->db_path); } else { if (!home) { - home = "/tmp"; + home = cbm_tmpdir(); } snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, p->project_name); diff --git a/src/store/store.c b/src/store/store.c index aa50955..9fdcf70 100644 --- a/src/store/store.c +++ b/src/store/store.c @@ -348,10 +348,9 @@ cbm_store_t *cbm_store_open(const char *project) { return NULL; } /* Build path: ~/.cache/codebase-memory-mcp/.db */ - const char *home = getenv("HOME"); // NOLINT(concurrency-mt-unsafe) — called once during - // single-threaded store open, never concurrently + const char *home = cbm_get_home_dir(); if (!home) { - home = "/tmp"; + home = cbm_tmpdir(); } char path[1024]; snprintf(path, sizeof(path), "%s/.cache/codebase-memory-mcp/%s.db", home, project); diff --git a/src/ui/config.c b/src/ui/config.c index 962d0ed..7ac5b41 100644 --- a/src/ui/config.c +++ b/src/ui/config.c @@ -6,7 +6,9 @@ */ #include "ui/config.h" #include "foundation/log.h" +#include "foundation/platform.h" #include "foundation/compat_fs.h" +#include "foundation/compat.h" #include @@ -17,9 +19,9 @@ /* ── Path ────────────────────────────────────────────────────── */ void cbm_ui_config_path(char *buf, int bufsz) { - const char *home = getenv("HOME"); // NOLINT(concurrency-mt-unsafe) + const char *home = cbm_get_home_dir(); if (!home) { - home = "/tmp"; + home = cbm_tmpdir(); } snprintf(buf, (size_t)bufsz, "%s/.cache/codebase-memory-mcp/config.json", home); } diff --git a/src/ui/http_server.c b/src/ui/http_server.c index a60559b..ead6cd1 100644 --- a/src/ui/http_server.c +++ b/src/ui/http_server.c @@ -315,7 +315,7 @@ static void handle_browse(struct mg_connection *c, struct mg_http_message *hm) { char path[1024] = {0}; if (!get_query_param(hm->query, "path", path, (int)sizeof(path)) || path[0] == '\0') { /* Default to home directory */ - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (home) snprintf(path, sizeof(path), "%s", home); else @@ -387,9 +387,9 @@ static void handle_adr_get(struct mg_connection *c, struct mg_http_message *hm) return; } - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); char db_path[1024]; snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, name); @@ -480,9 +480,9 @@ static void handle_adr_save(struct mg_connection *c, struct mg_http_message *hm) const char *proj = yyjson_get_str(v_proj); const char *content = yyjson_get_str(v_content); - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); char db_path[1024]; snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, proj); @@ -777,9 +777,9 @@ static void handle_delete_project(struct mg_connection *c, struct mg_http_messag return; } - const char *home = getenv("HOME"); // NOLINT(concurrency-mt-unsafe) + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); char db_path[1024]; snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, name); @@ -815,9 +815,9 @@ static void handle_project_health(struct mg_connection *c, struct mg_http_messag return; } - const char *home = getenv("HOME"); // NOLINT(concurrency-mt-unsafe) + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); char db_path[1024]; snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, name); @@ -873,9 +873,9 @@ static void handle_layout(struct mg_connection *c, struct mg_http_message *hm) { } /* Open a read-only store for this project */ - const char *home = getenv("HOME"); // NOLINT(concurrency-mt-unsafe) + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); char db_path[1024]; snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, project); diff --git a/tests/test_integration.c b/tests/test_integration.c index fcdf8a9..de8cec4 100644 --- a/tests/test_integration.c +++ b/tests/test_integration.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -105,9 +106,9 @@ static int integration_setup(void) { return -1; /* Build db path for direct store queries (pipeline writes here) */ - const char *home = getenv("HOME"); + const char *home = cbm_get_home_dir(); if (!home) - home = "/tmp"; + home = cbm_tmpdir(); snprintf(g_dbpath, sizeof(g_dbpath), "%s/.cache/codebase-memory-mcp/%s.db", home, g_project); /* Ensure cache dir exists */