Flutter Engine
path_win.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <windows.h>
8 
9 #include <direct.h>
10 #include <shellapi.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 
14 #include <algorithm>
15 #include <cerrno>
16 #include <cstring>
17 #include <functional>
18 #include <list>
19 #include <memory>
20 
21 namespace filesystem {
22 namespace {
23 
24 size_t RootLength(const std::string& path) {
25  if (path.size() == 0)
26  return 0;
27  if (path[0] == '/')
28  return 1;
29  if (path[0] == '\\') {
30  if (path.size() < 2 || path[1] != '\\')
31  return 1;
32  // The path is a network share. Search for up to two '\'s, as they are
33  // the server and share - and part of the root part.
34  size_t index = path.find('\\', 2);
35  if (index > 0) {
36  index = path.find('\\', index + 1);
37  if (index > 0)
38  return index;
39  }
40  return path.size();
41  }
42  // If the path is of the form 'C:/' or 'C:\', with C being any letter, it's
43  // a root part.
44  if (path.length() >= 2 && path[1] == ':' &&
45  (path[2] == '/' || path[2] == '\\') &&
46  ((path[0] >= 'A' && path[0] <= 'Z') ||
47  (path[0] >= 'a' && path[0] <= 'z'))) {
48  return 3;
49  }
50  return 0;
51 }
52 
53 size_t IsSeparator(const char sep) {
54  return sep == '/' || sep == '\\';
55 }
56 
57 size_t LastSeparator(const std::string& path) {
58  return path.find_last_of("/\\");
59 }
60 
61 size_t LastSeparator(const std::string& path, size_t pos) {
62  return path.find_last_of("/\\", pos);
63 }
64 
65 size_t FirstSeparator(const std::string& path, size_t pos) {
66  return path.find_first_of("/\\", pos);
67 }
68 
69 size_t ResolveParentDirectoryTraversal(const std::string& path,
70  size_t put,
71  size_t root_length) {
72  if (put <= root_length) {
73  return root_length;
74  }
75  size_t previous_separator = LastSeparator(path, put - 2);
76  if (previous_separator != std::string::npos)
77  return previous_separator + 1;
78  return 0;
79 }
80 } // namespace
81 
82 std::string SimplifyPath(std::string path) {
83  if (path.empty())
84  return ".";
85 
86  size_t put = 0;
87  size_t get = 0;
88  size_t traversal_root = 0;
89  size_t component_start = 0;
90 
91  size_t rootLength = RootLength(path);
92  if (rootLength > 0) {
93  put = rootLength;
94  get = rootLength;
95  component_start = rootLength;
96  }
97 
98  while (get < path.size()) {
99  char c = path[get];
100 
101  if (c == '.' && (get == component_start || get == component_start + 1)) {
102  // We've seen "." or ".." so far in this component. We need to continue
103  // searching.
104  ++get;
105  continue;
106  }
107 
108  if (IsSeparator(c)) {
109  if (get == component_start || get == component_start + 1) {
110  // We've found a "/" or a "./", which we can elide.
111  ++get;
112  component_start = get;
113  continue;
114  }
115  if (get == component_start + 2) {
116  // We've found a "../", which means we need to remove the previous
117  // component.
118  if (put == traversal_root) {
119  path[put++] = '.';
120  path[put++] = '.';
121  path[put++] = '\\';
122  traversal_root = put;
123  } else {
124  put = ResolveParentDirectoryTraversal(path, put, rootLength);
125  }
126  ++get;
127  component_start = get;
128  continue;
129  }
130  }
131 
132  size_t next_separator = FirstSeparator(path, get);
133  if (next_separator == std::string::npos) {
134  // We've reached the last component.
135  break;
136  }
137  size_t next_component_start = next_separator + 1;
138  ++next_separator;
139  size_t component_size = next_component_start - component_start;
140  if (put != component_start && component_size > 0) {
141  path.replace(put, component_size,
142  path.substr(component_start, component_size));
143  }
144  put += component_size;
145  get = next_component_start;
146  component_start = next_component_start;
147  }
148 
149  size_t last_component_size = path.size() - component_start;
150  if (last_component_size == 1 && path[component_start] == '.') {
151  // The last component is ".", which we can elide.
152  } else if (last_component_size == 2 && path[component_start] == '.' &&
153  path[component_start + 1] == '.') {
154  // The last component is "..", which means we need to remove the previous
155  // component.
156  if (put == traversal_root) {
157  path[put++] = '.';
158  path[put++] = '.';
159  path[put++] = '\\';
160  traversal_root = put;
161  } else {
162  put = ResolveParentDirectoryTraversal(path, put, rootLength);
163  }
164  } else {
165  // Otherwise, we need to copy over the last component.
166  if (put != component_start && last_component_size > 0) {
167  path.replace(put, last_component_size,
168  path.substr(component_start, last_component_size));
169  }
170  put += last_component_size;
171  }
172 
173  if (put >= 2 && IsSeparator(path[put - 1]))
174  --put; // Trim trailing /
175  else if (put == 0)
176  return "."; // Use . for otherwise empty paths to treat them as relative.
177 
178  path.resize(put);
179  std::replace(path.begin(), path.end(), '/', '\\');
180  return path;
181 }
182 
183 std::string AbsolutePath(const std::string& path) {
184  char absPath[MAX_PATH];
185  _fullpath(absPath, path.c_str(), MAX_PATH);
186  return std::string(absPath);
187 }
188 
189 std::string GetDirectoryName(const std::string& path) {
190  size_t rootLength = RootLength(path);
191  size_t separator = LastSeparator(path);
192  if (separator < rootLength)
193  separator = rootLength;
194  if (separator == std::string::npos)
195  return std::string();
196  return path.substr(0, separator);
197 }
198 
199 std::string GetBaseName(const std::string& path) {
200  size_t separator = LastSeparator(path);
201  if (separator == std::string::npos)
202  return path;
203  return path.substr(separator + 1);
204 }
205 
206 std::string GetAbsoluteFilePath(const std::string& path) {
207  HANDLE file =
208  CreateFileA(path.c_str(), FILE_READ_ATTRIBUTES,
209  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
210  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
211  if (file == INVALID_HANDLE_VALUE) {
212  return std::string();
213  }
214  char buffer[MAX_PATH];
215  DWORD ret =
216  GetFinalPathNameByHandleA(file, buffer, MAX_PATH, FILE_NAME_NORMALIZED);
217  if (ret == 0 || ret > MAX_PATH) {
218  CloseHandle(file);
219  return std::string();
220  }
221  std::string result(buffer);
222  result.erase(0, strlen("\\\\?\\"));
223  CloseHandle(file);
224  return result;
225 }
226 
227 } // namespace filesystem
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
std::string AbsolutePath(const std::string &path)
Definition: path_posix.cc:147
std::string GetBaseName(const std::string &path)
Definition: path_posix.cc:169
std::string GetAbsoluteFilePath(const std::string &path)
Definition: path_posix.cc:176
std::string GetDirectoryName(const std::string &path)
Definition: path_posix.cc:160
std::string SimplifyPath(std::string path)
Definition: path_posix.cc:48