1 /*  $Id: ncbireg.cpp 591546 2019-08-16 16:59:06Z vasilche $
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(&sections, 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.c_str());
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 == &reg) {
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 == &reg) {
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(&sections, 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 (&reg == 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