Skip to content

Commit ad2ab69

Browse files
committed
refactor: move path resolve to path.cc
1 parent 1ee5bec commit ad2ab69

File tree

7 files changed

+342
-313
lines changed

7 files changed

+342
-313
lines changed

src/path.cc

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
#include "path.h"
2+
#include <string>
3+
#include <vector>
4+
5+
namespace node {
6+
7+
#ifdef _WIN32
8+
bool IsPathSeparator(const char c) noexcept {
9+
return c == '\\' || c == kPathSeparator;
10+
}
11+
#else // POSIX
12+
bool IsPathSeparator(const char c) noexcept {
13+
return c == kPathSeparator;
14+
}
15+
#endif // _WIN32
16+
17+
std::string NormalizeString(const std::string_view path,
18+
bool allowAboveRoot,
19+
const std::string_view separator) {
20+
std::string res;
21+
int lastSegmentLength = 0;
22+
int lastSlash = -1;
23+
int dots = 0;
24+
char code;
25+
const auto pathLen = path.size();
26+
for (uint8_t i = 0; i <= pathLen; ++i) {
27+
if (i < pathLen) {
28+
code = path[i];
29+
} else if (IsPathSeparator(path[i])) {
30+
break;
31+
} else {
32+
code = node::kPathSeparator;
33+
}
34+
35+
if (IsPathSeparator(code)) {
36+
if (lastSlash == static_cast<int>(i - 1) || dots == 1) {
37+
// NOOP
38+
} else if (dots == 2) {
39+
int len = res.length();
40+
if (len < 2 || lastSegmentLength != 2 || res[len - 1] != '.' ||
41+
res[len - 2] != '.') {
42+
if (len > 2) {
43+
auto lastSlashIndex = res.find_last_of(separator);
44+
if (lastSlashIndex == std::string::npos) {
45+
res = "";
46+
lastSegmentLength = 0;
47+
} else {
48+
res = res.substr(0, lastSlashIndex);
49+
len = res.length();
50+
lastSegmentLength = len - 1 - res.find_last_of(separator);
51+
}
52+
lastSlash = i;
53+
dots = 0;
54+
continue;
55+
} else if (len != 0) {
56+
res = "";
57+
lastSegmentLength = 0;
58+
lastSlash = i;
59+
dots = 0;
60+
continue;
61+
}
62+
}
63+
64+
if (allowAboveRoot) {
65+
res += res.length() > 0 ? std::string(separator) + ".." : "..";
66+
lastSegmentLength = 2;
67+
}
68+
} else {
69+
if (!res.empty()) {
70+
res += std::string(separator) +
71+
std::string(path.substr(lastSlash + 1, i - (lastSlash + 1)));
72+
} else {
73+
res = path.substr(lastSlash + 1, i - (lastSlash + 1));
74+
}
75+
lastSegmentLength = i - lastSlash - 1;
76+
}
77+
lastSlash = i;
78+
dots = 0;
79+
} else if (code == '.' && dots != -1) {
80+
++dots;
81+
} else {
82+
dots = -1;
83+
}
84+
}
85+
86+
return res;
87+
}
88+
89+
#ifdef _WIN32
90+
bool IsWindowsDeviceRoot(const char c) noexcept {
91+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
92+
}
93+
94+
std::string PathResolve(Environment* env,
95+
const std::vector<std::string_view>& paths) {
96+
std::string resolvedDevice = "";
97+
std::string resolvedTail = "";
98+
bool resolvedAbsolute = false;
99+
const size_t numArgs = paths.size();
100+
auto cwd = env->GetCwd(env->exec_path());
101+
102+
for (int i = numArgs - 1; i >= -1 && !resolvedAbsolute; i--) {
103+
std::string path;
104+
if (i >= 0) {
105+
path = std::string(paths[i]);
106+
} else if (resolvedDevice.empty()) {
107+
path = cwd;
108+
} else {
109+
// Windows has the concept of drive-specific current working
110+
// directories. If we've resolved a drive letter but not yet an
111+
// absolute path, get cwd for that drive, or the process cwd if
112+
// the drive cwd is not available. We're sure the device is not
113+
// a UNC path at this points, because UNC paths are always absolute.
114+
std::string resolvedDevicePath;
115+
const std::string envvar = "=" + resolvedDevice;
116+
credentials::SafeGetenv(envvar.c_str(), &resolvedDevicePath);
117+
path = resolvedDevicePath.empty() ? cwd : resolvedDevicePath;
118+
119+
// Verify that a cwd was found and that it actually points
120+
// to our drive. If not, default to the drive's root.
121+
if (path.empty() ||
122+
(ToLower(path.substr(0, 2)) != ToLower(resolvedDevice) &&
123+
path[2] == '/')) {
124+
path = resolvedDevice + "\\";
125+
}
126+
}
127+
128+
const size_t len = path.length();
129+
int rootEnd = 0;
130+
std::string device = "";
131+
bool isAbsolute = false;
132+
const char code = path[0];
133+
134+
// Try to match a root
135+
if (len == 1) {
136+
if (IsPathSeparator(code)) {
137+
// `path` contains just a path separator
138+
rootEnd = 1;
139+
isAbsolute = true;
140+
}
141+
} else if (IsPathSeparator(code)) {
142+
// Possible UNC root
143+
144+
// If we started with a separator, we know we at least have an
145+
// absolute path of some kind (UNC or otherwise)
146+
isAbsolute = true;
147+
148+
if (IsPathSeparator(path[1])) {
149+
// Matched double path separator at beginning
150+
size_t j = 2;
151+
size_t last = j;
152+
// Match 1 or more non-path separators
153+
while (j < len && !IsPathSeparator(path[j])) {
154+
j++;
155+
}
156+
if (j < len && j != last) {
157+
const std::string firstPart = path.substr(last, j - last);
158+
// Matched!
159+
last = j;
160+
// Match 1 or more path separators
161+
while (j < len && IsPathSeparator(path[j])) {
162+
j++;
163+
}
164+
if (j < len && j != last) {
165+
// Matched!
166+
last = j;
167+
// Match 1 or more non-path separators
168+
while (j < len && !IsPathSeparator(path[j])) {
169+
j++;
170+
}
171+
if (j == len || j != last) {
172+
// We matched a UNC root
173+
device = "\\\\" + firstPart + "\\" + path.substr(last, j - last);
174+
rootEnd = j;
175+
}
176+
}
177+
}
178+
}
179+
} else if (IsWindowsDeviceRoot(code) && path[1] == ':') {
180+
// Possible device root
181+
device = path.substr(0, 2);
182+
rootEnd = 2;
183+
if (len > 2 && IsPathSeparator(path[2])) {
184+
// Treat separator following drive name as an absolute path
185+
// indicator
186+
isAbsolute = true;
187+
rootEnd = 3;
188+
}
189+
}
190+
191+
if (!device.empty()) {
192+
if (!resolvedDevice.empty()) {
193+
if (ToLower(device) != ToLower(resolvedDevice)) {
194+
// This path points to another device so it is not applicable
195+
continue;
196+
}
197+
} else {
198+
resolvedDevice = device;
199+
}
200+
}
201+
202+
if (resolvedAbsolute) {
203+
if (!resolvedDevice.empty()) {
204+
break;
205+
}
206+
} else {
207+
resolvedTail = path.substr(rootEnd) + "\\" + resolvedTail;
208+
resolvedAbsolute = isAbsolute;
209+
if (isAbsolute && !resolvedDevice.empty()) {
210+
break;
211+
}
212+
}
213+
}
214+
215+
// At this point the path should be resolved to a full absolute path,
216+
// but handle relative paths to be safe (might happen when process.cwd()
217+
// fails)
218+
219+
// Normalize the tail path
220+
resolvedTail = NormalizeString(resolvedTail, !resolvedAbsolute, "\\");
221+
222+
if (resolvedAbsolute) {
223+
return resolvedDevice + "\\" + resolvedTail;
224+
}
225+
226+
if (!resolvedDevice.empty() || !resolvedTail.empty()) {
227+
return resolvedDevice + resolvedTail;
228+
}
229+
230+
return ".";
231+
}
232+
#else // _WIN32
233+
std::string PathResolve(Environment* env,
234+
const std::vector<std::string_view>& paths) {
235+
std::string resolvedPath;
236+
bool resolvedAbsolute = false;
237+
auto cwd = env->GetCwd(env->exec_path());
238+
const size_t numArgs = paths.size();
239+
240+
for (int i = numArgs - 1; i >= -1 && !resolvedAbsolute; i--) {
241+
const std::string& path =
242+
(i >= 0) ? std::string(paths[i]) : env->GetCwd(env->exec_path());
243+
244+
if (!path.empty()) {
245+
resolvedPath = std::string(path) + "/" + resolvedPath;
246+
247+
if (path.front() == '/') {
248+
resolvedAbsolute = true;
249+
break;
250+
}
251+
}
252+
}
253+
254+
// Normalize the path
255+
auto normalizedPath = NormalizeString(resolvedPath, !resolvedAbsolute, "/");
256+
257+
if (resolvedAbsolute) {
258+
return "/" + normalizedPath;
259+
}
260+
261+
if (normalizedPath.empty()) {
262+
return ".";
263+
}
264+
265+
return normalizedPath;
266+
}
267+
#endif // _WIN32
268+
269+
} // namespace node

src/path.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef SRC_PATH_H_
2+
#define SRC_PATH_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include <vector>
7+
#include <string>
8+
9+
namespace node {
10+
11+
class Environment;
12+
13+
bool IsPathSeparator(const char c) noexcept;
14+
15+
std::string NormalizeString(const std::string_view path,
16+
bool allowAboveRoot,
17+
const std::string_view separator);
18+
19+
std::string PathResolve(Environment* env,
20+
const std::vector<std::string_view>& args);
21+
} // namespace node
22+
23+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
24+
25+
#endif // SRC_PATH_H_

src/permission/fs_permission.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "fs_permission.h"
22
#include "base_object-inl.h"
33
#include "debug_utils-inl.h"
4-
#include "util.h"
4+
#include "path.h"
55
#include "v8.h"
66

77
#include <fcntl.h>

0 commit comments

Comments
 (0)