1 /* $Id: ncbireg.cpp 633612 2021-06-22 17:38:24Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Denis Vakatov, Aaron Ucko
27 *
28 * File Description:
29 * Handle info in the NCBI configuration file(s):
30 * read and parse config. file
31 * search, edit, etc. in the retrieved configuration info
32 * dump info back to config. file
33 *
34 */
35
36 #include <ncbi_pch.hpp>
37 #include <corelib/ncbireg.hpp>
38 #include <corelib/env_reg.hpp>
39 #include <corelib/metareg.hpp>
40 #include <corelib/ncbiapp_api.hpp>
41 #include <corelib/ncbimtx.hpp>
42 #include <corelib/error_codes.hpp>
43 #include <corelib/resource_info.hpp>
44 #include "ncbisys.hpp"
45
46 #include <algorithm>
47 #include <set>
48
49
50 #define NCBI_USE_ERRCODE_X Corelib_Reg
51
52
53 BEGIN_NCBI_SCOPE
54
55 typedef CRegistryReadGuard TReadGuard;
56 typedef CRegistryWriteGuard TWriteGuard;
57
58
59 // Valid symbols for a section/entry name
s_IsNameSectionSymbol(char ch,IRegistry::TFlags flags)60 inline bool s_IsNameSectionSymbol(char ch, IRegistry::TFlags flags)
61 {
62 return (isalnum((unsigned char) ch)
63 || ch == '_' || ch == '-' || ch == '.' || ch == '/'
64 || ((flags & IRegistry::fInternalSpaces) && ch == ' '));
65 }
66
67
IsNameSection(const string & str,TFlags flags)68 bool IRegistry::IsNameSection(const string& str, TFlags flags)
69 {
70 // Allow empty section name in case of fSectionlessEntries set
71 if (str.empty() && !(flags & IRegistry::fSectionlessEntries) ) {
72 return false;
73 }
74
75 ITERATE (string, it, str) {
76 if (!s_IsNameSectionSymbol(*it, flags)) {
77 return false;
78 }
79 }
80 return true;
81 }
82
83
IsNameEntry(const string & str,TFlags flags)84 bool IRegistry::IsNameEntry(const string& str, TFlags flags)
85 {
86 return IsNameSection(str, flags & ~IRegistry::fSectionlessEntries);
87 }
88
89
90 // Convert "comment" from plain text to comment
s_ConvertComment(const string & comment,bool is_file_comment=false)91 static const string s_ConvertComment(const string& comment,
92 bool is_file_comment = false)
93 {
94 if ( !comment.length() )
95 return kEmptyStr;
96
97 string x_comment;
98 const char c_comment = is_file_comment ? '#' : ';';
99
100 SIZE_TYPE endl_pos = 0;
101 for (SIZE_TYPE beg = 0; beg < comment.length();
102 beg = endl_pos + 1) {
103 SIZE_TYPE pos = comment.find_first_not_of(" \t", beg);
104 endl_pos = comment.find_first_of("\n", beg);
105 if (endl_pos == NPOS) {
106 endl_pos = comment.length();
107 }
108 if (((pos != NPOS && comment[pos] != c_comment) ||
109 (pos == NPOS && endl_pos == comment.length())) &&
110 (is_file_comment || beg != endl_pos) ) {
111 x_comment += c_comment;
112 }
113 x_comment.append(comment, beg, endl_pos - beg);
114 x_comment += '\n';
115 }
116 return x_comment;
117 }
118
119
120 // Dump the comment to stream "os"
s_WriteComment(CNcbiOstream & os,const string & comment)121 static bool s_WriteComment(CNcbiOstream& os, const string& comment)
122 {
123 if (!comment.length())
124 return true;
125
126 if (strcmp(Endl(), "\n") == 0) {
127 os << comment;
128 } else {
129 ITERATE(string, i, comment) {
130 if (*i == '\n') {
131 os << Endl();
132 } else {
133 os << *i;
134 }
135 }
136 }
137 return os.good();
138 }
139
140 // Does pos follow an odd number of backslashes?
s_Backslashed(const string & s,SIZE_TYPE pos)141 inline bool s_Backslashed(const string& s, SIZE_TYPE pos)
142 {
143 if (pos == 0) {
144 return false;
145 }
146 SIZE_TYPE last_non_bs = s.find_last_not_of("\\", pos - 1);
147 return (pos - last_non_bs) % 2 == 0;
148 }
149
s_FlatKey(const string & section,const string & name)150 inline string s_FlatKey(const string& section, const string& name)
151 {
152 return section + '#' + name;
153 }
154
155
156 //////////////////////////////////////////////////////////////////////
157 //
158 // IRegistry
159
160 const char* IRegistry::sm_InSectionCommentName = "[]";
161
Empty(TFlags flags) const162 bool IRegistry::Empty(TFlags flags) const
163 {
164 x_CheckFlags("IRegistry::Empty", flags, fLayerFlags);
165 if ( !(flags & fTPFlags) ) {
166 flags |= fTPFlags;
167 }
168 TReadGuard LOCK(*this);
169 return x_Empty(flags);
170 }
171
172
Modified(TFlags flags) const173 bool IRegistry::Modified(TFlags flags) const
174 {
175 x_CheckFlags("IRegistry::Modified", flags, fLayerFlags);
176 if ( !(flags & fTransient) ) {
177 flags |= fPersistent;
178 }
179 TReadGuard LOCK(*this);
180 return x_Modified(flags);
181 }
182
183
SetModifiedFlag(bool modified,TFlags flags)184 void IRegistry::SetModifiedFlag(bool modified, TFlags flags)
185 {
186 x_CheckFlags("IRegistry::SetModifiedFlag", flags, fLayerFlags);
187 if ( !(flags & fTransient) ) {
188 flags |= fPersistent;
189 }
190 TReadGuard LOCK(*this); // Treat the flag as semi-mutable
191 x_SetModifiedFlag(modified, flags);
192 }
193
194
195 // Somewhat inefficient, but that can't really be helped....
Write(CNcbiOstream & os,TFlags flags) const196 bool IRegistry::Write(CNcbiOstream& os, TFlags flags) const
197 {
198 x_CheckFlags("IRegistry::Write", flags,
199 (TFlags)fLayerFlags | fInternalSpaces | fCountCleared
200 | fSectionlessEntries);
201
202 if ( !(flags & fTransient) ) {
203 flags |= fPersistent;
204 }
205 if ( !(flags & fNotJustCore) ) {
206 flags |= fJustCore;
207 }
208 TReadGuard LOCK(*this);
209
210 // Write file comment
211 if ( !s_WriteComment(os, GetComment(kEmptyStr, kEmptyStr, flags) + "\n") )
212 return false;
213
214 list<string> sections;
215 EnumerateSections(§ions, flags);
216
217 ITERATE (list<string>, section, sections) {
218 if ( !s_WriteComment(os, GetComment(*section, kEmptyStr, flags)) ) {
219 return false;
220 }
221 if(!(section->empty()))
222 os << '[' << *section << ']' << Endl();
223 if ( !os ) {
224 return false;
225 }
226 list<string> entries;
227 EnumerateEntries(*section, &entries, flags);
228 ITERATE (list<string>, entry, entries) {
229 s_WriteComment(os, GetComment(*section, *entry, flags));
230 // XXX - produces output that older versions can't handle
231 // when the value contains control characters other than
232 // CR (\r) or LF (\n).
233 os << *entry << " = \""
234 << Printable(Get(*section, *entry, flags)) << "\""
235 << Endl();
236 if ( !os ) {
237 return false;
238 }
239 }
240 // Make a line break between section entries and next section
241 // (or optional in-section comments)
242 os << Endl();
243 // Write in-section comment
244 list<string> in_section_comments;
245 EnumerateInSectionComments(*section, &in_section_comments, flags);
246 ITERATE(list<string>, comment, in_section_comments) {
247 s_WriteComment(os, *comment + "\n");
248 }
249 }
250
251 // Clear the modified bit (checking it first so as to perform the
252 // const_cast<> only if absolutely necessary).
253 if (Modified(flags & fLayerFlags)) {
254 const_cast<IRegistry*>(this)->SetModifiedFlag
255 (false, flags & fLayerFlags);
256 }
257
258 return true;
259 }
260
261
Get(const string & section,const string & name,TFlags flags) const262 const string& IRegistry::Get(const string& section, const string& name,
263 TFlags flags) const
264 {
265 if (flags & fInternalCheckedAndLocked) return x_Get(section, name, flags);
266
267 x_CheckFlags("IRegistry::Get", flags,
268 (TFlags)fLayerFlags | fInternalSpaces | fSectionlessEntries);
269
270 if ( !(flags & fTPFlags) ) {
271 flags |= fTPFlags;
272 }
273 string clean_section = NStr::TruncateSpaces(section);
274 if ( !IsNameSection(clean_section, flags) ) {
275 _TRACE("IRegistry::Get: bad section name \""
276 << NStr::PrintableString(section) << '\"');
277 return kEmptyStr;
278 }
279 string clean_name = NStr::TruncateSpaces(name);
280 if ( !IsNameEntry(clean_name, flags) ) {
281 _TRACE("IRegistry::Get: bad entry name \""
282 << NStr::PrintableString(name) << '\"');
283 return kEmptyStr;
284 }
285 TReadGuard LOCK(*this);
286 return x_Get(clean_section, clean_name, flags | fInternalCheckedAndLocked);
287 }
288
289
HasEntry(const string & section,const string & name,TFlags flags) const290 bool IRegistry::HasEntry(const string& section, const string& name,
291 TFlags flags) const
292 {
293 if (flags & fInternalCheckedAndLocked) return x_HasEntry(section, name, flags);
294
295 x_CheckFlags("IRegistry::HasEntry", flags,
296 (TFlags)fLayerFlags | fInternalSpaces | fCountCleared
297 | fSections | fSectionlessEntries);
298
299 if ( !(flags & fTPFlags) ) {
300 flags |= fTPFlags;
301 }
302 string clean_section = NStr::TruncateSpaces(section);
303 if ( !IsNameSection(clean_section, flags) ) {
304 _TRACE("IRegistry::HasEntry: bad section name \""
305 << NStr::PrintableString(section) << '\"');
306 return false;
307 }
308 string clean_name = NStr::TruncateSpaces(name);
309 bool is_special_name = clean_name.empty() ||
310 clean_name == sm_InSectionCommentName;
311 if ( !is_special_name && !IsNameEntry(clean_name, flags) ) {
312 _TRACE("IRegistry::HasEntry: bad entry name \""
313 << NStr::PrintableString(name) << '\"');
314 return false;
315 }
316 TReadGuard LOCK(*this);
317 return x_HasEntry(clean_section, clean_name, flags | fInternalCheckedAndLocked);
318 }
319
320
GetString(const string & section,const string & name,const string & default_value,TFlags flags) const321 string IRegistry::GetString(const string& section, const string& name,
322 const string& default_value, TFlags flags) const
323 {
324 const string& value = Get(section, name, flags);
325 return value.empty() ? default_value : value;
326 }
327
328
GetEncryptedString(const string & section,const string & name,TFlags flags,const string & password) const329 string IRegistry::GetEncryptedString(const string& section, const string& name,
330 TFlags flags, const string& password)
331 const
332 {
333 string clean_section = NStr::TruncateSpaces(section);
334 string clean_name = NStr::TruncateSpaces(name);
335 const string& raw_value = Get(clean_section, clean_name,
336 flags & ~fPlaintextAllowed);
337
338 if (CNcbiEncrypt::IsEncrypted(raw_value)) {
339 try {
340 if (password.empty()) {
341 return CNcbiEncrypt::Decrypt(raw_value);
342 } else {
343 return CNcbiEncrypt::Decrypt(raw_value, password);
344 }
345 } catch (CException& e) {
346 NCBI_RETHROW2(e, CRegistryException, eDecryptionFailed,
347 "Decryption failed for configuration value ["
348 + clean_section + "] " + clean_name + '.',
349 0);
350 }
351 } else if ( !raw_value.empty() && (flags & fPlaintextAllowed) == 0) {
352 NCBI_THROW2(CRegistryException, eUnencrypted,
353 "Configuration value for [" + clean_section + "] "
354 + clean_name + " should have been encrypted but wasn't.",
355 0);
356 } else {
357 return raw_value;
358 }
359 }
360
361
GetInt(const string & section,const string & name,int default_value,TFlags flags,EErrAction err_action) const362 int IRegistry::GetInt(const string& section, const string& name,
363 int default_value, TFlags flags, EErrAction err_action)
364 const
365 {
366 const string& value = Get(section, name, flags);
367 if (value.empty()) {
368 return default_value;
369 }
370
371 try {
372 return NStr::StringToInt(value);
373 } catch (CStringException& ex) {
374 if (err_action == eReturn) {
375 return default_value;
376 }
377
378 string msg = "IRegistry::GetInt(): [" + section + ']' + name;
379
380 if (err_action == eThrow) {
381 NCBI_RETHROW_SAME(ex, msg);
382 } else if (err_action == eErrPost) {
383 ERR_POST_X(1, ex.what() << msg);
384 }
385
386 return default_value;
387 }
388 }
389
390
GetBool(const string & section,const string & name,bool default_value,TFlags flags,EErrAction err_action) const391 bool IRegistry::GetBool(const string& section, const string& name,
392 bool default_value, TFlags flags,
393 EErrAction err_action) const
394 {
395 const string& value = Get(section, name, flags);
396 if (value.empty()) {
397 return default_value;
398 }
399
400 try {
401 return NStr::StringToBool(value);
402 } catch (CStringException& ex) {
403 if (err_action == eReturn) {
404 return default_value;
405 }
406
407 string msg = "IRegistry::GetBool(): [" + section + ']' + name;
408
409 if (err_action == eThrow) {
410 NCBI_RETHROW_SAME(ex, msg);
411 } else if (err_action == eErrPost) {
412 ERR_POST_X(2, ex.what() << msg);
413 }
414
415 return default_value;
416 }
417 }
418
419
GetDouble(const string & section,const string & name,double default_value,TFlags flags,EErrAction err_action) const420 double IRegistry::GetDouble(const string& section, const string& name,
421 double default_value, TFlags flags,
422 EErrAction err_action) const
423 {
424 const string& value = Get(section, name, flags);
425 if (value.empty()) {
426 return default_value;
427 }
428
429 try {
430 return NStr::StringToDouble(value, NStr::fDecimalPosixOrLocal);
431 } catch (CStringException& ex) {
432 if (err_action == eReturn) {
433 return default_value;
434 }
435
436 string msg = "IRegistry::GetDouble()";
437 msg += " Reg entry:" + section + ":" + name;
438
439 if (err_action == eThrow) {
440 NCBI_RETHROW_SAME(ex, msg);
441 } else if (err_action == eErrPost) {
442 ERR_POST_X(3, ex.what() << msg);
443 }
444
445 return default_value;
446 }
447 }
448
449
GetComment(const string & section,const string & name,TFlags flags) const450 const string& IRegistry::GetComment(const string& section, const string& name,
451 TFlags flags) const
452 {
453 x_CheckFlags("IRegistry::GetComment", flags,
454 (TFlags)fLayerFlags | fInternalSpaces | fSectionlessEntries);
455 string clean_section = NStr::TruncateSpaces(section);
456 if ( !clean_section.empty() && !IsNameSection(clean_section, flags) ) {
457 _TRACE("IRegistry::GetComment: bad section name \""
458 << NStr::PrintableString(section) << '\"');
459 return kEmptyStr;
460 }
461 string clean_name = NStr::TruncateSpaces(name);
462 bool is_special_name = clean_name.empty() ||
463 clean_name == sm_InSectionCommentName;
464 if ( !is_special_name && !IsNameSection(clean_name, flags) ) {
465 _TRACE("IRegistry::GetComment: bad entry name \""
466 << NStr::PrintableString(name) << '\"');
467 return kEmptyStr;
468 }
469 TReadGuard LOCK(*this);
470 return x_GetComment(clean_section, clean_name, flags);
471 }
472
473
EnumerateInSectionComments(const string & section,list<string> * comments,TFlags flags) const474 void IRegistry::EnumerateInSectionComments(const string& section,
475 list<string>* comments,
476 TFlags flags) const
477 {
478 x_CheckFlags("IRegistry::EnumerateInSectionComments", flags,
479 (TFlags)fLayerFlags);
480
481 if (!(flags & fTPFlags)) {
482 flags |= fTPFlags;
483 }
484 _ASSERT(comments);
485 comments->clear();
486 string clean_section = NStr::TruncateSpaces(section);
487 if (clean_section.empty() || !IsNameSection(clean_section, flags)) {
488 _TRACE("IRegistry::EnumerateInSectionComments: bad section name \""
489 << NStr::PrintableString(section) << '\"');
490 return;
491 }
492 TReadGuard LOCK(*this);
493 x_Enumerate(clean_section, *comments, flags | fInSectionComments);
494 }
495
496
EnumerateSections(list<string> * sections,TFlags flags) const497 void IRegistry::EnumerateSections(list<string>* sections, TFlags flags) const
498 {
499 // Should clear fSectionlessEntries if set
500 x_CheckFlags("IRegistry::EnumerateSections", flags,
501 (TFlags)fLayerFlags | fInternalSpaces | fCountCleared
502 | fSectionlessEntries);
503
504 if ( !(flags & fTPFlags) ) {
505 flags |= fTPFlags;
506 }
507 _ASSERT(sections);
508 sections->clear();
509 TReadGuard LOCK(*this);
510 x_Enumerate(kEmptyStr, *sections, flags | fSections);
511 }
512
513
EnumerateEntries(const string & section,list<string> * entries,TFlags flags) const514 void IRegistry::EnumerateEntries(const string& section, list<string>* entries,
515 TFlags flags) const
516 {
517 x_CheckFlags("IRegistry::EnumerateEntries", flags,
518 (TFlags)fLayerFlags | fInternalSpaces | fCountCleared
519 | fSectionlessEntries | fSections);
520
521 if ( !(flags & fTPFlags) ) {
522 flags |= fTPFlags;
523 }
524 _ASSERT(entries);
525 entries->clear();
526 string clean_section = NStr::TruncateSpaces(section);
527 if ( !clean_section.empty() && !IsNameSection(clean_section, flags) ) {
528 _TRACE("IRegistry::EnumerateEntries: bad section name \""
529 << NStr::PrintableString(section) << '\"');
530 return;
531 }
532 TReadGuard LOCK(*this);
533 x_Enumerate(clean_section, *entries, flags);
534 }
535
536
ReadLock(void)537 void IRegistry::ReadLock (void)
538 {
539 x_ChildLockAction(&IRegistry::ReadLock);
540 m_Lock.ReadLock();
541 }
542
543
WriteLock(void)544 void IRegistry::WriteLock(void)
545 {
546 x_ChildLockAction(&IRegistry::WriteLock);
547 m_Lock.WriteLock();
548 }
549
550
Unlock(void)551 void IRegistry::Unlock(void)
552 {
553 m_Lock.Unlock();
554 x_ChildLockAction(&IRegistry::Unlock);
555 }
556
557
x_CheckFlags(const string & _DEBUG_ARG (func),TFlags & flags,TFlags allowed)558 void IRegistry::x_CheckFlags(const string& _DEBUG_ARG(func),
559 TFlags& flags, TFlags allowed)
560 {
561 if (flags & ~allowed)
562 _TRACE(func << "(): extra flags passed: "
563 << resetiosflags(IOS_BASE::basefield)
564 << setiosflags(IOS_BASE::hex | IOS_BASE::showbase)
565 << flags);
566 flags &= allowed;
567 }
568
569
570 //////////////////////////////////////////////////////////////////////
571 //
572 // IRWRegistry
573
AssessImpact(TFlags flags,EOperation op)574 IRegistry::TFlags IRWRegistry::AssessImpact(TFlags flags, EOperation op)
575 {
576 // mask out irrelevant flags
577 flags &= fLayerFlags | fTPFlags;
578 switch (op) {
579 case eClear:
580 return flags;
581 case eRead:
582 case eSet:
583 return ((flags & fTransient) ? fTransient : fPersistent) | fJustCore;
584 default:
585 _TROUBLE;
586 return flags;
587 }
588 }
589
Clear(TFlags flags)590 void IRWRegistry::Clear(TFlags flags)
591 {
592 x_CheckFlags("IRWRegistry::Clear", flags,
593 (TFlags)fLayerFlags | fInternalSpaces);
594 TWriteGuard LOCK(*this);
595 if ( (flags & fPersistent) && !x_Empty(fPersistent) ) {
596 x_SetModifiedFlag(true, flags & ~fTransient);
597 }
598 if ( (flags & fTransient) && !x_Empty(fTransient) ) {
599 x_SetModifiedFlag(true, flags & ~fPersistent);
600 }
601 x_Clear(flags);
602 }
603
604
Read(CNcbiIstream & is,TFlags flags,const string & path)605 IRWRegistry* IRWRegistry::Read(CNcbiIstream& is, TFlags flags,
606 const string& path)
607 {
608 x_CheckFlags("IRWRegistry::Read", flags,
609 fTransient | fNoOverride | fIgnoreErrors | fInternalSpaces
610 | fWithNcbirc | fJustCore | fCountCleared
611 | fSectionlessEntries);
612
613 if ( !is ) {
614 return NULL;
615 }
616
617 // Ensure that x_Read gets a stream it can handle.
618 EEncodingForm ef = GetTextEncodingForm(is, eBOM_Discard);
619 if (ef == eEncodingForm_Utf16Native || ef == eEncodingForm_Utf16Foreign) {
620 CStringUTF8 s;
621 ReadIntoUtf8(is, &s, ef);
622 CNcbiIstrstream iss(s);
623 return x_Read(iss, flags, path);
624 } else {
625 return x_Read(is, flags, path);
626 }
627 }
628
629
x_Read(CNcbiIstream & is,TFlags flags,const string & path)630 IRWRegistry* IRWRegistry::x_Read(CNcbiIstream& is, TFlags flags,
631 const string& path)
632 {
633 // Whether to consider this read to be (unconditionally) non-modifying
634 TFlags layer = (flags & fTransient) ? fTransient : fPersistent;
635 TFlags impact = layer | fJustCore;
636 bool was_empty = Empty(impact);
637 bool non_modifying = was_empty && !Modified(impact);
638 bool ignore_errors = (flags & fIgnoreErrors) > 0;
639
640 // Adjust flags for Set() fSectionlessEntries must survive this change
641 flags = (flags & ~fTPFlags & ~fIgnoreErrors) | layer;
642
643 string str; // the line being parsed
644 SIZE_TYPE line; // # of the line being parsed
645 string section(kEmptyStr); // current section name
646 string comment; // current comment
647 string in_path = path.empty() ? kEmptyStr : (" in " + path);
648 // To collect in-section comments we need a special container, because
649 // these comments may be multiline with line breaks in the middle
650 map<string, string> in_section_comments;
651
652 for (line = 1; NcbiGetlineEOL(is, str); ++line) {
653 try {
654 SIZE_TYPE len = str.length();
655 SIZE_TYPE beg = 0;
656
657 while (beg < len && isspace((unsigned char) str[beg])) {
658 ++beg;
659 }
660 // If this line is empty, all comments
661 // that have just been read go to the current section.
662 if (beg == len) {
663 if (!comment.empty() && !section.empty()) {
664 in_section_comments[section] += comment + "\n";
665 SetComment(NStr::TruncateSpaces(
666 in_section_comments[section]), section,
667 sm_InSectionCommentName, flags | fCountCleared);
668 comment.erase();
669 }
670 continue;
671 }
672
673 switch (str[beg]) {
674
675 case '#': { // file comment
676 SetComment(GetComment() + str + '\n');
677 break;
678 }
679
680 case ';': { // section or entry comment
681 comment += str;
682 comment += '\n';
683 // If there is end of file next, all comments
684 // that have just been read go to the current section.
685 if (is.peek() == EOF && !section.empty()) {
686 SetComment(comment, section, sm_InSectionCommentName,
687 flags | fCountCleared);
688 comment.erase();
689 continue;
690 }
691 break;
692 }
693
694 case '[': { // section name
695 ++beg;
696 SIZE_TYPE end = str.find_first_of(']', beg + 1);
697 if (end == NPOS) {
698 NCBI_THROW2(CRegistryException, eSection,
699 "Invalid registry section" + in_path
700 + " (']' is missing): `" + str + "'", line);
701 }
702 section = NStr::TruncateSpaces(str.substr(beg, end - beg));
703 if (section.empty()) {
704 NCBI_THROW2(CRegistryException, eSection,
705 "Unnamed registry section" + in_path + ": `"
706 + str + "'", line);
707 } else if ( !IsNameSection(section, flags) ) {
708 NCBI_THROW2(CRegistryException, eSection,
709 "Invalid registry section name" + in_path
710 + ": `" + str + "'", line);
711 }
712 // Unconditional, to ensure the section becomes known
713 // even if it has no comment or contents
714 SetComment(comment, section, kEmptyStr, flags | fCountCleared);
715 comment.erase();
716 break;
717 }
718
719 default: { // regular entry
720 string name, value;
721 if ( !NStr::SplitInTwo(str, "=", name, value) ) {
722 NCBI_THROW2(CRegistryException, eEntry,
723 "Invalid registry entry format" + in_path
724 + ": '" + str + "'", line);
725 }
726 NStr::TruncateSpacesInPlace(name);
727 if ( !IsNameEntry(name, flags) ) {
728 NCBI_THROW2(CRegistryException, eEntry,
729 "Invalid registry entry name" + in_path + ": '"
730 + str + "'", line);
731 }
732
733 NStr::TruncateSpacesInPlace(value);
734 #if 0 // historic behavior; could inappropriately expose entries in lower layers
735 if (value.empty()) {
736 if ( !(flags & fNoOverride) ) {
737 Set(section, name, kEmptyStr, flags, comment);
738 comment.erase();
739 }
740 break;
741 }
742 #endif
743 // read continuation lines, if any
744 string cont;
745 while (s_Backslashed(value, value.size())
746 && NcbiGetlineEOL(is, cont)) {
747 ++line;
748 value[value.size() - 1] = '\n';
749 value += NStr::TruncateSpaces(cont);
750 str += 'n' + cont; // for presentation in exceptions
751 }
752
753 // Historically, " may appear unescaped at the beginning,
754 // end, both, or neither.
755 beg = 0;
756 SIZE_TYPE end = value.size();
757 for (SIZE_TYPE pos = value.find('\"');
758 pos < end && pos != NPOS;
759 pos = value.find('\"', pos + 1)) {
760 if (s_Backslashed(value, pos)) {
761 continue;
762 } else if (pos == beg) {
763 ++beg;
764 } else if (pos == end - 1) {
765 --end;
766 } else {
767 NCBI_THROW2(CRegistryException, eValue,
768 "Single(unescaped) '\"' in the middle "
769 "of registry value" + in_path + ": '"
770 + str + "'", line);
771 }
772 }
773
774 try {
775 value = NStr::ParseEscapes(value.substr(beg, end - beg));
776 } catch (CStringException&) {
777 NCBI_THROW2(CRegistryException, eValue,
778 "Badly placed '\\' in the registry value"
779 + in_path + ": '" + str + "'", line);
780
781 }
782 TFlags set_flags = flags & ~fJustCore;
783 if (NStr::EqualNocase(section, "NCBI")
784 && NStr::EqualNocase(name, ".Inherits")
785 && HasEntry(section, name, flags)) {
786 const string& old_value = Get(section, name, flags);
787 if (flags & fNoOverride) {
788 value = old_value + ' ' + value;
789 set_flags &= ~fNoOverride;
790 } else {
791 value += ' ';
792 value += old_value;
793 }
794 } else if (was_empty && HasEntry(section, name, flags)) {
795 ERR_POST_X(8, Warning
796 << "Found multiple [" << section << "] "
797 << name << " settings" << in_path
798 << "; using the one from line " << line);
799 }
800 Set(section, name, value, set_flags, comment);
801 comment.erase();
802 }
803 }
804 } catch (exception& e) {
805 if (ignore_errors) {
806 ERR_POST_X(4, e.what());
807 } else {
808 throw;
809 }
810 }
811 }
812
813 if ( !is.eof() ) {
814 ERR_POST_X(4, "Error reading the registry after line " << line
815 << in_path << ": " << str);
816 }
817
818 if ( non_modifying ) {
819 SetModifiedFlag(false, impact);
820 }
821
822 return NULL;
823 }
824
825
Set(const string & section,const string & name,const string & value,TFlags flags,const string & comment)826 bool IRWRegistry::Set(const string& section, const string& name,
827 const string& value, TFlags flags,
828 const string& comment)
829 {
830 x_CheckFlags("IRWRegistry::Set", flags,
831 fPersistent | fNoOverride | fTruncate | fInternalSpaces
832 | fCountCleared | fSectionlessEntries);
833 string clean_section = NStr::TruncateSpaces(section);
834 if ( !IsNameSection(clean_section, flags) ) {
835 _TRACE("IRWRegistry::Set: bad section name \""
836 << NStr::PrintableString(section) << '\"');
837 return false;
838 }
839 string clean_name = NStr::TruncateSpaces(name);
840 if ( !IsNameEntry(clean_name, flags) ) {
841 _TRACE("IRWRegistry::Set: bad entry name \""
842 << NStr::PrintableString(name) << '\"');
843 return false;
844 }
845 SIZE_TYPE beg = 0, end = value.size();
846 if (flags & fTruncate) {
847 // don't use TruncateSpaces, since newlines should stay
848 beg = value.find_first_not_of(" \r\t\v");
849 end = value.find_last_not_of (" \r\t\v");
850 if (beg == NPOS) {
851 _ASSERT(end == NPOS);
852 beg = 1;
853 end = 0;
854 }
855 }
856 TWriteGuard LOCK(*this);
857 if (x_Set(clean_section, clean_name, value.substr(beg, end - beg + 1),
858 flags, s_ConvertComment(comment, section.empty()))) {
859 x_SetModifiedFlag(true, flags);
860 return true;
861 } else {
862 return false;
863 }
864 }
865
866
Unset(const string & section,const string & name,TFlags flags)867 bool IRWRegistry::Unset(const string& section, const string& name,
868 TFlags flags)
869 {
870 x_CheckFlags("IRWRegistry::Unset", flags,
871 fTPFlags | fCountCleared | fSectionlessEntries);
872 string clean_section = NStr::TruncateSpaces(section);
873 if ( !IsNameSection(clean_section, flags) ) {
874 _TRACE("IRWRegistry::Unset: bad section name \""
875 << NStr::PrintableString(section) << '\"');
876 return false;
877 }
878 string clean_name = NStr::TruncateSpaces(name);
879 if ( !IsNameEntry(clean_name, flags) ) {
880 _TRACE("IRWRegistry::Unset: bad entry name \""
881 << NStr::PrintableString(name) << '\"');
882 return false;
883 }
884 TWriteGuard LOCK(*this);
885 if (x_Unset(clean_section, clean_name, flags)) {
886 x_SetModifiedFlag(true, flags);
887 return true;
888 } else {
889 return false;
890 }
891 }
892
893
SetComment(const string & comment,const string & section,const string & name,TFlags flags)894 bool IRWRegistry::SetComment(const string& comment, const string& section,
895 const string& name, TFlags flags)
896 {
897 x_CheckFlags("IRWRegistry::SetComment", flags,
898 fTransient | fNoOverride | fInternalSpaces | fCountCleared);
899 string clean_section = NStr::TruncateSpaces(section);
900 if ( !clean_section.empty() && !IsNameSection(clean_section, flags) ) {
901 _TRACE("IRWRegistry::SetComment: bad section name \""
902 << NStr::PrintableString(section) << '\"');
903 return false;
904 }
905 string clean_name = NStr::TruncateSpaces(name);
906 bool is_special_name = clean_name.empty() ||
907 clean_name == sm_InSectionCommentName;
908 if ( !is_special_name && !IsNameEntry(clean_name, flags) ) {
909 _TRACE("IRWRegistry::SetComment: bad entry name \""
910 << NStr::PrintableString(name) << '\"');
911 return false;
912 }
913 TWriteGuard LOCK(*this);
914 if (x_SetComment(s_ConvertComment(comment, section.empty()),
915 clean_section, clean_name, flags)) {
916 x_SetModifiedFlag(true, fPersistent);
917 return true;
918 } else {
919 return false;
920 }
921 }
922
923
MaybeSet(string & target,const string & value,TFlags flags)924 bool IRWRegistry::MaybeSet(string& target, const string& value, TFlags flags)
925 {
926 if (target.empty()) {
927 target = value;
928 /* "return !value.empty()" was here before, which prevented to set
929 comments to empty values, but now empty string is
930 considered a value, not an unset variable, so we always return
931 true */
932 return true;
933 } else if ( !(flags & fNoOverride) ) {
934 target = value;
935 return true;
936 } else {
937 return false;
938 }
939 }
940
941
942 //////////////////////////////////////////////////////////////////////
943 //
944 // CMemoryRegistry
945
x_Empty(TFlags) const946 bool CMemoryRegistry::x_Empty(TFlags) const
947 {
948 TReadGuard LOCK(*this);
949 return m_Sections.empty() && m_RegistryComment.empty();
950 }
951
952
x_Get(const string & section,const string & name,TFlags) const953 const string& CMemoryRegistry::x_Get(const string& section, const string& name,
954 TFlags) const
955 {
956 TSections::const_iterator sit = m_Sections.find(section);
957 if (sit == m_Sections.end()) {
958 return kEmptyStr;
959 }
960 const TEntries& entries = sit->second.entries;
961 TEntries::const_iterator eit = entries.find(name);
962 return (eit == entries.end()) ? kEmptyStr : eit->second.value;
963 }
964
x_HasEntry(const string & section,const string & name,TFlags flags) const965 bool CMemoryRegistry::x_HasEntry(const string& section, const string& name,
966 TFlags flags) const
967 {
968 TSections::const_iterator sit = m_Sections.find(section);
969 if (sit == m_Sections.end()) {
970 return false;
971 } else if (name.empty()) {
972 return ((flags & fCountCleared) != 0) || !sit->second.cleared;
973 }
974 if (name == sm_InSectionCommentName) {
975 const string& inner_comment = sit->second.in_section_comment;
976 if (!inner_comment.empty()) {
977 return true;
978 } else {
979 return false;
980 }
981 }
982 const TEntries& entries = sit->second.entries;
983 TEntries::const_iterator eit = entries.find(name);
984 if (eit == entries.end()) {
985 return false;
986 } else if ((flags & fCountCleared) != 0) {
987 return true;
988 } else {
989 return !eit->second.value.empty();
990 }
991 }
992
993
x_GetComment(const string & section,const string & name,TFlags) const994 const string& CMemoryRegistry::x_GetComment(const string& section,
995 const string& name,
996 TFlags) const
997 {
998 if (section.empty()) {
999 return m_RegistryComment;
1000 }
1001 TSections::const_iterator sit = m_Sections.find(section);
1002 if (sit == m_Sections.end()) {
1003 return kEmptyStr;
1004 } else if (name.empty()) {
1005 return sit->second.comment;
1006 } else if (name == sm_InSectionCommentName) {
1007 return sit->second.in_section_comment;
1008 }
1009 const TEntries& entries = sit->second.entries;
1010 TEntries::const_iterator eit = entries.find(name);
1011 return (eit == entries.end()) ? kEmptyStr : eit->second.comment;
1012 }
1013
1014
x_Enumerate(const string & section,list<string> & entries,TFlags flags) const1015 void CMemoryRegistry::x_Enumerate(const string& section, list<string>& entries,
1016 TFlags flags) const
1017 {
1018 if (section.empty() &&
1019 ( (flags & IRegistry::fSections)
1020 || !(flags & IRegistry::fSectionlessEntries))) {
1021 // Enumerate sections
1022 if(!(flags & (IRegistry::fSections | IRegistry::fSectionlessEntries)))
1023 _TRACE("Deprecated call to x_Enumerate with empty section name, "
1024 " but with no fSections flag set");
1025
1026 ITERATE (TSections, it, m_Sections) {
1027 if (IsNameSection(it->first, flags)
1028 && HasEntry(it->first, kEmptyStr, flags)) {
1029 entries.push_back(it->first);
1030 }
1031 }
1032 } else if (flags & IRegistry::fInSectionComments) {
1033 // Enumerate in-section comments
1034 string comment = x_GetComment(section, "[]", flags);
1035 if (!comment.empty()) {
1036 entries.push_back(comment);
1037 }
1038 } else {
1039 // Enumerate entries
1040 TSections::const_iterator sit = m_Sections.find(section);
1041 if (sit != m_Sections.end()) {
1042 ITERATE (TEntries, it, sit->second.entries) {
1043 if (IsNameSection(it->first, flags)
1044 && ((flags & fCountCleared) != 0
1045 || !it->second.value.empty() )) {
1046 entries.push_back(it->first);
1047 }
1048 }
1049 }
1050 }
1051 }
1052
1053
x_Clear(TFlags)1054 void CMemoryRegistry::x_Clear(TFlags)
1055 {
1056 m_RegistryComment.erase();
1057 m_Sections.clear();
1058 }
1059
x_Set(const string & section,const string & name,const string & value,TFlags flags,const string & comment)1060 bool CMemoryRegistry::x_Set(const string& section, const string& name,
1061 const string& value, TFlags flags,
1062 const string& comment)
1063 {
1064 _TRACE(this << ": [" << section << ']' << name << " = " << value);
1065 #if 0 // historic behavior; could inappropriately expose entries in lower layers
1066 if (value.empty()) {
1067 return x_Unset(section, name, flags);
1068 } else
1069 #endif
1070 {
1071 TSections::iterator sit = m_Sections.find(section);
1072 if (sit == m_Sections.end()) {
1073 sit = m_Sections.insert(make_pair(section, SSection(m_Flags)))
1074 .first;
1075 sit->second.cleared = false;
1076 }
1077 SEntry& entry = sit->second.entries[name];
1078 #if 0
1079 if (entry.value == value) {
1080 if (entry.comment != comment) {
1081 return MaybeSet(entry.comment, comment, flags);
1082 }
1083 return false; // not actually modified
1084 }
1085 #endif
1086 if ( !value.empty() ) {
1087 sit->second.cleared = false;
1088 } else if ( !entry.value.empty() ) {
1089 _ASSERT( !sit->second.cleared );
1090 bool cleared = true;
1091 ITERATE (TEntries, eit, sit->second.entries) {
1092 if (&eit->second != &entry && !eit->second.value.empty() ) {
1093 cleared = false;
1094 break;
1095 }
1096 }
1097 sit->second.cleared = cleared;
1098 }
1099 if (MaybeSet(entry.value, value, flags)) {
1100 MaybeSet(entry.comment, comment, flags);
1101 return true;
1102 }
1103 return false;
1104 }
1105 }
1106
1107
x_Unset(const string & section,const string & name,TFlags flags)1108 bool CMemoryRegistry::x_Unset(const string& section, const string& name,
1109 TFlags flags)
1110 {
1111 _TRACE(this << ": [" << section << ']' << name << " to be unset");
1112 TSections::iterator sit = m_Sections.find(section);
1113 if (sit == m_Sections.end()) {
1114 return false;
1115 }
1116 TEntries& entries = sit->second.entries;
1117 TEntries::iterator eit = entries.find(name);
1118 if (eit == entries.end()) {
1119 return false;
1120 } else {
1121 entries.erase(eit);
1122 if (entries.empty() && sit->second.comment.empty()
1123 && (flags & fCountCleared) == 0) {
1124 m_Sections.erase(sit);
1125 }
1126 return true;
1127 }
1128 }
1129
1130
x_SetComment(const string & comment,const string & section,const string & name,TFlags flags)1131 bool CMemoryRegistry::x_SetComment(const string& comment,
1132 const string& section, const string& name,
1133 TFlags flags)
1134 {
1135 if (comment.empty() && (flags & fNoOverride)) {
1136 return false;
1137 }
1138 if (section.empty()) {
1139 return MaybeSet(m_RegistryComment, comment, flags);
1140 }
1141 TSections::iterator sit = m_Sections.find(section);
1142 if (sit == m_Sections.end()) {
1143 if (comment.empty() && (flags & fCountCleared) == 0) {
1144 return false;
1145 } else {
1146 sit = m_Sections.insert(make_pair(section, SSection(m_Flags)))
1147 .first;
1148 sit->second.cleared = false;
1149 }
1150 }
1151 TEntries& entries = sit->second.entries;
1152 string& inner_comment = sit->second.in_section_comment;
1153 string& outer_comment = sit->second.comment;
1154 if (name.empty()) {
1155 if (comment.empty() && entries.empty() && inner_comment.empty()
1156 && (flags & fCountCleared) == 0) {
1157 m_Sections.erase(sit);
1158 return true;
1159 } else {
1160 return MaybeSet(outer_comment, comment, flags);
1161 }
1162 }
1163 if (name == sm_InSectionCommentName) {
1164 if (comment.empty() && entries.empty() && outer_comment.empty()
1165 && (flags & fCountCleared) == 0) {
1166 m_Sections.erase(sit);
1167 return true;
1168 } else {
1169 return MaybeSet(inner_comment, comment, flags);
1170 }
1171 }
1172 TEntries::iterator eit = entries.find(name);
1173 if (eit == entries.end()) {
1174 return false;
1175 } else {
1176 return MaybeSet(outer_comment, comment, flags);
1177 }
1178 }
1179
1180
1181 //////////////////////////////////////////////////////////////////////
1182 //
1183 // CCompoundRegistry
1184
Add(const IRegistry & reg,TPriority prio,const string & name)1185 void CCompoundRegistry::Add(const IRegistry& reg, TPriority prio,
1186 const string& name)
1187 {
1188 // Needed for some operations that touch (only) metadata...
1189 IRegistry& nc_reg = const_cast<IRegistry&>(reg);
1190 // XXX - Check whether reg is a duplicate, at least in debug mode?
1191 m_PriorityMap.insert(TPriorityMap::value_type
1192 (prio, CRef<IRegistry>(&nc_reg)));
1193 if (name.size()) {
1194 CRef<IRegistry>& preg = m_NameMap[name];
1195 if (preg) {
1196 NCBI_THROW2(CRegistryException, eErr,
1197 "CCompoundRegistry::Add: name " + name
1198 + " already in use", 0);
1199 } else {
1200 preg.Reset(&nc_reg);
1201 }
1202 }
1203 }
1204
1205
Remove(const IRegistry & reg)1206 void CCompoundRegistry::Remove(const IRegistry& reg)
1207 {
1208 NON_CONST_ITERATE (TNameMap, it, m_NameMap) {
1209 if (it->second == ®) {
1210 m_NameMap.erase(it);
1211 break; // subregistries should be unique
1212 }
1213 }
1214 NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
1215 if (it->second == ®) {
1216 m_PriorityMap.erase(it);
1217 return; // subregistries should be unique
1218 }
1219 }
1220 // already returned if found...
1221 NCBI_THROW2(CRegistryException, eErr,
1222 "CCompoundRegistry::Remove:"
1223 " reg is not a (direct) subregistry of this.", 0);
1224 }
1225
1226
FindByName(const string & name) const1227 CConstRef<IRegistry> CCompoundRegistry::FindByName(const string& name) const
1228 {
1229 TNameMap::const_iterator it = m_NameMap.find(name);
1230 return it == m_NameMap.end() ? CConstRef<IRegistry>() : it->second;
1231 }
1232
1233
FindByContents(const string & section,const string & entry,TFlags flags) const1234 CConstRef<IRegistry> CCompoundRegistry::FindByContents(const string& section,
1235 const string& entry,
1236 TFlags flags) const
1237 {
1238 TFlags has_entry_flags = (flags | fCountCleared) & ~fJustCore;
1239 REVERSE_ITERATE(TPriorityMap, it, m_PriorityMap) {
1240 if (it->second->HasEntry(section, entry, has_entry_flags)) {
1241 return it->second;
1242 }
1243 }
1244 return null;
1245 }
1246
1247
x_Empty(TFlags flags) const1248 bool CCompoundRegistry::x_Empty(TFlags flags) const
1249 {
1250 REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
1251 if ((flags & fJustCore) && (it->first < m_CoreCutoff)) {
1252 break;
1253 }
1254 if ( !it->second->Empty(flags & ~fJustCore) ) {
1255 return false;
1256 }
1257 }
1258 return true;
1259 }
1260
1261
x_Modified(TFlags flags) const1262 bool CCompoundRegistry::x_Modified(TFlags flags) const
1263 {
1264 REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
1265 if ((flags & fJustCore) && (it->first < m_CoreCutoff)) {
1266 break;
1267 }
1268 if ( it->second->Modified(flags & ~fJustCore) ) {
1269 return true;
1270 }
1271 }
1272 return false;
1273 }
1274
1275
x_SetModifiedFlag(bool modified,TFlags flags)1276 void CCompoundRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
1277 {
1278 _ASSERT( !modified );
1279 for (TPriorityMap::reverse_iterator it = m_PriorityMap.rbegin();
1280 it != m_PriorityMap.rend(); ++it) {
1281 if ((flags & fJustCore) && (it->first < m_CoreCutoff)) {
1282 break;
1283 }
1284 it->second->SetModifiedFlag(modified, flags & ~fJustCore);
1285 }
1286 }
1287
1288
x_Get(const string & section,const string & name,TFlags flags) const1289 const string& CCompoundRegistry::x_Get(const string& section,
1290 const string& name,
1291 TFlags flags) const
1292 {
1293 CConstRef<IRegistry> reg = FindByContents(section, name,
1294 flags & ~fJustCore);
1295 return reg ? reg->Get(section, name, flags & ~fJustCore) : kEmptyStr;
1296 }
1297
1298
x_HasEntry(const string & section,const string & name,TFlags flags) const1299 bool CCompoundRegistry::x_HasEntry(const string& section, const string& name,
1300 TFlags flags) const
1301 {
1302 return FindByContents(section, name, flags).NotEmpty();
1303 }
1304
1305
x_GetComment(const string & section,const string & name,TFlags flags) const1306 const string& CCompoundRegistry::x_GetComment(const string& section,
1307 const string& name, TFlags flags)
1308 const
1309 {
1310 if ( m_PriorityMap.empty() ) {
1311 return kEmptyStr;
1312 }
1313
1314 CConstRef<IRegistry> reg;
1315 if (section.empty()) {
1316 reg = m_PriorityMap.rbegin()->second;
1317 } else {
1318 reg = FindByContents(section, name, flags & ~fJustCore);
1319 }
1320 return reg ? reg->GetComment(section, name, flags & ~fJustCore)
1321 : kEmptyStr;
1322 }
1323
1324
x_Enumerate(const string & section,list<string> & entries,TFlags flags) const1325 void CCompoundRegistry::x_Enumerate(const string& section,
1326 list<string>& entries, TFlags flags) const
1327 {
1328 set<string> accum;
1329 REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
1330 if ((flags & fJustCore) && (it->first < m_CoreCutoff)) {
1331 break;
1332 }
1333
1334 list<string> tmp;
1335
1336 if (flags & fInSectionComments) {
1337 it->second->EnumerateInSectionComments(section, &tmp,
1338 flags & ~fJustCore);
1339 } else {
1340 it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
1341 }
1342 ITERATE (list<string>, it2, tmp) {
1343 accum.insert(*it2);
1344 }
1345 }
1346 ITERATE (set<string>, it, accum) {
1347 entries.push_back(*it);
1348 }
1349 }
1350
1351
x_ChildLockAction(FLockAction action)1352 void CCompoundRegistry::x_ChildLockAction(FLockAction action)
1353 {
1354 NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
1355 ((*it->second).*action)();
1356 }
1357 }
1358
1359
1360 //////////////////////////////////////////////////////////////////////
1361 //
1362 // CTwoLayerRegistry
1363
CTwoLayerRegistry(IRWRegistry * persistent,TFlags flags)1364 CTwoLayerRegistry::CTwoLayerRegistry(IRWRegistry* persistent, TFlags flags)
1365 : m_Transient(CRegRef(new CMemoryRegistry(flags))),
1366 m_Persistent(CRegRef(persistent ? persistent
1367 : new CMemoryRegistry(flags)))
1368 {
1369 }
1370
1371
x_Empty(TFlags flags) const1372 bool CTwoLayerRegistry::x_Empty(TFlags flags) const
1373 {
1374 // mask out fTPFlags whe
1375 if (flags & fTransient && !m_Transient->Empty(flags | fTPFlags) ) {
1376 return false;
1377 } else if (flags & fPersistent
1378 && !m_Persistent->Empty(flags | fTPFlags) ) {
1379 return false;
1380 } else {
1381 return true;
1382 }
1383 }
1384
1385
x_Modified(TFlags flags) const1386 bool CTwoLayerRegistry::x_Modified(TFlags flags) const
1387 {
1388 if (flags & fTransient && m_Transient->Modified(flags | fTPFlags)) {
1389 return true;
1390 } else if (flags & fPersistent
1391 && m_Persistent->Modified(flags | fTPFlags)) {
1392 return true;
1393 } else {
1394 return false;
1395 }
1396 }
1397
1398
x_SetModifiedFlag(bool modified,TFlags flags)1399 void CTwoLayerRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
1400 {
1401 if (flags & fTransient) {
1402 m_Transient->SetModifiedFlag(modified, flags | fTPFlags);
1403 }
1404 if (flags & fPersistent) {
1405 m_Persistent->SetModifiedFlag(modified, flags | fTPFlags);
1406 }
1407 }
1408
1409
x_Get(const string & section,const string & name,TFlags flags) const1410 const string& CTwoLayerRegistry::x_Get(const string& section,
1411 const string& name, TFlags flags) const
1412 {
1413 if (flags & fTransient) {
1414 const string& result = m_Transient->Get(section, name,
1415 flags & ~fTPFlags);
1416 if ( !result.empty() || !(flags & fPersistent) ) {
1417 return result;
1418 }
1419 }
1420 return m_Persistent->Get(section, name, flags & ~fTPFlags);
1421 }
1422
1423
x_HasEntry(const string & section,const string & name,TFlags flags) const1424 bool CTwoLayerRegistry::x_HasEntry(const string& section, const string& name,
1425 TFlags flags) const
1426 {
1427 return (((flags & fTransient)
1428 && m_Transient->HasEntry(section, name, flags & ~fTPFlags)) ||
1429 ((flags & fPersistent)
1430 && m_Persistent->HasEntry(section, name, flags & ~fTPFlags)));
1431 }
1432
1433
x_GetComment(const string & section,const string & name,TFlags flags) const1434 const string& CTwoLayerRegistry::x_GetComment(const string& section,
1435 const string& name,
1436 TFlags flags) const
1437 {
1438 if (flags & fTransient) {
1439 const string& result = m_Transient->GetComment(section, name,
1440 flags & ~fTPFlags);
1441 if ( !result.empty() || !(flags & fPersistent) ) {
1442 return result;
1443 }
1444 }
1445 return m_Persistent->GetComment(section, name, flags & ~fTPFlags);
1446 }
1447
1448
x_Enumerate(const string & section,list<string> & entries,TFlags flags) const1449 void CTwoLayerRegistry::x_Enumerate(const string& section,
1450 list<string>& entries, TFlags flags) const
1451 {
1452 switch (flags & fTPFlags) {
1453 case fTransient:
1454 if (flags & fInSectionComments) {
1455 m_Transient->EnumerateInSectionComments(section, &entries,
1456 flags | fTPFlags);
1457 } else {
1458 m_Transient->EnumerateEntries(section, &entries,
1459 flags | fTPFlags);
1460 }
1461 break;
1462 case fPersistent:
1463 if (flags & fInSectionComments) {
1464 m_Persistent->EnumerateInSectionComments(section, &entries,
1465 flags | fTPFlags);
1466 } else {
1467 m_Persistent->EnumerateEntries(section, &entries,
1468 flags | fTPFlags);
1469 }
1470 break;
1471 case fTPFlags:
1472 {
1473 list<string> tl, pl;
1474 if (flags & fInSectionComments) {
1475 m_Transient ->EnumerateInSectionComments(section, &tl,
1476 flags | fTPFlags);
1477 m_Persistent->EnumerateInSectionComments(section, &pl,
1478 flags | fTPFlags);
1479 } else {
1480 m_Transient ->EnumerateEntries(section, &tl, flags | fTPFlags);
1481 m_Persistent->EnumerateEntries(section, &pl, flags | fTPFlags);
1482 }
1483 set_union(pl.begin(), pl.end(), tl.begin(), tl.end(),
1484 back_inserter(entries), PNocase());
1485 break;
1486 }
1487 default:
1488 _TROUBLE;
1489 }
1490 }
1491
1492
x_ChildLockAction(FLockAction action)1493 void CTwoLayerRegistry::x_ChildLockAction(FLockAction action)
1494 {
1495 ((*m_Transient).*action)();
1496 ((*m_Persistent).*action)();
1497 }
1498
1499
x_Clear(TFlags flags)1500 void CTwoLayerRegistry::x_Clear(TFlags flags)
1501 {
1502 if (flags & fTransient) {
1503 m_Transient->Clear(flags | fTPFlags);
1504 }
1505 if (flags & fPersistent) {
1506 m_Persistent->Clear(flags | fTPFlags);
1507 }
1508 }
1509
1510
x_Set(const string & section,const string & name,const string & value,TFlags flags,const string & comment)1511 bool CTwoLayerRegistry::x_Set(const string& section, const string& name,
1512 const string& value, TFlags flags,
1513 const string& comment)
1514 {
1515 if (flags & fPersistent) {
1516 return m_Persistent->Set(section, name, value, flags & ~fTPFlags,
1517 comment);
1518 } else {
1519 return m_Transient->Set(section, name, value, flags & ~fTPFlags,
1520 comment);
1521 }
1522 }
1523
1524
x_Unset(const string & section,const string & name,TFlags flags)1525 bool CTwoLayerRegistry::x_Unset(const string& section, const string& name,
1526 TFlags flags)
1527 {
1528 bool result = false;
1529 if ((flags & fTPFlags) != fTransient) {
1530 result |= m_Persistent->Unset(section, name, flags & ~fTPFlags);
1531 }
1532 if ((flags & fTPFlags) != fPersistent) {
1533 result |= m_Transient->Unset(section, name, flags & ~fTPFlags);
1534 }
1535 return result;
1536 }
1537
1538
x_SetComment(const string & comment,const string & section,const string & name,TFlags flags)1539 bool CTwoLayerRegistry::x_SetComment(const string& comment,
1540 const string& section, const string& name,
1541 TFlags flags)
1542 {
1543 if (flags & fTransient) {
1544 return m_Transient->SetComment(comment, section, name,
1545 flags & ~fTPFlags);
1546 } else {
1547 return m_Persistent->SetComment(comment, section, name,
1548 flags & ~fTPFlags);
1549 }
1550 }
1551
1552
1553
1554 //////////////////////////////////////////////////////////////////////
1555 //
1556 // CNcbiRegistry -- compound R/W registry with extra policy and
1557 // compatibility features. (See below for CCompoundRWRegistry,
1558 // which has been factored out.)
1559
1560 const char* CNcbiRegistry::sm_EnvRegName = ".env";
1561 const char* CNcbiRegistry::sm_FileRegName = ".file";
1562 const char* CNcbiRegistry::sm_OverrideRegName = ".overrides";
1563 const char* CNcbiRegistry::sm_SysRegName = ".ncbirc";
1564
1565 inline
x_Init(void)1566 void CNcbiRegistry::x_Init(void)
1567 {
1568 CNcbiApplication* app = CNcbiApplication::Instance();
1569 TFlags cf = m_Flags & fCaseFlags;
1570 if (app) {
1571 m_EnvRegistry.Reset(new CEnvironmentRegistry(app->SetEnvironment(),
1572 eNoOwnership, cf));
1573 } else {
1574 m_EnvRegistry.Reset(new CEnvironmentRegistry(cf));
1575 }
1576 x_Add(*m_EnvRegistry, ePriority_Environment, sm_EnvRegName);
1577
1578 m_FileRegistry.Reset(new CTwoLayerRegistry(NULL, cf));
1579 x_Add(*m_FileRegistry, ePriority_File, sm_FileRegName);
1580
1581 m_SysRegistry.Reset(new CCompoundRWRegistry(cf));
1582 x_Add(*m_SysRegistry, ePriority_Default - 1, sm_SysRegName);
1583
1584 const TXChar* xoverride_path = NcbiSys_getenv(_TX("NCBI_CONFIG_OVERRIDES"));
1585 if (xoverride_path && *xoverride_path) {
1586 string override_path = _T_STDSTRING(xoverride_path);
1587 m_OverrideRegistry.Reset(new CCompoundRWRegistry(cf));
1588 CMetaRegistry::SEntry entry
1589 = CMetaRegistry::Load(override_path, CMetaRegistry::eName_AsIs,
1590 0, cf, m_OverrideRegistry.GetPointer());
1591 if (entry.registry) {
1592 if (entry.registry != m_OverrideRegistry) {
1593 ERR_POST_X(5, Warning << "Resetting m_OverrideRegistry");
1594 m_OverrideRegistry.Reset(entry.registry);
1595 }
1596 x_Add(*m_OverrideRegistry, ePriority_Overrides, sm_OverrideRegName);
1597 } else {
1598 ERR_POST_ONCE(Warning
1599 << "NCBI_CONFIG_OVERRIDES names nonexistent file "
1600 << override_path);
1601 m_OverrideRegistry.Reset();
1602 }
1603 }
1604 }
1605
1606
CNcbiRegistry(TFlags flags)1607 CNcbiRegistry::CNcbiRegistry(TFlags flags)
1608 : m_RuntimeOverrideCount(0), m_Flags(flags)
1609 {
1610 x_Init();
1611 }
1612
1613
CNcbiRegistry(CNcbiIstream & is,TFlags flags,const string & path)1614 CNcbiRegistry::CNcbiRegistry(CNcbiIstream& is, TFlags flags,
1615 const string& path)
1616 : m_RuntimeOverrideCount(0), m_Flags(flags)
1617 {
1618 x_CheckFlags("CNcbiRegistry::CNcbiRegistry", flags,
1619 fTransient | fInternalSpaces | fWithNcbirc | fCaseFlags
1620 | fSectionlessEntries);
1621 x_Init();
1622 m_FileRegistry->Read(is, flags & ~(fWithNcbirc | fCaseFlags));
1623 LoadBaseRegistries(flags, 0, path);
1624 IncludeNcbircIfAllowed(flags & ~fCaseFlags);
1625 }
1626
1627
~CNcbiRegistry()1628 CNcbiRegistry::~CNcbiRegistry()
1629 {
1630 }
1631
1632
IncludeNcbircIfAllowed(TFlags flags)1633 bool CNcbiRegistry::IncludeNcbircIfAllowed(TFlags flags)
1634 {
1635 if (flags & fWithNcbirc) {
1636 flags &= ~fWithNcbirc;
1637 } else {
1638 return false;
1639 }
1640
1641 if (getenv("NCBI_DONT_USE_NCBIRC")) {
1642 return false;
1643 }
1644
1645 if (HasEntry("NCBI", "DONT_USE_NCBIRC")) {
1646 return false;
1647 }
1648
1649 try {
1650 CMetaRegistry::SEntry entry
1651 = CMetaRegistry::Load("ncbi", CMetaRegistry::eName_RcOrIni,
1652 0, flags, m_SysRegistry.GetPointer());
1653 if (entry.registry && entry.registry != m_SysRegistry) {
1654 ERR_POST_X(5, Warning << "Resetting m_SysRegistry");
1655 m_SysRegistry.Reset(entry.registry);
1656 }
1657 } catch (CRegistryException& e) {
1658 ERR_POST_X(6, Critical << "CNcbiRegistry: "
1659 "Syntax error in system-wide configuration file: "
1660 << e.what());
1661 return false;
1662 }
1663
1664 if ( !m_SysRegistry->Empty() ) {
1665 return true;
1666 }
1667
1668 return false;
1669 }
1670
1671
x_Clear(TFlags flags)1672 void CNcbiRegistry::x_Clear(TFlags flags) // XXX - should this do more?
1673 {
1674 CCompoundRWRegistry::x_Clear(flags);
1675 m_FileRegistry->Clear(flags);
1676 }
1677
1678
x_Read(CNcbiIstream & is,TFlags flags,const string & path)1679 IRWRegistry* CNcbiRegistry::x_Read(CNcbiIstream& is, TFlags flags,
1680 const string& path)
1681 {
1682 // Normally, all settings should go to the main portion. However,
1683 // loading an initial configuration file should instead go to the
1684 // file portion so that environment settings can take priority.
1685 CConstRef<IRegistry> main_reg(FindByName(sm_MainRegName));
1686 if (main_reg->Empty() && m_FileRegistry->Empty()) {
1687 m_FileRegistry->Read(is, flags & ~fWithNcbirc);
1688 LoadBaseRegistries(flags, 0, path);
1689 IncludeNcbircIfAllowed(flags);
1690 return NULL;
1691 } else if ((flags & fNoOverride) == 0) { // ensure proper layering
1692 CRef<CCompoundRWRegistry> crwreg
1693 (new CCompoundRWRegistry(m_Flags & fCaseFlags));
1694 crwreg->Read(is, flags);
1695 // Allow contents to override anything previously Set() directly.
1696 IRWRegistry& nc_main_reg
1697 = dynamic_cast<IRWRegistry&>(const_cast<IRegistry&>(*main_reg));
1698 if ((flags & fTransient) == 0) {
1699 flags |= fPersistent;
1700 }
1701 list<string> sections;
1702 crwreg->EnumerateSections(§ions, flags | fCountCleared);
1703 ITERATE (list<string>, sit, sections) {
1704 list<string> entries;
1705 crwreg->EnumerateEntries(*sit, &entries, flags | fCountCleared);
1706 ITERATE (list<string>, eit, entries) {
1707 // In principle, it should be possible to clear the setting
1708 // in nc_main_reg rather than duplicating it; however,
1709 // letting the entry in crwreg be visible would require
1710 // having CCompoundRegistry::FindByContents no longer force
1711 // fCountCleared, which breaks other corner cases (as shown
1712 // by test_sub_reg). :-/
1713 if (nc_main_reg.HasEntry(*sit, *eit, flags | fCountCleared)) {
1714 nc_main_reg.Set(*sit, *eit, crwreg->Get(*sit, *eit), flags);
1715 }
1716 }
1717 }
1718 ++m_RuntimeOverrideCount;
1719 x_Add(*crwreg, ePriority_RuntimeOverrides + m_RuntimeOverrideCount,
1720 sm_OverrideRegName + NStr::UIntToString(m_RuntimeOverrideCount));
1721 return crwreg.GetPointer();
1722 } else {
1723 // This will only affect the main registry, but still needs to
1724 // go through CCompoundRWRegistry::x_Set.
1725 return CCompoundRWRegistry::x_Read(is, flags, path);
1726 }
1727 }
1728
1729
x_GetComment(const string & section,const string & name,TFlags flags) const1730 const string& CNcbiRegistry::x_GetComment(const string& section,
1731 const string& name,
1732 TFlags flags) const
1733 {
1734 if (section.empty()) {
1735 return m_FileRegistry->GetComment(section, name, flags);
1736 }
1737
1738 return CCompoundRWRegistry::x_GetComment(section, name, flags);
1739 }
1740
1741
1742 //////////////////////////////////////////////////////////////////////
1743 //
1744 // CCompoundRWRegistry -- general-purpose setup
1745
1746 const char* CCompoundRWRegistry::sm_MainRegName = ".main";
1747 const char* CCompoundRWRegistry::sm_BaseRegNamePrefix = ".base:";
1748
1749
CCompoundRWRegistry(TFlags flags)1750 CCompoundRWRegistry::CCompoundRWRegistry(TFlags flags)
1751 : m_MainRegistry(new CTwoLayerRegistry),
1752 m_AllRegistries(new CCompoundRegistry),
1753 m_Flags(flags)
1754 {
1755 x_Add(*m_MainRegistry, CCompoundRegistry::ePriority_Max - 1,
1756 sm_MainRegName);
1757 }
1758
1759
~CCompoundRWRegistry()1760 CCompoundRWRegistry::~CCompoundRWRegistry()
1761 {
1762 }
1763
1764
GetCoreCutoff(void) const1765 CCompoundRWRegistry::TPriority CCompoundRWRegistry::GetCoreCutoff(void) const
1766 {
1767 return m_AllRegistries->GetCoreCutoff();
1768 }
1769
1770
SetCoreCutoff(TPriority prio)1771 void CCompoundRWRegistry::SetCoreCutoff(TPriority prio)
1772 {
1773 m_AllRegistries->SetCoreCutoff(prio);
1774 }
1775
1776
Add(const IRegistry & reg,TPriority prio,const string & name)1777 void CCompoundRWRegistry::Add(const IRegistry& reg, TPriority prio,
1778 const string& name)
1779 {
1780 if (name.size() > 1 && name[0] == '.') {
1781 NCBI_THROW2(CRegistryException, eErr,
1782 "The sub-registry name " + name + " is reserved.", 0);
1783 }
1784 if (prio > ePriority_MaxUser) {
1785 ERR_POST_X(7, Warning
1786 << "Reserved priority value automatically downgraded.");
1787 prio = ePriority_MaxUser;
1788 }
1789 x_Add(reg, prio, name);
1790 }
1791
1792
Remove(const IRegistry & reg)1793 void CCompoundRWRegistry::Remove(const IRegistry& reg)
1794 {
1795 if (® == m_MainRegistry.GetPointer()) {
1796 NCBI_THROW2(CRegistryException, eErr,
1797 "The primary portion of the registry may not be removed.",
1798 0);
1799 } else {
1800 m_AllRegistries->Remove(reg);
1801 }
1802 }
1803
1804
FindByName(const string & name) const1805 CConstRef<IRegistry> CCompoundRWRegistry::FindByName(const string& name) const
1806 {
1807 return m_AllRegistries->FindByName(name);
1808 }
1809
1810
FindByContents(const string & section,const string & entry,TFlags flags) const1811 CConstRef<IRegistry> CCompoundRWRegistry::FindByContents(const string& section,
1812 const string& entry,
1813 TFlags flags) const
1814 {
1815 return m_AllRegistries->FindByContents(section, entry, flags);
1816 }
1817
1818
LoadBaseRegistries(TFlags flags,int metareg_flags,const string & path)1819 bool CCompoundRWRegistry::LoadBaseRegistries(TFlags flags, int metareg_flags,
1820 const string& path)
1821 {
1822 if (flags & fJustCore) {
1823 return false;
1824 }
1825
1826 list<string> names;
1827 {{
1828 string s = m_MainRegistry->Get("NCBI", ".Inherits");
1829
1830 REVERSE_ITERATE(CCompoundRegistry::TPriorityMap, it, m_AllRegistries->m_PriorityMap) {
1831 s += ',';
1832 s += it->second->Get("NCBI", ".Inherits");
1833 }
1834
1835 {
1836 if (dynamic_cast<CNcbiRegistry*>(this) != NULL) {
1837 _TRACE("LoadBaseRegistries(" << this
1838 << "): trying file registry");
1839 s += ',';
1840 s += FindByName(CNcbiRegistry::sm_FileRegName)
1841 ->Get("NCBI", ".Inherits");
1842 }
1843 }
1844 NStr::Split(s, ", ", names,
1845 NStr::fSplit_CanSingleQuote |
1846 NStr::fSplit_MergeDelimiters |
1847 NStr::fSplit_Truncate);
1848 if (names.empty()) return false;
1849 _TRACE("LoadBaseRegistries(" << this << "): using " << s);
1850 }}
1851
1852 typedef pair<string, CRef<IRWRegistry> > TNewBase;
1853 typedef vector<TNewBase> TNewBases;
1854 TNewBases bases;
1855 SIZE_TYPE initial_num_bases = m_BaseRegNames.size();
1856
1857 ITERATE (list<string>, it, names) {
1858 if (m_BaseRegNames.find(*it) != m_BaseRegNames.end()) {
1859 continue;
1860 }
1861 CRef<CCompoundRWRegistry> reg2
1862 (new CCompoundRWRegistry(m_Flags & fCaseFlags));
1863 // First try adding .ini unless it's already present; when a
1864 // file with the unsuffixed name also exists, it is likely an
1865 // executable that would be inappropriate to try to parse.
1866 CMetaRegistry::SEntry entry2;
1867 if (NStr::EndsWith(*it, ".ini")) {
1868 entry2.registry = NULL;
1869 } else {
1870 entry2 = CMetaRegistry::Load(*it, CMetaRegistry::eName_Ini,
1871 metareg_flags, flags,
1872 reg2.GetPointer(), path);
1873 }
1874 if (entry2.registry == NULL) {
1875 entry2 = CMetaRegistry::Load(*it, CMetaRegistry::eName_AsIs,
1876 metareg_flags, flags,
1877 reg2.GetPointer(), path);
1878 }
1879 if (entry2.registry) {
1880 m_BaseRegNames.insert(*it);
1881 bases.push_back(TNewBase(*it, entry2.registry));
1882 } else {
1883 ERR_POST(Critical << "Base registry " << *it
1884 << " absent or unreadable");
1885 }
1886 }
1887
1888 for (SIZE_TYPE i = 0; i < bases.size(); ++i) {
1889 x_Add(*bases[i].second,
1890 TPriority(ePriority_MaxUser - initial_num_bases - i),
1891 sm_BaseRegNamePrefix + bases[i].first);
1892 }
1893
1894 return !bases.empty();
1895 }
1896
1897
x_Empty(TFlags flags) const1898 bool CCompoundRWRegistry::x_Empty(TFlags flags) const
1899 {
1900 return m_AllRegistries->Empty(flags);
1901 }
1902
1903
x_Modified(TFlags flags) const1904 bool CCompoundRWRegistry::x_Modified(TFlags flags) const
1905 {
1906 return m_AllRegistries->Modified(flags);
1907 }
1908
1909
x_SetModifiedFlag(bool modified,TFlags flags)1910 void CCompoundRWRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
1911 {
1912 if (modified) {
1913 m_MainRegistry->SetModifiedFlag(modified, flags);
1914 } else {
1915 // CCompoundRegistry only permits clearing...
1916 m_AllRegistries->SetModifiedFlag(modified, flags);
1917 }
1918 }
1919
1920
x_Get(const string & section,const string & name,TFlags flags) const1921 const string& CCompoundRWRegistry::x_Get(const string& section,
1922 const string& name,
1923 TFlags flags) const
1924 {
1925 TClearedEntries::const_iterator it
1926 = m_ClearedEntries.find(s_FlatKey(section, name));
1927 if (it != m_ClearedEntries.end()) {
1928 flags &= ~it->second;
1929 if ( !(flags & ~fJustCore) ) {
1930 return kEmptyStr;
1931 }
1932 }
1933 return m_AllRegistries->Get(section, name, flags);
1934 }
1935
1936
x_HasEntry(const string & section,const string & name,TFlags flags) const1937 bool CCompoundRWRegistry::x_HasEntry(const string& section, const string& name,
1938 TFlags flags) const
1939 {
1940 TClearedEntries::const_iterator it
1941 = m_ClearedEntries.find(s_FlatKey(section, name));
1942 if (it != m_ClearedEntries.end()) {
1943 if ((flags & fCountCleared) && (flags & it->second)) {
1944 return true;
1945 }
1946 flags &= ~it->second;
1947 if ( !(flags & ~fJustCore) ) {
1948 return false;
1949 }
1950 }
1951 return m_AllRegistries->HasEntry(section, name, flags);
1952 }
1953
1954
x_GetComment(const string & section,const string & name,TFlags flags) const1955 const string& CCompoundRWRegistry::x_GetComment(const string& section,
1956 const string& name,
1957 TFlags flags) const
1958 {
1959 const string* result;
1960 if (section.empty() || name.empty()) {
1961 result = &(m_MainRegistry->GetComment(section, name, flags));
1962 if (result->empty()) {
1963 auto reg = FindByName(".file");
1964 if (reg != NULL)
1965 result = &(reg->GetComment(section, name, flags));
1966 }
1967 return *result;
1968 }
1969
1970 return m_AllRegistries->GetComment(section, name, flags);
1971 }
1972
1973
x_Enumerate(const string & section,list<string> & entries,TFlags flags) const1974 void CCompoundRWRegistry::x_Enumerate(const string& section,
1975 list<string>& entries,
1976 TFlags flags) const
1977 {
1978 typedef set<string, PNocase> SetNoCase;
1979 SetNoCase accum;
1980 REVERSE_ITERATE (CCompoundRegistry::TPriorityMap, it,
1981 m_AllRegistries->m_PriorityMap) {
1982 if ((flags & fJustCore) && (it->first < GetCoreCutoff())) {
1983 break;
1984 }
1985
1986 list<string> tmp;
1987
1988 if (flags & fInSectionComments) {
1989 it->second->EnumerateInSectionComments(section, &tmp,
1990 flags & ~fJustCore);
1991 } else {
1992 it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
1993 }
1994
1995 ITERATE (list<string>, it2, tmp) {
1996 // avoid reporting cleared entries
1997 TClearedEntries::const_iterator ceci
1998 = (flags & fCountCleared) ? m_ClearedEntries.end()
1999 : m_ClearedEntries.find(s_FlatKey(section, *it2));
2000 if (ceci == m_ClearedEntries.end()
2001 || (flags & ~fJustCore & ~ceci->second)) {
2002 accum.insert(*it2);
2003 }
2004 }
2005 }
2006 ITERATE(SetNoCase, it, accum) {
2007 entries.push_back(*it);
2008 }
2009 }
2010
2011
x_ChildLockAction(FLockAction action)2012 void CCompoundRWRegistry::x_ChildLockAction(FLockAction action)
2013 {
2014 ((*m_AllRegistries).*action)();
2015 }
2016
2017
x_Clear(TFlags flags)2018 void CCompoundRWRegistry::x_Clear(TFlags flags) // XXX - should this do more?
2019 {
2020 m_MainRegistry->Clear(flags);
2021
2022 ITERATE (set<string>, it, m_BaseRegNames) {
2023 Remove(*FindByName(sm_BaseRegNamePrefix + *it));
2024 }
2025 m_BaseRegNames.clear();
2026 }
2027
2028
x_Set(const string & section,const string & name,const string & value,TFlags flags,const string & comment)2029 bool CCompoundRWRegistry::x_Set(const string& section, const string& name,
2030 const string& value, TFlags flags,
2031 const string& comment)
2032 {
2033 TFlags flags2 = (flags & fPersistent) ? flags : (flags | fTransient);
2034 flags2 &= fLayerFlags;
2035 _TRACE('[' << section << ']' << name << " = " << value);
2036 if ((flags & fNoOverride) && HasEntry(section, name, flags)) {
2037 return false;
2038 }
2039 if (value.empty()) {
2040 bool was_empty = Get(section, name, flags).empty();
2041 m_MainRegistry->Set(section, name, value, flags, comment);
2042 m_ClearedEntries[s_FlatKey(section, name)] |= flags2;
2043 return !was_empty;
2044 } else {
2045 TClearedEntries::iterator it
2046 = m_ClearedEntries.find(s_FlatKey(section, name));
2047 if (it != m_ClearedEntries.end()) {
2048 if ((it->second &= ~flags2) == 0) {
2049 m_ClearedEntries.erase(it);
2050 }
2051 }
2052 }
2053 return m_MainRegistry->Set(section, name, value, flags, comment);
2054 }
2055
2056
x_Unset(const string & section,const string & name,TFlags flags)2057 bool CCompoundRWRegistry::x_Unset(const string& section, const string& name,
2058 TFlags flags)
2059 {
2060 bool result = false;
2061 NON_CONST_ITERATE (CCompoundRegistry::TPriorityMap, it,
2062 m_AllRegistries->m_PriorityMap) {
2063 IRWRegistry& subreg = dynamic_cast<IRWRegistry&>(*it->second);
2064 result |= subreg.Unset(section, name, flags);
2065 }
2066 return result;
2067 }
2068
2069
x_SetComment(const string & comment,const string & section,const string & name,TFlags flags)2070 bool CCompoundRWRegistry::x_SetComment(const string& comment,
2071 const string& section,
2072 const string& name, TFlags flags)
2073 {
2074 return m_MainRegistry->SetComment(comment, section, name, flags);
2075 }
2076
2077
x_Read(CNcbiIstream & in,TFlags flags,const string & path)2078 IRWRegistry* CCompoundRWRegistry::x_Read(CNcbiIstream& in, TFlags flags,
2079 const string& path)
2080 {
2081 TFlags lbr_flags = flags;
2082 if ((flags & fNoOverride) == 0 && !Empty(fPersistent) ) {
2083 lbr_flags |= fOverride;
2084 } else {
2085 lbr_flags &= ~fOverride;
2086 }
2087 IRWRegistry::x_Read(in, flags, path);
2088 LoadBaseRegistries(lbr_flags, 0, path);
2089 return NULL;
2090 }
2091
2092
x_Add(const IRegistry & reg,TPriority prio,const string & name)2093 void CCompoundRWRegistry::x_Add(const IRegistry& reg, TPriority prio,
2094 const string& name)
2095 {
2096 m_AllRegistries->Add(reg, prio, name);
2097 }
2098
2099
2100 //////////////////////////////////////////////////////////////////////
2101 //
2102 // CRegistryException -- error reporting
2103
GetErrCodeString(void) const2104 const char* CRegistryException::GetErrCodeString(void) const
2105 {
2106 switch (GetErrCode()) {
2107 case eSection: return "eSection";
2108 case eEntry: return "eEntry";
2109 case eValue: return "eValue";
2110 case eUnencrypted: return "eUnencrypted";
2111 case eDecryptionFailed: return "eDecryptionFailed";
2112 case eErr: return "eErr";
2113 default: return CException::GetErrCodeString();
2114 }
2115 }
2116
2117
2118 END_NCBI_SCOPE
2119