summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AK/LexicalPath.cpp121
-rw-r--r--AK/LexicalPath.h24
-rw-r--r--Kernel/FileSystem/VirtualFileSystem.cpp2
-rw-r--r--Kernel/Syscalls/unveil.cpp6
-rw-r--r--Userland/Libraries/LibCore/File.cpp9
-rw-r--r--Userland/Libraries/LibGUI/EmojiInputDialog.cpp4
-rw-r--r--Userland/Libraries/LibGUI/FileSystemModel.cpp7
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp2
-rw-r--r--Userland/Utilities/basename.cpp2
-rw-r--r--Userland/Utilities/mkdir.cpp2
10 files changed, 99 insertions, 80 deletions
diff --git a/AK/LexicalPath.cpp b/AK/LexicalPath.cpp
index 803cfa5676..8a9ae25155 100644
--- a/AK/LexicalPath.cpp
+++ b/AK/LexicalPath.cpp
@@ -12,85 +12,102 @@
namespace AK {
+char s_single_dot = '.';
+
LexicalPath::LexicalPath(String s)
: m_string(move(s))
{
canonicalize();
}
+Vector<String> LexicalPath::parts() const
+{
+ Vector<String> vector;
+ vector.ensure_capacity(m_parts.size());
+ for (auto& part : m_parts)
+ vector.unchecked_append(part);
+ return vector;
+}
+
void LexicalPath::canonicalize()
{
+ // NOTE: We never allow an empty m_string, if it's empty, we just set it to '.'.
if (m_string.is_empty()) {
+ m_string = ".";
+ m_dirname = m_string;
+ m_basename = {};
+ m_title = {};
+ m_extension = {};
m_parts.clear();
return;
}
- m_is_absolute = m_string[0] == '/';
- auto parts = m_string.split_view('/');
-
- size_t approximate_canonical_length = 0;
- Vector<String> canonical_parts;
-
- for (size_t i = 0; i < parts.size(); ++i) {
- auto& part = parts[i];
- if (part == ".")
- continue;
- if (part == "..") {
- if (canonical_parts.is_empty()) {
- if (m_is_absolute) {
- // At the root, .. does nothing.
- continue;
- }
- } else {
- if (canonical_parts.last() != "..") {
- // A .. and a previous non-.. part cancel each other.
- canonical_parts.take_last();
- continue;
+ // NOTE: If there are no dots, no '//' and the path doesn't end with a slash, it is already canonical.
+ if (m_string.contains("."sv) || m_string.contains("//"sv) || m_string.ends_with('/')) {
+ auto parts = m_string.split_view('/');
+ size_t approximate_canonical_length = 0;
+ Vector<String> canonical_parts;
+
+ for (auto& part : parts) {
+ if (part == ".")
+ continue;
+ if (part == "..") {
+ if (canonical_parts.is_empty()) {
+ if (is_absolute()) {
+ // At the root, .. does nothing.
+ continue;
+ }
+ } else {
+ if (canonical_parts.last() != "..") {
+ // A .. and a previous non-.. part cancel each other.
+ canonical_parts.take_last();
+ continue;
+ }
}
}
- }
- if (!part.is_empty()) {
approximate_canonical_length += part.length() + 1;
canonical_parts.append(part);
}
- }
- if (canonical_parts.is_empty()) {
- m_string = m_basename = m_dirname = m_is_absolute ? "/" : ".";
- return;
- }
- StringBuilder dirname_builder(approximate_canonical_length);
- for (size_t i = 0; i < canonical_parts.size() - 1; ++i) {
- auto& canonical_part = canonical_parts[i];
- if (m_is_absolute || i != 0)
- dirname_builder.append('/');
- dirname_builder.append(canonical_part);
- }
- m_dirname = dirname_builder.to_string();
+ if (canonical_parts.is_empty() && !is_absolute())
+ canonical_parts.append(".");
- if (m_dirname.is_empty()) {
- m_dirname = m_is_absolute ? "/" : ".";
+ StringBuilder builder(approximate_canonical_length);
+ if (is_absolute())
+ builder.append('/');
+ builder.join('/', canonical_parts);
+ m_string = builder.to_string();
}
- m_basename = canonical_parts.last();
+ m_parts = m_string.split_view('/');
- Optional<size_t> last_dot = StringView(m_basename).find_last_of('.');
- if (last_dot.has_value()) {
- m_title = m_basename.substring(0, last_dot.value());
- m_extension = m_basename.substring(last_dot.value() + 1, m_basename.length() - last_dot.value() - 1);
+ auto last_slash_index = m_string.view().find_last_of('/');
+ if (!last_slash_index.has_value()) {
+ // The path contains a single part and is not absolute. m_dirname = "."sv
+ m_dirname = { &s_single_dot, 1 };
+ } else if (*last_slash_index == 0) {
+ // The path contains a single part and is absolute. m_dirname = "/"sv
+ m_dirname = m_string.substring_view(0, 1);
} else {
- m_title = m_basename;
+ m_dirname = m_string.substring_view(0, *last_slash_index);
}
- StringBuilder builder(approximate_canonical_length);
- for (size_t i = 0; i < canonical_parts.size(); ++i) {
- auto& canonical_part = canonical_parts[i];
- if (m_is_absolute || i != 0)
- builder.append('/');
- builder.append(canonical_part);
+ if (m_string == "/")
+ m_basename = m_string;
+ else {
+ VERIFY(m_parts.size() > 0);
+ m_basename = m_parts.last();
+ }
+
+ auto last_dot_index = m_basename.find_last_of('.');
+ // NOTE: if the dot index is 0, this means we have ".foo", it's not an extension, as the title would then be "".
+ if (last_dot_index.has_value() && *last_dot_index != 0) {
+ m_title = m_basename.substring_view(0, *last_dot_index);
+ m_extension = m_basename.substring_view(*last_dot_index + 1);
+ } else {
+ m_title = m_basename;
+ m_extension = {};
}
- m_parts = move(canonical_parts);
- m_string = builder.to_string();
}
bool LexicalPath::has_extension(StringView const& extension) const
diff --git a/AK/LexicalPath.h b/AK/LexicalPath.h
index 257b39963b..4c425c4ed3 100644
--- a/AK/LexicalPath.h
+++ b/AK/LexicalPath.h
@@ -17,15 +17,16 @@ public:
LexicalPath() = default;
explicit LexicalPath(String);
- bool is_absolute() const { return m_is_absolute; }
+ bool is_absolute() const { return !m_string.is_empty() && m_string[0] == '/'; }
String const& string() const { return m_string; }
- String const& dirname() const { return m_dirname; }
- String const& basename() const { return m_basename; }
- String const& title() const { return m_title; }
- String const& extension() const { return m_extension; }
+ StringView const& dirname() const { return m_dirname; }
+ StringView const& basename() const { return m_basename; }
+ StringView const& title() const { return m_title; }
+ StringView const& extension() const { return m_extension; }
- Vector<String> const& parts() const { return m_parts; }
+ Vector<StringView> const& parts_view() const { return m_parts; }
+ Vector<String> parts() const;
bool has_extension(StringView const&) const;
@@ -71,13 +72,12 @@ public:
private:
void canonicalize();
- Vector<String> m_parts;
+ Vector<StringView> m_parts;
String m_string;
- String m_dirname;
- String m_basename;
- String m_title;
- String m_extension;
- bool m_is_absolute { false };
+ StringView m_dirname;
+ StringView m_basename;
+ StringView m_title;
+ StringView m_extension;
};
template<>
diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp
index 1002b9f67b..a6341df577 100644
--- a/Kernel/FileSystem/VirtualFileSystem.cpp
+++ b/Kernel/FileSystem/VirtualFileSystem.cpp
@@ -834,7 +834,7 @@ UnveilNode const& VFS::find_matching_unveiled_path(StringView path)
auto& unveil_root = Process::current()->unveiled_paths();
LexicalPath lexical_path { path };
- auto& path_parts = lexical_path.parts();
+ auto& path_parts = lexical_path.parts_view();
return unveil_root.traverse_until_last_accessible_node(path_parts.begin(), path_parts.end());
}
diff --git a/Kernel/Syscalls/unveil.cpp b/Kernel/Syscalls/unveil.cpp
index 409c0df71e..0a9248b823 100644
--- a/Kernel/Syscalls/unveil.cpp
+++ b/Kernel/Syscalls/unveil.cpp
@@ -99,8 +99,8 @@ KResultOr<FlatPtr> Process::sys$unveil(Userspace<const Syscall::SC_unveil_params
}
LexicalPath lexical_path(new_unveiled_path);
- auto it = lexical_path.parts().begin();
- auto& matching_node = m_unveiled_paths.traverse_until_last_accessible_node(it, lexical_path.parts().end());
+ auto it = lexical_path.parts_view().begin();
+ auto& matching_node = m_unveiled_paths.traverse_until_last_accessible_node(it, lexical_path.parts_view().end());
if (it.is_end()) {
// If the path has already been explicitly unveiled, do not allow elevating its permissions.
if (matching_node.was_explicitly_unveiled()) {
@@ -122,7 +122,7 @@ KResultOr<FlatPtr> Process::sys$unveil(Userspace<const Syscall::SC_unveil_params
matching_node.insert(
it,
- lexical_path.parts().end(),
+ lexical_path.parts_view().end(),
{ new_unveiled_path, (UnveilAccess)new_permissions, true },
[](auto& parent, auto& it) -> Optional<UnveilMetadata> {
auto path = LexicalPath::join(parent.path(), *it).string();
diff --git a/Userland/Libraries/LibCore/File.cpp b/Userland/Libraries/LibCore/File.cpp
index 0a08414396..a8e62d05a0 100644
--- a/Userland/Libraries/LibCore/File.cpp
+++ b/Userland/Libraries/LibCore/File.cpp
@@ -271,16 +271,17 @@ static String get_duplicate_name(const String& path, int duplicate_count)
LexicalPath lexical_path(path);
StringBuilder duplicated_name;
duplicated_name.append('/');
- for (size_t i = 0; i < lexical_path.parts().size() - 1; ++i) {
- duplicated_name.appendff("{}/", lexical_path.parts()[i]);
+ auto& parts = lexical_path.parts_view();
+ for (size_t i = 0; i < parts.size() - 1; ++i) {
+ duplicated_name.appendff("{}/", parts[i]);
}
auto prev_duplicate_tag = String::formatted("({})", duplicate_count);
auto title = lexical_path.title();
if (title.ends_with(prev_duplicate_tag)) {
// remove the previous duplicate tag "(n)" so we can add a new tag.
- title = title.substring(0, title.length() - prev_duplicate_tag.length());
+ title = title.substring_view(0, title.length() - prev_duplicate_tag.length());
}
- duplicated_name.appendff("{} ({})", lexical_path.title(), duplicate_count);
+ duplicated_name.appendff("{} ({})", title, duplicate_count);
if (!lexical_path.extension().is_empty()) {
duplicated_name.appendff(".{}", lexical_path.extension());
}
diff --git a/Userland/Libraries/LibGUI/EmojiInputDialog.cpp b/Userland/Libraries/LibGUI/EmojiInputDialog.cpp
index 098a2ce1ab..7a25e701c3 100644
--- a/Userland/Libraries/LibGUI/EmojiInputDialog.cpp
+++ b/Userland/Libraries/LibGUI/EmojiInputDialog.cpp
@@ -26,10 +26,10 @@ static Vector<u32> supported_emoji_code_points()
auto lexical_path = LexicalPath(filename);
if (lexical_path.extension() != "png")
continue;
- auto basename = lexical_path.basename();
+ auto& basename = lexical_path.basename();
if (!basename.starts_with("U+"))
continue;
- u32 code_point = strtoul(basename.characters() + 2, nullptr, 16);
+ u32 code_point = strtoul(basename.to_string().characters() + 2, nullptr, 16);
code_points.append(code_point);
}
return code_points;
diff --git a/Userland/Libraries/LibGUI/FileSystemModel.cpp b/Userland/Libraries/LibGUI/FileSystemModel.cpp
index 71e08653f7..fe5f31a4be 100644
--- a/Userland/Libraries/LibGUI/FileSystemModel.cpp
+++ b/Userland/Libraries/LibGUI/FileSystemModel.cpp
@@ -202,15 +202,16 @@ FileSystemModel::Node const* FileSystemModel::node_for_path(String const& path)
if (lexical_path.string() == "/")
return node;
- for (size_t i = 0; i < lexical_path.parts().size(); ++i) {
- auto& part = lexical_path.parts()[i];
+ auto& parts = lexical_path.parts_view();
+ for (size_t i = 0; i < parts.size(); ++i) {
+ auto& part = parts[i];
bool found = false;
for (auto& child : node->children) {
if (child.name == part) {
const_cast<Node&>(child).reify_if_needed();
node = &child;
found = true;
- if (i == lexical_path.parts().size() - 1)
+ if (i == parts.size() - 1)
return node;
break;
}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
index 31626bdb6f..9b5371a801 100644
--- a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
+++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
@@ -402,7 +402,7 @@ int main(int argc, char** argv)
}
LexicalPath lexical_path(path);
- auto namespace_ = lexical_path.parts().at(lexical_path.parts().size() - 2);
+ auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2);
auto data = file_or_error.value()->read_all();
auto interface = IDL::parse_interface(path, data);
diff --git a/Userland/Utilities/basename.cpp b/Userland/Utilities/basename.cpp
index 4b6031715e..fa2b480f1d 100644
--- a/Userland/Utilities/basename.cpp
+++ b/Userland/Utilities/basename.cpp
@@ -27,7 +27,7 @@ int main(int argc, char** argv)
auto result = LexicalPath::basename(path);
if (!suffix.is_null() && result.length() != suffix.length() && result.ends_with(suffix))
- result = result.substring(0, result.length() - suffix.length());
+ result = result.substring_view(0, result.length() - suffix.length());
outln("{}", result);
return 0;
diff --git a/Userland/Utilities/mkdir.cpp b/Userland/Utilities/mkdir.cpp
index 05752966b9..74a02e2246 100644
--- a/Userland/Utilities/mkdir.cpp
+++ b/Userland/Utilities/mkdir.cpp
@@ -44,7 +44,7 @@ int main(int argc, char** argv)
StringBuilder path_builder;
if (lexical_path.is_absolute())
path_builder.append("/");
- for (auto& part : lexical_path.parts()) {
+ for (auto& part : lexical_path.parts_view()) {
path_builder.append(part);
auto path = path_builder.build();
struct stat st;