Skip to content

Instantly share code, notes, and snippets.

@skeeto
Last active August 1, 2025 02:11
Show Gist options
  • Save skeeto/0fe0e5b57d2c4d506b7a8d1c3ac9492a to your computer and use it in GitHub Desktop.
Save skeeto/0fe0e5b57d2c4d506b7a8d1c3ac9492a to your computer and use it in GitHub Desktop.
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define S(s) (Str){s, sizeof(s)-1}
#define new(a, n, t) (t *)alloc(a, n, sizeof(t), _Alignof(t))
typedef struct {
char *beg;
char *end;
} Arena;
static void *alloc(Arena *a, ptrdiff_t count, ptrdiff_t size, ptrdiff_t align)
{
ptrdiff_t pad = -(uintptr_t)a->beg & (align - 1);
assert(count < (a->end - a->beg - pad)/size); // TODO: OOM handler
char *r = a->beg + pad;
a->beg += pad + count*size;
return memset(r, 0, count*size);
}
typedef struct {
char *data;
ptrdiff_t len;
} Str;
static bool equals(Str a, Str b)
{
return a.len==b.len && !memcmp(a.data, b.data, a.len);
}
static Str takehead(Str s, ptrdiff_t i)
{
assert(i <= s.len);
s.len = i;
return s;
}
static Str drophead(Str s, ptrdiff_t i)
{
assert(i <= s.len);
s.data += i;
s.len -= i;
return s;
}
static Str clone(Arena *a, Str s)
{
Str r = s;
r.data = new(a, s.len, char);
__builtin_memcpy(r.data, s.data, r.len);
return r;
}
static Str concat(Arena *a, Str head, Str tail)
{
if (head.data+head.len != a->beg) {
head = clone(a, head);
}
head.len += clone(a, tail).len;
return head;
}
typedef struct {
Str head;
Str tail;
bool ok;
} Cut;
static Cut cut(Str s, char c)
{
ptrdiff_t i = 0;
for (; i<s.len && s.data[i]!=c; i++) {}
Cut r = {};
r.ok = i < s.len;
r.head = takehead(s, i);
r.tail = drophead(s, i+r.ok);
return r;
}
static bool isvalidpath_strict(Str path)
{
if (!path.len || path.data[0]!='/') {
return false;
}
for (Cut c = {.tail = drophead(path, 1)}; c.tail.len;) {
c = cut(c.tail, '/');
Str seg = c.head;
if (!seg.len || equals(seg, S(".")) || equals(seg, S(".."))) {
return false;
}
}
return true;
}
static bool isvalidpath(Str path)
{
if (!path.len || path.data[0]!='/') {
return false;
}
ptrdiff_t depth = 0;
for (Cut c = {.tail = drophead(path, 1)}; c.tail.len;) {
c = cut(c.tail, '/');
Str seg = c.head;
if (equals(seg, S(".."))) {
if (--depth < 0) {
return false;
}
} else if (equals(seg, S("")) || equals(seg, S("."))) {
// do not count
} else {
depth++;
}
}
return true;
}
typedef struct {
Str *data;
ptrdiff_t len;
ptrdiff_t cap;
} Strs;
static Strs split(Str s, char delim, Arena *a)
{
Strs r = {};
for (Cut c = {.tail = s, .ok = true}; c.ok;) {
c = cut(c.tail, delim);
r.cap++;
}
r.data = new(a, r.cap, Str);
for (Cut c = {.tail = s, .ok = true}; c.ok;) {
c = cut(c.tail, delim);
r.data[r.len++] = c.head;
}
return r;
}
static Str resolvepath(Str path, Arena *a)
{
Str r = {};
Strs segs = split(path, '/', a);
if (segs.data[0].len) {
return r; // invalid (no starting /)
}
ptrdiff_t len = 0;
for (ptrdiff_t i = 0; i < segs.len; i++) {
if (!segs.data[i].len || equals(segs.data[i], S("."))) {
// skip
} else if (equals(segs.data[i], S(".."))) {
if (--len < 0) {
return r; // invalid (traversed above root)
}
} else {
segs.data[len++] = segs.data[i]; // keep
}
}
// Construct the new path
for (ptrdiff_t i = 0; i < len; i++) {
r = concat(a, r, S("/"));
r = concat(a, r, segs.data[i]);
}
return r;
}
int main(int argc, char **argv)
{
int cap = 1<<21;
char *mem = malloc(cap);
Arena a = {mem, mem+cap};
Str path = S("/foo/bar/../baz/index.html");
Str resolved = resolvepath(path, &a);
printf("%.*s\n", (int)resolved.len, resolved.data);
assert(!isvalidpath_strict(path));
assert( isvalidpath(path));
assert(!isvalidpath(S("/a/b/../../../c")));
assert(!isvalidpath(S("/a/b/./../../../c")));
assert(!isvalidpath(S("/a/b//../../../c")));
for (int i = 1; i < argc; i++) {
Arena scratch = a;
Str path = {argv[i], strlen(argv[i])};
Str resolved = resolvepath(path, &scratch);
printf("%s -> %.*s\n", argv[i], (int)resolved.len, resolved.data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment