CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/File/AndroidContentURI.cpp
Views: 1401
1
#include "Common/File/AndroidContentURI.h"
2
3
bool AndroidContentURI::Parse(std::string_view path) {
4
const char *prefix = "content://";
5
if (!startsWith(path, prefix)) {
6
return false;
7
}
8
9
std::string_view components = path.substr(strlen(prefix));
10
11
std::vector<std::string_view> parts;
12
SplitString(components, '/', parts);
13
if (parts.size() == 3) {
14
// Single file URI.
15
provider = parts[0];
16
if (parts[1] == "tree") {
17
// Single directory URI.
18
// Not sure when we encounter these?
19
// file empty signals this type.
20
root = UriDecode(parts[2]);
21
return true;
22
} else if (parts[1] == "document") {
23
// root empty signals this type.
24
file = UriDecode(parts[2]);
25
return true;
26
} else {
27
// What's this?
28
return false;
29
}
30
} else if (parts.size() == 5) {
31
// Tree URI
32
provider = parts[0];
33
if (parts[1] != "tree") {
34
return false;
35
}
36
root = UriDecode(parts[2]);
37
if (parts[3] != "document") {
38
return false;
39
}
40
file = UriDecode(parts[4]);
41
// Sanity check file path.
42
return startsWith(file, root);
43
} else {
44
// Invalid Content URI
45
return false;
46
}
47
}
48
49
AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePath) {
50
if (root.empty()) {
51
ERROR_LOG(Log::System, "WithRootFilePath cannot be used with single file URIs.");
52
return *this;
53
}
54
55
AndroidContentURI uri = *this;
56
uri.file = uri.root;
57
if (!filePath.empty()) {
58
uri.file += "/" + filePath;
59
}
60
return uri;
61
}
62
63
AndroidContentURI AndroidContentURI::WithComponent(std::string_view filePath) {
64
AndroidContentURI uri = *this;
65
if (uri.file.empty()) {
66
// Not sure what to do.
67
return uri;
68
}
69
if (uri.file.back() == ':') {
70
// Special case handling for Document URIs: Treat the ':' as a directory separator too (but preserved in the filename).
71
uri.file.append(filePath);
72
} else {
73
uri.file.push_back('/');
74
uri.file.append(filePath);
75
}
76
return uri;
77
}
78
79
AndroidContentURI AndroidContentURI::WithExtraExtension(std::string_view extension) {
80
AndroidContentURI uri = *this;
81
uri.file.append(extension);
82
return uri;
83
}
84
85
AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {
86
_dbg_assert_(!oldExtension.empty() && oldExtension[0] == '.');
87
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
88
AndroidContentURI uri = *this;
89
if (endsWithNoCase(file, oldExtension)) {
90
uri.file = file.substr(0, file.size() - oldExtension.size()) + newExtension;
91
}
92
return uri;
93
}
94
95
AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &newExtension) const {
96
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
97
AndroidContentURI uri = *this;
98
if (file.empty()) {
99
return uri;
100
}
101
std::string extension = GetFileExtension();
102
uri.file = file.substr(0, file.size() - extension.size()) + newExtension;
103
return uri;
104
}
105
106
bool AndroidContentURI::CanNavigateUp() const {
107
if (IsTreeURI()) {
108
return file.size() > root.size();
109
} else {
110
return file.find(':') != std::string::npos && file.back() != ':';
111
}
112
}
113
114
// Only goes downwards in hierarchies. No ".." will ever be generated.
115
bool AndroidContentURI::ComputePathTo(const AndroidContentURI &other, std::string &path) const {
116
size_t offset = FilePath().size() + 1;
117
const auto &otherFilePath = other.FilePath();
118
if (offset >= otherFilePath.size()) {
119
ERROR_LOG(Log::System, "Bad call to PathTo. '%s' -> '%s'", FilePath().c_str(), other.FilePath().c_str());
120
return false;
121
}
122
123
path = other.FilePath().substr(FilePath().size() + 1);
124
return true;
125
}
126
127
std::string AndroidContentURI::GetFileExtension() const {
128
size_t pos = file.rfind('.');
129
if (pos == std::string::npos) {
130
return "";
131
}
132
size_t slash_pos = file.rfind('/');
133
if (slash_pos != std::string::npos && slash_pos > pos) {
134
// Don't want to detect "df/file" from "/as.df/file"
135
return "";
136
}
137
std::string ext = file.substr(pos);
138
for (size_t i = 0; i < ext.size(); i++) {
139
ext[i] = tolower(ext[i]);
140
}
141
return ext;
142
}
143
144
std::string AndroidContentURI::GetLastPart() const {
145
if (file.empty()) {
146
// Can't do anything anyway.
147
return std::string();
148
}
149
150
if (!CanNavigateUp()) {
151
size_t colon = file.rfind(':');
152
if (colon == std::string::npos) {
153
return std::string();
154
}
155
if (file.back() == ':') {
156
return file;
157
}
158
return file.substr(colon + 1);
159
}
160
161
size_t slash = file.rfind('/');
162
if (slash == std::string::npos) {
163
// ok, look for the final colon. If it's the last char, we would have been caught above in !CanNavigateUp.
164
size_t colon = file.rfind(':');
165
if (colon == std::string::npos) {
166
return std::string();
167
}
168
return file.substr(colon + 1);
169
}
170
171
std::string part = file.substr(slash + 1);
172
return part;
173
}
174
175
bool AndroidContentURI::NavigateUp() {
176
if (!CanNavigateUp()) {
177
return false;
178
}
179
180
size_t slash = file.rfind('/');
181
if (slash == std::string::npos) {
182
// ok, look for the final colon.
183
size_t colon = file.rfind(':');
184
if (colon == std::string::npos) {
185
return false;
186
}
187
file = file.substr(0, colon + 1); // Note: we include the colon in these paths.
188
return true;
189
}
190
191
file = file.substr(0, slash);
192
return true;
193
}
194
195
196
std::string AndroidContentURI::ToString() const {
197
if (file.empty()) {
198
// Tree URI
199
return StringFromFormat("content://%s/tree/%s", provider.c_str(), UriEncode(root).c_str());
200
} else if (root.empty()) {
201
// Single file URI
202
return StringFromFormat("content://%s/document/%s", provider.c_str(), UriEncode(file).c_str());
203
} else {
204
// File URI from Tree
205
return StringFromFormat("content://%s/tree/%s/document/%s", provider.c_str(), UriEncode(root).c_str(), UriEncode(file).c_str());
206
}
207
}
208
209