xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/util/path.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/util/path.h"
16 
17 #include <deque>
18 #include <initializer_list>
19 #include <string>
20 #include <utility>
21 
22 #include "absl/strings/match.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/str_join.h"
25 #include "absl/strings/str_split.h"
26 #include "absl/strings/string_view.h"
27 #include "absl/strings/strip.h"
28 
29 namespace sapi::file {
30 namespace internal {
31 
32 constexpr char kPathSeparator[] = "/";
33 
JoinPathImpl(std::initializer_list<absl::string_view> paths)34 std::string JoinPathImpl(std::initializer_list<absl::string_view> paths) {
35   std::string result;
36   for (const auto& path : paths) {
37     if (path.empty()) {
38       continue;
39     }
40     if (result.empty()) {
41       absl::StrAppend(&result, path);
42       continue;
43     }
44     const auto comp = absl::StripPrefix(path, kPathSeparator);
45     if (absl::EndsWith(result, kPathSeparator)) {
46       absl::StrAppend(&result, comp);
47     } else {
48       absl::StrAppend(&result, kPathSeparator, comp);
49     }
50   }
51   return result;
52 }
53 
54 }  // namespace internal
55 
IsAbsolutePath(absl::string_view path)56 bool IsAbsolutePath(absl::string_view path) {
57   return !path.empty() && path[0] == '/';
58 }
59 
SplitPath(absl::string_view path)60 std::pair<absl::string_view, absl::string_view> SplitPath(
61     absl::string_view path) {
62   const auto pos = path.find_last_of('/');
63 
64   // Handle the case with no '/' in 'path'.
65   if (pos == absl::string_view::npos) {
66     return {path.substr(0, 0), path};
67   }
68 
69   // Handle the case with a single leading '/' in 'path'.
70   if (pos == 0) {
71     return {path.substr(0, 1), absl::ClippedSubstr(path, 1)};
72   }
73   return {path.substr(0, pos), absl::ClippedSubstr(path, pos + 1)};
74 }
75 
CleanPath(const absl::string_view unclean_path)76 std::string CleanPath(const absl::string_view unclean_path) {
77   int dotdot_num = 0;
78   std::deque<absl::string_view> parts;
79   for (absl::string_view part :
80        absl::StrSplit(unclean_path, '/', absl::SkipEmpty())) {
81     if (part == "..") {
82       if (parts.empty()) {
83         ++dotdot_num;
84       } else {
85         parts.pop_back();
86       }
87     } else if (part != ".") {
88       parts.push_back(part);
89     }
90   }
91   if (absl::StartsWith(unclean_path, "/")) {
92     if (parts.empty()) {
93       return "/";
94     }
95     parts.push_front("");
96   } else {
97     for (; dotdot_num; --dotdot_num) {
98       parts.push_front("..");
99     }
100     if (parts.empty()) {
101       return ".";
102     }
103   }
104   return absl::StrJoin(parts, "/");
105 }
106 
107 }  // namespace sapi::file
108