@@ -98,6 +98,48 @@ struct canonicalizing_path_io_error {
98
98
99
99
canonical_path::canonical_path (std::string &&path) : path_(std::move(path)) {
100
100
QLJS_ASSERT (!this ->path_ .empty ());
101
+
102
+ #if QLJS_PATHS_POSIX
103
+ std::string_view p (this ->path_ );
104
+ while (!ends_with (p, " /" )) {
105
+ std::size_t slash_index = p.find_last_of (" /" );
106
+ QLJS_ASSERT (slash_index != std::string::npos);
107
+ if (slash_index == 0 || slash_index == 1 ) {
108
+ // Preserve the slash for resulting root paths.
109
+ this ->path_lengths_ .push_back (slash_index + 1 );
110
+ p = p.substr (0 , slash_index + 1 );
111
+ } else {
112
+ this ->path_lengths_ .push_back (slash_index);
113
+ p = p.substr (0 , slash_index);
114
+ }
115
+ }
116
+ std::reverse (this ->path_lengths_ .begin (), this ->path_lengths_ .end ());
117
+ #elif QLJS_PATHS_WIN32
118
+ // TODO(strager): path_lengths_ is in UTF-16 code units, but it's interpreted
119
+ // as UTF-8 code units! Fix by storing a std::wstring in canonical_path
120
+ // instead of a std::string.
121
+ // TODO(strager): Avoid string conversions and copying.
122
+ std::optional<std::wstring> wpath = mbstring_to_wstring (this ->path_ .c_str ());
123
+ QLJS_ASSERT (wpath.has_value ());
124
+ for (;;) {
125
+ HRESULT result = ::PathCchRemoveFileSpec (wpath->data (), wpath->size () + 1 );
126
+ switch (result) {
127
+ case S_OK:
128
+ this ->path_lengths_ .push_back (std::wcslen (wpath->data ()));
129
+ break ;
130
+ case S_FALSE:
131
+ // Path is a root path already.
132
+ goto done;
133
+ default :
134
+ QLJS_UNIMPLEMENTED ();
135
+ break ;
136
+ }
137
+ }
138
+ done:
139
+ std::reverse (this ->path_lengths_ .begin (), this ->path_lengths_ .end ());
140
+ #else
141
+ #error "Unsupported platform"
142
+ #endif
101
143
}
102
144
103
145
std::string_view canonical_path::path () const &noexcept { return this ->path_ ; }
@@ -135,6 +177,7 @@ bool operator!=(const canonical_path &lhs, std::string_view rhs) noexcept {
135
177
}
136
178
137
179
void canonical_path::append_component (std::string_view component) {
180
+ this ->path_lengths_ .push_back (this ->path_ .size ());
138
181
if (this ->path_ [this ->path_ .size () - 1 ] !=
139
182
preferred_component_separator_char) {
140
183
this ->path_ .push_back (preferred_component_separator_char);
@@ -143,39 +186,13 @@ void canonical_path::append_component(std::string_view component) {
143
186
}
144
187
145
188
bool canonical_path::parent () {
146
- #if QLJS_PATHS_POSIX
147
- if (this ->path_ [this ->path_ .size () - 1 ] == ' /' ) {
189
+ if (this ->path_lengths_ .empty ()) {
148
190
return false ;
149
191
}
150
- std::size_t slash_index = this ->path_ .find_last_of (" /" );
151
- QLJS_ASSERT (slash_index != std::string::npos);
152
- if (slash_index == 0 || slash_index == 1 ) {
153
- // Preserve the slash for resulting root paths.
154
- this ->path_ .resize (slash_index + 1 );
155
- } else {
156
- this ->path_ .resize (slash_index);
157
- }
192
+ std::size_t path_length = this ->path_lengths_ .back ();
193
+ this ->path_lengths_ .pop_back ();
194
+ this ->path_ .resize (path_length);
158
195
return true ;
159
- #elif QLJS_PATHS_WIN32
160
- // TODO(strager): Avoid string conversions and copying.
161
- std::optional<std::wstring> wpath = mbstring_to_wstring (this ->path_ .c_str ());
162
- QLJS_ASSERT (wpath.has_value ());
163
- HRESULT result = ::PathCchRemoveFileSpec (wpath->data (), wpath->size () + 1 );
164
- switch (result) {
165
- case S_OK:
166
- wpath->resize (std::wcslen (wpath->data ()));
167
- this ->path_ = std::filesystem::path (*wpath).string ();
168
- return true ;
169
- case S_FALSE:
170
- // Path is a root path already.
171
- return false ;
172
- default :
173
- QLJS_UNIMPLEMENTED ();
174
- break ;
175
- }
176
- #else
177
- #error "Unsupported platform"
178
- #endif
179
196
}
180
197
181
198
canonical_path_result::canonical_path_result (std::string &&path,
@@ -207,6 +224,10 @@ bool canonical_path_result::have_missing_components() const noexcept {
207
224
}
208
225
209
226
void canonical_path_result::drop_missing_components () {
227
+ while (!this ->path_ .path_lengths_ .empty () &&
228
+ this ->path_ .path_lengths_ .back () >= this ->existing_path_length_ ) {
229
+ this ->path_ .path_lengths_ .pop_back ();
230
+ }
210
231
this ->path_ .path_ .resize (this ->existing_path_length_ );
211
232
}
212
233
0 commit comments