4
static int inside_git_dir = -1;
5
static int inside_work_tree = -1;
6
7
-const char *prefix_path(const char *prefix, int len, const char *path)
7
+static int sanitary_path_copy(char *dst, const char *src)
8
{
9
- const char *orig = path;
9
+ char *dst0 = dst;
10
+
11
+ if (*src == '/') {
12
+ *dst++ = '/';
13
+ while (*src == '/')
14
+ src++;
15
+ }
16
+
17
for (;;) {
11
- char c;
12
- if (*path != '.')
13
- break;
14
- c = path[1];
15
- /* "." */
16
- if (!c) {
17
- path++;
18
- break;
18
+ char c = *src;
19
+
20
+ /*
21
+ * A path component that begins with . could be
22
+ * special:
23
+ * (1) "." and ends -- ignore and terminate.
24
+ * (2) "./" -- ignore them, eat slash and continue.
25
+ * (3) ".." and ends -- strip one and terminate.
26
+ * (4) "../" -- strip one, eat slash and continue.
27
+ */
28
+ if (c == '.') {
29
+ switch (src[1]) {
30
+ case '\0':
31
+ /* (1) */
32
+ src++;
33
+ break;
34
+ case '/':
35
+ /* (2) */
36
+ src += 2;
37
+ while (*src == '/')
38
+ src++;
39
+ continue;
40
+ case '.':
41
+ switch (src[2]) {
42
+ case '\0':
43
+ /* (3) */
44
+ src += 2;
45
+ goto up_one;
46
+ case '/':
47
+ /* (4) */
48
+ src += 3;
49
+ while (*src == '/')
50
+ src++;
51
+ goto up_one;
52
+ }
53
+ }
54
}
20
- /* "./" */
55
+
56
+ /* copy up to the next '/', and eat all '/' */
57
+ while ((c = *src++) != '\0' && c != '/')
58
+ *dst++ = c;
59
if (c == '/') {
22
- path += 2;
23
- continue;
24
- }
25
- if (c != '.')
60
+ *dst++ = c;
61
+ while (c == '/')
62
+ c = *src++;
63
+ src--;
64
+ } else if (!c)
65
break;
27
- c = path[2];
28
- if (!c)
29
- path += 2;
30
- else if (c == '/')
31
- path += 3;
32
- else
33
- break;
34
- /* ".." and "../" */
35
- /* Remove last component of the prefix */
36
- do {
37
- if (!len)
38
- die("'%s' is outside repository", orig);
39
- len--;
40
- } while (len && prefix[len-1] != '/');
66
continue;
67
+
68
+ up_one:
69
+ /*
70
+ * dst0..dst is prefix portion, and dst[-1] is '/';
71
+ * go up one level.
72
+ */
73
+ dst -= 2; /* go past trailing '/' if any */
74
+ if (dst < dst0)
75
+ return -1;
76
+ while (1) {
77
+ if (dst <= dst0)
78
+ break;
79
+ c = *dst--;
80
+ if (c == '/') {
81
+ dst += 2;
82
+ break;
83
+ }
84
+ }
85
}
43
- if (len) {
44
- int speclen = strlen(path);
45
- char *n = xmalloc(speclen + len + 1);
86
+ *dst = '\0';
87
+ return 0;
88
+}
89
47
- memcpy(n, prefix, len);
48
- memcpy(n + len, path, speclen+1);
49
- path = n;
90
+const char *prefix_path(const char *prefix, int len, const char *path)
91
+{
92
+ const char *orig = path;
93
+ char *sanitized = xmalloc(len + strlen(path) + 1);
94
+ if (*orig == '/')
95
+ strcpy(sanitized, path);
96
+ else {
97
+ if (len)
98
+ memcpy(sanitized, prefix, len);
99
+ strcpy(sanitized + len, path);
100
}
51
- return path;
101
+ if (sanitary_path_copy(sanitized, sanitized))
102
+ goto error_out;
103
+ if (*orig == '/') {
104
+ const char *work_tree = get_git_work_tree();
105
+ size_t len = strlen(work_tree);
106
+ size_t total = strlen(sanitized) + 1;
107
+ if (strncmp(sanitized, work_tree, len) ||
108
+ (sanitized[len] != '\0' && sanitized[len] != '/')) {
109
+ error_out:
110
+ error("'%s' is outside repository", orig);
111
+ free(sanitized);
112
+ return NULL;
113
+ }
114
+ if (sanitized[len] == '/')
115
+ len++;
116
+ memmove(sanitized, sanitized + len, total - len);
117
+ }
118
+ return sanitized;
119
}
120
121
/*
181
const char **get_pathspec(const char *prefix, const char **pathspec)
182
{
183
const char *entry = *pathspec;
117
- const char **p;
184
+ const char **src, **dst;
185
int prefixlen;
186
187
if (!prefix && !entry)
195
}
196
197
/* Otherwise we have to re-write the entries.. */
131
- p = pathspec;
198
+ src = pathspec;
199
+ dst = pathspec;
200
prefixlen = prefix ? strlen(prefix) : 0;
133
- do {
134
- *p = prefix_path(prefix, prefixlen, entry);
135
- } while ((entry = *++p) != NULL);
136
- return (const char **) pathspec;
201
+ while (*src) {
202
+ const char *p = prefix_path(prefix, prefixlen, *src);
203
+ if (p)
204
+ *(dst++) = p;
205
+ src++;
206
+ }
207
+ *dst = NULL;
208
+ if (!*pathspec)
209
+ return NULL;
210
+ return pathspec;
211
}
212
213
/*