-
-
Notifications
You must be signed in to change notification settings - Fork 621
/
path_normalize.c
155 lines (121 loc) · 3.34 KB
/
path_normalize.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* \file path_normalize.c
* \brief Removes any weirdness from a file system path string.
* \author Copyright (c) 2013 Jason Perkins and the Premake project
*/
#include "premake.h"
#include <ctype.h>
#include <string.h>
// isspace custom version (visual c++'s one doesn't like utf8 characters)
static int is_space(char c)
{
return (c >= 9 && c <= 13) || c == 32;
}
static void* normalize_substring(const char* str, const char* endPtr, char* writePtr) {
const char* const source = str;
const char* const writeBegin = writePtr;
const char* ptr;
char last = 0;
char ch;
while (str != endPtr) {
ch = (*str);
/* make sure we're using '/' for all separators */
if (ch == '\\') {
ch = '/';
}
/* filter out .. except when it's part of the file or folder name */
if (ch == '.' && last == '.' && *(str - 2) == '/' && (*(str + 1) == '/' || str + 1 == endPtr)) {
last = 0;
ptr = writePtr - 3;
while (ptr >= writeBegin) {
/* break on '/' except when it's '/../' */
if (ptr[0] == '/' && !(ptr[1] == '.' && ptr[2] == '.' && ptr[3] == '/')) {
writePtr -= writePtr - ptr;
/* special fix for cases, when '..' is the last chars in path i.e. d:\game\.., this should be converted into d:\,
but without this case, it will be converted into d: */
if (writePtr - 1 >= writeBegin && *(writePtr - 1) == ':' && str + 1 == endPtr) {
++writePtr;
}
break;
}
--ptr;
}
if (ptr < writeBegin) {
*(writePtr++) = ch;
}
++str;
continue;
}
/* filter out /./ */
if (ch == '/' && last == '.') {
ptr = str - 2;
if (*ptr == '/') { // there is no need to check whether ptr >= source since all the leading ./ will be skipped in path_normalize
if (ptr - 1 < source || *(ptr - 1) != ':') {
--writePtr;
}
++str;
continue;
}
}
/* add to the result, filtering out duplicate slashes */
if (ch != '/' || last != '/') {
*(writePtr++) = ch;
}
last = ch;
++str;
}
/* remove any trailing slashes, except those, that follow the ':', to avoid a path corruption i.e. D:\ -> D: */
while (*(--endPtr) == '/' && *(endPtr - 1) != ':') {
--writePtr;
}
*writePtr = *str;
return writePtr;
}
int path_normalize(lua_State* L)
{
const char* path = luaL_checkstring(L, 1);
const char* readPtr = path;
char buffer[0x4000] = { 0 };
char* writePtr = buffer;
const char* endPtr;
// skip leading white spaces
while (*readPtr && is_space(*readPtr)) {
++readPtr;
}
endPtr = readPtr;
while (*endPtr) {
/* remove any leading "./" sequences */
while (strncmp(readPtr, "./", 2) == 0) {
readPtr += 2;
}
// find the end of sub path
while (*endPtr && !is_space(*endPtr)) {
++endPtr;
}
writePtr = normalize_substring(readPtr, endPtr, writePtr);
// skip any white spaces between sub paths
while (*endPtr && is_space(*endPtr)) {
*(writePtr++) = *(endPtr++);
}
readPtr = endPtr;
}
// skip any trailing white spaces
while (is_space(*(--endPtr))) {
--writePtr;
}
*writePtr = 0;
lua_pushstring(L, buffer);
return 1;
}
/* Call the scripted path.normalize(), to allow for overrides */
void do_normalize(lua_State* L, char* buffer, const char* path)
{
int top = lua_gettop(L);
lua_getglobal(L, "path");
lua_getfield(L, -1, "normalize");
lua_pushstring(L, path);
lua_call(L, 1, 1);
path = luaL_checkstring(L, -1);
strcpy(buffer, path);
lua_settop(L, top);
}