Uri class の作成 †Uri は取扱が面倒である。特に相対 uri から絶対 uri にする部分には注意が 必要である。ここでは、RFC 3986 をベースになるべく文書そのまま実装してみた。 文字列は std::string を使っている。New 演算子を使うメンバは無いので、 copy constructor や代入演算子等は省略時設定でよいはずなので、定義してない。 ヘッダ #ifndef URI_H_INCLUDED #define URI_H_INCLUDED #include <string> class Uri { public: Uri(const char* uri); Uri(const std::string& struri); Uri(const Uri& base, const Uri& ref); virtual ~Uri(void); const std::string& UriString(void) const; const std::string& SchemeString(void) const; const std::string& AuthorityString(void) const; const std::string& PathString(void) const; const std::string& QueryString(void) const; const std::string& FragmentString(void) const; bool SchemeDefined(void) const; bool AuthorityDefined(void) const; bool PathEmpty(void) const; bool QueryDefined(void) const; bool FragmentDefined(void) const; bool SameDocument(const Uri& ref) const; // Substructure interface bool UserInfoDefined(void) const; bool HostPortDefined(void) const; const std::string& UserInfoString(void) const; const std::string& HostPortString(void) const; bool HostDefined(void) const; bool PortDefined(void) const; const std::string& HostString(void) const; int Port(void) const; protected: class Authority { public: Authority(void); Authority(const std::string& strAuth); virtual ~Authority(void); const std::string& UserInfoString(void) const; const std::string& HostPortString(void) const; bool UserInfoDefined(void) const; bool HostPortDefined(void) const; protected: class HostPort { public: HostPort(void); HostPort(const std::string& strHostPort); virtual ~HostPort(void); const std::string& HostString(void) const; const std::string& PortString(void) const; int Port(void) const; bool HostDefined(void) const; bool PortDefined(void) const; protected: bool m_bHost; bool m_bPort; std::string m_strHost; std::string m_strPort; int m_nPort; }; protected: bool m_bUserInfo; bool m_bHostPort; std::string m_strUserInfo; std::string m_strHostPort; public: HostPort m_HostPort; }; private: void Init(void); // std::string::const_iterator GenScheme(std::string::const_iterator puri, std::string::const_iterator pend); std::string::const_iterator GenAuthority(std::string::const_iterator puri, std::string::const_iterator pend); std::string::const_iterator GenPath(std::string::const_iterator puri, std::string::const_iterator pend); std::string::const_iterator GenQuery(std::string::const_iterator, std::string::const_iterator pend); std::string::const_iterator GenFragment(std::string::const_iterator, std::string::const_iterator pend); // bool MergePaths(const Uri& base, const Uri& ref); bool CopyScheme(const Uri& src); bool CopyAuthority(const Uri& src); bool CopyPath(const Uri& src); bool CopyQuery(const Uri& src); bool CopyFragment(const Uri& src); void RemoveDotSegments(void); void DeCompose(void); void ReCompose(void); private: static void Normalize(std::string& str); protected: std::string m_strUri; std::string m_strScheme; std::string m_strAuthority; std::string m_strPath; std::string m_strQuery; std::string m_strFragment; bool m_bScheme; bool m_bAuthority; bool m_bQuery; bool m_bFragment; // SubStructure Authority m_Authority; }; #endif // URI_H_INCLUDED 本体 uri.cpp // uri.cpp #include "uri.h" // RFC3986 // // Uniform Resource Inditifier (URI): Generic Syntax // http://gbiv.com/protocols/uri/rfc/rfc3986.html // Uri::Authority::HostPort::HostPort(void) { m_bHost = false; m_bPort = false; m_nPort = 0; } Uri::Authority::HostPort::HostPort(const std::string& strHostPort) { m_bHost = false; m_bPort = false; m_nPort = 0; std::string::const_iterator p = strHostPort.begin(); std::string::const_iterator pcol = strHostPort.end(); while(p != strHostPort.end()) { if(*p == (char)':') { pcol = p; break; } ++p; } p = strHostPort.begin(); while(p != pcol) m_strHost.push_back(*p++); if(m_strHost.size() > 0) m_bHost = true; if(pcol == strHostPort.end()) return; p = pcol + 1; while(p != strHostPort.end()) { int c = *p; if(!::isdigit(c)) break; m_nPort = m_nPort * 10 + c - (int)'0'; m_strPort.push_back(*p++); } if(m_strPort.size() > 0) m_bPort = true; } Uri::Authority::HostPort::~HostPort(void) { } bool Uri::Authority::HostPort::HostDefined(void) const { return m_bHost; } const std::string& Uri::Authority::HostPort::HostString(void) const { return m_strHost; } bool Uri::Authority::HostPort::PortDefined(void) const { return m_bPort; } const std::string& Uri::Authority::HostPort::PortString(void) const { return m_strPort; } int Uri::Authority::HostPort::Port(void) const { return m_nPort; } Uri::Authority::Authority(void) { m_bUserInfo = false; m_bHostPort = false; } Uri::Authority::Authority(const std::string& strAuth) { m_bUserInfo = false; m_bHostPort = false; std::string::const_iterator p = strAuth.begin(); std::string::const_iterator pat = strAuth.end(); while(p != strAuth.end()) { if(*p == (char)'@') { pat = p; break; } ++p; } p = strAuth.begin(); if(pat != strAuth.end()) { while(p != pat) m_strUserInfo.push_back(*p++); ++p; m_bUserInfo = true; } while(p != strAuth.end()) m_strHostPort.push_back(*p++); m_bHostPort = true; m_HostPort = HostPort(m_strHostPort); } Uri::Authority::~Authority(void) { } bool Uri::Authority::UserInfoDefined(void) const { return m_bUserInfo; } const std::string& Uri::Authority::UserInfoString(void) const { return m_strUserInfo; } bool Uri::Authority::HostPortDefined(void) const { return m_bHostPort; } const std::string& Uri::Authority::HostPortString(void) const { return m_strHostPort; } Uri::Uri(const char* uri) : m_strUri(uri) { Init(); DeCompose(); if(m_strScheme.size() > 0) RemoveDotSegments(); ReCompose(); if(m_bAuthority) m_Authority = Authority(m_strAuthority); } Uri::Uri(const std::string& struri) : m_strUri(struri) { Init(); DeCompose(); if(m_strScheme.size() > 0) RemoveDotSegments(); ReCompose(); if(m_bAuthority) m_Authority = Authority(m_strAuthority); } Uri::Uri(const Uri& base, const Uri& ref) { Init(); if(!base.SchemeDefined()) return; bool bRefSchemeDef = ref.SchemeDefined(); if(bRefSchemeDef) { CopyScheme(ref); CopyAuthority(ref); CopyPath(ref); CopyQuery(ref); } else { CopyScheme(base); if(ref.AuthorityDefined()) { CopyAuthority(ref); CopyPath(ref); CopyQuery(ref); } else { CopyAuthority(base); if(ref.PathEmpty()) { CopyPath(base); if(ref.QueryDefined()) CopyQuery(ref); else CopyQuery(base); } else { if(*(ref.m_strPath.begin()) == (char)'/') CopyPath(ref); else MergePaths(base, ref); CopyQuery(ref); } } } CopyFragment(ref); RemoveDotSegments(); ReCompose(); if(m_bAuthority) m_Authority = Authority(m_strAuthority); } Uri::~Uri(void) { } void Uri::Init(void) { m_bScheme = false; m_bAuthority = false; m_bQuery = false; m_bFragment = false; } void Uri::DeCompose(void) { std::string::const_iterator p = m_strUri.begin(); std::string::const_iterator pend = m_strUri.end(); p = GenScheme(p, pend); p = GenAuthority(p, pend); p = GenPath(p, pend); p = GenQuery(p, pend); p = GenFragment(p, pend); Normalize(m_strAuthority); Normalize(m_strPath); Normalize(m_strQuery); Normalize(m_strFragment); } const std::string& Uri::UriString(void) const { return m_strUri; } std::string::const_iterator Uri::GenScheme(std::string::const_iterator puri, std::string::const_iterator pend) { m_strScheme.clear(); m_bScheme = false; if(puri == pend) return puri; std::string::const_iterator p = puri; int n = 0; int c = *p++; if(!::isalpha(c)) return puri; n = 1; while(p != pend) { c = *p++; if((!::isalnum(c)) && (c != '+') && (c != '-') && (c != '.')) break; ++n; } if((p == pend) || (c != ':')) return puri; // No delimiter found p = puri; for(int i = 0; i < n; i++) m_strScheme.push_back((char)::tolower((int)*p++)); m_bScheme = true; ++p; // next to ':' return p; } bool Uri::CopyScheme(const Uri& src) { m_strScheme = src.m_strScheme; m_bScheme = src.m_bScheme; return true; } bool Uri::SchemeDefined(void) const { return m_bScheme; } const std::string& Uri::SchemeString(void) const { return m_strScheme; } std::string::const_iterator Uri::GenAuthority(std::string::const_iterator puri, std::string::const_iterator pend) { m_strAuthority.clear(); m_bAuthority = false; if(puri == pend) return puri; std::string::const_iterator p = puri; int c = *p++; if(c != '/') return puri; if(p == pend) return puri; c = *p++; if(c != '/') return puri; while(p != pend) { c = *p; if((c == '/') || (c == '?') || (c == '#')) break; m_strAuthority.push_back(*p++); } m_bAuthority = true; return p; } bool Uri::CopyAuthority(const Uri& src) { m_strAuthority = src.m_strAuthority; m_bAuthority = src.m_bAuthority; return true; } bool Uri::AuthorityDefined(void) const { return m_bAuthority; } const std::string& Uri::AuthorityString(void) const { return m_strAuthority; } std::string::const_iterator Uri::GenPath(std::string::const_iterator puri, std::string::const_iterator pend) { m_strPath.clear(); if(puri == pend) return puri; std::string::const_iterator p = puri; while(p != pend) { if((*p == (char)'?') || (*p == (char)'#')) break; m_strPath.push_back(*p++); } return p; } bool Uri::CopyPath(const Uri& src) { m_strPath = src.m_strPath; return true; } bool Uri::PathEmpty(void) const { return m_strPath.empty(); } const std::string& Uri::PathString(void) const { return m_strPath; } std::string::const_iterator Uri::GenQuery(std::string::const_iterator puri, std::string::const_iterator pend) { m_strQuery.clear(); m_bQuery = false; if(puri == pend) return puri; std::string::const_iterator p = puri; int c = *p++; if(c != '?') return puri; while(p != pend) { if(*p == (char)'#') break; m_strQuery.push_back(*p++); } m_bQuery = true; return p; } bool Uri::CopyQuery(const Uri& src) { m_strQuery = src.m_strQuery; m_bQuery = src.m_bQuery; return true; } bool Uri::QueryDefined(void) const { return m_bQuery; } const std::string& Uri::QueryString(void) const { return m_strQuery; } std::string::const_iterator Uri::GenFragment(std::string::const_iterator puri, std::string::const_iterator pend) { m_strFragment.clear(); m_bFragment = false; if(puri == pend) return puri; std::string::const_iterator p = puri; int c = *p++; if(c != '#') return puri; while(p != pend) m_strFragment.push_back(*p++); m_bFragment = true; return p; } bool Uri::CopyFragment(const Uri& src) { m_strFragment = src.m_strFragment; m_bFragment = src.m_bFragment; return true; } bool Uri::FragmentDefined(void) const { return m_bFragment; } const std::string& Uri::FragmentString(void) const { return m_strFragment; } void Uri::RemoveDotSegments(void) { // This will remove the dot segments from the path. std::string::iterator pin = m_strPath.begin(); std::string::iterator pout = pin; size_t nStartSize = m_strPath.size(); size_t nLeft = nStartSize; size_t nOut = 0; while(nLeft) { if((nLeft >= 3) && (*pin == '.') && (*(pin + 1) == '.') && (*(pin + 2) == '/')) { nLeft -= 3; pin += 3; } else if((nLeft >= 2) && (*pin == '.') && (*(pin + 1) == '/')) { nLeft -= 2; pin += 2; } else { // B. if((nLeft >= 3) && (*pin == '/') && (*(pin + 1) == '.') && (*(pin + 2) == '/')) { nLeft -= 2; pin += 2; } else if((nLeft == 2) && (*pin == '/') && (*(pin + 1) == '.')) { nLeft -= 1; pin += 1; *pin = '/'; } else { // C. if((nLeft >= 4) && (*pin == '/') && (*(pin + 1) == '.') && (*(pin + 2) == '.') && (*(pin + 3) == '/')) { nLeft -= 3; pin += 3; *pin = '/'; // remove last segment while(nOut) { --nOut; --pout; if(*pout == '/') break; } } else if((nLeft == 3) && (*pin == '/') && (*(pin + 1) == '.') && (*(pin + 2) == '.')) { nLeft -= 2; pin += 2; *pin = '/'; // remove last segment while(nOut) { --nOut; --pout; if(*pout == '/') break; } } else { // D. if((nLeft == 1) && (*pin == '.')) { nLeft -= 1; pin += 1; } else if((nLeft == 2) && (*pin == '.') && (*(pin + 1) == '.')) { nLeft -= 2; pin += 2; } else { // E. // Move the first path segment *pout++ = *pin++; ++nOut; --nLeft; while(nLeft) { if(*pin == '/') break; *pout++ = *pin++; ++nOut; --nLeft; } } } } } } if(nOut != nStartSize) m_strPath.erase(nOut); } bool Uri::MergePaths(const Uri& base, const Uri& ref) { m_strPath.clear(); if(base.AuthorityDefined() && base.PathEmpty()) { m_strPath.push_back((char)'/'); m_strPath.append(ref.m_strPath); } else { // Remove the last segment (after right-most '/') of the path of base, // then concatinate the path of ref. std::string::const_iterator it = base.m_strPath.end(); size_t n = base.m_strPath.size(); while(n) { --it; if(*it == '/') break; --n; } it = base.m_strPath.begin(); while(n) { m_strPath.push_back(*it++); --n; } m_strPath.append(ref.m_strPath); } return true; } void Uri::ReCompose(void) { // RFC-3986 5.3 m_strUri.clear(); if(m_bScheme) { m_strUri.append(m_strScheme); m_strUri.push_back((char)':'); } if(m_bAuthority) { m_strUri.push_back((char)'/'); m_strUri.push_back((char)'/'); m_strUri.append(m_strAuthority); } m_strUri.append(m_strPath); if(m_bQuery) { m_strUri.push_back((char)'?'); m_strUri.append(m_strQuery); } if(m_bFragment) { m_strUri.push_back((char)'#'); m_strUri.append(m_strFragment); } } void Uri::Normalize(std::string& str) { std::string::iterator pin = str.begin(); std::string::iterator pout = pin; size_t nleft = str.size(); size_t nout = 0; while(nleft) { if((nleft >= 3) && (*pin == '%') && (::isxdigit((int)*(pin + 1))) && (::isxdigit((int)*(pin + 2)))) { int c = 0; int c1 = (int)*(pin + 1); int c2 = (int)*(pin + 2); if(::isdigit(c1)) c = c1 - (int)'0'; else { c1 = ::toupper(c1); c = c1 - (int)'A' + 10; } if(::isdigit(c2)) c = c * 16 + c2 - (int)'0'; else { c2 = ::toupper(c2); c = c * 16 + c2 - (int)'A' + 10; } // // check if unreserved if(((c >= 0x41) && (c <= 0x5a)) || ((c >= 0x61) && (c <= 0x7a)) || ((c >= 0x30) && (c <= 0x39)) || (c == 0x2d) || (c == 0x2e) || (c == 0x5f) || (c == 0x7e)) { *pout++ = (char)c; nout++; pin += 3; nleft -= 3; } else { *pout++ = *pin++; *pout++ = (char)c1; *pout++ = (char)c2; nout += 3; pin += 2; nleft -= 3; } } else { *pout++ = *pin++; nout++; nleft--; } } if(nout != str.size()) str.erase(nout); } bool Uri::UserInfoDefined(void) const { return m_Authority.UserInfoDefined(); } bool Uri::HostPortDefined(void) const { return m_Authority.HostPortDefined(); } const std::string& Uri::UserInfoString(void) const { return m_Authority.UserInfoString(); } const std::string& Uri::HostPortString(void) const { return m_Authority.HostPortString(); } bool Uri::HostDefined(void) const { return m_Authority.m_HostPort.HostDefined(); } bool Uri::PortDefined(void) const { return m_Authority.m_HostPort.PortDefined(); } const std::string& Uri::HostString(void) const { return m_Authority.m_HostPort.HostString(); } int Uri::Port(void) const { return m_Authority.m_HostPort.Port(); } bool Uri::SameDocument(const Uri& ref) const { if(m_bScheme != ref.m_bScheme) return false; if(m_bScheme && (m_strScheme != ref.m_strScheme)) return false; if(m_bAuthority != ref.m_bAuthority) return false; if(m_bAuthority && (m_strAuthority != ref.m_strAuthority)) return false; if(m_strPath != ref.m_strPath) return false; if(m_bQuery != ref.m_bQuery) return false; if(m_bQuery && (m_strQuery != ref.m_strQuery)) return false; return true; } |