diff options
Diffstat (limited to 'graphics/asymptote/LspCpp/third_party/uri/src/detail/uri_parse.cpp')
-rw-r--r-- | graphics/asymptote/LspCpp/third_party/uri/src/detail/uri_parse.cpp | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/graphics/asymptote/LspCpp/third_party/uri/src/detail/uri_parse.cpp b/graphics/asymptote/LspCpp/third_party/uri/src/detail/uri_parse.cpp new file mode 100644 index 0000000000..7f771480db --- /dev/null +++ b/graphics/asymptote/LspCpp/third_party/uri/src/detail/uri_parse.cpp @@ -0,0 +1,384 @@ +// Copyright 2016-2017 Glyn Matthews. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "uri_parse.hpp" +#include <iterator> +#include <limits> +#include "grammar.hpp" +#include <network/uri/detail/uri_parts.hpp> + +namespace network { +namespace detail { +namespace { +enum class uri_state { scheme, hier_part, query, fragment }; + +enum class hier_part_state { + first_slash, + second_slash, + authority, + host, + host_ipv6, + port, + path +}; + +bool validate_scheme(string_view::const_iterator &it, + string_view::const_iterator last) { + if (it == last) { + return false; + } + + // The first character must be a letter + if (!std::isalpha(*it, std::locale("C"))) { + return false; + } + ++it; + + while (it != last) { + if (*it == ':') { + break; + } else if (!isalnum(it, last) && !is_in(it, last, "+-.")) { + return false; + } + } + + return true; +} + +bool validate_user_info(string_view::const_iterator it, + string_view::const_iterator last) { + while (it != last) { + if (!is_unreserved(it, last) && !is_pct_encoded(it, last) && + !is_sub_delim(it, last) && !is_in(it, last, ":")) { + return false; + } + } + return true; +} + +bool set_host_and_port(string_view::const_iterator first, + string_view::const_iterator last, + string_view::const_iterator last_colon, + uri_parts &parts) { + if (first >= last_colon) { + parts.hier_part.host = uri_part(first, last); + } else { + auto port_start = last_colon; + ++port_start; + parts.hier_part.host = uri_part(first, last_colon); + if (!is_valid_port(port_start)) { + return false; + } + parts.hier_part.port = uri_part(port_start, last); + } + return true; +} + +bool validate_fragment(string_view::const_iterator &it, + string_view::const_iterator last) { + while (it != last) { + if (!is_pchar(it, last) && !is_in(it, last, "?/")) { + return false; + } + } + return true; +} +} // namespace + +bool parse(string_view::const_iterator &it, string_view::const_iterator last, + uri_parts &parts) { + auto state = uri_state::scheme; + + auto first = it; + + if (it == last) { + return false; + } + + if (validate_scheme(it, last)) { + parts.scheme = uri_part(first, it); + // move past the scheme delimiter + ++it; + state = uri_state::hier_part; + } else { + return false; + } + + // Hierarchical part + auto hp_state = hier_part_state::first_slash; + // this is used by the user_info/port + auto last_colon = first; + while (it != last) { + if (hp_state == hier_part_state::first_slash) { + if (*it == '/') { + hp_state = hier_part_state::second_slash; + // set the first iterator in case the second slash is not forthcoming + first = it; + ++it; + continue; + } else { + hp_state = hier_part_state::path; + first = it; + } + } else if (hp_state == hier_part_state::second_slash) { + if (*it == '/') { + hp_state = hier_part_state::authority; + ++it; + first = it; + continue; + } else { + // it's a valid URI, and this is the beginning of the path + hp_state = hier_part_state::path; + } + } else if (hp_state == hier_part_state::authority) { + if (is_in(first, last, "@:")) { + return false; + } + + // reset the last colon + if (first == it) { + last_colon = first; + } + + if (*it == '@') { + if (!validate_user_info(first, it)) { + return false; + } + parts.hier_part.user_info = uri_part(first, it); + hp_state = hier_part_state::host; + ++it; + first = it; + + if (*first == '[') { + // this is an IPv6 address + hp_state = hier_part_state::host_ipv6; + } + + continue; + } else if (*it == '[') { + // this is an IPv6 address + hp_state = hier_part_state::host_ipv6; + first = it; + continue; + } else if (*it == ':') { + last_colon = it; + } else if (*it == '/') { + // we skipped right past the host and port, and are at the path. + if (!set_host_and_port(first, it, last_colon, parts)) { + return false; + } + hp_state = hier_part_state::path; + first = it; + continue; + } else if (*it == '?') { + // the path is empty, but valid, and the next part is the query + if (!set_host_and_port(first, it, last_colon, parts)) { + return false; + } + parts.hier_part.path = uri_part(it, it); + state = uri_state::query; + ++it; + first = it; + break; + } else if (*it == '#') { + // the path is empty, but valid, and the next part is the fragment + if (!set_host_and_port(first, it, last_colon, parts)) { + return false; + } + parts.hier_part.path = uri_part(it, it); + state = uri_state::fragment; + ++it; + first = it; + break; + } + } else if (hp_state == hier_part_state::host) { + if (*first == ':') { + return false; + } + + if (*it == ':') { + parts.hier_part.host = uri_part(first, it); + hp_state = hier_part_state::port; + ++it; + first = it; + continue; + } else if (*it == '/') { + parts.hier_part.host = uri_part(first, it); + hp_state = hier_part_state::path; + first = it; + continue; + } else if (*it == '?') { + // the path is empty, but valid, and the next part is the query + parts.hier_part.host = uri_part(first, it); + parts.hier_part.path = uri_part(it, it); + state = uri_state::query; + ++it; + first = it; + break; + } else if (*it == '#') { + // the path is empty, but valid, and the next part is the fragment + parts.hier_part.host = uri_part(first, it); + parts.hier_part.path = uri_part(it, it); + state = uri_state::fragment; + ++it; + first = it; + break; + } + } else if (hp_state == hier_part_state::host_ipv6) { + if (*first != '[') { + return false; + } + + if (*it == ']') { + ++it; + // Then test if the next part is a host, part, or the end of the file + if (it == last) { + break; + } else if (*it == ':') { + parts.hier_part.host = uri_part(first, it); + hp_state = hier_part_state::port; + ++it; + first = it; + } else if (*it == '/') { + parts.hier_part.host = uri_part(first, it); + hp_state = hier_part_state::path; + first = it; + } else if (*it == '?') { + parts.hier_part.host = uri_part(first, it); + parts.hier_part.path = uri_part(it, it); + state = uri_state::query; + ++it; + first = it; + break; + } else if (*it == '#') { + parts.hier_part.host = uri_part(first, it); + parts.hier_part.path = uri_part(it, it); + state = uri_state::fragment; + ++it; + first = it; + break; + } + continue; + } + } else if (hp_state == hier_part_state::port) { + if (*first == '/') { + // the port is empty, but valid + if (!is_valid_port(first)) { + return false; + } + parts.hier_part.port = uri_part(first, it); + + // the port isn't set, but the path is + hp_state = hier_part_state::path; + continue; + } + + if (*it == '/') { + if (!is_valid_port(first)) { + return false; + } + parts.hier_part.port = uri_part(first, it); + hp_state = hier_part_state::path; + first = it; + continue; + } else if (!isdigit(it, last)) { + return false; + } + continue; + } else if (hp_state == hier_part_state::path) { + if (*it == '?') { + parts.hier_part.path = uri_part(first, it); + // move past the query delimiter + ++it; + first = it; + state = uri_state::query; + break; + } else if (*it == '#') { + parts.hier_part.path = uri_part(first, it); + // move past the fragment delimiter + ++it; + first = it; + state = uri_state::fragment; + break; + } + + if (!is_pchar(it, last) && !is_in(it, last, "/")) { + return false; + } else { + continue; + } + } + + ++it; + } + + if (state == uri_state::query) { + while (it != last) { + if (!is_pchar(it, last) && !is_in(it, last, "?/")) { + // If this is a fragment, keep going + if (*it == '#') { + parts.query = uri_part(first, it); + // move past the fragment delimiter + ++it; + first = it; + state = uri_state::fragment; + break; + } else { + return false; + } + } + } + } + + if (state == uri_state::fragment) { + if (!validate_fragment(it, last)) { + return false; + } + } + + // we're done! + if (state == uri_state::hier_part) { + if (hp_state == hier_part_state::authority) { + if (first == last) { + return false; + } + + if (!set_host_and_port(first, last, last_colon, parts)) { + return false; + } + parts.hier_part.path = uri_part(last, last); + } else if (hp_state == hier_part_state::host) { + if (first == last) { + return false; + } + + if (!set_host_and_port(first, last, last_colon, parts)) { + return false; + } + parts.hier_part.path = uri_part(last, last); + } else if (hp_state == hier_part_state::host_ipv6) { + if (!set_host_and_port(first, last, last_colon, parts)) { + return false; + } + parts.hier_part.path = uri_part(last, last); + } else if (hp_state == hier_part_state::port) { + if (!is_valid_port(first)) { + return false; + } + parts.hier_part.port = uri_part(first, last); + parts.hier_part.path = uri_part(last, last); + } else if (hp_state == hier_part_state::path) { + parts.hier_part.path = uri_part(first, last); + } + } else if (state == uri_state::query) { + parts.query = uri_part(first, last); + } else if (state == uri_state::fragment) { + parts.fragment = uri_part(first, last); + } + + return true; +} +} // namespace detail +} // namespace network |