diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /library/cpp/yconf/conf.cpp | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'library/cpp/yconf/conf.cpp')
-rw-r--r-- | library/cpp/yconf/conf.cpp | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/library/cpp/yconf/conf.cpp b/library/cpp/yconf/conf.cpp new file mode 100644 index 00000000000..dba3d3600f3 --- /dev/null +++ b/library/cpp/yconf/conf.cpp @@ -0,0 +1,759 @@ +#include "conf.h" + +#include <library/cpp/logger/all.h> + +#include <library/cpp/charset/ci_string.h> +#include <util/generic/algorithm.h> +#include <util/stream/file.h> +#include <util/string/split.h> +#include <util/string/type.h> + +#include <cstdlib> + +void TYandexConfig::Clear() { + delete[] FileData; + FileData = nullptr; + CurrentMemoryPtr = nullptr; + Len = 0; + while (!CurSections.empty()) + CurSections.pop(); + for (size_t i = 0; i < AllSections.size(); i++) { + if (AllSections[i]->Owner) + delete AllSections[i]->Cookie; + delete AllSections[i]; + } + AllSections.clear(); + Errors.clear(); + EndLines.clear(); + ConfigPath.remove(); +} + +void TYandexConfig::PrintErrors(TLog* Log) { + size_t sz = Errors.size(); + if (sz) { + Log->AddLog("Processing of \'%s\':\n", ConfigPath.data()); + for (size_t i = 0; i < sz; i++) + *Log << Errors[i]; + Errors.clear(); + } +} + +void TYandexConfig::PrintErrors(TString& Err) { + size_t sz = Errors.size(); + if (sz) { + char buf[512]; + snprintf(buf, 512, "Processing of \'%s\':\n", ConfigPath.data()); + Err += buf; + for (size_t i = 0; i < sz; i++) + Err += Errors[i]; + Errors.clear(); + } +} + +void TYandexConfig::ReportError(const char* ptr, const char* err, bool warning) { + if (ptr) { + char buf[1024]; + int line = 0, col = 0; + if (!EndLines.empty()) { + TVector<const char*>::iterator I = UpperBound(EndLines.begin(), EndLines.end(), ptr); + if (I == EndLines.end()) + I = EndLines.end() - 1; + line = int(I - EndLines.begin()); + if (line) + I--; + col = int(ptr - (*I)); + } + if (warning) + snprintf(buf, 1024, "Warning at line %d, col %d: %s.\n", line, col, err); + else + snprintf(buf, 1024, "Error at line %d, col %d: %s.\n", line, col, err); + Errors.push_back(buf); + } else + Errors.push_back(err); +} + +void TYandexConfig::ReportError(const char* ptr, bool warning, const char* format, ...) { + va_list args; + va_start(args, format); + char buf[512]; + vsnprintf(buf, 512, format, args); + ReportError(ptr, buf, warning); +} + +bool TYandexConfig::Read(const TString& path) { + assert(FileData == nullptr); + ConfigPath = path; + //read the file to memory + TFile doc(path, OpenExisting | RdOnly); + if (!doc.IsOpen()) { + Errors.push_back(TString("can't open file ") + path + "\n"); + return false; + } + Len = (ui32)doc.GetLength(); + FileData = new char[Len + 1]; + doc.Load(FileData, Len); + FileData[Len] = 0; + doc.Close(); + return PrepareLines(); +} + +bool TYandexConfig::ReadMemory(const TStringBuf& buffer, const char* configPath) { + assert(FileData == nullptr); + if (configPath != nullptr) { + ConfigPath = configPath; + } + Len = (ui32)buffer.size(); + FileData = new char[Len + 1]; + memcpy(FileData, buffer.data(), Len); + FileData[Len] = 0; + return PrepareLines(); +} + +bool TYandexConfig::ReadMemory(const char* buffer, const char* configPath) { + Y_ASSERT(buffer); + return ReadMemory(TStringBuf(buffer), configPath); +} + +bool TYandexConfig::PrepareLines() { + //scan line breaks + EndLines.push_back(FileData - 1); + CurrentMemoryPtr = FileData; + while (*CurrentMemoryPtr) { // Are you in a great hurry? I am not... :-) + if (iscntrl((unsigned char)*CurrentMemoryPtr) && !isspace((unsigned char)*CurrentMemoryPtr)) { + ReportError(CurrentMemoryPtr, "it's a binary file"); + return false; + } + if (*CurrentMemoryPtr++ == '\n') + EndLines.push_back(CurrentMemoryPtr - 1); + } + EndLines.push_back(CurrentMemoryPtr); + + // convert simple comments inceptive with '#' or '!' or ';' to blanks + ProcessComments(); + + //convert the XML comments to blanks + char* endptr = nullptr; + CurrentMemoryPtr = strstr(FileData, "<!--"); + while (CurrentMemoryPtr != nullptr) { + endptr = strstr(CurrentMemoryPtr, "-->"); + if (endptr) { + endptr += 3; + while (CurrentMemoryPtr != endptr) + *CurrentMemoryPtr++ = ' '; + CurrentMemoryPtr = strstr(endptr, "<!--"); + } else { + ReportError(CurrentMemoryPtr, "unclosed comment"); + return false; + } + } + return true; +} + +bool TYandexConfig::ParseMemory(const TStringBuf& buffer, bool processDirectives, const char* configPath) { + if (!ReadMemory(buffer, configPath)) + return false; + return ProcessRoot(processDirectives); +} + +bool TYandexConfig::ParseMemory(const char* buffer, bool process_directives, const char* configPath) { + if (!ReadMemory(buffer, configPath)) + return false; + return ProcessRoot(process_directives); +} + +bool TYandexConfig::Parse(const TString& path, bool process_directives) { + if (!Read(path)) + return false; + return ProcessRoot(process_directives); +} + +bool TYandexConfig::ProcessRoot(bool process_directives) { + CurrentMemoryPtr = FileData; + // Add the unnamed root section + assert(AllSections.empty()); + AllSections.push_back(new Section); + AllSections.back()->Parent = AllSections.back(); + if (!OnBeginSection(*AllSections.back())) + return false; + CurSections.push(AllSections.back()); + bool ret = ProcessAll(process_directives) && OnEndSection(*CurSections.top()); + CurSections.pop(); + while (!CurSections.empty()) { + // There are some not closed main sections. + OnEndSection(*CurSections.top()); + CurSections.pop(); + } + return ret; +} + +bool TYandexConfig::FindEndOfSection(const char* SecName, const char* begin, char*& endsec, char*& endptr) { + // find "</SecName" and set '<' and '>' to '\0' + char* p = (char*)begin; + char* EndName = (char*)alloca(strlen(SecName) + 3); + *EndName = '<'; + *(EndName + 1) = '/'; + strcpy(EndName + 2, SecName); + while (p && p < FileData + Len) { + p = strstr(p, EndName); + if (p == nullptr) { + ReportError(SecName, "mismatched section"); + return false; + } + endsec = p; + p += strlen(SecName) + 2; + if (*p != '>' && !isspace((unsigned char)*p)) + continue; // it's a prefix but not the required section-end + endptr = strchr(p, '>'); + if (endptr == nullptr) { + ReportError(p, "mismatched \'<\'"); + return false; + } + *endptr = 0; + *endsec++ = 0; + *endsec++ = 0; + p = endptr - 1; + while (p > endsec && isspace((unsigned char)*p)) + *p-- = 0; + return true; + } + ReportError(SecName, "mismatched section"); + return false; +} + +bool TYandexConfig::ParseSection(const char* SecName, const char* idname, const char* idvalue) { + assert(FileData); // Call Read() firstly + size_t slen = strlen(SecName); + CurrentMemoryPtr = FileData; + + assert(AllSections.empty()); + AllSections.push_back(new Section); + AllSections.back()->Parent = AllSections.back(); + if (!OnBeginSection(*AllSections.back())) + return false; + CurSections.push(AllSections.back()); + + bool ret = false; + while (CurrentMemoryPtr < FileData + Len) { + // May be *CurrentMemoryPtr == 0 if FileData has been parsed. + while (*CurrentMemoryPtr++ != '<' && CurrentMemoryPtr < FileData + Len) { + } + if (strnicmp(CurrentMemoryPtr, SecName, slen) == 0) { + char* p = CurrentMemoryPtr + slen; + char* endptr = strchr(p, '>'); + if (endptr == nullptr) + continue; // a section may be parsed + if (*p != '>' && *p != '/' && !isspace((unsigned char)*p)) + continue; // required section must match the name and may not be parsed + //parse now + *endptr = 0; + bool endnow = false; + p = endptr - 1; + if (*p == '/') { + *p-- = 0; + endnow = true; + } + while (isspace((unsigned char)*p)) + *p-- = 0; + char *body = endptr + 1, *endsec = nullptr; + if (!ProcessBeginSection()) + break; // false + if (!endnow) { + if (!FindEndOfSection(CurSections.top()->Name, body, endsec, endptr)) + break; // false + } + if (idname && idvalue) { + SectionAttrs::iterator I = CurSections.top()->Attrs.find(idname); + if (I != CurSections.top()->Attrs.end()) { + if (stricmp((*I).second, idvalue) != 0) { + CurrentMemoryPtr = endptr + 1; + CurSections.pop(); + assert(AllSections.size() == 2); + Section* Last = AllSections.back(); + assert(Last->Parent->Next == nullptr); + assert(Last->Parent->Child == Last); + assert(Last->Next == nullptr); + Last->Parent->Child = nullptr; + delete Last; + AllSections.pop_back(); + continue; + } + } else { + if (*idvalue != 0) { + CurrentMemoryPtr = endptr + 1; + CurSections.pop(); + assert(AllSections.size() == 2); + Section* Last = AllSections.back(); + assert(Last->Parent->Next == nullptr); + assert(Last->Parent->Child == Last); + assert(Last->Next == nullptr); + Last->Parent->Child = nullptr; + delete Last; + AllSections.pop_back(); + continue; + } + } + } + if (!OnBeginSection(*CurSections.top())) + break; // false + if (!endnow) { + CurrentMemoryPtr = body; + if (!ProcessAll(true)) + break; // false + CurrentMemoryPtr = endsec; + } + if (!OnEndSection(*CurSections.top())) + break; // false + if (!ProcessEndSection()) + break; // false + CurrentMemoryPtr = endptr + 1; + ret = true; + break; // section found and processed + } + } + CurSections.pop(); + while (!CurSections.empty()) { + OnEndSection(*CurSections.top()); + CurSections.pop(); + } + return ret; +} + +// Parse some chunk of memory ended by \0 +bool TYandexConfig::ProcessAll(bool process_directives) { + char* endptr; + while (CurrentMemoryPtr && *CurrentMemoryPtr) { + switch (*CurrentMemoryPtr) { + case ' ': + case '\t': + case '\r': + case '\n': + CurrentMemoryPtr++; + break; + case '>': + ReportError(CurrentMemoryPtr, "mismatched \'>\'"); + return false; + break; + //It is not XML. We need (\n[\n\r\t ]*<) for the section started. + case '<': { + endptr = strchr(CurrentMemoryPtr, '>'); + if (endptr == nullptr) { + ReportError(CurrentMemoryPtr, "mismatched \'<\'"); + return false; + } + *endptr = 0; + char* p = CurrentMemoryPtr + 1; + if (*p != '/' && !isalpha(*p)) { + ReportError(p, "invalid character"); + return false; + } + bool endnow = false; + p = endptr - 1; + if (*p == '/') { + *p-- = 0; + endnow = true; + } + while (isspace((unsigned char)*p)) + *p-- = 0; + *CurrentMemoryPtr++ = 0; + if (*CurrentMemoryPtr == '/') { + *CurrentMemoryPtr++ = 0; + if (!OnEndSection(*CurSections.top())) + return false; + if (!ProcessEndSection()) + return false; + } else { + if (!ProcessBeginSection()) + return false; + if (!OnBeginSection(*CurSections.top())) + return false; + } + if (endnow) { + if (!OnEndSection(*CurSections.top())) + return false; + if (!ProcessEndSection()) + return false; + } + CurrentMemoryPtr = endptr + 1; + } break; + default: + if (process_directives && CurSections.top()->Cookie) { + if (!ProcessDirective()) + return false; + } else { + CurrentMemoryPtr = strchr(CurrentMemoryPtr, '\n'); + if (!CurrentMemoryPtr) + return true; // the end of file + } + break; + } + } + return true; +} + +void TYandexConfig::ProcessLineBreak(char*& LineBreak, char toChange) { + assert(*LineBreak == '\n'); + assert(toChange == ' ' || toChange == 0); + if (toChange == 0) { + char* p = LineBreak - 1; + while ((*p == ' ' || *p == '\r' || *p == '\t') && p >= FileData) + *p-- = 0; + } + *LineBreak++ = toChange; +} + +// convert simple comments inceptive with '#' or '!' or ';' to blanks +void TYandexConfig::ProcessComments() { + assert(FileData); // Call Read() firstly + char* endptr = FileData; + while (true) { + //process the leading blanks for the next + endptr += strspn(endptr, " \t\r"); + + //process the comment-line + if (*endptr == '!' || *endptr == '#' || *endptr == ';') { + while (*endptr != 0 && *endptr != '\n') + *endptr++ = ' '; + if (*endptr == '\n') { + endptr++; + continue; + } else // may be the last line in file + break; + } + + //process the regular line + endptr = strchr(endptr, '\n'); + if (endptr) + endptr++; + else // may be the last line in file + break; + } +} + +bool TYandexConfig::ProcessDirective() { + char* endptr = CurrentMemoryPtr; + + //find the end of the directive + while (true) { + //process the leading blanks for the next + endptr += strspn(endptr, " \t\r"); + + //process the blank line + if (*endptr == '\n') { + ProcessLineBreak(endptr, ' '); + continue; + } + + //process the regular line + endptr = strchr(endptr, '\n'); + if (!endptr) // may be the last line in file + break; + //may be continue at the next line + char* p = endptr - 1; + while ((*p == ' ' || *p == '\r' || *p == '\t') && p > FileData) + p--; + if (*p == '\\') { + *p = ' '; + ProcessLineBreak(endptr, ' '); + } else { + ProcessLineBreak(endptr, 0); + break; + } + } + assert(endptr == nullptr || *endptr == 0 || *(endptr - 1) == 0); + + //split the directive into key and value + char* args = CurrentMemoryPtr; + args += strcspn(CurrentMemoryPtr, " \t\r=:"); + if (*args) { + bool olddelimiter = (*args == ':' || *args == '='); + *args++ = 0; + args += strspn(args, " \t\r"); + if ((*args == ':' || *args == '=') && !olddelimiter) { + args++; + args += strspn(args, " \t\r"); + } + } + + //add the directive at last + assert(!CurSections.empty()); + Section* sec = CurSections.top(); + if (!AddKeyValue(*sec, CurrentMemoryPtr, args)) + return false; + + CurrentMemoryPtr = endptr; + return true; +} + +void TYandexConfig::AddSection(Section* sec) { + assert(sec && sec->Parent); + if (sec->Parent->Child == nullptr) + sec->Parent->Child = sec; + else { + Section** pNext = &sec->Parent->Child->Next; + while (*pNext) + pNext = &(*pNext)->Next; + *pNext = sec; + } + AllSections.push_back(sec); +} + +bool TYandexConfig::ProcessBeginSection() { + assert(!CurSections.empty()); + Section* sec = new Section; + sec->Parent = CurSections.top(); + AddSection(sec); + char* endptr = CurrentMemoryPtr; + endptr += strcspn(endptr, " \t\r\n"); + if (endptr && *endptr) + *endptr++ = 0; + AllSections.back()->Name = CurrentMemoryPtr; + + //find the attributes + const char* AttrName = nullptr; + bool EqPassed = false; + while (endptr && *endptr) { + switch (*endptr) { + case ' ': + case '\t': + case '\r': + case '\n': + endptr++; + break; + case '=': + if (!AttrName || EqPassed) { + ReportError(endptr, "invalid character"); + return false; + } else { + EqPassed = true; + *endptr++ = 0; + } + break; + case '\"': + case '\'': + case '`': + if (!AttrName || !EqPassed) { + ReportError(endptr, "invalid character"); + return false; + } + { + char* endattr = strchr(endptr + 1, *endptr); + if (!endattr) { + ReportError(endptr, "mismatched character"); + return false; + } + *endattr++ = 0; + AllSections.back()->Attrs[AttrName] = endptr + 1; + AttrName = nullptr; + EqPassed = false; + endptr = endattr; + } + if (!(*endptr == 0 || isspace((unsigned char)*endptr))) { + ReportError(endptr, "invalid character"); + return false; + } + break; + default: + if (AttrName || EqPassed) { + ReportError(endptr, "invalid character"); + return false; + } + AttrName = endptr; + endptr += strcspn(endptr, " \t\r\n="); + if (*endptr == 0) { + ReportError(AttrName, "invalid characters"); + return false; + } + if (*endptr == '=') + EqPassed = true; + *endptr++ = 0; + break; + } + } + CurSections.push(AllSections.back()); + return true; +} + +bool TYandexConfig::ProcessEndSection() { + assert(!CurSections.empty()); + Section* sec = CurSections.top(); + if (sec->Name && CurrentMemoryPtr && strcmp(sec->Name, CurrentMemoryPtr) != 0) { + ReportError(CurrentMemoryPtr, "mismatched open element"); + return false; + } + CurSections.pop(); + return true; +} + +bool TYandexConfig::AddKeyValue(Section& sec, const char* key, const char* value) { + assert(sec.Cookie); + if (!sec.Cookie->AddKeyValue(key, value)) { + if (*sec.Name) + ReportError(key, true, "section \'%s\' does not allow directive \'%s\'. The directive will be ignored", sec.Name, key); + else + ReportError(key, true, "directive \'%s\' not allowed here. It will be ignored", key); + } + return true; +} + +bool TYandexConfig::OnBeginSection(Section& secnew) { + if (*secnew.Name) { + ReportError(secnew.Name, false, "section \'%s\' not allowed here", secnew.Name); + return false; + } + return true; +} + +bool TYandexConfig::OnEndSection(Section& sec) { + if (sec.Cookie) { + if (!sec.Cookie->CheckOnEnd(*this, sec)) { + if (sec.Owner) { + delete sec.Cookie; + sec.Cookie = nullptr; + } + return false; + } + } + return true; +} + +TYandexConfig::Section* TYandexConfig::GetFirstChild(const char* Name, TYandexConfig::Section* CurSection /*= NULL*/) { + if (CurSection == nullptr) + CurSection = GetRootSection(); + CurSection = CurSection->Child; + while (CurSection) { + if (CurSection->Parsed()) { + if (stricmp(CurSection->Name, Name) == 0) + break; + } + CurSection = CurSection->Next; + } + return CurSection; +} + +void TYandexConfig::PrintSectionConfig(const TYandexConfig::Section* section, IOutputStream& os, bool printNextSection) { + if (section == nullptr || !section->Parsed()) + return; + bool hasName = section->Name && *section->Name; + if (hasName) { + os << "<" << section->Name; + for (const auto& attr : section->Attrs) { + os << " " << attr.first << "=\"" << attr.second << "\""; + } + os << ">\n"; + } + for (const auto& iter : section->GetDirectives()) { + if (iter.second != nullptr && *iter.second && !IsSpace(iter.second)) { + os << iter.first; + os << " " << iter.second << "\n"; + } + } + if (section->Child) { + PrintSectionConfig(section->Child, os); + } + if (hasName) + os << "</" << section->Name << ">\n"; + + if (printNextSection && section->Next) { + PrintSectionConfig(section->Next, os); + } +} + +void TYandexConfig::PrintConfig(IOutputStream& os) const { + const Section* sec = GetRootSection(); + return PrintSectionConfig(sec, os); +} + +//********************************************************************************************* +bool TYandexConfig::Directives::CheckOnEnd(TYandexConfig&, TYandexConfig::Section&) { + return true; +} + +bool TYandexConfig::Directives::AddKeyValue(const TString& key, const char* value) { + iterator I = find(key); + if (I == end()) { + if (strict) + return false; + else + I = insert(value_type(key, nullptr)).first; + } + (*I).second = value; + return true; +} + +bool TYandexConfig::Directives::GetValue(TStringBuf key, TString& value) const { + const_iterator I = find(key); + + if (I == end()) { + if (strict) { + ythrow yexception() << "key " << key << " not declared"; + } + + return false; + } + + if ((*I).second == nullptr) { + return false; + } + + value = (*I).second; + + return true; +} + +bool TYandexConfig::Directives::GetNonEmptyValue(TStringBuf key, TString& value) const { + TString tempValue; + GetValue(key, tempValue); + if (tempValue) { + value = tempValue; + return true; + } + return false; +} + +bool TYandexConfig::Directives::GetValue(TStringBuf key, bool& value) const { + TString tmp; + + if (GetValue(key, tmp)) { // IsTrue won't return true on empty strings anymore + value = !tmp || IsTrue(tmp); + + return true; + } + + return false; +} + +bool TYandexConfig::Directives::FillArray(TStringBuf key, TVector<TString>& values) const { + const_iterator I = find(key); + + if (I == end()) { + return false; + } + + if ((*I).second != nullptr) { + Split((*I).second, " ,\t\r", values); + return true; + } + + return false; +} + +void TYandexConfig::Directives::Clear() { + if (strict) { + clear(); + } else { + for (auto& I : *this) + I.second = nullptr; + } +} + +TYandexConfig::TSectionsMap TYandexConfig::Section::GetAllChildren() const { + TSectionsMap result; + if (Child == nullptr) + return result; + Section* curSection = Child; + while (curSection) { + result.insert(TMultiMap<TCiString, Section*>::value_type(curSection->Name, curSection)); + curSection = curSection->Next; + } + return result; +} |