10 #include "llvm/ADT/StringExtras.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/Format.h"
14 #include "llvm/Support/FormatVariadic.h"
15 #include "llvm/Support/Path.h"
25 return llvm::make_error<llvm::StringError>(
Message,
26 llvm::inconvertibleErrorCode());
33 class FileSystemScheme :
public URIScheme {
35 llvm::Expected<std::string>
36 getAbsolutePath(llvm::StringRef , llvm::StringRef Body,
37 llvm::StringRef )
const override {
38 if (!Body.startswith(
"/"))
39 return make_string_error(
"File scheme: expect body to be an absolute "
40 "path starting with '/': " +
43 if (Body.size() > 2 && Body[0] ==
'/' && Body[2] ==
':')
44 Body.consume_front(
"/");
45 llvm::SmallVector<char, 16>
Path(Body.begin(), Body.end());
46 llvm::sys::path::native(
Path);
47 return std::string(
Path.begin(),
Path.end());
51 uriFromAbsolutePath(llvm::StringRef AbsolutePath)
const override {
54 if (AbsolutePath.size() > 1 && AbsolutePath[1] ==
':')
56 Body += llvm::sys::path::convert_to_slash(AbsolutePath);
57 return URI(
"file",
"", Body);
61 llvm::Expected<std::unique_ptr<URIScheme>>
62 findSchemeByName(llvm::StringRef Scheme) {
64 return std::make_unique<FileSystemScheme>();
66 for (
const auto &URIScheme : URISchemeRegistry::entries()) {
67 if (URIScheme.getName() != Scheme)
69 return URIScheme.instantiate();
71 return make_string_error(
"Can't find scheme: " + Scheme);
74 bool shouldEscape(
unsigned char C) {
76 if ((C >=
'a' && C <=
'z') || (C >=
'A' && C <=
'Z') ||
77 (C >=
'0' && C <=
'9'))
96 void percentEncode(llvm::StringRef Content, std::string &
Out) {
98 for (
unsigned char C : Content)
102 Out.push_back(llvm::hexdigit(C / 16));
103 Out.push_back(llvm::hexdigit(C % 16));
105 {
Out.push_back(C); }
109 std::string percentDecode(llvm::StringRef Content) {
111 for (
auto I = Content.begin(),
E = Content.end(); I !=
E; ++I) {
116 if (*I ==
'%' && I + 2 < Content.end() && llvm::isHexDigit(*(I + 1)) &&
117 llvm::isHexDigit(*(I + 2))) {
118 Result.push_back(llvm::hexFromNibbles(*(I + 1), *(I + 2)));
121 Result.push_back(*I);
126 bool isValidScheme(llvm::StringRef Scheme) {
129 if (!llvm::isAlpha(Scheme[0]))
131 return std::all_of(Scheme.begin() + 1, Scheme.end(), [](
char C) {
132 return llvm::isAlnum(C) || C ==
'+' || C ==
'.' || C ==
'-';
138 URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
139 llvm::StringRef Body)
140 : Scheme(Scheme), Authority(Authority), Body(Body) {
141 assert(!Scheme.empty());
142 assert((Authority.empty() || Body.startswith(
"/")) &&
143 "URI body must start with '/' when authority is present.");
148 percentEncode(Scheme, Result);
149 Result.push_back(
':');
150 if (Authority.empty() && Body.empty())
154 if (!Authority.empty() || llvm::StringRef(Body).startswith(
"/"))
157 percentEncode(Authority, Result);
159 percentEncode(Body, Result);
165 llvm::StringRef Uri = OrigUri;
167 auto Pos = Uri.find(
':');
168 if (
Pos == llvm::StringRef::npos)
169 return make_string_error(
"Scheme must be provided in URI: " + OrigUri);
170 auto SchemeStr = Uri.substr(0,
Pos);
171 U.Scheme = percentDecode(SchemeStr);
172 if (!isValidScheme(U.Scheme))
173 return make_string_error(llvm::formatv(
"Invalid scheme: {0} (decoded: {1})",
174 SchemeStr, U.Scheme));
175 Uri = Uri.substr(
Pos + 1);
176 if (Uri.consume_front(
"//")) {
178 U.Authority = percentDecode(Uri.substr(0,
Pos));
179 Uri = Uri.substr(
Pos);
181 U.Body = percentDecode(Uri);
186 llvm::StringRef HintPath) {
189 return Uri.takeError();
192 return Path.takeError();
197 llvm::StringRef Scheme) {
198 if (!llvm::sys::path::is_absolute(AbsolutePath))
199 return make_string_error(
"Not a valid absolute path: " + AbsolutePath);
200 auto S = findSchemeByName(Scheme);
202 return S.takeError();
203 return S->get()->uriFromAbsolutePath(AbsolutePath);
207 if (!llvm::sys::path::is_absolute(AbsolutePath))
209 (
"Not a valid absolute path: " + AbsolutePath).str().c_str());
210 for (
auto &
Entry : URISchemeRegistry::entries()) {
211 auto URI =
Entry.instantiate()->uriFromAbsolutePath(AbsolutePath);
216 llvm::consumeError(
URI.takeError());
219 return std::move(*
URI);
226 auto U = FileSystemScheme().uriFromAbsolutePath(AbsolutePath);
229 return std::move(*U);
233 llvm::StringRef HintPath) {
234 auto S = findSchemeByName(Uri.Scheme);
236 return S.takeError();
237 return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
241 llvm::StringRef HintPath) {
242 if (!llvm::sys::path::is_absolute(AbsPath))
243 llvm_unreachable((
"Not a valid absolute path: " + AbsPath).str().c_str());
244 for (
auto &
Entry : URISchemeRegistry::entries()) {
245 auto S =
Entry.instantiate();
246 auto U = S->uriFromAbsolutePath(AbsPath);
251 llvm::consumeError(U.takeError());
254 return S->getAbsolutePath(U->Authority, U->Body, HintPath);
257 return std::string(AbsPath);
261 auto S = findSchemeByName(Uri.Scheme);
263 return S.takeError();
264 return S->get()->getIncludeSpelling(Uri);