src/corelib/ncbireg.cpp

Go to the documentation of this file.
00001 /*  $Id: ncbireg.cpp 164260 2009-06-24 19:10:38Z ucko $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Denis Vakatov, Aaron Ucko
00027  *
00028  * File Description:
00029  *   Handle info in the NCBI configuration file(s):
00030  *      read and parse config. file
00031  *      search, edit, etc. in the retrieved configuration info
00032  *      dump info back to config. file
00033  *
00034  */
00035 
00036 #include <ncbi_pch.hpp>
00037 #include <corelib/ncbireg.hpp>
00038 #include <corelib/env_reg.hpp>
00039 #include <corelib/metareg.hpp>
00040 #include <corelib/ncbiapp.hpp>
00041 #include <corelib/ncbimtx.hpp>
00042 #include <corelib/error_codes.hpp>
00043 
00044 #include <algorithm>
00045 #include <set>
00046 
00047 
00048 #define NCBI_USE_ERRCODE_X   Corelib_Reg
00049 
00050 
00051 BEGIN_NCBI_SCOPE
00052 
00053 typedef CRegistryReadGuard  TReadGuard;
00054 typedef CRegistryWriteGuard TWriteGuard;
00055 
00056 
00057 // Valid symbols for a section/entry name
00058 inline bool s_IsNameSectionSymbol(char ch, IRegistry::TFlags flags)
00059 {
00060     return (isalnum((unsigned char) ch)
00061             ||  ch == '_'  ||  ch == '-' ||  ch == '.'  ||  ch == '/'
00062             ||  ((flags & IRegistry::fInternalSpaces)  &&  ch == ' '));
00063 }
00064 
00065 
00066 // Check if "str" consists of alphanumeric and '_' only
00067 static bool s_IsNameSection(const string& str, IRegistry::TFlags flags)
00068 {
00069     if (str.empty()) {
00070         return false;
00071     }
00072 
00073     ITERATE (string, it, str) {
00074         if (!s_IsNameSectionSymbol(*it, flags)) {
00075             return false;
00076         }
00077     }
00078     return true;
00079 }
00080 
00081 
00082 // Convert "comment" from plain text to comment
00083 static const string s_ConvertComment(const string& comment,
00084                                      bool is_file_comment = false)
00085 {
00086     if ( !comment.length() )
00087         return kEmptyStr;
00088 
00089     string x_comment;
00090     const char c_comment = is_file_comment ? '#' : ';';
00091 
00092     SIZE_TYPE endl_pos = 0;
00093     for (SIZE_TYPE beg = 0;  beg < comment.length();
00094          beg = endl_pos + 1) {
00095         SIZE_TYPE pos = comment.find_first_not_of(" \t", beg);
00096         endl_pos = comment.find_first_of("\n", beg);
00097         if (endl_pos == NPOS) {
00098             endl_pos = comment.length();
00099         }
00100         if (((pos != NPOS  &&  comment[pos] != c_comment) ||
00101              (pos == NPOS  &&  endl_pos == comment.length())) &&
00102             (is_file_comment  ||  beg != endl_pos) ) {
00103             x_comment += c_comment;
00104         }
00105         x_comment.append(comment, beg, endl_pos - beg);
00106         x_comment += '\n';
00107     }
00108     return x_comment;
00109 }
00110 
00111 
00112 // Dump the comment to stream "os"
00113 static bool s_WriteComment(CNcbiOstream& os, const string& comment)
00114 {
00115     if (!comment.length())
00116         return true;
00117 
00118     if (strcmp(Endl(), "\n") == 0) {
00119         os << comment;
00120     } else {
00121         ITERATE(string, i, comment) {
00122             if (*i == '\n') {
00123                 os << Endl();
00124             } else {
00125                 os << *i;
00126             }
00127         }
00128     }
00129     return os.good();
00130 }
00131 
00132 // Does pos follow an odd number of backslashes?
00133 inline bool s_Backslashed(const string& s, SIZE_TYPE pos)
00134 {
00135     if (pos == 0) {
00136         return false;
00137     }
00138     SIZE_TYPE last_non_bs = s.find_last_not_of("\\", pos - 1);
00139     return (pos - last_non_bs) % 2 == 0;
00140 }
00141 
00142 inline string s_FlatKey(const string& section, const string& name)
00143 {
00144     return section + '#' + name;
00145 }
00146 
00147 
00148 //////////////////////////////////////////////////////////////////////
00149 //
00150 // IRegistry
00151 
00152 bool IRegistry::Empty(TFlags flags) const
00153 {
00154     x_CheckFlags("IRegistry::Empty", flags, fLayerFlags);
00155     if ( !(flags & fTPFlags) ) {
00156         flags |= fTPFlags;
00157     }
00158     TReadGuard LOCK(*this);
00159     return x_Empty(flags);
00160 }
00161 
00162 
00163 bool IRegistry::Modified(TFlags flags) const
00164 {
00165     x_CheckFlags("IRegistry::Modified", flags, fLayerFlags);
00166     if ( !(flags & fTransient) ) {
00167         flags |= fPersistent;
00168     }
00169     TReadGuard LOCK(*this);
00170     return x_Modified(flags);
00171 }
00172 
00173 
00174 void IRegistry::SetModifiedFlag(bool modified, TFlags flags)
00175 {
00176     x_CheckFlags("IRegistry::SetModifiedFlag", flags, fLayerFlags);
00177     if ( !(flags & fTransient) ) {
00178         flags |= fPersistent;
00179     }
00180     TReadGuard LOCK(*this); // Treat the flag as semi-mutable
00181     x_SetModifiedFlag(modified, flags);
00182 }
00183 
00184 
00185 // Somewhat inefficient, but that can't really be helped....
00186 bool IRegistry::Write(CNcbiOstream& os, TFlags flags) const
00187 {
00188     x_CheckFlags("IRegistry::Write", flags,
00189                  (TFlags)fLayerFlags | fInternalSpaces | fCountCleared);
00190     if ( !(flags & fTransient) ) {
00191         flags |= fPersistent;
00192     }
00193     if ( !(flags & fNotJustCore) ) {
00194         flags |= fJustCore;
00195     }
00196     TReadGuard LOCK(*this);
00197 
00198     // Write file comment
00199     if ( !s_WriteComment(os, GetComment(kEmptyStr, kEmptyStr, flags)) )
00200         return false;
00201 
00202     list<string> sections;
00203     EnumerateSections(&sections, flags);
00204 
00205     ITERATE (list<string>, section, sections) {
00206         if ( !s_WriteComment(os, GetComment(*section, kEmptyStr, flags)) ) {
00207             return false;
00208         }
00209         os << '[' << *section << ']' << Endl();
00210         if ( !os ) {
00211             return false;
00212         }
00213         list<string> entries;
00214         EnumerateEntries(*section, &entries, flags);
00215         ITERATE (list<string>, entry, entries) {
00216             s_WriteComment(os, GetComment(*section, *entry, flags));
00217             // XXX - produces output that older versions can't handle
00218             // when the value contains control characters other than
00219             // CR (\r) or LF (\n).
00220             os << *entry << " = \""
00221                << Printable(Get(*section, *entry, flags)) << "\""
00222                << Endl();
00223             if ( !os ) {
00224                 return false;
00225             }
00226         }
00227     }
00228 
00229     // Clear the modified bit (checking it first so as to perform the
00230     // const_cast<> only if absolutely necessary).
00231     if (Modified(flags & fLayerFlags)) {
00232         const_cast<IRegistry*>(this)->SetModifiedFlag
00233             (false, flags & fLayerFlags);
00234     }
00235 
00236     return true;
00237 }
00238 
00239 
00240 const string& IRegistry::Get(const string& section, const string& name,
00241                              TFlags flags) const
00242 {
00243     x_CheckFlags("IRegistry::Get", flags,
00244                  (TFlags)fLayerFlags | fInternalSpaces);
00245     if ( !(flags & fTPFlags) ) {
00246         flags |= fTPFlags;
00247     }
00248     string clean_section = NStr::TruncateSpaces(section);
00249     if ( !s_IsNameSection(clean_section, flags) ) {
00250         _TRACE("IRegistry::Get: bad section name \""
00251                << NStr::PrintableString(section) << '\"');
00252         return kEmptyStr;
00253     }
00254     string clean_name = NStr::TruncateSpaces(name);
00255     if ( !s_IsNameSection(clean_name, flags) ) {
00256         _TRACE("IRegistry::Get: bad entry name \""
00257                << NStr::PrintableString(name) << '\"');
00258         return kEmptyStr;
00259     }
00260     TReadGuard LOCK(*this);
00261     return x_Get(clean_section, clean_name, flags);
00262 }
00263 
00264 
00265 bool IRegistry::HasEntry(const string& section, const string& name,
00266                          TFlags flags) const
00267 {
00268     x_CheckFlags("IRegistry::HasEntry", flags,
00269                  (TFlags)fLayerFlags | fInternalSpaces | fCountCleared);
00270     if ( !(flags & fTPFlags) ) {
00271         flags |= fTPFlags;
00272     }
00273     string clean_section = NStr::TruncateSpaces(section);
00274     if ( !s_IsNameSection(clean_section, flags) ) {
00275         _TRACE("IRegistry::HasEntry: bad section name \""
00276                << NStr::PrintableString(section) << '\"');
00277         return false;
00278     }
00279     string clean_name = NStr::TruncateSpaces(name);
00280     if ( !clean_name.empty()  &&  !s_IsNameSection(clean_name, flags) ) {
00281         _TRACE("IRegistry::HasEntry: bad entry name \""
00282                << NStr::PrintableString(name) << '\"');
00283         return false;
00284     }
00285     TReadGuard LOCK(*this);
00286     return x_HasEntry(clean_section, clean_name, flags);
00287 }
00288 
00289 
00290 string IRegistry::GetString(const string& section, const string& name,
00291                             const string& default_value, TFlags flags) const
00292 {
00293     const string& value = Get(section, name, flags);
00294     return value.empty() ? default_value : value;
00295 }
00296 
00297 
00298 int IRegistry::GetInt(const string& section, const string& name,
00299                       int default_value, TFlags flags, EErrAction err_action)
00300     const
00301 {
00302     const string& value = Get(section, name, flags);
00303     if (value.empty()) {
00304         return default_value;
00305     }
00306 
00307     try {
00308         return NStr::StringToInt(value);
00309     } catch (CStringException& ex) {
00310         if (err_action == eReturn) {
00311             return default_value;
00312         }
00313 
00314         string msg = "IRegistry::GetInt(): [" + section + ']' + name;
00315 
00316         if (err_action == eThrow) {
00317             NCBI_RETHROW_SAME(ex, msg);
00318         } else if (err_action == eErrPost) {
00319             ERR_POST_X(1, ex.what() << msg);
00320         }
00321 
00322         return default_value;
00323     }
00324 }
00325 
00326 
00327 bool IRegistry::GetBool(const string& section, const string& name,
00328                         bool default_value, TFlags flags,
00329                         EErrAction err_action) const
00330 {
00331     const string& value = Get(section, name, flags);
00332     if (value.empty()) {
00333         return default_value;
00334     }
00335 
00336     try {
00337         return NStr::StringToBool(value);
00338     } catch (CStringException& ex) {
00339         if (err_action == eReturn) {
00340             return default_value;
00341         }
00342 
00343         string msg = "IRegistry::GetBool(): [" + section + ']' + name;
00344 
00345         if (err_action == eThrow) {
00346             NCBI_RETHROW_SAME(ex, msg);
00347         } else if (err_action == eErrPost) {
00348             ERR_POST_X(2, ex.what() << msg);
00349         }
00350 
00351         return default_value;
00352     }
00353 }
00354 
00355 
00356 double IRegistry::GetDouble(const string& section, const string& name,
00357                             double default_value, TFlags flags,
00358                             EErrAction err_action) const
00359 {
00360     const string& value = Get(section, name, flags);
00361     if (value.empty()) {
00362         return default_value;
00363     }
00364 
00365     try {
00366         return NStr::StringToDouble(value);
00367     } catch (CStringException& ex) {
00368         if (err_action == eReturn) {
00369             return default_value;
00370         }
00371 
00372         string msg = "IRegistry::GetDouble()";
00373         msg += " Reg entry:" + section + ":" + name;
00374 
00375         if (err_action == eThrow) {
00376             NCBI_RETHROW_SAME(ex, msg);
00377         } else if (err_action == eErrPost) {
00378             ERR_POST_X(3, ex.what() << msg);
00379         }
00380 
00381         return default_value;
00382     }
00383 }
00384 
00385 
00386 const string& IRegistry::GetComment(const string& section, const string& name,
00387                                     TFlags flags) const
00388 {
00389     x_CheckFlags("IRegistry::GetComment", flags,
00390                  (TFlags)fLayerFlags | fInternalSpaces);
00391     string clean_section = NStr::TruncateSpaces(section);
00392     if ( !clean_section.empty()  &&  !s_IsNameSection(clean_section, flags) ) {
00393         _TRACE("IRegistry::GetComment: bad section name \""
00394                << NStr::PrintableString(section) << '\"');
00395         return kEmptyStr;
00396     }
00397     string clean_name = NStr::TruncateSpaces(name);
00398     if ( !clean_name.empty()  &&  !s_IsNameSection(clean_name, flags) ) {
00399         _TRACE("IRegistry::GetComment: bad entry name \""
00400                << NStr::PrintableString(name) << '\"');
00401         return kEmptyStr;
00402     }
00403     TReadGuard LOCK(*this);
00404     return x_GetComment(clean_section, clean_name, flags);
00405 }
00406 
00407 
00408 void IRegistry::EnumerateSections(list<string>* sections, TFlags flags) const
00409 {
00410     x_CheckFlags("IRegistry::EnumerateSections", flags,
00411                  (TFlags)fLayerFlags | fInternalSpaces | fCountCleared);
00412     if ( !(flags & fTPFlags) ) {
00413         flags |= fTPFlags;
00414     }
00415     _ASSERT(sections);
00416     sections->clear();
00417     TReadGuard LOCK(*this);
00418     x_Enumerate(kEmptyStr, *sections, flags);
00419 }
00420 
00421 
00422 void IRegistry::EnumerateEntries(const string& section, list<string>* entries,
00423                                  TFlags flags) const
00424 {
00425     x_CheckFlags("IRegistry::EnumerateEntries", flags,
00426                  (TFlags)fLayerFlags | fInternalSpaces | fCountCleared);
00427     if ( !(flags & fTPFlags) ) {
00428         flags |= fTPFlags;
00429     }
00430     _ASSERT(entries);
00431     entries->clear();
00432     string clean_section = NStr::TruncateSpaces(section);
00433     if ( !clean_section.empty()  &&  !s_IsNameSection(clean_section, flags) ) {
00434         _TRACE("IRegistry::EnumerateEntries: bad section name \""
00435                << NStr::PrintableString(section) << '\"');
00436         return;
00437     }
00438     TReadGuard LOCK(*this);
00439     x_Enumerate(clean_section, *entries, flags);
00440 }
00441 
00442 
00443 void IRegistry::ReadLock (void)
00444 {
00445     x_ChildLockAction(&IRegistry::ReadLock);
00446     m_Lock.ReadLock();
00447 }
00448 
00449 
00450 void IRegistry::WriteLock(void)
00451 {
00452     x_ChildLockAction(&IRegistry::WriteLock);
00453     m_Lock.WriteLock();
00454 }
00455 
00456 
00457 void IRegistry::Unlock(void)
00458 {
00459     m_Lock.Unlock();
00460     x_ChildLockAction(&IRegistry::Unlock);
00461 }
00462 
00463 
00464 void IRegistry::x_CheckFlags(const string& _DEBUG_ARG(func),
00465                              TFlags& flags, TFlags allowed)
00466 {
00467     if (flags & ~allowed)
00468         _TRACE(func << "(): extra flags passed: "
00469                << resetiosflags(IOS_BASE::basefield)
00470                << setiosflags(IOS_BASE::hex | IOS_BASE::showbase)
00471                << flags);
00472     flags &= allowed;
00473 }
00474 
00475 
00476 //////////////////////////////////////////////////////////////////////
00477 //
00478 // IRWRegistry
00479 
00480 IRegistry::TFlags IRWRegistry::AssessImpact(TFlags flags, EOperation op)
00481 {
00482     // mask out irrelevant flags
00483     flags &= fLayerFlags | fTPFlags;
00484     switch (op) {
00485     case eClear:
00486         return flags;
00487     case eRead:
00488     case eSet:
00489         return ((flags & fTransient) ? fTransient : fPersistent) | fJustCore;
00490     default:
00491         _TROUBLE;
00492         return flags;
00493     }
00494 }
00495 
00496 void IRWRegistry::Clear(TFlags flags)
00497 {
00498     x_CheckFlags("IRWRegistry::Clear", flags,
00499                  (TFlags)fLayerFlags | fInternalSpaces);
00500     TWriteGuard LOCK(*this);
00501     if ( (flags & fPersistent)  &&  !x_Empty(fPersistent) ) {
00502         x_SetModifiedFlag(true, flags & ~fTransient);
00503     }
00504     if ( (flags & fTransient)  &&  !x_Empty(fTransient) ) {
00505         x_SetModifiedFlag(true, flags & ~fPersistent);
00506     }
00507     x_Clear(flags);
00508 }
00509 
00510 
00511 IRWRegistry* IRWRegistry::Read(CNcbiIstream& is, TFlags flags)
00512 {
00513     x_CheckFlags("IRWRegistry::Read", flags,
00514                  fTransient | fNoOverride | fIgnoreErrors | fInternalSpaces
00515                  | fWithNcbirc | fJustCore | fCountCleared);
00516     return x_Read(is, flags);
00517 }
00518 
00519 
00520 IRWRegistry* IRWRegistry::x_Read(CNcbiIstream& is, TFlags flags)
00521 {
00522     // Whether to consider this read to be (unconditionally) non-modifying
00523     TFlags layer         = (flags & fTransient) ? fTransient : fPersistent;
00524     TFlags impact        = layer | fJustCore;
00525     bool   non_modifying = Empty(impact)  &&  !Modified(impact);
00526     bool   ignore_errors = (flags & fIgnoreErrors) > 0;
00527 
00528     // Adjust flags for Set()
00529     flags = (flags & ~fTPFlags & ~fIgnoreErrors) | layer;
00530 
00531     string    str;          // the line being parsed
00532     SIZE_TYPE line;         // # of the line being parsed
00533     string    section;      // current section name
00534     string    comment;      // current comment
00535 
00536     for (line = 1;  NcbiGetlineEOL(is, str);  ++line) {
00537         try {
00538             SIZE_TYPE len = str.length();
00539             SIZE_TYPE beg = 0;
00540 
00541             while (beg < len  &&  isspace((unsigned char) str[beg])) {
00542                 ++beg;
00543             }
00544             if (beg == len) {
00545                 comment += str;
00546                 comment += '\n';
00547                 continue;
00548             }
00549 
00550             switch (str[beg]) {
00551 
00552             case '#':  { // file comment
00553                 SetComment(GetComment() + str + '\n');
00554                 break;
00555             }
00556 
00557             case ';':  { // section or entry comment
00558                 comment += str;
00559                 comment += '\n';
00560                 break;
00561             }
00562 
00563             case '[':  { // section name
00564                 ++beg;
00565                 SIZE_TYPE end = str.find_first_of(']', beg + 1);
00566                 if (end == NPOS) {
00567                     NCBI_THROW2(CRegistryException, eSection,
00568                                 "Invalid registry section(']' is missing): `"
00569                                 + str + "'", line);
00570                 }
00571                 section = NStr::TruncateSpaces(str.substr(beg, end - beg));
00572                 if (section.empty()) {
00573                     NCBI_THROW2(CRegistryException, eSection,
00574                                 "Unnamed registry section: `" + str + "'",
00575                                 line);
00576                 } else if ( !s_IsNameSection(section, flags) ) {
00577                     NCBI_THROW2(CRegistryException, eSection,
00578                                 "Invalid registry section name: `"
00579                                 + str + "'", line);
00580                 }
00581                 // add section comment
00582                 if ( !comment.empty() ) {
00583                     SetComment(GetComment(section) + comment, section);
00584                     comment.erase();
00585                 }
00586                 break;
00587             }
00588 
00589             default:  { // regular entry
00590                 string name, value;
00591                 if ( !NStr::SplitInTwo(str, "=", name, value) ) {
00592                     NCBI_THROW2(CRegistryException, eEntry,
00593                                 "Invalid registry entry format: '" + str + "'",
00594                                 line);
00595                 }
00596                 NStr::TruncateSpacesInPlace(name);
00597                 if ( !s_IsNameSection(name, flags) ) {
00598                     NCBI_THROW2(CRegistryException, eEntry,
00599                                 "Invalid registry entry name: '" + str + "'",
00600                                 line);
00601                 }
00602             
00603                 NStr::TruncateSpacesInPlace(value);
00604 #if 0 // historic behavior; could inappropriately expose entries in lower layers
00605                 if (value.empty()) {
00606                     if ( !(flags & fNoOverride) ) {
00607                         Set(section, name, kEmptyStr, flags, comment);
00608                         comment.erase();
00609                     }
00610                     break;
00611                 }
00612 #endif
00613                 // read continuation lines, if any
00614                 string cont;
00615                 while (s_Backslashed(value, value.size())
00616                        &&  NcbiGetlineEOL(is, cont)) {
00617                     ++line;
00618                     value[value.size() - 1] = '\n';
00619                     value += NStr::TruncateSpaces(cont);
00620                     str   += 'n' + cont; // for presentation in exceptions
00621                 }
00622 
00623                 // Historically, " may appear unescaped at the beginning,
00624                 // end, both, or neither.
00625                 beg = 0;
00626                 SIZE_TYPE end = value.size();
00627                 for (SIZE_TYPE pos = value.find('\"');
00628                      pos < end  &&  pos != NPOS;
00629                      pos = value.find('\"', pos + 1)) {
00630                     if (s_Backslashed(value, pos)) {
00631                         continue;
00632                     } else if (pos == beg) {
00633                         ++beg;
00634                     } else if (pos == end - 1) {
00635                         --end;
00636                     } else {
00637                         NCBI_THROW2(CRegistryException, eValue,
00638                                     "Single(unescaped) '\"' in the middle "
00639                                     "of registry value: '" + str + "'",
00640                                     line);
00641                     }
00642                 }
00643 
00644                 try {
00645                     value = NStr::ParseEscapes(value.substr(beg, end - beg));
00646                 } catch (CStringException&) {
00647                     NCBI_THROW2(CRegistryException, eValue,
00648                                 "Badly placed '\\' in the registry value: '"
00649                                 + str + "'", line);
00650 
00651                 }
00652                 TFlags set_flags = flags;
00653                 if (NStr::EqualNocase(section, "NCBI")
00654                     &&  NStr::EqualNocase(name, ".Inherits")
00655                     &&  HasEntry(section, name, flags)) {
00656                     const string& old_value = Get(section, name, flags);
00657                     if (flags & fNoOverride) {
00658                         value = old_value + ' ' + value;
00659                         set_flags &= ~fNoOverride;
00660                     } else {
00661                         value += ' ';
00662                         value += old_value;
00663                     }
00664                 }
00665                 Set(section, name, value, set_flags, comment);
00666                 comment.erase();
00667             }
00668             }
00669         } catch (exception& e) {
00670             if (ignore_errors) {
00671                 ERR_POST_X(4, e.what());
00672             } else {
00673                 throw;
00674             }
00675         }
00676     }
00677 
00678     if ( !is.eof() ) {
00679         NCBI_THROW2(CRegistryException, eErr,
00680                     "Error in reading the registry: '" + str + "'", line);
00681     }
00682 
00683     if ( non_modifying ) {
00684         SetModifiedFlag(false, impact);
00685     }
00686 
00687     return NULL;
00688 }
00689 
00690 
00691 bool IRWRegistry::Set(const string& section, const string& name,
00692                       const string& value, TFlags flags,
00693                       const string& comment)
00694 {
00695     x_CheckFlags("IRWRegistry::Set", flags,
00696                  fPersistent | fNoOverride | fTruncate | fInternalSpaces
00697                  | fCountCleared);
00698     string clean_section = NStr::TruncateSpaces(section);
00699     if ( !s_IsNameSection(clean_section, flags) ) {
00700         _TRACE("IRWRegistry::Set: bad section name \""
00701                << NStr::PrintableString(section) << '\"');
00702         return false;
00703     }
00704     string clean_name = NStr::TruncateSpaces(name);
00705     if ( !s_IsNameSection(clean_name, flags) ) {
00706         _TRACE("IRWRegistry::Set: bad entry name \""
00707                << NStr::PrintableString(name) << '\"');
00708         return false;
00709     }
00710     SIZE_TYPE beg = 0, end = value.size();
00711     if (flags & fTruncate) {
00712         // don't use TruncateSpaces, since newlines should stay
00713         beg = value.find_first_not_of(" \r\t\v");
00714         end = value.find_last_not_of (" \r\t\v");
00715         if (beg == NPOS) {
00716             _ASSERT(end == NPOS);
00717             beg = 1;
00718             end = 0;
00719         }
00720     }
00721     TWriteGuard LOCK(*this);
00722     if (x_Set(clean_section, clean_name, value.substr(beg, end - beg + 1),
00723               flags, s_ConvertComment(comment, section.empty()))) {
00724         x_SetModifiedFlag(true, flags);
00725         return true;
00726     } else {
00727         return false;
00728     }
00729 }
00730 
00731 
00732 bool IRWRegistry::SetComment(const string& comment, const string& section,
00733                              const string& name, TFlags flags)
00734 {
00735     x_CheckFlags("IRWRegistry::SetComment", flags,
00736                  fTransient | fNoOverride | fInternalSpaces);
00737     string clean_section = NStr::TruncateSpaces(section);
00738     if ( !clean_section.empty()  &&  !s_IsNameSection(clean_section, flags) ) {
00739         _TRACE("IRWRegistry::SetComment: bad section name \""
00740                << NStr::PrintableString(section) << '\"');
00741         return false;
00742     }
00743     string clean_name = NStr::TruncateSpaces(name);
00744     if ( !clean_name.empty()  &&  !s_IsNameSection(clean_name, flags) ) {
00745         _TRACE("IRWRegistry::SetComment: bad entry name \""
00746                << NStr::PrintableString(name) << '\"');
00747         return false;
00748     }
00749     TWriteGuard LOCK(*this);
00750     if (x_SetComment(s_ConvertComment(comment, section.empty()),
00751                      clean_section, clean_name, flags)) {
00752         x_SetModifiedFlag(true, fPersistent);
00753         return true;
00754     } else {
00755         return false;
00756     }
00757 }
00758 
00759 
00760 bool IRWRegistry::MaybeSet(string& target, const string& value, TFlags flags)
00761 {
00762     if (target.empty()) {
00763         target = value;
00764         return !value.empty();
00765     } else if ( !(flags & fNoOverride) ) {
00766         target = value;
00767         return true;
00768     } else {
00769         return false;
00770     }
00771 }
00772 
00773 
00774 //////////////////////////////////////////////////////////////////////
00775 //
00776 // CMemoryRegistry
00777 
00778 bool CMemoryRegistry::x_Empty(TFlags) const
00779 {
00780     TReadGuard LOCK(*this);
00781     return m_Sections.empty()  &&  m_RegistryComment.empty();
00782 }
00783 
00784 
00785 const string& CMemoryRegistry::x_Get(const string& section, const string& name,
00786                                      TFlags) const
00787 {
00788     TSections::const_iterator sit = m_Sections.find(section);
00789     if (sit == m_Sections.end()) {
00790         return kEmptyStr;
00791     }
00792     const TEntries& entries = sit->second.entries;
00793     TEntries::const_iterator eit = entries.find(name);
00794     return (eit == entries.end()) ? kEmptyStr : eit->second.value;
00795 }
00796 
00797 bool CMemoryRegistry::x_HasEntry(const string& section, const string& name,
00798                                  TFlags flags) const
00799 {
00800     TSections::const_iterator sit = m_Sections.find(section);
00801     if (sit == m_Sections.end()) {
00802         return false;
00803     } else if (name.empty()) {
00804         return ((flags & fCountCleared) != 0) || !sit->second.cleared;
00805     }
00806     const TEntries& entries = sit->second.entries;
00807     TEntries::const_iterator eit = entries.find(name);
00808     if (eit == entries.end()) {
00809         return false;
00810     } else if ((flags & fCountCleared) != 0) {
00811         return true;
00812     } else {
00813         return !eit->second.value.empty();
00814     }
00815 }
00816 
00817 
00818 const string& CMemoryRegistry::x_GetComment(const string& section,
00819                                             const string& name,
00820                                             TFlags) const
00821 {
00822     if (section.empty()) {
00823         return m_RegistryComment;
00824     }
00825     TSections::const_iterator sit = m_Sections.find(section);
00826     if (sit == m_Sections.end()) {
00827         return kEmptyStr;
00828     } else if (name.empty()) {
00829         return sit->second.comment;
00830     }
00831     const TEntries& entries = sit->second.entries;
00832     TEntries::const_iterator eit = entries.find(name);
00833     return (eit == entries.end()) ? kEmptyStr : eit->second.comment;
00834 }
00835 
00836 
00837 void CMemoryRegistry::x_Enumerate(const string& section, list<string>& entries,
00838                                   TFlags flags) const
00839 {
00840     if (section.empty()) {
00841         ITERATE (TSections, it, m_Sections) {
00842             if (s_IsNameSection(it->first, flags)
00843                 &&  HasEntry(it->first, kEmptyStr, flags)) {
00844                 entries.push_back(it->first);
00845             }
00846         }
00847     } else {
00848         TSections::const_iterator sit = m_Sections.find(section);
00849         if (sit != m_Sections.end()) {
00850             ITERATE (TEntries, it, sit->second.entries) {
00851                 if (s_IsNameSection(it->first, flags)
00852                     &&  ((flags & fCountCleared) != 0
00853                          ||  !it->second.value.empty() )) {
00854                     entries.push_back(it->first);
00855                 }
00856             }
00857         }
00858     }
00859 }
00860 
00861 
00862 void CMemoryRegistry::x_Clear(TFlags)
00863 {
00864     m_RegistryComment.erase();
00865     m_Sections.clear();
00866 }
00867 
00868 bool CMemoryRegistry::x_Set(const string& section, const string& name,
00869                             const string& value, TFlags flags,
00870                             const string& comment)
00871 {
00872     _TRACE(this << ": [" << section << ']' << name << " = " << value);
00873 #if 0 // historic behavior; could inappropriately expose entries in lower layers
00874     if (value.empty()) {
00875         if (flags & fNoOverride) {
00876             return false;
00877         }
00878         // remove
00879         TSections::iterator sit = m_Sections.find(section);
00880         if (sit == m_Sections.end()) {
00881             return false;
00882         }
00883         TEntries& entries = sit->second.entries;
00884         TEntries::iterator eit = entries.find(name);
00885         if (eit == entries.end()) {
00886             return false;
00887         } else {
00888             entries.erase(eit);
00889             if (entries.empty()  &&  sit->second.comment.empty()) {
00890                 m_Sections.erase(sit);
00891             }
00892             return true;
00893         }
00894     } else
00895 #endif
00896     {
00897         TSections::iterator sit = m_Sections.find(section);
00898         if (sit == m_Sections.end()) {
00899             sit = m_Sections.insert(make_pair(section, SSection())).first;
00900             sit->second.cleared = false;
00901         }
00902         SEntry& entry = sit->second.entries[name];
00903 #if 0
00904         if (entry.value == value) {
00905             if (entry.comment != comment) {
00906                 return MaybeSet(entry.comment, comment, flags);
00907             }
00908             return false; // not actually modified
00909         }
00910 #endif
00911         if ( !value.empty() ) {
00912             sit->second.cleared = false;
00913         } else if ( !entry.value.empty() ) {
00914             _ASSERT( !sit->second.cleared );
00915             bool cleared = true;
00916             ITERATE (TEntries, eit, sit->second.entries) {
00917                 if (&eit->second != &entry  &&  !eit->second.value.empty() ) {
00918                     cleared = false;
00919                     break;
00920                 }
00921             }
00922             sit->second.cleared = cleared;
00923         }
00924         if (MaybeSet(entry.value, value, flags)) {
00925             MaybeSet(entry.comment, comment, flags);
00926             return true;
00927         }
00928         return false;
00929     }
00930 }
00931 
00932 
00933 bool CMemoryRegistry::x_SetComment(const string& comment,
00934                                    const string& section, const string& name,
00935                                    TFlags flags)
00936 {
00937     if (comment.empty()  &&  (flags & fNoOverride)) {
00938         return false;
00939     }
00940     if (section.empty()) {
00941         return MaybeSet(m_RegistryComment, comment, flags);
00942     }
00943     TSections::iterator sit = m_Sections.find(section);
00944     if (sit == m_Sections.end()) {
00945         if (comment.empty()) {
00946             return false;
00947         } else {
00948             sit = m_Sections.insert(make_pair(section, SSection())).first;
00949             sit->second.cleared = false;
00950         }
00951     }
00952     TEntries& entries = sit->second.entries;
00953     if (name.empty()) {
00954         if (comment.empty()  &&  entries.empty()) {
00955             m_Sections.erase(sit);
00956             return true;
00957         } else {
00958             return MaybeSet(sit->second.comment, comment, flags);
00959         }
00960     }
00961     TEntries::iterator eit = entries.find(name);
00962     if (eit == entries.end()) {
00963         return false;
00964     } else {
00965         return MaybeSet(eit->second.comment, comment, flags);
00966     }
00967 }
00968 
00969 
00970 //////////////////////////////////////////////////////////////////////
00971 //
00972 // CCompoundRegistry
00973 
00974 void CCompoundRegistry::Add(const IRegistry& reg, TPriority prio,
00975                             const string& name)
00976 {
00977     // Needed for some operations that touch (only) metadata...
00978     IRegistry& nc_reg = const_cast<IRegistry&>(reg);
00979     // XXX - Check whether reg is a duplicate, at least in debug mode?
00980     m_PriorityMap.insert(TPriorityMap::value_type
00981                          (prio, CRef<IRegistry>(&nc_reg)));
00982     if (name.size()) {
00983         CRef<IRegistry>& preg = m_NameMap[name];
00984         if (preg) {
00985             NCBI_THROW2(CRegistryException, eErr,
00986                         "CCompoundRegistry::Add: name " + name
00987                         + " already in use", 0);
00988         } else {
00989             preg.Reset(&nc_reg);
00990         }
00991     }
00992 }
00993 
00994 
00995 void CCompoundRegistry::Remove(const IRegistry& reg)
00996 {
00997     NON_CONST_ITERATE (TNameMap, it, m_NameMap) {
00998         if (it->second == &reg) {
00999             m_NameMap.erase(it);
01000             break; // subregistries should be unique
01001         }
01002     }
01003     NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
01004         if (it->second == &reg) {
01005             m_PriorityMap.erase(it);
01006             return; // subregistries should be unique
01007         }
01008     }
01009     // already returned if found...
01010     NCBI_THROW2(CRegistryException, eErr,
01011                 "CCompoundRegistry::Remove:"
01012                 " reg is not a (direct) subregistry of this.", 0);
01013 }
01014 
01015 
01016 CConstRef<IRegistry> CCompoundRegistry::FindByName(const string& name) const
01017 {
01018     TNameMap::const_iterator it = m_NameMap.find(name);
01019     return it == m_NameMap.end() ? CConstRef<IRegistry>() : it->second;
01020 }
01021 
01022 
01023 CConstRef<IRegistry> CCompoundRegistry::FindByContents(const string& section,
01024                                                        const string& entry,
01025                                                        TFlags flags) const
01026 {
01027     TFlags has_entry_flags = (flags | fCountCleared) & ~fJustCore;
01028     REVERSE_ITERATE(TPriorityMap, it, m_PriorityMap) {
01029         if (it->second->HasEntry(section, entry, has_entry_flags)) {
01030             return it->second;
01031         }
01032     }
01033     return null;
01034 }
01035 
01036 
01037 bool CCompoundRegistry::x_Empty(TFlags flags) const
01038 {
01039     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01040         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01041             break;
01042         }
01043         if ( !it->second->Empty(flags & ~fJustCore) ) {
01044             return false;
01045         }
01046     }
01047     return true;
01048 }
01049 
01050 
01051 bool CCompoundRegistry::x_Modified(TFlags flags) const
01052 {
01053     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01054         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01055             break;
01056         }
01057         if ( it->second->Modified(flags & ~fJustCore) ) {
01058             return true;
01059         }
01060     }
01061     return false;
01062 }
01063 
01064 
01065 void CCompoundRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01066 {
01067     _ASSERT( !modified );
01068     for (TPriorityMap::reverse_iterator it = m_PriorityMap.rbegin();
01069          it != m_PriorityMap.rend();  ++it) {
01070         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01071             break;
01072         }
01073         it->second->SetModifiedFlag(modified, flags & ~fJustCore);
01074     }
01075 }
01076 
01077 
01078 const string& CCompoundRegistry::x_Get(const string& section,
01079                                        const string& name,
01080                                        TFlags flags) const
01081 {
01082     CConstRef<IRegistry> reg = FindByContents(section, name,
01083                                               flags & ~fJustCore);
01084     return reg ? reg->Get(section, name, flags & ~fJustCore) : kEmptyStr;
01085 }
01086 
01087 
01088 bool CCompoundRegistry::x_HasEntry(const string& section, const string& name,
01089                                    TFlags flags) const
01090 {
01091     return FindByContents(section, name, flags).NotEmpty();
01092 }
01093 
01094 
01095 const string& CCompoundRegistry::x_GetComment(const string& section,
01096                                               const string& name, TFlags flags)
01097     const
01098 {
01099     if ( m_PriorityMap.empty() ) {
01100         return kEmptyStr;
01101     }
01102 
01103     CConstRef<IRegistry> reg;
01104     if (section.empty()) {
01105         reg = m_PriorityMap.rbegin()->second;
01106     } else {
01107         reg = FindByContents(section, name, flags);
01108     }
01109     return reg ? reg->GetComment(section, name, flags & ~fJustCore)
01110         : kEmptyStr;
01111 }
01112 
01113 
01114 void CCompoundRegistry::x_Enumerate(const string& section,
01115                                     list<string>& entries, TFlags flags) const
01116 {
01117     set<string> accum;
01118     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01119         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01120             break;
01121         }
01122         list<string> tmp;
01123         it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
01124         ITERATE (list<string>, it2, tmp) {
01125             accum.insert(*it2);
01126         }
01127     }
01128     ITERATE (set<string>, it, accum) {
01129         entries.push_back(*it);
01130     }
01131 }
01132 
01133 
01134 void CCompoundRegistry::x_ChildLockAction(FLockAction action)
01135 {
01136     NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
01137         ((*it->second).*action)();
01138     }
01139 }
01140 
01141 
01142 //////////////////////////////////////////////////////////////////////
01143 //
01144 // CTwoLayerRegistry
01145 
01146 CTwoLayerRegistry::CTwoLayerRegistry(IRWRegistry* persistent)
01147     : m_Transient(CRegRef(new CMemoryRegistry)),
01148       m_Persistent(CRegRef(persistent ? persistent : new CMemoryRegistry))
01149 {
01150 }
01151 
01152 
01153 bool CTwoLayerRegistry::x_Empty(TFlags flags) const
01154 {
01155     // mask out fTPFlags whe 
01156     if (flags & fTransient  &&  !m_Transient->Empty(flags | fTPFlags) ) {
01157         return false;
01158     } else if (flags & fPersistent
01159                &&  !m_Persistent->Empty(flags | fTPFlags) ) {
01160         return false;
01161     } else {
01162         return true;
01163     }
01164 }
01165 
01166 
01167 bool CTwoLayerRegistry::x_Modified(TFlags flags) const
01168 {
01169     if (flags & fTransient  &&  m_Transient->Modified(flags | fTPFlags)) {
01170         return true;
01171     } else if (flags & fPersistent
01172                &&  m_Persistent->Modified(flags | fTPFlags)) {
01173         return true;
01174     } else {
01175         return false;
01176     }
01177 }
01178 
01179 
01180 void CTwoLayerRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01181 {
01182     if (flags & fTransient) {
01183         m_Transient->SetModifiedFlag(modified, flags | fTPFlags);
01184     }
01185     if (flags & fPersistent) {
01186         m_Persistent->SetModifiedFlag(modified, flags | fTPFlags);
01187     }
01188 }
01189 
01190 
01191 const string& CTwoLayerRegistry::x_Get(const string& section,
01192                                        const string& name, TFlags flags) const
01193 {
01194     if (flags & fTransient) {
01195         const string& result = m_Transient->Get(section, name,
01196                                                 flags & ~fTPFlags);
01197         if ( !result.empty()  ||  !(flags & fPersistent) ) {
01198             return result;
01199         }
01200     }
01201     return m_Persistent->Get(section, name, flags & ~fTPFlags);
01202 }
01203 
01204 
01205 bool CTwoLayerRegistry::x_HasEntry(const string& section, const string& name,
01206                                    TFlags flags) const
01207 {
01208     return (((flags & fTransient)
01209              &&  m_Transient->HasEntry(section, name, flags & ~fTPFlags))  ||
01210             ((flags & fPersistent)
01211              &&  m_Persistent->HasEntry(section, name, flags & ~fTPFlags)));
01212 }
01213 
01214 
01215 const string& CTwoLayerRegistry::x_GetComment(const string& section,
01216                                               const string& name,
01217                                               TFlags flags) const
01218 {
01219     if (flags & fTransient) {
01220         const string& result = m_Transient->GetComment(section, name,
01221                                                        flags & ~fTPFlags);
01222         if ( !result.empty()  ||  !(flags & fPersistent) ) {
01223             return result;
01224         }
01225     }
01226     return m_Persistent->GetComment(section, name, flags & ~fTPFlags);
01227 }
01228 
01229 
01230 void CTwoLayerRegistry::x_Enumerate(const string& section,
01231                                     list<string>& entries, TFlags flags) const
01232 {
01233     switch (flags & fTPFlags) {
01234     case fTransient:
01235         m_Transient->EnumerateEntries(section, &entries, flags | fTPFlags);
01236         break;
01237     case fPersistent:
01238         m_Persistent->EnumerateEntries(section, &entries, flags | fTPFlags);
01239         break;
01240     case fTPFlags:
01241     {
01242         list<string> tl, pl;
01243         m_Transient ->EnumerateEntries(section, &tl, flags | fTPFlags);
01244         m_Persistent->EnumerateEntries(section, &pl, flags | fTPFlags);
01245         set_union(pl.begin(), pl.end(), tl.begin(), tl.end(),
01246                   back_inserter(entries), PNocase());
01247         break;
01248     }
01249     default:
01250         _TROUBLE;
01251     }
01252 }
01253 
01254 
01255 void CTwoLayerRegistry::x_ChildLockAction(FLockAction action)
01256 {
01257     ((*m_Transient).*action)();
01258     ((*m_Persistent).*action)();
01259 }
01260 
01261 
01262 void CTwoLayerRegistry::x_Clear(TFlags flags)
01263 {
01264     if (flags & fTransient) {
01265         m_Transient->Clear(flags | fTPFlags);
01266     }
01267     if (flags & fPersistent) {
01268         m_Persistent->Clear(flags | fTPFlags);
01269     }
01270 }
01271 
01272 
01273 bool CTwoLayerRegistry::x_Set(const string& section, const string& name,
01274                               const string& value, TFlags flags,
01275                               const string& comment)
01276 {
01277     if (flags & fPersistent) {
01278         return m_Persistent->Set(section, name, value, flags & ~fTPFlags,
01279                                  comment);
01280     } else {
01281         return m_Transient->Set(section, name, value, flags & ~fTPFlags,
01282                                 comment);
01283     }
01284 }
01285 
01286 
01287 bool CTwoLayerRegistry::x_SetComment(const string& comment,
01288                                      const string& section, const string& name,
01289                                      TFlags flags)
01290 {
01291     if (flags & fTransient) {
01292         return m_Transient->SetComment(comment, section, name,
01293                                        flags & ~fTPFlags);
01294     } else {
01295         return m_Persistent->SetComment(comment, section, name,
01296                                         flags & ~fTPFlags);
01297     }
01298 }
01299 
01300 
01301 
01302 //////////////////////////////////////////////////////////////////////
01303 //
01304 // CNcbiRegistry -- compound R/W registry with extra policy and
01305 // compatibility features.  (See below for CCompoundRWRegistry,
01306 // which has been factored out.)
01307 
01308 const char* CNcbiRegistry::sm_EnvRegName      = ".env";
01309 const char* CNcbiRegistry::sm_FileRegName     = ".file";
01310 const char* CNcbiRegistry::sm_OverrideRegName = ".overrides";
01311 const char* CNcbiRegistry::sm_SysRegName      = ".ncbirc";
01312 
01313 inline
01314 void CNcbiRegistry::x_Init(void)
01315 {
01316     CNcbiApplication* app = CNcbiApplication::Instance();
01317     if (app) {
01318         m_EnvRegistry.Reset(new CEnvironmentRegistry(app->SetEnvironment()));
01319     } else {
01320         m_EnvRegistry.Reset(new CEnvironmentRegistry);
01321     }
01322     x_Add(*m_EnvRegistry, ePriority_Environment, sm_EnvRegName);
01323 
01324     m_FileRegistry.Reset(new CTwoLayerRegistry);
01325     x_Add(*m_FileRegistry, ePriority_File, sm_FileRegName);
01326 
01327     m_SysRegistry.Reset(new CTwoLayerRegistry);
01328     x_Add(*m_SysRegistry, ePriority_Default - 1, sm_SysRegName);
01329 
01330     const char* override_path = getenv("NCBI_CONFIG_OVERRIDES");
01331     if (override_path  &&  *override_path) {
01332         m_OverrideRegistry.Reset(new CCompoundRWRegistry);
01333         CMetaRegistry::SEntry entry
01334             = CMetaRegistry::Load(override_path, CMetaRegistry::eName_AsIs,
01335                                   0, 0, m_OverrideRegistry.GetPointer());
01336         if (entry.registry) {
01337             if (entry.registry != m_OverrideRegistry) {
01338                 ERR_POST_X(5, Warning << "Resetting m_OverrideRegistry");
01339                 m_OverrideRegistry.Reset(entry.registry);
01340             }
01341             x_Add(*m_OverrideRegistry, ePriority_Overrides, sm_OverrideRegName);
01342         } else {
01343             ERR_POST_ONCE(Warning
01344                           << "NCBI_CONFIG_OVERRIDES names nonexistent file "
01345                           << override_path);
01346             m_OverrideRegistry.Reset();
01347         }
01348     }
01349 }
01350 
01351 
01352 CNcbiRegistry::CNcbiRegistry(void)
01353     : m_RuntimeOverrideCount(0)
01354 {
01355     x_Init();
01356 }
01357 
01358 
01359 CNcbiRegistry::CNcbiRegistry(CNcbiIstream& is, TFlags flags)
01360     : m_RuntimeOverrideCount(0)
01361 {
01362     x_CheckFlags("CNcbiRegistry::CNcbiRegistry", flags,
01363                  fTransient | fInternalSpaces | fWithNcbirc);
01364     x_Init();
01365     m_FileRegistry->Read(is, flags & ~fWithNcbirc);
01366     IncludeNcbircIfAllowed(flags);
01367 }
01368 
01369 
01370 CNcbiRegistry::~CNcbiRegistry()
01371 {
01372 }
01373 
01374 
01375 bool CNcbiRegistry::IncludeNcbircIfAllowed(TFlags flags)
01376 {
01377     if (flags & fWithNcbirc) {
01378         flags &= ~fWithNcbirc;
01379     } else {
01380         return false;
01381     }
01382 
01383     if (getenv("NCBI_DONT_USE_NCBIRC")) {
01384         return false;
01385     }
01386 
01387     if (HasEntry("NCBI", "DONT_USE_NCBIRC")) {
01388         return false;
01389     }
01390 
01391     try {
01392         CMetaRegistry::SEntry entry
01393             = CMetaRegistry::Load("ncbi", CMetaRegistry::eName_RcOrIni,
01394                                   0, flags, m_SysRegistry.GetPointer());
01395         if (entry.registry  &&  entry.registry != m_SysRegistry) {
01396             ERR_POST_X(5, Warning << "Resetting m_SysRegistry");
01397             m_SysRegistry.Reset(entry.registry);
01398         }
01399     } catch (CRegistryException& e) {
01400         ERR_POST_X(6, Critical << "CNcbiRegistry: "
01401                       "Syntax error in system-wide configuration file: "
01402                       << e.what());
01403         return false;
01404     }
01405 
01406     if ( !m_SysRegistry->Empty() ) {
01407         return true;
01408     }
01409 
01410     return false;
01411 }
01412 
01413 
01414 void CNcbiRegistry::x_Clear(TFlags flags) // XXX - should this do more?
01415 {
01416     CCompoundRWRegistry::x_Clear(flags);
01417     m_FileRegistry->Clear(flags);
01418 }
01419 
01420 
01421 IRWRegistry* CNcbiRegistry::x_Read(CNcbiIstream& is, TFlags flags)
01422 {
01423     // Normally, all settings should go to the main portion.  However,
01424     // loading an initial configuration file should instead go to the
01425     // file portion so that environment settings can take priority.
01426     if (FindByName(sm_MainRegName)->Empty()  &&  m_FileRegistry->Empty()) {
01427         m_FileRegistry->Read(is, flags);
01428         LoadBaseRegistries(flags);
01429         IncludeNcbircIfAllowed(flags);
01430         return NULL;
01431     } else if ((flags & fNoOverride) == 0) { // ensure proper layering
01432         CRef<CCompoundRWRegistry> crwreg(new CCompoundRWRegistry);
01433         crwreg->Read(is, flags);
01434         ++m_RuntimeOverrideCount;
01435         x_Add(*crwreg, ePriority_RuntimeOverrides + m_RuntimeOverrideCount,
01436               sm_OverrideRegName + NStr::IntToString(m_RuntimeOverrideCount));
01437         return crwreg.GetPointer();
01438     } else {
01439         // This will only affect the main registry, but still needs to
01440         // go through CCompoundRWRegistry::x_Set.
01441         return CCompoundRWRegistry::x_Read(is, flags);
01442     }
01443 }
01444 
01445 
01446 //////////////////////////////////////////////////////////////////////
01447 //
01448 // CCompoundRWRegistry -- general-purpose setup
01449 
01450 const char* CCompoundRWRegistry::sm_MainRegName       = ".main";
01451 const char* CCompoundRWRegistry::sm_BaseRegNamePrefix = ".base:";
01452 
01453 
01454 CCompoundRWRegistry::CCompoundRWRegistry(void)
01455     : m_MainRegistry(new CTwoLayerRegistry),
01456       m_AllRegistries(new CCompoundRegistry)
01457 {
01458     x_Add(*m_MainRegistry, CCompoundRegistry::ePriority_Max - 1,
01459           sm_MainRegName);
01460 }
01461 
01462 
01463 CCompoundRWRegistry::~CCompoundRWRegistry()
01464 {
01465 }
01466 
01467 
01468 CCompoundRWRegistry::TPriority CCompoundRWRegistry::GetCoreCutoff(void) const
01469 {
01470     return m_AllRegistries->GetCoreCutoff();
01471 }
01472 
01473 
01474 void CCompoundRWRegistry::SetCoreCutoff(TPriority prio)
01475 {
01476     m_AllRegistries->SetCoreCutoff(prio);
01477 }
01478 
01479 
01480 void CCompoundRWRegistry::Add(const IRegistry& reg, TPriority prio,
01481                               const string& name)
01482 {
01483     if (name.size() > 1  &&  name[0] == '.') {
01484         NCBI_THROW2(CRegistryException, eErr,
01485                     "The sub-registry name " + name + " is reserved.", 0);
01486     }
01487     if (prio > ePriority_MaxUser) {
01488         ERR_POST_X(7, Warning
01489                       << "Reserved priority value automatically downgraded.");
01490         prio = ePriority_MaxUser;
01491     }
01492     x_Add(reg, prio, name);
01493 }
01494 
01495 
01496 void CCompoundRWRegistry::Remove(const IRegistry& reg)
01497 {
01498     if (&reg == m_MainRegistry.GetPointer()) {
01499         NCBI_THROW2(CRegistryException, eErr,
01500                     "The primary portion of the registry may not be removed.",
01501                     0);
01502     } else {
01503         m_AllRegistries->Remove(reg);
01504     }
01505 }
01506 
01507 
01508 CConstRef<IRegistry> CCompoundRWRegistry::FindByName(const string& name) const
01509 {
01510     return m_AllRegistries->FindByName(name);
01511 }
01512 
01513 
01514 CConstRef<IRegistry> CCompoundRWRegistry::FindByContents(const string& section,
01515                                                          const string& entry,
01516                                                          TFlags flags) const
01517 {
01518     return m_AllRegistries->FindByContents(section, entry, flags);
01519 }
01520 
01521 
01522 bool CCompoundRWRegistry::LoadBaseRegistries(TFlags flags, int metareg_flags)
01523 {
01524     if (flags & fJustCore) {
01525         return false;
01526     }
01527 
01528     list<string> names;
01529     {{
01530         string s = m_MainRegistry->Get("NCBI", ".Inherits");
01531         if (s.empty()) {
01532             if (dynamic_cast<CNcbiRegistry*>(this) != NULL) {
01533                 _TRACE("LoadBaseRegistries(" << this
01534                        << "): trying file registry");
01535                 s = FindByName(CNcbiRegistry::sm_FileRegName)
01536                     ->Get("NCBI", ".Inherits");
01537             }
01538             if (s.empty()) {
01539                 return false;
01540             }
01541         }
01542         _TRACE("LoadBaseRegistries(" << this << "): using " << s);
01543         NStr::Split(s, ", ", names);
01544     }}
01545 
01546     typedef pair<string, CRef<IRWRegistry> > TNewBase;
01547     typedef vector<TNewBase> TNewBases;
01548     TNewBases bases;
01549     SIZE_TYPE initial_num_bases = m_BaseRegNames.size();
01550 
01551     ITERATE (list<string>, it, names) {
01552         if (m_BaseRegNames.find(*it) != m_BaseRegNames.end()) {
01553             continue;
01554         }
01555         CMetaRegistry::ENameStyle style
01556             = ((it->find('.') == NPOS)
01557                ? CMetaRegistry::eName_Ini : CMetaRegistry::eName_AsIs);
01558         CRef<CCompoundRWRegistry> reg2(new CCompoundRWRegistry);
01559         CMetaRegistry::SEntry entry2
01560             = CMetaRegistry::Load(*it, style, metareg_flags, flags,
01561                                   reg2.GetPointer());
01562         if (entry2.registry) {
01563             m_BaseRegNames.insert(*it);
01564             bases.push_back(TNewBase(*it, entry2.registry));
01565         }
01566     }
01567 
01568     for (SIZE_TYPE i = 0;  i < bases.size();  ++i) {
01569         x_Add(*bases[i].second, ePriority_MaxUser - initial_num_bases - i,
01570               sm_BaseRegNamePrefix + bases[i].first);
01571     }
01572 
01573     return !bases.empty();
01574 }
01575 
01576 
01577 bool CCompoundRWRegistry::x_Empty(TFlags flags) const
01578 {
01579     return m_AllRegistries->Empty(flags);
01580 }
01581 
01582 
01583 bool CCompoundRWRegistry::x_Modified(TFlags flags) const
01584 {
01585     return m_AllRegistries->Modified(flags);
01586 }
01587 
01588 
01589 void CCompoundRWRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01590 {
01591     if (modified) {
01592         m_MainRegistry->SetModifiedFlag(modified, flags);
01593     } else {
01594         // CCompoundRegistry only permits clearing...
01595         m_AllRegistries->SetModifiedFlag(modified, flags);
01596     }
01597 }
01598 
01599 
01600 const string& CCompoundRWRegistry::x_Get(const string& section,
01601                                          const string& name,
01602                                          TFlags flags) const
01603 {
01604     TClearedEntries::const_iterator it
01605         = m_ClearedEntries.find(s_FlatKey(section, name));
01606     if (it != m_ClearedEntries.end()) {
01607         flags &= ~it->second;
01608         if ( !(flags & ~fJustCore) ) {
01609             return kEmptyStr;
01610         }
01611     }
01612     return m_AllRegistries->Get(section, name, flags);
01613 }
01614 
01615 
01616 bool CCompoundRWRegistry::x_HasEntry(const string& section, const string& name,
01617                                      TFlags flags) const
01618 {
01619     TClearedEntries::const_iterator it
01620         = m_ClearedEntries.find(s_FlatKey(section, name));
01621     if (it != m_ClearedEntries.end()) {
01622         if ((flags & fCountCleared)  &&  (flags & it->second)) {
01623             return true;
01624         }
01625         flags &= ~it->second;
01626         if ( !(flags & ~fJustCore) ) {
01627             return false;
01628         }
01629     }
01630     return m_AllRegistries->HasEntry(section, name, flags);
01631 }
01632 
01633 
01634 const string& CCompoundRWRegistry::x_GetComment(const string& section,
01635                                                 const string& name,
01636                                                 TFlags flags) const
01637 {
01638     return m_AllRegistries->GetComment(section, name, flags);
01639 }
01640 
01641 
01642 void CCompoundRWRegistry::x_Enumerate(const string& section,
01643                                       list<string>& entries,
01644                                       TFlags flags) const
01645 {
01646     set<string> accum;
01647     REVERSE_ITERATE (CCompoundRegistry::TPriorityMap, it,
01648                      m_AllRegistries->m_PriorityMap) {
01649         if ((flags & fJustCore)  &&  (it->first < GetCoreCutoff())) {
01650             break;
01651         }
01652         list<string> tmp;
01653         it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
01654         ITERATE (list<string>, it2, tmp) {
01655             // avoid reporting cleared entries
01656             TClearedEntries::const_iterator ceci
01657                 = (flags & fCountCleared) ? m_ClearedEntries.end() 
01658                 : m_ClearedEntries.find(s_FlatKey(section, *it2));
01659             if (ceci == m_ClearedEntries.end()
01660                 ||  (flags & ~fJustCore & ~ceci->second)) {
01661                 accum.insert(*it2);
01662             }
01663         }
01664     }
01665     ITERATE (set<string>, it, accum) {
01666         entries.push_back(*it);
01667     }
01668 }
01669 
01670 
01671 void CCompoundRWRegistry::x_ChildLockAction(FLockAction action)
01672 {
01673     ((*m_AllRegistries).*action)();
01674 }
01675 
01676 
01677 void CCompoundRWRegistry::x_Clear(TFlags flags) // XXX - should this do more?
01678 {
01679     m_MainRegistry->Clear(flags);
01680 
01681     ITERATE (set<string>, it, m_BaseRegNames) {
01682         Remove(*FindByName(sm_BaseRegNamePrefix + *it));
01683     }
01684     m_BaseRegNames.clear();
01685 }
01686 
01687 
01688 bool CCompoundRWRegistry::x_Set(const string& section, const string& name,
01689                                 const string& value, TFlags flags,
01690                                 const string& comment)
01691 {
01692     TFlags flags2 = (flags & fPersistent) ? flags : (flags | fTransient);
01693     flags2 &= fLayerFlags;
01694     _TRACE('[' << section << ']' << name << " = " << value);
01695     if ((flags & fNoOverride)  &&  HasEntry(section, name, flags)) {
01696         return false;
01697     }
01698     if (value.empty()) {
01699         bool was_empty = Get(section, name, flags).empty();
01700         m_MainRegistry->Set(section, name, value, flags, comment);
01701         m_ClearedEntries[s_FlatKey(section, name)] |= flags2;
01702         return !was_empty;
01703     } else {
01704         TClearedEntries::iterator it
01705             = m_ClearedEntries.find(s_FlatKey(section, name));
01706         if (it != m_ClearedEntries.end()) {
01707             if ((it->second &= ~flags2) == 0) {
01708                 m_ClearedEntries.erase(it);
01709             }
01710         }
01711     }
01712     return m_MainRegistry->Set(section, name, value, flags, comment);
01713 }
01714 
01715 
01716 bool CCompoundRWRegistry::x_SetComment(const string& comment,
01717                                        const string& section,
01718                                        const string& name, TFlags flags)
01719 {
01720     return m_MainRegistry->SetComment(comment, section, name, flags);
01721 }
01722 
01723 
01724 IRWRegistry* CCompoundRWRegistry::x_Read(CNcbiIstream& in, TFlags flags)
01725 {
01726     TFlags lbr_flags = flags;
01727     if ((flags & fNoOverride) == 0  &&  !Empty(fPersistent) ) {
01728         lbr_flags |= fOverride;
01729     } else {
01730         lbr_flags &= ~fOverride;
01731     }
01732     IRWRegistry::x_Read(in, flags);
01733     LoadBaseRegistries(lbr_flags);
01734     return NULL;
01735 }
01736 
01737 
01738 void CCompoundRWRegistry::x_Add(const IRegistry& reg, TPriority prio,
01739                                 const string& name)
01740 {
01741     m_AllRegistries->Add(reg, prio, name);
01742 }
01743 
01744 
01745 //////////////////////////////////////////////////////////////////////
01746 //
01747 // CRegistryException -- error reporting
01748 
01749 const char* CRegistryException::GetErrCodeString(void) const
01750 {
01751     switch (GetErrCode()) {
01752     case eSection: return "eSection";
01753     case eEntry:   return "eEntry";
01754     case eValue:   return "eValue";
01755     case eErr:     return "eErr";
01756     default:       return CException::GetErrCodeString();
01757     }
01758 }
01759 
01760 
01761 END_NCBI_SCOPE
01762 
01763 

Generated on Sun Jul 5 23:05:19 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Jul 06 17:16:15 2009 by modify_doxy.py rev. 117643