pub fn slashsplit(s: &str) -> Vec<String>Expand description
Split path into components (from utils.c slashsplit).
Port of static char **slashsplit(char *s) from Src/utils.c:837.
C body (c:837-863):
if (!*s) return zshcalloc(...); // c:842 — empty input → empty
for (t = s, t0 = 0; *t; t++) // c:845 — count slashes
if (*t == '/') t0++;
q = r = zalloc(sizeof(char*) * (t0 + 2));
while ((t = strchr(s, '/'))) { // c:850
*q++ = ztrduppfx(s, t - s); // c:851 — emit prefix
while (*t == '/') t++; // c:852-853 — collapse runs
if (!*t) { *q = NULL; return r; } // c:854-857 — trailing `/` ends
s = t;
}
*q++ = ztrdup(s); // c:860 — final tailThree behaviors that the previous split('/').filter(non_empty)
Rust port got WRONG:
- Leading
/keeps an empty segment (c:851 witht == s). C:slashsplit("/usr")→["", "usr"]; previous Rust dropped the empty, returning["usr"]. Callerxsymlinks(c:879) iterates the result buildingxbufbyte-by-byte — the empty leading segment is how it knows to start from/. Without it, absolute-path resolution silently became relative. - Consecutive slashes collapse (c:852-853 inner while-loop).
C:
"a//b"→["a", "b"](drop empty between). Filter-on- empty Rust gets this right by coincidence. - Trailing
/drops (c:854-857). C:"a/b/"→["a", "b"]. Filter-on-empty Rust gets this right by coincidence.
Pin: the leading-empty-segment behavior IS the C contract, not
an oversight to filter out. Matches xsymlinks and any future
path-walker port that reads slashsplit output.