Skip to main content

slashsplit

Function slashsplit 

Source
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 tail

Three behaviors that the previous split('/').filter(non_empty) Rust port got WRONG:

  1. Leading / keeps an empty segment (c:851 with t == s). C: slashsplit("/usr")["", "usr"]; previous Rust dropped the empty, returning ["usr"]. Caller xsymlinks (c:879) iterates the result building xbuf byte-by-byte — the empty leading segment is how it knows to start from /. Without it, absolute-path resolution silently became relative.
  2. 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.
  3. 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.