File size: 2,845 Bytes
bf9c466
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* tasks/path/source.c
 * Path canonicalization in C.
 * Agents must migrate this to Rust using Vec<String> for segment lists.
 *
 * Key invariants to preserve:
 *   - ".." at root is clamped (no going above "/")
 *   - "." segments are removed
 *   - joining an absolute path (starts with "/") replaces the base
 *   - double slashes are collapsed
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_SEGMENTS 256
#define MAX_SEG_LEN  256

/* Normalize a path in-place (modifies path[] buffer).
 * segments[] is a working array, out_segments[] receives the result.
 * Returns number of output segments. */
int normalize_path(const char *path, char out_segments[][MAX_SEG_LEN], int max_out) {
    char buf[4096];
    strncpy(buf, path, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

    /* Tokenise on '/' */
    char *segs[MAX_SEGMENTS];
    int n = 0;
    char *tok = strtok(buf, "/");
    while (tok && n < MAX_SEGMENTS) {
        segs[n++] = tok;
        tok = strtok(NULL, "/");
    }

    /* Stack-based resolution */
    char *stack[MAX_SEGMENTS];
    int top = 0;
    for (int i = 0; i < n; i++) {
        if (strcmp(segs[i], ".") == 0) {
            /* skip */
        } else if (strcmp(segs[i], "..") == 0) {
            if (top > 0) top--;  /* clamp at root */
        } else {
            stack[top++] = segs[i];
        }
    }

    int count = top < max_out ? top : max_out;
    for (int i = 0; i < count; i++) {
        strncpy(out_segments[i], stack[i], MAX_SEG_LEN - 1);
        out_segments[i][MAX_SEG_LEN - 1] = '\0';
    }
    return count;
}

/* Join base and rel path segments.
 * If rel starts with '/' it replaces base entirely. */
int join_paths(
    const char *base, const char *rel,
    char out_segments[][MAX_SEG_LEN], int max_out)
{
    char combined[8192];
    if (rel[0] == '/') {
        /* Absolute rel — ignore base */
        snprintf(combined, sizeof(combined), "%s", rel);
    } else {
        snprintf(combined, sizeof(combined), "%s/%s", base, rel);
    }
    return normalize_path(combined, out_segments, max_out);
}

/* Depth = number of segments after normalization */
int path_depth(const char *path) {
    char segs[MAX_SEGMENTS][MAX_SEG_LEN];
    return normalize_path(path, segs, MAX_SEGMENTS);
}

/* Example usage */
int main(void) {
    char out[MAX_SEGMENTS][MAX_SEG_LEN];
    int n = normalize_path("usr/local/../bin", out, MAX_SEGMENTS);
    printf("normalize(usr/local/../bin): ");
    for (int i = 0; i < n; i++) printf("%s%s", out[i], i + 1 < n ? "/" : "");
    printf("\n");

    n = join_paths("usr/local", "bin", out, MAX_SEGMENTS);
    printf("join(usr/local, bin): ");
    for (int i = 0; i < n; i++) printf("%s%s", out[i], i + 1 < n ? "/" : "");
    printf("\n");

    printf("depth(usr/local/bin): %d\n", path_depth("usr/local/bin"));
    return 0;
}