conf.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. #include "conf.h"
  2. #include <library/cpp/logger/all.h>
  3. #include <library/cpp/charset/ci_string.h>
  4. #include <util/generic/algorithm.h>
  5. #include <util/stream/file.h>
  6. #include <util/string/split.h>
  7. #include <util/string/type.h>
  8. #include <cstdlib>
  9. void TYandexConfig::Clear() {
  10. delete[] FileData;
  11. FileData = nullptr;
  12. CurrentMemoryPtr = nullptr;
  13. Len = 0;
  14. while (!CurSections.empty())
  15. CurSections.pop();
  16. for (size_t i = 0; i < AllSections.size(); i++) {
  17. if (AllSections[i]->Owner)
  18. delete AllSections[i]->Cookie;
  19. delete AllSections[i];
  20. }
  21. AllSections.clear();
  22. Errors.clear();
  23. EndLines.clear();
  24. ConfigPath.remove();
  25. }
  26. void TYandexConfig::PrintErrors(TLog* Log) {
  27. size_t sz = Errors.size();
  28. if (sz) {
  29. Log->AddLog("Processing of \'%s\':\n", ConfigPath.data());
  30. for (size_t i = 0; i < sz; i++)
  31. *Log << Errors[i];
  32. Errors.clear();
  33. }
  34. }
  35. void TYandexConfig::PrintErrors(TString& Err) {
  36. size_t sz = Errors.size();
  37. if (sz) {
  38. char buf[512];
  39. snprintf(buf, 512, "Processing of \'%s\':\n", ConfigPath.data());
  40. Err += buf;
  41. for (size_t i = 0; i < sz; i++)
  42. Err += Errors[i];
  43. Errors.clear();
  44. }
  45. }
  46. void TYandexConfig::ReportError(const char* ptr, const char* err, bool warning) {
  47. if (ptr) {
  48. char buf[1024];
  49. int line = 0, col = 0;
  50. if (!EndLines.empty()) {
  51. TVector<const char*>::iterator I = UpperBound(EndLines.begin(), EndLines.end(), ptr);
  52. if (I == EndLines.end())
  53. I = EndLines.end() - 1;
  54. line = int(I - EndLines.begin());
  55. if (line)
  56. I--;
  57. col = int(ptr - (*I));
  58. }
  59. if (warning)
  60. snprintf(buf, 1024, "Warning at line %d, col %d: %s.\n", line, col, err);
  61. else
  62. snprintf(buf, 1024, "Error at line %d, col %d: %s.\n", line, col, err);
  63. Errors.push_back(buf);
  64. } else
  65. Errors.push_back(err);
  66. }
  67. void TYandexConfig::ReportError(const char* ptr, bool warning, const char* format, ...) {
  68. va_list args;
  69. va_start(args, format);
  70. char buf[512];
  71. vsnprintf(buf, 512, format, args);
  72. ReportError(ptr, buf, warning);
  73. }
  74. bool TYandexConfig::Read(const TString& path) {
  75. assert(FileData == nullptr);
  76. ConfigPath = path;
  77. //read the file to memory
  78. TFile doc(path, OpenExisting | RdOnly);
  79. if (!doc.IsOpen()) {
  80. Errors.push_back(TString("can't open file ") + path + "\n");
  81. return false;
  82. }
  83. Len = (ui32)doc.GetLength();
  84. FileData = new char[Len + 1];
  85. doc.Load(FileData, Len);
  86. FileData[Len] = 0;
  87. doc.Close();
  88. return PrepareLines();
  89. }
  90. bool TYandexConfig::ReadMemory(const TStringBuf& buffer, const char* configPath) {
  91. assert(FileData == nullptr);
  92. if (configPath != nullptr) {
  93. ConfigPath = configPath;
  94. }
  95. Len = (ui32)buffer.size();
  96. FileData = new char[Len + 1];
  97. memcpy(FileData, buffer.data(), Len);
  98. FileData[Len] = 0;
  99. return PrepareLines();
  100. }
  101. bool TYandexConfig::ReadMemory(const char* buffer, const char* configPath) {
  102. Y_ASSERT(buffer);
  103. return ReadMemory(TStringBuf(buffer), configPath);
  104. }
  105. bool TYandexConfig::PrepareLines() {
  106. //scan line breaks
  107. EndLines.push_back(FileData - 1);
  108. CurrentMemoryPtr = FileData;
  109. while (*CurrentMemoryPtr) { // Are you in a great hurry? I am not... :-)
  110. if (iscntrl((unsigned char)*CurrentMemoryPtr) && !isspace((unsigned char)*CurrentMemoryPtr)) {
  111. ReportError(CurrentMemoryPtr, "it's a binary file");
  112. return false;
  113. }
  114. if (*CurrentMemoryPtr++ == '\n')
  115. EndLines.push_back(CurrentMemoryPtr - 1);
  116. }
  117. EndLines.push_back(CurrentMemoryPtr);
  118. // convert simple comments inceptive with '#' or '!' or ';' to blanks
  119. ProcessComments();
  120. //convert the XML comments to blanks
  121. char* endptr = nullptr;
  122. CurrentMemoryPtr = strstr(FileData, "<!--");
  123. while (CurrentMemoryPtr != nullptr) {
  124. endptr = strstr(CurrentMemoryPtr, "-->");
  125. if (endptr) {
  126. endptr += 3;
  127. while (CurrentMemoryPtr != endptr)
  128. *CurrentMemoryPtr++ = ' ';
  129. CurrentMemoryPtr = strstr(endptr, "<!--");
  130. } else {
  131. ReportError(CurrentMemoryPtr, "unclosed comment");
  132. return false;
  133. }
  134. }
  135. return true;
  136. }
  137. bool TYandexConfig::ParseMemory(const TStringBuf& buffer, bool processDirectives, const char* configPath) {
  138. if (!ReadMemory(buffer, configPath))
  139. return false;
  140. return ProcessRoot(processDirectives);
  141. }
  142. bool TYandexConfig::ParseMemory(const char* buffer, bool process_directives, const char* configPath) {
  143. if (!ReadMemory(buffer, configPath))
  144. return false;
  145. return ProcessRoot(process_directives);
  146. }
  147. bool TYandexConfig::Parse(const TString& path, bool process_directives) {
  148. if (!Read(path))
  149. return false;
  150. return ProcessRoot(process_directives);
  151. }
  152. bool TYandexConfig::ProcessRoot(bool process_directives) {
  153. CurrentMemoryPtr = FileData;
  154. // Add the unnamed root section
  155. assert(AllSections.empty());
  156. AllSections.push_back(new Section);
  157. AllSections.back()->Parent = AllSections.back();
  158. if (!OnBeginSection(*AllSections.back()))
  159. return false;
  160. CurSections.push(AllSections.back());
  161. bool ret = ProcessAll(process_directives) && OnEndSection(*CurSections.top());
  162. CurSections.pop();
  163. while (!CurSections.empty()) {
  164. // There are some not closed main sections.
  165. OnEndSection(*CurSections.top());
  166. CurSections.pop();
  167. }
  168. return ret;
  169. }
  170. bool TYandexConfig::FindEndOfSection(const char* SecName, const char* begin, char*& endsec, char*& endptr) {
  171. // find "</SecName" and set '<' and '>' to '\0'
  172. char* p = (char*)begin;
  173. char* EndName = (char*)alloca(strlen(SecName) + 3);
  174. *EndName = '<';
  175. *(EndName + 1) = '/';
  176. strcpy(EndName + 2, SecName);
  177. while (p && p < FileData + Len) {
  178. p = strstr(p, EndName);
  179. if (p == nullptr) {
  180. ReportError(SecName, "mismatched section");
  181. return false;
  182. }
  183. endsec = p;
  184. p += strlen(SecName) + 2;
  185. if (*p != '>' && !isspace((unsigned char)*p))
  186. continue; // it's a prefix but not the required section-end
  187. endptr = strchr(p, '>');
  188. if (endptr == nullptr) {
  189. ReportError(p, "mismatched \'<\'");
  190. return false;
  191. }
  192. *endptr = 0;
  193. *endsec++ = 0;
  194. *endsec++ = 0;
  195. p = endptr - 1;
  196. while (p > endsec && isspace((unsigned char)*p))
  197. *p-- = 0;
  198. return true;
  199. }
  200. ReportError(SecName, "mismatched section");
  201. return false;
  202. }
  203. bool TYandexConfig::ParseSection(const char* SecName, const char* idname, const char* idvalue) {
  204. assert(FileData); // Call Read() firstly
  205. size_t slen = strlen(SecName);
  206. CurrentMemoryPtr = FileData;
  207. assert(AllSections.empty());
  208. AllSections.push_back(new Section);
  209. AllSections.back()->Parent = AllSections.back();
  210. if (!OnBeginSection(*AllSections.back()))
  211. return false;
  212. CurSections.push(AllSections.back());
  213. bool ret = false;
  214. while (CurrentMemoryPtr < FileData + Len) {
  215. // May be *CurrentMemoryPtr == 0 if FileData has been parsed.
  216. while (*CurrentMemoryPtr++ != '<' && CurrentMemoryPtr < FileData + Len) {
  217. }
  218. if (strnicmp(CurrentMemoryPtr, SecName, slen) == 0) {
  219. char* p = CurrentMemoryPtr + slen;
  220. char* endptr = strchr(p, '>');
  221. if (endptr == nullptr)
  222. continue; // a section may be parsed
  223. if (*p != '>' && *p != '/' && !isspace((unsigned char)*p))
  224. continue; // required section must match the name and may not be parsed
  225. //parse now
  226. *endptr = 0;
  227. bool endnow = false;
  228. p = endptr - 1;
  229. if (*p == '/') {
  230. *p-- = 0;
  231. endnow = true;
  232. }
  233. while (isspace((unsigned char)*p))
  234. *p-- = 0;
  235. char *body = endptr + 1, *endsec = nullptr;
  236. if (!ProcessBeginSection())
  237. break; // false
  238. if (!endnow) {
  239. if (!FindEndOfSection(CurSections.top()->Name, body, endsec, endptr))
  240. break; // false
  241. }
  242. if (idname && idvalue) {
  243. SectionAttrs::iterator I = CurSections.top()->Attrs.find(idname);
  244. if (I != CurSections.top()->Attrs.end()) {
  245. if (stricmp((*I).second, idvalue) != 0) {
  246. CurrentMemoryPtr = endptr + 1;
  247. CurSections.pop();
  248. assert(AllSections.size() == 2);
  249. Section* Last = AllSections.back();
  250. assert(Last->Parent->Next == nullptr);
  251. assert(Last->Parent->Child == Last);
  252. assert(Last->Next == nullptr);
  253. Last->Parent->Child = nullptr;
  254. delete Last;
  255. AllSections.pop_back();
  256. continue;
  257. }
  258. } else {
  259. if (*idvalue != 0) {
  260. CurrentMemoryPtr = endptr + 1;
  261. CurSections.pop();
  262. assert(AllSections.size() == 2);
  263. Section* Last = AllSections.back();
  264. assert(Last->Parent->Next == nullptr);
  265. assert(Last->Parent->Child == Last);
  266. assert(Last->Next == nullptr);
  267. Last->Parent->Child = nullptr;
  268. delete Last;
  269. AllSections.pop_back();
  270. continue;
  271. }
  272. }
  273. }
  274. if (!OnBeginSection(*CurSections.top()))
  275. break; // false
  276. if (!endnow) {
  277. CurrentMemoryPtr = body;
  278. if (!ProcessAll(true))
  279. break; // false
  280. CurrentMemoryPtr = endsec;
  281. }
  282. if (!OnEndSection(*CurSections.top()))
  283. break; // false
  284. if (!ProcessEndSection())
  285. break; // false
  286. CurrentMemoryPtr = endptr + 1;
  287. ret = true;
  288. break; // section found and processed
  289. }
  290. }
  291. CurSections.pop();
  292. while (!CurSections.empty()) {
  293. OnEndSection(*CurSections.top());
  294. CurSections.pop();
  295. }
  296. return ret;
  297. }
  298. // Parse some chunk of memory ended by \0
  299. bool TYandexConfig::ProcessAll(bool process_directives) {
  300. char* endptr;
  301. while (CurrentMemoryPtr && *CurrentMemoryPtr) {
  302. switch (*CurrentMemoryPtr) {
  303. case ' ':
  304. case '\t':
  305. case '\r':
  306. case '\n':
  307. CurrentMemoryPtr++;
  308. break;
  309. case '>':
  310. ReportError(CurrentMemoryPtr, "mismatched \'>\'");
  311. return false;
  312. break;
  313. //It is not XML. We need (\n[\n\r\t ]*<) for the section started.
  314. case '<': {
  315. endptr = strchr(CurrentMemoryPtr, '>');
  316. if (endptr == nullptr) {
  317. ReportError(CurrentMemoryPtr, "mismatched \'<\'");
  318. return false;
  319. }
  320. *endptr = 0;
  321. char* p = CurrentMemoryPtr + 1;
  322. if (*p != '/' && !isalpha(*p)) {
  323. ReportError(p, "invalid character");
  324. return false;
  325. }
  326. bool endnow = false;
  327. p = endptr - 1;
  328. if (*p == '/') {
  329. *p-- = 0;
  330. endnow = true;
  331. }
  332. while (isspace((unsigned char)*p))
  333. *p-- = 0;
  334. *CurrentMemoryPtr++ = 0;
  335. if (*CurrentMemoryPtr == '/') {
  336. *CurrentMemoryPtr++ = 0;
  337. if (!OnEndSection(*CurSections.top()))
  338. return false;
  339. if (!ProcessEndSection())
  340. return false;
  341. } else {
  342. if (!ProcessBeginSection())
  343. return false;
  344. if (!OnBeginSection(*CurSections.top()))
  345. return false;
  346. }
  347. if (endnow) {
  348. if (!OnEndSection(*CurSections.top()))
  349. return false;
  350. if (!ProcessEndSection())
  351. return false;
  352. }
  353. CurrentMemoryPtr = endptr + 1;
  354. } break;
  355. default:
  356. if (process_directives && CurSections.top()->Cookie) {
  357. if (!ProcessDirective())
  358. return false;
  359. } else {
  360. CurrentMemoryPtr = strchr(CurrentMemoryPtr, '\n');
  361. if (!CurrentMemoryPtr)
  362. return true; // the end of file
  363. }
  364. break;
  365. }
  366. }
  367. return true;
  368. }
  369. void TYandexConfig::ProcessLineBreak(char*& LineBreak, char toChange) {
  370. assert(*LineBreak == '\n');
  371. assert(toChange == ' ' || toChange == 0);
  372. if (toChange == 0) {
  373. char* p = LineBreak - 1;
  374. while ((*p == ' ' || *p == '\r' || *p == '\t') && p >= FileData)
  375. *p-- = 0;
  376. }
  377. *LineBreak++ = toChange;
  378. }
  379. // convert simple comments inceptive with '#' or '!' or ';' to blanks
  380. void TYandexConfig::ProcessComments() {
  381. assert(FileData); // Call Read() firstly
  382. char* endptr = FileData;
  383. while (true) {
  384. //process the leading blanks for the next
  385. endptr += strspn(endptr, " \t\r");
  386. //process the comment-line
  387. if (*endptr == '!' || *endptr == '#' || *endptr == ';') {
  388. while (*endptr != 0 && *endptr != '\n')
  389. *endptr++ = ' ';
  390. if (*endptr == '\n') {
  391. endptr++;
  392. continue;
  393. } else // may be the last line in file
  394. break;
  395. }
  396. //process the regular line
  397. endptr = strchr(endptr, '\n');
  398. if (endptr)
  399. endptr++;
  400. else // may be the last line in file
  401. break;
  402. }
  403. }
  404. bool TYandexConfig::ProcessDirective() {
  405. char* endptr = CurrentMemoryPtr;
  406. //find the end of the directive
  407. while (true) {
  408. //process the leading blanks for the next
  409. endptr += strspn(endptr, " \t\r");
  410. //process the blank line
  411. if (*endptr == '\n') {
  412. ProcessLineBreak(endptr, ' ');
  413. continue;
  414. }
  415. //process the regular line
  416. endptr = strchr(endptr, '\n');
  417. if (!endptr) // may be the last line in file
  418. break;
  419. //may be continue at the next line
  420. char* p = endptr - 1;
  421. while ((*p == ' ' || *p == '\r' || *p == '\t') && p > FileData)
  422. p--;
  423. if (*p == '\\') {
  424. *p = ' ';
  425. ProcessLineBreak(endptr, ' ');
  426. } else {
  427. ProcessLineBreak(endptr, 0);
  428. break;
  429. }
  430. }
  431. assert(endptr == nullptr || *endptr == 0 || *(endptr - 1) == 0);
  432. //split the directive into key and value
  433. char* args = CurrentMemoryPtr;
  434. args += strcspn(CurrentMemoryPtr, " \t\r=:");
  435. if (*args) {
  436. bool olddelimiter = (*args == ':' || *args == '=');
  437. *args++ = 0;
  438. args += strspn(args, " \t\r");
  439. if ((*args == ':' || *args == '=') && !olddelimiter) {
  440. args++;
  441. args += strspn(args, " \t\r");
  442. }
  443. }
  444. //add the directive at last
  445. assert(!CurSections.empty());
  446. Section* sec = CurSections.top();
  447. if (!AddKeyValue(*sec, CurrentMemoryPtr, args))
  448. return false;
  449. CurrentMemoryPtr = endptr;
  450. return true;
  451. }
  452. void TYandexConfig::AddSection(Section* sec) {
  453. assert(sec && sec->Parent);
  454. if (sec->Parent->Child == nullptr)
  455. sec->Parent->Child = sec;
  456. else {
  457. Section** pNext = &sec->Parent->Child->Next;
  458. while (*pNext)
  459. pNext = &(*pNext)->Next;
  460. *pNext = sec;
  461. }
  462. AllSections.push_back(sec);
  463. }
  464. bool TYandexConfig::ProcessBeginSection() {
  465. assert(!CurSections.empty());
  466. Section* sec = new Section;
  467. sec->Parent = CurSections.top();
  468. AddSection(sec);
  469. char* endptr = CurrentMemoryPtr;
  470. endptr += strcspn(endptr, " \t\r\n");
  471. if (endptr && *endptr)
  472. *endptr++ = 0;
  473. AllSections.back()->Name = CurrentMemoryPtr;
  474. //find the attributes
  475. const char* AttrName = nullptr;
  476. bool EqPassed = false;
  477. while (endptr && *endptr) {
  478. switch (*endptr) {
  479. case ' ':
  480. case '\t':
  481. case '\r':
  482. case '\n':
  483. endptr++;
  484. break;
  485. case '=':
  486. if (!AttrName || EqPassed) {
  487. ReportError(endptr, "invalid character");
  488. return false;
  489. } else {
  490. EqPassed = true;
  491. *endptr++ = 0;
  492. }
  493. break;
  494. case '\"':
  495. case '\'':
  496. case '`':
  497. if (!AttrName || !EqPassed) {
  498. ReportError(endptr, "invalid character");
  499. return false;
  500. }
  501. {
  502. char* endattr = strchr(endptr + 1, *endptr);
  503. if (!endattr) {
  504. ReportError(endptr, "mismatched character");
  505. return false;
  506. }
  507. *endattr++ = 0;
  508. AllSections.back()->Attrs[AttrName] = endptr + 1;
  509. AttrName = nullptr;
  510. EqPassed = false;
  511. endptr = endattr;
  512. }
  513. if (!(*endptr == 0 || isspace((unsigned char)*endptr))) {
  514. ReportError(endptr, "invalid character");
  515. return false;
  516. }
  517. break;
  518. default:
  519. if (AttrName || EqPassed) {
  520. ReportError(endptr, "invalid character");
  521. return false;
  522. }
  523. AttrName = endptr;
  524. endptr += strcspn(endptr, " \t\r\n=");
  525. if (*endptr == 0) {
  526. ReportError(AttrName, "invalid characters");
  527. return false;
  528. }
  529. if (*endptr == '=')
  530. EqPassed = true;
  531. *endptr++ = 0;
  532. break;
  533. }
  534. }
  535. CurSections.push(AllSections.back());
  536. return true;
  537. }
  538. bool TYandexConfig::ProcessEndSection() {
  539. assert(!CurSections.empty());
  540. Section* sec = CurSections.top();
  541. if (sec->Name && CurrentMemoryPtr && strcmp(sec->Name, CurrentMemoryPtr) != 0) {
  542. ReportError(CurrentMemoryPtr, "mismatched open element");
  543. return false;
  544. }
  545. CurSections.pop();
  546. return true;
  547. }
  548. bool TYandexConfig::AddKeyValue(Section& sec, const char* key, const char* value) {
  549. assert(sec.Cookie);
  550. if (!sec.Cookie->AddKeyValue(key, value)) {
  551. if (*sec.Name)
  552. ReportError(key, true, "section \'%s\' does not allow directive \'%s\'. The directive will be ignored", sec.Name, key);
  553. else
  554. ReportError(key, true, "directive \'%s\' not allowed here. It will be ignored", key);
  555. }
  556. return true;
  557. }
  558. bool TYandexConfig::OnBeginSection(Section& secnew) {
  559. if (*secnew.Name) {
  560. ReportError(secnew.Name, false, "section \'%s\' not allowed here", secnew.Name);
  561. return false;
  562. }
  563. return true;
  564. }
  565. bool TYandexConfig::OnEndSection(Section& sec) {
  566. if (sec.Cookie) {
  567. if (!sec.Cookie->CheckOnEnd(*this, sec)) {
  568. if (sec.Owner) {
  569. delete sec.Cookie;
  570. sec.Cookie = nullptr;
  571. }
  572. return false;
  573. }
  574. }
  575. return true;
  576. }
  577. TYandexConfig::Section* TYandexConfig::GetFirstChild(const char* Name, TYandexConfig::Section* CurSection /*= NULL*/) {
  578. if (CurSection == nullptr)
  579. CurSection = GetRootSection();
  580. CurSection = CurSection->Child;
  581. while (CurSection) {
  582. if (CurSection->Parsed()) {
  583. if (stricmp(CurSection->Name, Name) == 0)
  584. break;
  585. }
  586. CurSection = CurSection->Next;
  587. }
  588. return CurSection;
  589. }
  590. void TYandexConfig::PrintSectionConfig(const TYandexConfig::Section* section, IOutputStream& os, bool printNextSection) {
  591. if (section == nullptr || !section->Parsed())
  592. return;
  593. bool hasName = section->Name && *section->Name;
  594. if (hasName) {
  595. os << "<" << section->Name;
  596. for (const auto& attr : section->Attrs) {
  597. os << " " << attr.first << "=\"" << attr.second << "\"";
  598. }
  599. os << ">\n";
  600. }
  601. for (const auto& iter : section->GetDirectives()) {
  602. if (iter.second != nullptr && *iter.second && !IsSpace(iter.second)) {
  603. os << iter.first;
  604. os << " " << iter.second << "\n";
  605. }
  606. }
  607. if (section->Child) {
  608. PrintSectionConfig(section->Child, os);
  609. }
  610. if (hasName)
  611. os << "</" << section->Name << ">\n";
  612. if (printNextSection && section->Next) {
  613. PrintSectionConfig(section->Next, os);
  614. }
  615. }
  616. void TYandexConfig::PrintConfig(IOutputStream& os) const {
  617. const Section* sec = GetRootSection();
  618. return PrintSectionConfig(sec, os);
  619. }
  620. //*********************************************************************************************
  621. bool TYandexConfig::Directives::CheckOnEnd(TYandexConfig&, TYandexConfig::Section&) {
  622. return true;
  623. }
  624. bool TYandexConfig::Directives::AddKeyValue(const TString& key, const char* value) {
  625. iterator I = find(key);
  626. if (I == end()) {
  627. if (strict)
  628. return false;
  629. else
  630. I = insert(value_type(key, nullptr)).first;
  631. }
  632. (*I).second = value;
  633. return true;
  634. }
  635. bool TYandexConfig::Directives::GetValue(TStringBuf key, TString& value) const {
  636. const_iterator I = find(key);
  637. if (I == end()) {
  638. if (strict) {
  639. ythrow yexception() << "key " << key << " not declared";
  640. }
  641. return false;
  642. }
  643. if ((*I).second == nullptr) {
  644. return false;
  645. }
  646. value = (*I).second;
  647. return true;
  648. }
  649. bool TYandexConfig::Directives::GetNonEmptyValue(TStringBuf key, TString& value) const {
  650. TString tempValue;
  651. GetValue(key, tempValue);
  652. if (tempValue) {
  653. value = tempValue;
  654. return true;
  655. }
  656. return false;
  657. }
  658. bool TYandexConfig::Directives::GetValue(TStringBuf key, bool& value) const {
  659. TString tmp;
  660. if (GetValue(key, tmp)) { // IsTrue won't return true on empty strings anymore
  661. value = !tmp || IsTrue(tmp);
  662. return true;
  663. }
  664. return false;
  665. }
  666. bool TYandexConfig::Directives::FillArray(TStringBuf key, TVector<TString>& values) const {
  667. const_iterator I = find(key);
  668. if (I == end()) {
  669. return false;
  670. }
  671. if ((*I).second != nullptr) {
  672. Split((*I).second, " ,\t\r", values);
  673. return true;
  674. }
  675. return false;
  676. }
  677. void TYandexConfig::Directives::Clear() {
  678. if (strict) {
  679. clear();
  680. } else {
  681. for (auto& I : *this)
  682. I.second = nullptr;
  683. }
  684. }
  685. TYandexConfig::TSectionsMap TYandexConfig::Section::GetAllChildren() const {
  686. TSectionsMap result;
  687. if (Child == nullptr)
  688. return result;
  689. Section* curSection = Child;
  690. while (curSection) {
  691. result.insert(TMultiMap<TCiString, Section*>::value_type(curSection->Name, curSection));
  692. curSection = curSection->Next;
  693. }
  694. return result;
  695. }