diff options
author | Anton Samokhvalov <pg83@yandex.ru> | 2022-02-10 16:45:15 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:15 +0300 |
commit | 72cb13b4aff9bc9cf22e49251bc8fd143f82538f (patch) | |
tree | da2c34829458c7d4e74bdfbdf85dff449e9e7fb8 /library/cpp/uri/uri.cpp | |
parent | 778e51ba091dc39e7b7fcab2b9cf4dbedfb6f2b5 (diff) | |
download | ydb-72cb13b4aff9bc9cf22e49251bc8fd143f82538f.tar.gz |
Restoring authorship annotation for Anton Samokhvalov <pg83@yandex.ru>. Commit 1 of 2.
Diffstat (limited to 'library/cpp/uri/uri.cpp')
-rw-r--r-- | library/cpp/uri/uri.cpp | 1004 |
1 files changed, 502 insertions, 502 deletions
diff --git a/library/cpp/uri/uri.cpp b/library/cpp/uri/uri.cpp index 56a9a4e5ef..55cfe57e28 100644 --- a/library/cpp/uri/uri.cpp +++ b/library/cpp/uri/uri.cpp @@ -1,621 +1,621 @@ #include "uri.h" #include "parse.h" - + #include <util/string/cast.h> #include <util/string/util.h> #include <util/system/yassert.h> - + namespace NUri { - TState::EParsed TUri::CheckHost(const TStringBuf& host) { + TState::EParsed TUri::CheckHost(const TStringBuf& host) { if (host.empty()) - return ParsedOK; - - unsigned domainLevel = 0; - unsigned domainLevelOfUnderscore = 0; - - bool isAlnum = false; - bool startLabel = true; - for (size_t i = 0; i != host.length(); ++i) { - const char ch = host[i]; - - if ('.' == ch) { // label separator - if (!isAlnum || startLabel) // previous label must end in alnum - return ParsedBadHost; - startLabel = true; - continue; - } - - isAlnum = isalnum((const unsigned char)ch); - - if (startLabel) { // label is starting - if (!isAlnum && '_' != ch) // new label must start with alnum or '_' - return ParsedBadHost; - startLabel = false; - ++domainLevel; - if (ch == '_') - domainLevelOfUnderscore = domainLevel; - continue; - } - - if (isAlnum || '-' == ch) - continue; - - if (ch == '_') { // non-standard case we allow for certain hosts + return ParsedOK; + + unsigned domainLevel = 0; + unsigned domainLevelOfUnderscore = 0; + + bool isAlnum = false; + bool startLabel = true; + for (size_t i = 0; i != host.length(); ++i) { + const char ch = host[i]; + + if ('.' == ch) { // label separator + if (!isAlnum || startLabel) // previous label must end in alnum + return ParsedBadHost; + startLabel = true; + continue; + } + + isAlnum = isalnum((const unsigned char)ch); + + if (startLabel) { // label is starting + if (!isAlnum && '_' != ch) // new label must start with alnum or '_' + return ParsedBadHost; + startLabel = false; + ++domainLevel; + if (ch == '_') + domainLevelOfUnderscore = domainLevel; + continue; + } + + if (isAlnum || '-' == ch) + continue; + + if (ch == '_') { // non-standard case we allow for certain hosts domainLevelOfUnderscore = domainLevel; - continue; - } - - return ParsedBadHost; + continue; + } + + return ParsedBadHost; } - if (0 < domainLevelOfUnderscore && domainLevel < 2 + domainLevelOfUnderscore) - return ParsedBadHost; - - return ParsedOK; - } - - /********************************************************/ - TUri::TUri(const TStringBuf& host, ui16 port, const TStringBuf& path, const TStringBuf& query, const TStringBuf& scheme, unsigned defaultPort) - : FieldsSet(0) - , Port(port) - , DefaultPort(0) - , Scheme(SchemeEmpty) - , FieldsDirty(0) - { + if (0 < domainLevelOfUnderscore && domainLevel < 2 + domainLevelOfUnderscore) + return ParsedBadHost; + + return ParsedOK; + } + + /********************************************************/ + TUri::TUri(const TStringBuf& host, ui16 port, const TStringBuf& path, const TStringBuf& query, const TStringBuf& scheme, unsigned defaultPort) + : FieldsSet(0) + , Port(port) + , DefaultPort(0) + , Scheme(SchemeEmpty) + , FieldsDirty(0) + { if (!scheme.empty()) { if (SetSchemeImpl(TSchemeInfo::Get(scheme)).Str.empty()) - FldSet(FieldScheme, scheme); + FldSet(FieldScheme, scheme); } - if (0 < defaultPort) // override the scheme's default port - DefaultPort = static_cast<ui16>(defaultPort); + if (0 < defaultPort) // override the scheme's default port + DefaultPort = static_cast<ui16>(defaultPort); - char sport[6]; // enough for ui16 - if (0 != port) { - const size_t len = ToString(port, sport, sizeof(sport)); - FldSet(FieldPort, TStringBuf(sport, len)); - } + char sport[6]; // enough for ui16 + if (0 != port) { + const size_t len = ToString(port, sport, sizeof(sport)); + FldSet(FieldPort, TStringBuf(sport, len)); + } - FldTrySet(FieldHost, host); - FldTrySet(FieldPath, path); - FldTrySet(FieldQuery, query); + FldTrySet(FieldHost, host); + FldTrySet(FieldPath, path); + FldTrySet(FieldQuery, query); - Rewrite(); + Rewrite(); } - /********************************************************/ - bool TUri::FldSetImpl( - EField field, TStringBuf value, bool strconst, bool nocopy) { - if (!FldIsValid(field)) - return false; + /********************************************************/ + bool TUri::FldSetImpl( + EField field, TStringBuf value, bool strconst, bool nocopy) { + if (!FldIsValid(field)) + return false; - switch (field) { - case FieldScheme: + switch (field) { + case FieldScheme: if (!SetScheme(TSchemeInfo::Get(value)).Str.empty()) - return false; - break; + return false; + break; - case FieldPort: + case FieldPort: Port = value.empty() ? 0 : FromString<ui16>(value); - break; + break; - default: - break; - } + default: + break; + } - if (!value.IsInited()) { - FldClr(field); - return false; - } + if (!value.IsInited()) { + FldClr(field); + return false; + } - if (strconst) { // string constants don't need to be saved in the buffer - FldMarkClean(field); - FldSetNoDirty(field, value); + if (strconst) { // string constants don't need to be saved in the buffer + FldMarkClean(field); + FldSetNoDirty(field, value); return false; - } + } - if (nocopy) { - FldSet(field, value); - return true; - } + if (nocopy) { + FldSet(field, value); + return true; + } - return FldTryCpy(field, value); + return FldTryCpy(field, value); } - /********************************************************/ - bool TUri::FldTryCpy(EField field, const TStringBuf& value) { - if (!FldIsDirty(field)) { - do { + /********************************************************/ + bool TUri::FldTryCpy(EField field, const TStringBuf& value) { + if (!FldIsDirty(field)) { + do { if (!FldIsSet(field)) - break; - - TStringBuf& fld = Fields[field]; - if (fld.length() < value.length()) - break; - - char* oldV = (char*)fld.data(); - if (!IsInBuffer(oldV)) - break; - - memcpy(oldV, value.data(), value.length()); - oldV[value.length()] = 0; - fld.Trunc(value.length()); - return false; - } while (false); - - FldMarkDirty(field); - } - + break; + + TStringBuf& fld = Fields[field]; + if (fld.length() < value.length()) + break; + + char* oldV = (char*)fld.data(); + if (!IsInBuffer(oldV)) + break; + + memcpy(oldV, value.data(), value.length()); + oldV[value.length()] = 0; + fld.Trunc(value.length()); + return false; + } while (false); + + FldMarkDirty(field); + } + FldSetNoDirty(field, value); return true; } - /********************************************************/ - void TUri::RewriteImpl() { - size_t len = 0; - for (int i = 0; i < FieldAllMAX; ++i) { - const EField fld = EField(i); - if (FldIsSet(fld)) - len += 1 + Fields[fld].length(); - } + /********************************************************/ + void TUri::RewriteImpl() { + size_t len = 0; + for (int i = 0; i < FieldAllMAX; ++i) { + const EField fld = EField(i); + if (FldIsSet(fld)) + len += 1 + Fields[fld].length(); + } - if (!len) + if (!len) Buffer.Clear(); - else { + else { TBuffer newbuf; newbuf.Resize(len); TMemoryWriteBuffer out(newbuf.data(), newbuf.size()); - for (int i = 0; i < FieldAllMAX; ++i) { - const EField fld = EField(i); - if (!FldIsSet(fld)) - continue; - - const char* beg = out.Buf(); - const TStringBuf& val = Fields[fld]; - out << val; - FldSetNoDirty(fld, TStringBuf(beg, val.length())); + for (int i = 0; i < FieldAllMAX; ++i) { + const EField fld = EField(i); + if (!FldIsSet(fld)) + continue; + + const char* beg = out.Buf(); + const TStringBuf& val = Fields[fld]; + out << val; + FldSetNoDirty(fld, TStringBuf(beg, val.length())); out << '\0'; - } + } Buffer = std::move(newbuf); } - CheckMissingFields(); + CheckMissingFields(); - FieldsDirty = 0; + FieldsDirty = 0; } - void TUri::CheckMissingFields() { - // if host is set but path is not... - if (FldSetCmp(FlagPath | FlagHost, FlagHost)) - // ... and the scheme requires a path... - if (GetSchemeInfo().FldReq & FlagPath) - // ... set path + void TUri::CheckMissingFields() { + // if host is set but path is not... + if (FldSetCmp(FlagPath | FlagHost, FlagHost)) + // ... and the scheme requires a path... + if (GetSchemeInfo().FldReq & FlagPath) + // ... set path FldSetNoDirty(FieldPath, TStringBuf("/")); } - /********************************************************/ - void TUri::Merge(const TUri& base, int correctAbs) { - if (base.Scheme == SchemeUnknown) - return; + /********************************************************/ + void TUri::Merge(const TUri& base, int correctAbs) { + if (base.Scheme == SchemeUnknown) + return; - if (!base.IsValidGlobal()) - return; + if (!base.IsValidGlobal()) + return; - const TStringBuf& selfscheme = GetField(FieldScheme); - // basescheme is present since IsValidGlobal() succeeded - const TStringBuf& basescheme = base.GetField(FieldScheme); - const bool noscheme = !selfscheme.IsInited(); - if (!noscheme && !EqualNoCase(selfscheme, basescheme)) - return; + const TStringBuf& selfscheme = GetField(FieldScheme); + // basescheme is present since IsValidGlobal() succeeded + const TStringBuf& basescheme = base.GetField(FieldScheme); + const bool noscheme = !selfscheme.IsInited(); + if (!noscheme && !EqualNoCase(selfscheme, basescheme)) + return; - const ui32 cleanFields = ~FieldsDirty; - do { + const ui32 cleanFields = ~FieldsDirty; + do { static constexpr TStringBuf rootPath = "/"; - if (noscheme) { + if (noscheme) { if (!basescheme.empty()) { - FldSetNoDirty(FieldScheme, basescheme); - // check if it is canonical - if (basescheme.data() != base.GetSchemeInfo().Str.data()) - FldMarkDirty(FieldScheme); - } - Scheme = base.Scheme; - DefaultPort = base.DefaultPort; - } - - if (!IsNull(FlagHost)) - break; // no merge - - FldTrySet(FieldHost, base); - FldChkSet(FieldPort, base); - Port = base.Port; - - if (noscheme && IsNull(FlagQuery) && IsNull(FlagPath)) - FldTrySet(FieldQuery, base); - - if (IsNull(FlagAuth) && !base.IsNull(FlagAuth)) { - FldChkSet(FieldUser, base); - FldChkSet(FieldPass, base); + FldSetNoDirty(FieldScheme, basescheme); + // check if it is canonical + if (basescheme.data() != base.GetSchemeInfo().Str.data()) + FldMarkDirty(FieldScheme); + } + Scheme = base.Scheme; + DefaultPort = base.DefaultPort; + } + + if (!IsNull(FlagHost)) + break; // no merge + + FldTrySet(FieldHost, base); + FldChkSet(FieldPort, base); + Port = base.Port; + + if (noscheme && IsNull(FlagQuery) && IsNull(FlagPath)) + FldTrySet(FieldQuery, base); + + if (IsNull(FlagAuth) && !base.IsNull(FlagAuth)) { + FldChkSet(FieldUser, base); + FldChkSet(FieldPass, base); } - if (IsValidAbs()) - break; + if (IsValidAbs()) + break; - TStringBuf p0 = base.GetField(FieldPath); - if (!p0.IsInited()) - p0 = rootPath; + TStringBuf p0 = base.GetField(FieldPath); + if (!p0.IsInited()) + p0 = rootPath; - TStringBuf p1 = GetField(FieldPath); - if (!p1.IsInited()) { - if (p0.data() != rootPath.data()) - FldSet(FieldPath, p0); - else - FldSetNoDirty(FieldPath, rootPath); - break; - } - if (p1 && '/' == p1[0]) - p1.Skip(1); // p0 will have one + TStringBuf p1 = GetField(FieldPath); + if (!p1.IsInited()) { + if (p0.data() != rootPath.data()) + FldSet(FieldPath, p0); + else + FldSetNoDirty(FieldPath, rootPath); + break; + } + if (p1 && '/' == p1[0]) + p1.Skip(1); // p0 will have one - bool pathop = true; + bool pathop = true; TTempBufOutput out(p0.length() + p1.length() + 4); - out << p0; - if ('/' != p0.back()) - out << "/../"; - else if (p1.empty() || '.' != p1[0]) - pathop = false; - out << p1; - - char* beg = out.Data(); - char* end = beg + out.Filled(); - if (pathop && !PathOperation(beg, end, correctAbs)) { - Clear(); - break; - } - - // Needs immediate forced rewrite because of TTempBuf - FldSetNoDirty(FieldPath, TStringBuf(beg, end)); - RewriteImpl(); - } while (false); - - CheckMissingFields(); - - // rewrite only if borrowed fields from base - if (cleanFields & FieldsDirty) - RewriteImpl(); + out << p0; + if ('/' != p0.back()) + out << "/../"; + else if (p1.empty() || '.' != p1[0]) + pathop = false; + out << p1; + + char* beg = out.Data(); + char* end = beg + out.Filled(); + if (pathop && !PathOperation(beg, end, correctAbs)) { + Clear(); + break; + } + + // Needs immediate forced rewrite because of TTempBuf + FldSetNoDirty(FieldPath, TStringBuf(beg, end)); + RewriteImpl(); + } while (false); + + CheckMissingFields(); + + // rewrite only if borrowed fields from base + if (cleanFields & FieldsDirty) + RewriteImpl(); } - /********************************************************/ - TUri::TLinkType TUri::Normalize(const TUri& base, - const TStringBuf& link, const TStringBuf& codebase, long careFlags, ECharset enc) { - // parse URL - if (ParsedOK != ParseImpl(link, careFlags, 0, SchemeEmpty, enc)) - return LinkIsBad; + /********************************************************/ + TUri::TLinkType TUri::Normalize(const TUri& base, + const TStringBuf& link, const TStringBuf& codebase, long careFlags, ECharset enc) { + // parse URL + if (ParsedOK != ParseImpl(link, careFlags, 0, SchemeEmpty, enc)) + return LinkIsBad; - const TStringBuf& host = GetHost(); + const TStringBuf& host = GetHost(); - // merge with base URL - // taken either from _BASE_ property or from optional argument + // merge with base URL + // taken either from _BASE_ property or from optional argument if (!codebase.empty()) { - // if optional code base given -- parse it - TUri codebaseUrl; - if (codebaseUrl.ParseImpl(codebase, careFlags, 0, SchemeEmpty, enc) != ParsedOK || !codebaseUrl.IsValidAbs()) - return LinkIsBad; - Merge(codebaseUrl); - } else { - // Base is already in this variable - // see SetProperty() for details - Merge(base); - } - - // check result: must be correct absolute URL - if (!IsValidAbs()) - return LinkBadAbs; + // if optional code base given -- parse it + TUri codebaseUrl; + if (codebaseUrl.ParseImpl(codebase, careFlags, 0, SchemeEmpty, enc) != ParsedOK || !codebaseUrl.IsValidAbs()) + return LinkIsBad; + Merge(codebaseUrl); + } else { + // Base is already in this variable + // see SetProperty() for details + Merge(base); + } + + // check result: must be correct absolute URL + if (!IsValidAbs()) + return LinkBadAbs; if (!host.empty()) { - // - we don't care about different ports for the same server - // - we don't care about win|www|koi|etc. preffixes for the same server - if (GetPort() != base.GetPort() || !EqualNoCase(host, base.GetHost())) - return LinkIsGlobal; - } + // - we don't care about different ports for the same server + // - we don't care about win|www|koi|etc. preffixes for the same server + if (GetPort() != base.GetPort() || !EqualNoCase(host, base.GetHost())) + return LinkIsGlobal; + } - // find out if it is link to itself then ignore it - if (!Compare(base, FlagPath | FlagQuery)) - return LinkIsFragment; + // find out if it is link to itself then ignore it + if (!Compare(base, FlagPath | FlagQuery)) + return LinkIsFragment; - return LinkIsLocal; + return LinkIsLocal; } - /********************************************************/ - - size_t TUri::PrintSize(ui32 flags) const { - size_t len = 10; - flags &= FieldsSet; // can't output what we don't have - if (flags & FlagHostAscii) - flags &= ~FlagHost; // don't want to print both of them - ui32 opt = 1; - for (int fld = 0; opt <= flags && fld < FieldAllMAX; ++fld, opt <<= 1) { - if (opt & flags) { - const TStringBuf& v = Fields[fld]; - if (v.IsInited()) { - if (opt & FlagAuth) - len += 3 * v.length() + 1; - else - len += v.length() + 1; - } + /********************************************************/ + + size_t TUri::PrintSize(ui32 flags) const { + size_t len = 10; + flags &= FieldsSet; // can't output what we don't have + if (flags & FlagHostAscii) + flags &= ~FlagHost; // don't want to print both of them + ui32 opt = 1; + for (int fld = 0; opt <= flags && fld < FieldAllMAX; ++fld, opt <<= 1) { + if (opt & flags) { + const TStringBuf& v = Fields[fld]; + if (v.IsInited()) { + if (opt & FlagAuth) + len += 3 * v.length() + 1; + else + len += v.length() + 1; + } } } - - return len; + + return len; } - IOutputStream& TUri::PrintImpl(IOutputStream& out, int flags) const { - TStringBuf v; + IOutputStream& TUri::PrintImpl(IOutputStream& out, int flags) const { + TStringBuf v; - const int wantFlags = flags; // save the original - flags &= FieldsSet; // can't print what we don't have - if (flags & FlagHostAscii) - flags |= FlagHost; // to make host checks simpler below + const int wantFlags = flags; // save the original + flags &= FieldsSet; // can't print what we don't have + if (flags & FlagHostAscii) + flags |= FlagHost; // to make host checks simpler below - if (flags & FlagScheme) { - v = Fields[FieldScheme]; + if (flags & FlagScheme) { + v = Fields[FieldScheme]; if (!v.empty()) - out << v << ':'; - } - - TStringBuf host; - if (flags & FlagHost) { - const EField fldhost = - flags & FlagHostAscii ? FieldHostAscii : FieldHost; - host = Fields[fldhost]; - } - - TStringBuf port; - if ((flags & FlagPort) && 0 != Port && Port != DefaultPort) - port = Fields[FieldPort]; - - if (host) { - if (wantFlags & FlagScheme) - out << "//"; - - if (flags & FlagAuth) { - if (flags & FlagUser) { - v = Fields[FieldUser]; + out << v << ':'; + } + + TStringBuf host; + if (flags & FlagHost) { + const EField fldhost = + flags & FlagHostAscii ? FieldHostAscii : FieldHost; + host = Fields[fldhost]; + } + + TStringBuf port; + if ((flags & FlagPort) && 0 != Port && Port != DefaultPort) + port = Fields[FieldPort]; + + if (host) { + if (wantFlags & FlagScheme) + out << "//"; + + if (flags & FlagAuth) { + if (flags & FlagUser) { + v = Fields[FieldUser]; if (!v.empty()) - TEncoder::EncodeNotAlnum(out, v); - } - - if (flags & FlagPass) { - v = Fields[FieldPass]; - if (v.IsInited()) { - out << ':'; - TEncoder::EncodeAll(out, v); - } - } - - out << '@'; + TEncoder::EncodeNotAlnum(out, v); + } + + if (flags & FlagPass) { + v = Fields[FieldPass]; + if (v.IsInited()) { + out << ':'; + TEncoder::EncodeAll(out, v); + } + } + + out << '@'; } - out << host; + out << host; - if (port) - out << ':'; + if (port) + out << ':'; } - if (port) - out << port; + if (port) + out << port; - if (flags & FlagPath) { - v = Fields[FieldPath]; - // for relative, empty path is not the same as missing + if (flags & FlagPath) { + v = Fields[FieldPath]; + // for relative, empty path is not the same as missing if (v.empty() && 0 == (flags & FlagHost)) v = TStringBuf("."); - out << v; - } - - if (flags & FlagQuery) { - v = Fields[FieldQuery]; - if (v.IsInited()) - out << '?' << v; - } - - if (flags & FlagFrag) { - v = Fields[FieldFrag]; - if (v.IsInited()) - out << '#' << v; - } - - return out; + out << v; + } + + if (flags & FlagQuery) { + v = Fields[FieldQuery]; + if (v.IsInited()) + out << '?' << v; + } + + if (flags & FlagFrag) { + v = Fields[FieldFrag]; + if (v.IsInited()) + out << '#' << v; + } + + return out; } - /********************************************************/ - int TUri::CompareField(EField fld, const TUri& url) const { - const TStringBuf& v0 = GetField(fld); - const TStringBuf& v1 = url.GetField(fld); - switch (fld) { - case FieldScheme: - case FieldHost: - return CompareNoCase(v0, v1); - default: - return v0.compare(v1); - } + /********************************************************/ + int TUri::CompareField(EField fld, const TUri& url) const { + const TStringBuf& v0 = GetField(fld); + const TStringBuf& v1 = url.GetField(fld); + switch (fld) { + case FieldScheme: + case FieldHost: + return CompareNoCase(v0, v1); + default: + return v0.compare(v1); + } } - /********************************************************/ - int TUri::Compare(const TUri& url, int flags) const { - // first compare fields with default values - if (flags & FlagPort) { - const int ret = GetPort() - url.GetPort(); - if (ret) - return ret; - flags &= ~FlagPort; - } - - // compare remaining sets of available fields - const int rtflags = flags & url.FieldsSet; - flags &= FieldsSet; - const int fldcmp = flags - rtflags; - if (fldcmp) - return fldcmp; - - // field sets are the same, compare the fields themselves - for (int i = 0; i < FieldAllMAX; ++i) { - const EField fld = EField(i); - if (flags & FldFlag(fld)) { - const int ret = CompareField(fld, url); - if (ret) - return ret; - } - } - - return 0; + /********************************************************/ + int TUri::Compare(const TUri& url, int flags) const { + // first compare fields with default values + if (flags & FlagPort) { + const int ret = GetPort() - url.GetPort(); + if (ret) + return ret; + flags &= ~FlagPort; + } + + // compare remaining sets of available fields + const int rtflags = flags & url.FieldsSet; + flags &= FieldsSet; + const int fldcmp = flags - rtflags; + if (fldcmp) + return fldcmp; + + // field sets are the same, compare the fields themselves + for (int i = 0; i < FieldAllMAX; ++i) { + const EField fld = EField(i); + if (flags & FldFlag(fld)) { + const int ret = CompareField(fld, url); + if (ret) + return ret; + } + } + + return 0; } - /********************************************************/ - bool TUri::PathOperation(char*& pathPtr, char*& pathEnd, int correctAbs) { - if (!pathPtr) - return false; - if (pathPtr == pathEnd) - return true; + /********************************************************/ + bool TUri::PathOperation(char*& pathPtr, char*& pathEnd, int correctAbs) { + if (!pathPtr) + return false; + if (pathPtr == pathEnd) + return true; - if ((pathEnd - pathPtr) >= 2 && *(pathEnd - 2) == '/' && *(pathEnd - 1) == '.') { - --pathEnd; + if ((pathEnd - pathPtr) >= 2 && *(pathEnd - 2) == '/' && *(pathEnd - 1) == '.') { + --pathEnd; } - char* p_wr = pathEnd; - int upCount = 0; - - char* p_prev = pathEnd; - Y_ASSERT(p_prev > pathPtr); - while (p_prev > pathPtr && *(p_prev - 1) == '/') - p_prev--; - - for (char* p_rd = p_prev; p_rd; p_rd = p_prev) { - Y_ASSERT(p_rd == pathEnd || p_rd[0] == '/'); - p_prev = nullptr; - - char* p = p_rd; - - if (p > pathPtr) { - for (p--; *p != '/'; p--) { - if (p == pathPtr) - break; - } - if (*p == '/') { - p_prev = p++; - if ((p_prev - pathPtr >= 6 && !strnicmp(p_prev - 6, "http://", 7)) || - (p_prev - pathPtr >= 7 && !strnicmp(p_prev - 7, "https://", 8))) { - --p_prev; - --p; - } else { - //skip multiple from head '/' - while (p_prev > pathPtr && *(p_prev - 1) == '/') - p_prev--; - } - } - } - - Y_ASSERT(p_prev == nullptr || p_prev[0] == '/'); - //and the first symbol !='/' after p_prev is p - - if (p == p_rd) { - //empty block: - if (p_prev) { //either tail: - Y_ASSERT(p_rd == p_wr && *(p - 1) == '/'); - --p_wr; - continue; - } else { //or head of abs path - *(--p_wr) = '/'; + char* p_wr = pathEnd; + int upCount = 0; + + char* p_prev = pathEnd; + Y_ASSERT(p_prev > pathPtr); + while (p_prev > pathPtr && *(p_prev - 1) == '/') + p_prev--; + + for (char* p_rd = p_prev; p_rd; p_rd = p_prev) { + Y_ASSERT(p_rd == pathEnd || p_rd[0] == '/'); + p_prev = nullptr; + + char* p = p_rd; + + if (p > pathPtr) { + for (p--; *p != '/'; p--) { + if (p == pathPtr) + break; + } + if (*p == '/') { + p_prev = p++; + if ((p_prev - pathPtr >= 6 && !strnicmp(p_prev - 6, "http://", 7)) || + (p_prev - pathPtr >= 7 && !strnicmp(p_prev - 7, "https://", 8))) { + --p_prev; + --p; + } else { + //skip multiple from head '/' + while (p_prev > pathPtr && *(p_prev - 1) == '/') + p_prev--; + } + } + } + + Y_ASSERT(p_prev == nullptr || p_prev[0] == '/'); + //and the first symbol !='/' after p_prev is p + + if (p == p_rd) { + //empty block: + if (p_prev) { //either tail: + Y_ASSERT(p_rd == p_wr && *(p - 1) == '/'); + --p_wr; + continue; + } else { //or head of abs path + *(--p_wr) = '/'; break; - } + } } - - if (p[0] == '.') { - if (p + 1 == p_rd) { - if (correctAbs || p_prev > pathPtr || pathPtr[0] != '/') - // ignore "./" - continue; - } else { - if ((p[1] == '.') && (p + 2 == p_rd)) { - // register "../" but not print - upCount++; - continue; - } + + if (p[0] == '.') { + if (p + 1 == p_rd) { + if (correctAbs || p_prev > pathPtr || pathPtr[0] != '/') + // ignore "./" + continue; + } else { + if ((p[1] == '.') && (p + 2 == p_rd)) { + // register "../" but not print + upCount++; + continue; + } } } - if (upCount) { - //unregister "../" and not print - upCount--; + if (upCount) { + //unregister "../" and not print + upCount--; continue; } - // print - Y_ASSERT(p < p_rd); - Y_ASSERT(!p_prev || *(p - 1) == '/'); - if (p_wr == p_rd) { //just skip - p_wr = p; - } else { //copy - int l = p_rd - p + 1; - p_wr -= l; - memmove(p_wr, p, l); + // print + Y_ASSERT(p < p_rd); + Y_ASSERT(!p_prev || *(p - 1) == '/'); + if (p_wr == p_rd) { //just skip + p_wr = p; + } else { //copy + int l = p_rd - p + 1; + p_wr -= l; + memmove(p_wr, p, l); } } - if (upCount) { - if (*pathPtr != '/') { - if (pathEnd == p_wr && *(p_wr - 1) == '.') { - Y_ASSERT(*(p_wr - 2) == '.'); - p_wr -= 2; - upCount--; - } - for (; upCount > 0; upCount--) { - *(--p_wr) = '/'; + if (upCount) { + if (*pathPtr != '/') { + if (pathEnd == p_wr && *(p_wr - 1) == '.') { + Y_ASSERT(*(p_wr - 2) == '.'); + p_wr -= 2; + upCount--; + } + for (; upCount > 0; upCount--) { + *(--p_wr) = '/'; *(--p_wr) = '.'; *(--p_wr) = '.'; } - } else { - if (correctAbs > 0) - return false; - if (correctAbs == 0) { - //Bad path but present in RFC: - // "Similarly, parsers must avoid treating "." and ".." - // as special when they are not complete components of - // a relative path. " - for (; upCount > 0; upCount--) { - *(--p_wr) = '.'; - *(--p_wr) = '.'; - *(--p_wr) = '/'; - } - } else { - upCount = false; - } + } else { + if (correctAbs > 0) + return false; + if (correctAbs == 0) { + //Bad path but present in RFC: + // "Similarly, parsers must avoid treating "." and ".." + // as special when they are not complete components of + // a relative path. " + for (; upCount > 0; upCount--) { + *(--p_wr) = '.'; + *(--p_wr) = '.'; + *(--p_wr) = '/'; + } + } else { + upCount = false; + } } } - Y_ASSERT(p_wr >= pathPtr); - - if (upCount) - return false; - pathPtr = p_wr; - return true; - } - - /********************************************************/ - const char* LinkTypeToString(const TUri::TLinkType& t) { - switch (t) { - case TUri::LinkIsBad: - return "LinkIsBad"; - case TUri::LinkBadAbs: - return "LinkBadAbs"; - case TUri::LinkIsFragment: - return "LinkIsFragment"; - case TUri::LinkIsLocal: - return "LinkIsLocal"; - case TUri::LinkIsGlobal: - return "LinkIsGlobal"; - } - Y_ASSERT(0); - return ""; + Y_ASSERT(p_wr >= pathPtr); + + if (upCount) + return false; + pathPtr = p_wr; + return true; + } + + /********************************************************/ + const char* LinkTypeToString(const TUri::TLinkType& t) { + switch (t) { + case TUri::LinkIsBad: + return "LinkIsBad"; + case TUri::LinkBadAbs: + return "LinkBadAbs"; + case TUri::LinkIsFragment: + return "LinkIsFragment"; + case TUri::LinkIsLocal: + return "LinkIsLocal"; + case TUri::LinkIsGlobal: + return "LinkIsGlobal"; + } + Y_ASSERT(0); + return ""; } } |