Serenity Operating System
1/*
2 * Copyright (c) 2021, Max Wipfli <max.wipfli@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Vector.h>
8#include <Kernel/KLexicalPath.h>
9
10namespace Kernel::KLexicalPath {
11
12static StringView const s_single_dot = "."sv;
13
14bool is_absolute(StringView path)
15{
16 return !path.is_empty() && path[0] == '/';
17}
18
19bool is_canonical(StringView path)
20{
21 // FIXME: This can probably be done more efficiently.
22 if (path.is_empty())
23 return false;
24 if (path.ends_with('/') && path.length() != 1)
25 return false;
26 if (path.starts_with("./"sv) || path.contains("/./"sv) || path.ends_with("/."sv))
27 return false;
28 if (path.starts_with("../"sv) || path.contains("/../"sv) || path.ends_with("/.."sv))
29 return false;
30 if (path.contains("//"sv))
31 return false;
32 return true;
33}
34
35StringView basename(StringView a_path)
36{
37 if (a_path == "/"sv)
38 return a_path;
39 if (a_path.is_empty())
40 return s_single_dot;
41 auto path = a_path.trim("/"sv, TrimMode::Right);
42 // NOTE: If it's empty now, it means the path was just a series of slashes.
43 if (path.is_empty())
44 return a_path.substring_view(0, 1);
45 auto slash_index = path.find_last('/');
46 if (!slash_index.has_value())
47 return path;
48 auto basename = path.substring_view(*slash_index + 1);
49 return basename;
50}
51
52StringView dirname(StringView path)
53{
54 VERIFY(is_canonical(path));
55 auto slash_index = path.find_last('/');
56 VERIFY(slash_index.has_value());
57 return path.substring_view(0, *slash_index);
58}
59
60Vector<StringView> parts(StringView path)
61{
62 VERIFY(is_canonical(path));
63 return path.split_view('/');
64}
65
66ErrorOr<NonnullOwnPtr<KString>> try_join(StringView first, StringView second)
67{
68 VERIFY(is_canonical(first));
69 VERIFY(is_canonical(second));
70 VERIFY(!is_absolute(second));
71
72 if (first == "/"sv) {
73 char* buffer;
74 auto string = TRY(KString::try_create_uninitialized(1 + second.length(), buffer));
75 buffer[0] = '/';
76 __builtin_memcpy(buffer + 1, second.characters_without_null_termination(), second.length());
77 buffer[string->length()] = 0;
78 return string;
79 }
80 char* buffer;
81 auto string = TRY(KString::try_create_uninitialized(first.length() + 1 + second.length(), buffer));
82 __builtin_memcpy(buffer, first.characters_without_null_termination(), first.length());
83 buffer[first.length()] = '/';
84 __builtin_memcpy(buffer + first.length() + 1, second.characters_without_null_termination(), second.length());
85 buffer[string->length()] = 0;
86 return string;
87}
88
89}