-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpathutil.c
124 lines (97 loc) · 3.05 KB
/
pathutil.c
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#define AEM_INTERNAL
#include <aem/log.h>
#include <aem/translate.h>
#include "pathutil.h"
struct aem_stringslice aem_stringslice_match_pathcomponent(struct aem_stringslice *in, int *trailing_slash_p)
{
aem_assert(in);
struct aem_stringslice out = *in;
while (aem_stringslice_ok(*in) && *in->start != '/') {
in->start++;
}
out.end = in->start;
// Eat all trailing slashes
int trailing_slash = 0;
while (aem_stringslice_match(in, "/"))
trailing_slash = 1;
if (trailing_slash_p)
*trailing_slash_p = trailing_slash;
return out;
}
int aem_sandbox_path(struct aem_stringbuf *out, struct aem_stringslice base, struct aem_stringslice subpath, const char *ext)
{
aem_assert(out);
if (aem_stringslice_ok(base))
aem_stringbuf_putss(out, base);
else
aem_stringbuf_puts(out, ".");
// Ensure a slash separates the base from the subpath
if (aem_stringbuf_index(out, out->n-1) != '/')
aem_stringbuf_putc(out, '/');
// TODO: What about going into paths that don't exist or that are files, not directories, and then ..'ing out of them again?
size_t base_i = out->n;
int rc = 0;
int trailing_slash;
while (aem_stringslice_ok(subpath)) {
struct aem_stringslice component = aem_stringslice_match_pathcomponent(&subpath, &trailing_slash);
AEM_LOG_MULTI(out, AEM_LOG_DEBUG3) {
aem_stringbuf_puts(out, "Component: ");
aem_string_escape(out, component);
}
if (!aem_stringslice_ok(component)) {
// Leading /
} else if (aem_stringslice_eq(component, ".")) {
// Ignore "."
continue;
} else if (aem_stringslice_eq(component, "..")) {
// Back up until the previous slash
aem_logf_ctx(AEM_LOG_DEBUG3, "Before ..: \"%s\"", aem_stringbuf_get(out));
if (out->n > base_i) {
aem_assert(aem_stringbuf_index(out, out->n-1) == '/');
out->n--;
}
size_t n = out->n;
while (out->n > base_i && aem_stringbuf_index(out, out->n-1) != '/')
out->n--;
aem_logf_ctx(AEM_LOG_DEBUG3, "After ..: \"%s\"", aem_stringbuf_get(out));
if (n == out->n)
rc |= 1;
} else {
aem_stringbuf_putss(out, component);
if (trailing_slash)
aem_stringbuf_putc(out, '/');
}
}
if (ext) {
// A trailing slash doesn't make sense with an extension.
if (trailing_slash)
rc |= 2;
// You can't have an extension without a filename.
if (out->n == base_i)
rc |= 2;
// Only add the extension if it isn't already present.
struct aem_stringslice ss = aem_stringslice_new_str(out);
if (!aem_stringslice_match_end(&ss, ext))
aem_stringbuf_puts(out, ext);
}
return rc;
}
struct aem_stringslice aem_dirname(struct aem_stringslice path)
{
// Remove trailing slashes
while (aem_stringslice_ok(path) && path.end[-1] == '/')
path.end--;
// Remove trailing non-slashes
while (aem_stringslice_ok(path) && path.end[-1] != '/')
path.end--;
// If empty, return .
if (!aem_stringslice_ok(path))
return aem_stringslice_new_cstr(".");
// Remove trailing slashes
while (aem_stringslice_ok(path) && path.end[-1] == '/')
path.end--;
// If empty, return /
if (!aem_stringslice_ok(path))
return aem_stringslice_new_cstr("/");
return path;
}