Skip to content

Commit b23445b

Browse files
authored
fix: normalize mixed path separators to forward slashes on Windows (#146)
Adds `cbm_normalize_path_sep()` to platform.h — converts backslash to forward slash on Windows, no-op on POSIX. Applied at entry points: - `cbm_get_home_dir()` — HOME/USERPROFILE paths - `handle_index_repository()` — repo_path argument - `build_snippet_response()` — _fullpath results - FQN computation — replaces inline loops Thanks @jimpark for the clean, focused fix!
1 parent f52376b commit b23445b

File tree

4 files changed

+39
-21
lines changed

4 files changed

+39
-21
lines changed

src/foundation/platform.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ int64_t cbm_file_size(const char *path) {
9797
return (int64_t)sz.QuadPart;
9898
}
9999

100+
char *cbm_normalize_path_sep(char *path) {
101+
if (path) {
102+
for (char *p = path; *p; p++) {
103+
if (*p == '\\') {
104+
*p = '/';
105+
}
106+
}
107+
}
108+
return path;
109+
}
110+
100111
#else /* POSIX (macOS + Linux) */
101112

102113
/* ── POSIX implementation ─────────────────────────────────────── */
@@ -215,20 +226,31 @@ int64_t cbm_file_size(const char *path) {
215226
return (int64_t)st.st_size;
216227
}
217228

229+
char *cbm_normalize_path_sep(char *path) {
230+
/* No-op on POSIX — paths already use forward slashes. */
231+
(void)path;
232+
return path;
233+
}
234+
218235
#endif /* _WIN32 */
219236

220237
/* ── Home directory (cross-platform) ──────────────────────────── */
221238

222239
const char *cbm_get_home_dir(void) {
240+
static char buf[1024];
223241
// NOLINTNEXTLINE(concurrency-mt-unsafe)
224242
const char *h = getenv("HOME");
225243
if (h && h[0]) {
226-
return h;
244+
snprintf(buf, sizeof(buf), "%s", h);
245+
cbm_normalize_path_sep(buf);
246+
return buf;
227247
}
228248
// NOLINTNEXTLINE(concurrency-mt-unsafe)
229249
h = getenv("USERPROFILE");
230250
if (h && h[0]) {
231-
return h;
251+
snprintf(buf, sizeof(buf), "%s", h);
252+
cbm_normalize_path_sep(buf);
253+
return buf;
232254
}
233255
return NULL;
234256
}

src/foundation/platform.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,9 @@ bool cbm_is_dir(const char *path);
8181
/* Get file size. Returns -1 on error. */
8282
int64_t cbm_file_size(const char *path);
8383

84+
/* Normalize path separators to forward slashes (in-place).
85+
* On Windows, converts backslashes to forward slashes.
86+
* On POSIX, this is a no-op. Returns the input pointer. */
87+
char *cbm_normalize_path_sep(char *path);
88+
8489
#endif /* CBM_PLATFORM_H */

src/mcp/mcp.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,7 @@ static char *get_project_root(cbm_mcp_server_t *srv, const char *project) {
14231423
static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) {
14241424
char *repo_path = cbm_mcp_get_string_arg(args, "repo_path");
14251425
char *mode_str = cbm_mcp_get_string_arg(args, "mode");
1426+
cbm_normalize_path_sep(repo_path);
14261427

14271428
if (!repo_path) {
14281429
free(mode_str);
@@ -1584,13 +1585,14 @@ static char *build_snippet_response(cbm_mcp_server_t *srv, cbm_node_t *node,
15841585
#ifdef _WIN32
15851586
if (_fullpath(real_root, root_path, sizeof(real_root)) &&
15861587
_fullpath(real_file, abs_path, sizeof(real_file))) {
1588+
cbm_normalize_path_sep(real_root);
1589+
cbm_normalize_path_sep(real_file);
15871590
#else
15881591
if (realpath(root_path, real_root) && realpath(abs_path, real_file)) {
15891592
#endif
15901593
size_t root_len = strlen(real_root);
15911594
if (strncmp(real_file, real_root, root_len) == 0 &&
1592-
(real_file[root_len] == '/' || real_file[root_len] == '\\' ||
1593-
real_file[root_len] == '\0')) {
1595+
(real_file[root_len] == '/' || real_file[root_len] == '\0')) {
15941596
path_ok = true;
15951597
}
15961598
}

src/pipeline/fqn.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Handles Python __init__.py, JS/TS index.{js,ts}, path separators.
66
*/
77
#include "pipeline/pipeline.h"
8+
#include "foundation/platform.h"
89

910
#include <stddef.h> // NULL
1011
#include <stdio.h>
@@ -58,12 +59,8 @@ char *cbm_pipeline_fqn_compute(const char *project, const char *rel_path, const
5859
/* Work on a mutable copy for path manipulation */
5960
char *path = strdup(rel_path ? rel_path : "");
6061

61-
/* Convert backslash to forward slash */
62-
for (char *p = path; *p; p++) {
63-
if (*p == '\\') {
64-
*p = '/';
65-
}
66-
}
62+
/* Normalize path separators */
63+
cbm_normalize_path_sep(path);
6764

6865
/* Strip file extension */
6966
{
@@ -136,11 +133,7 @@ char *cbm_pipeline_fqn_folder(const char *project, const char *rel_dir) {
136133

137134
/* Work on mutable copy */
138135
char *dir = strdup(rel_dir ? rel_dir : "");
139-
for (char *p = dir; *p; p++) {
140-
if (*p == '\\') {
141-
*p = '/';
142-
}
143-
}
136+
cbm_normalize_path_sep(dir);
144137

145138
const char *segments[256];
146139
int seg_count = 0;
@@ -174,12 +167,8 @@ char *cbm_project_name_from_path(const char *abs_path) {
174167
char *path = strdup(abs_path);
175168
size_t len = strlen(path);
176169

177-
/* Convert \ to / */
178-
for (size_t i = 0; i < len; i++) {
179-
if (path[i] == '\\') {
180-
path[i] = '/';
181-
}
182-
}
170+
/* Normalize path separators */
171+
cbm_normalize_path_sep(path);
183172

184173
/* Replace / and : with - */
185174
for (size_t i = 0; i < len; i++) {

0 commit comments

Comments
 (0)