// // aegis - project change supervisor // Copyright (C) 2004-2006, 2008 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include // for assert #include #include #include url::~url() { } url::url() : protocol("file"), hostname("localhost"), port(80) { } url::url(const nstring &s) : port(0) { split(s.c_str()); } url::url(const char *s) : port(0) { split(s); } url::url(const url &arg) : protocol(arg.protocol), userpass(arg.userpass), hostname(arg.hostname), port(arg.port), path(arg.path), query(arg.query), anchor_name(arg.anchor_name) { } url & url::operator=(const url &arg) { if (this != &arg) { protocol = arg.protocol; userpass = arg.userpass; hostname = arg.hostname; port = arg.port; path = arg.path; query = arg.query; anchor_name = arg.anchor_name; } return *this; } static bool check_exists(const nstring &path) { os_become_orig(); bool result = os_exists(path); os_become_undo(); trace(("check_exists(\"%s\")->%d\n", path.c_str(), (int)result)); return result; } void url::split(const char *cp) { assert(cp); trace(("url::split(cp = \"%s\")\n{\n", cp)); // // Clear everything to a known state. // (But make sure it *exactly* matches the default constructor.) // protocol = "file"; userpass = ""; hostname = "localhost"; port = 80; path = ""; query = ""; anchor_name = ""; // // We have a couple of escapes, to ensure that ordinary files get // through as a file, and not as some weird and incorrect URL. // if (cp[0] == '/') { path = nstring(cp); trace(("return \"%s\";\n}\n", reassemble().c_str())); return; } if (check_exists(nstring(cp))) { os_become_orig(); nstring here(os_curdir()); os_become_undo(); path = os_path_rel2abs(here, nstring(cp)); trace(("return \"%s\";\n}\n", reassemble().c_str())); return; } // // Extract the protocol portion. // const char *colon = strpbrk(cp, ":/?#"); if (colon && colon[0] == ':') { protocol = nstring(cp, colon - cp).downcase(); cp = colon + 1; } else { protocol = "http"; } // // Extract the hostname portion. // if (cp[0] == '/' && cp[1] == '/') { // we are definitely looking at a host name cp += 2; const char *end_of_hostname = strpbrk(cp, "/?#"); if (!end_of_hostname) { hostname = nstring(cp); cp += hostname.size(); path = "/"; } else if (end_of_hostname == cp) { hostname = "localhost"; } else { hostname = nstring(cp, end_of_hostname - cp).downcase(); cp = end_of_hostname; } } else if (check_exists(nstring(cp))) { // // We are looking at a file name. // protocol = "file"; userpass = ""; hostname = "localhost"; os_become_orig(); nstring here(os_curdir()); os_become_undo(); path = os_path_rel2abs(here, nstring(cp)); cp += strlen(cp); trace(("return \"%s\";\n}\n", reassemble().c_str())); return; } else { // // We are looking at a host name. // const char *end_of_hostname = strpbrk(cp, "/?#"); if (!end_of_hostname) { hostname = nstring(cp).downcase(); cp += hostname.size(); } else if (end_of_hostname == cp) hostname = "localhost"; else { hostname = nstring(cp, end_of_hostname - cp).downcase(); cp = end_of_hostname; } } // // Extract the user name and password portion from the hostname. // const char *at = strchr(hostname.c_str(), '@'); if (at) { userpass = nstring(hostname.c_str(), at - hostname.c_str()); ++at; // must exclude @ sign hostname = nstring(at); } // // Extract the port portion from the hostname. // colon = strchr(hostname.c_str(), ':'); if (colon) { port = atoi(colon + 1); if (port <= 0 || port >= 0x10000) port = 80; hostname = nstring(hostname.c_str(), colon - hostname.c_str()); } // // Extract the path portion. // if (*cp != '?' && *cp != '#') { const char *end_of_path = strpbrk(cp, "?#"); if (!end_of_path) { path = nstring(cp); trace(("return \"%s\";\n}\n", reassemble().c_str())); return; } path = nstring(cp, end_of_path - cp); cp = end_of_path; } else { // No path given. Leave it empty for now. } // // Extract the query portion. // if (*cp == '?') { const char *end_of_query = strchr(cp, '#'); if (!end_of_query) { query = nstring(cp); cp += query.size(); } else { query = nstring(cp, end_of_query - cp); cp = end_of_query; } } // // Extract the anchor_name portion. // if (*cp == '#') { anchor_name = nstring(cp); cp += anchor_name.size(); } assert(*cp == '\0'); trace(("return \"%s\";\n}\n", reassemble().c_str())); } bool url::is_a_file() const { return (protocol == "file"); } nstring url::get_path() const { assert(is_a_file()); if (path.empty()) return "/"; assert(path[0] == '/'); return path; } void url::set_path_if_empty(const nstring &s) { assert(!strpbrk(s.c_str(), "?#")); if (path.empty()) { if (path[0] == '/') path = s; else path = "/" + s; } } void url::set_query_if_empty(const nstring &s) { assert(s.c_str()[0] != '?'); if (query.empty()) query = "?" + s; } nstring url::reassemble(bool exclude_userpass) const { if (is_a_file()) return get_path(); assert(path.empty() || path[0] == '/'); assert(query.empty() || query[0] == '?'); assert(anchor_name.empty() || anchor_name[0] == '#'); return ( protocol + "://" + // This includes the trailing '@' if needed ((exclude_userpass || userpass.empty()) ? "" : userpass + "@") + hostname + (port == 80 ? "" : nstring::format(":%d", port)) + // The path includes the '/' or is empty (path.empty() ? "/" : path) + // This includes the '?' or is empty query + // This includes the '#' or is empty anchor_name ); } void url::set_host_part_from(const url &arg) { protocol = arg.get_protocol(); hostname = arg.get_hostname(); port = arg.get_port(); }