src/corelib/ncbireg.cpp

Go to the documentation of this file.
00001 /*  $Id: ncbireg.cpp 174500 2009-10-28 14:36:35Z 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(m_Flags)))
00900                 .first;
00901             sit->second.cleared = false;
00902         }
00903         SEntry& entry = sit->second.entries[name];
00904 #if 0
00905         if (entry.value == value) {
00906             if (entry.comment != comment) {
00907                 return MaybeSet(entry.comment, comment, flags);
00908             }
00909             return false; // not actually modified
00910         }
00911 #endif
00912         if ( !value.empty() ) {
00913             sit->second.cleared = false;
00914         } else if ( !entry.value.empty() ) {
00915             _ASSERT( !sit->second.cleared );
00916             bool cleared = true;
00917             ITERATE (TEntries, eit, sit->second.entries) {
00918                 if (&eit->second != &entry  &&  !eit->second.value.empty() ) {
00919                     cleared = false;
00920                     break;
00921                 }
00922             }
00923             sit->second.cleared = cleared;
00924         }
00925         if (MaybeSet(entry.value, value, flags)) {
00926             MaybeSet(entry.comment, comment, flags);
00927             return true;
00928         }
00929         return false;
00930     }
00931 }
00932 
00933 
00934 bool CMemoryRegistry::x_SetComment(const string& comment,
00935                                    const string& section, const string& name,
00936                                    TFlags flags)
00937 {
00938     if (comment.empty()  &&  (flags & fNoOverride)) {
00939         return false;
00940     }
00941     if (section.empty()) {
00942         return MaybeSet(m_RegistryComment, comment, flags);
00943     }
00944     TSections::iterator sit = m_Sections.find(section);
00945     if (sit == m_Sections.end()) {
00946         if (comment.empty()) {
00947             return false;
00948         } else {
00949             sit = m_Sections.insert(make_pair(section, SSection(m_Flags)))
00950                   .first;
00951             sit->second.cleared = false;
00952         }
00953     }
00954     TEntries& entries = sit->second.entries;
00955     if (name.empty()) {
00956         if (comment.empty()  &&  entries.empty()) {
00957             m_Sections.erase(sit);
00958             return true;
00959         } else {
00960             return MaybeSet(sit->second.comment, comment, flags);
00961         }
00962     }
00963     TEntries::iterator eit = entries.find(name);
00964     if (eit == entries.end()) {
00965         return false;
00966     } else {
00967         return MaybeSet(eit->second.comment, comment, flags);
00968     }
00969 }
00970 
00971 
00972 //////////////////////////////////////////////////////////////////////
00973 //
00974 // CCompoundRegistry
00975 
00976 void CCompoundRegistry::Add(const IRegistry& reg, TPriority prio,
00977                             const string& name)
00978 {
00979     // Needed for some operations that touch (only) metadata...
00980     IRegistry& nc_reg = const_cast<IRegistry&>(reg);
00981     // XXX - Check whether reg is a duplicate, at least in debug mode?
00982     m_PriorityMap.insert(TPriorityMap::value_type
00983                          (prio, CRef<IRegistry>(&nc_reg)));
00984     if (name.size()) {
00985         CRef<IRegistry>& preg = m_NameMap[name];
00986         if (preg) {
00987             NCBI_THROW2(CRegistryException, eErr,
00988                         "CCompoundRegistry::Add: name " + name
00989                         + " already in use", 0);
00990         } else {
00991             preg.Reset(&nc_reg);
00992         }
00993     }
00994 }
00995 
00996 
00997 void CCompoundRegistry::Remove(const IRegistry& reg)
00998 {
00999     NON_CONST_ITERATE (TNameMap, it, m_NameMap) {
01000         if (it->second == &reg) {
01001             m_NameMap.erase(it);
01002             break; // subregistries should be unique
01003         }
01004     }
01005     NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
01006         if (it->second == &reg) {
01007             m_PriorityMap.erase(it);
01008             return; // subregistries should be unique
01009         }
01010     }
01011     // already returned if found...
01012     NCBI_THROW2(CRegistryException, eErr,
01013                 "CCompoundRegistry::Remove:"
01014                 " reg is not a (direct) subregistry of this.", 0);
01015 }
01016 
01017 
01018 CConstRef<IRegistry> CCompoundRegistry::FindByName(const string& name) const
01019 {
01020     TNameMap::const_iterator it = m_NameMap.find(name);
01021     return it == m_NameMap.end() ? CConstRef<IRegistry>() : it->second;
01022 }
01023 
01024 
01025 CConstRef<IRegistry> CCompoundRegistry::FindByContents(const string& section,
01026                                                        const string& entry,
01027                                                        TFlags flags) const
01028 {
01029     TFlags has_entry_flags = (flags | fCountCleared) & ~fJustCore;
01030     REVERSE_ITERATE(TPriorityMap, it, m_PriorityMap) {
01031         if (it->second->HasEntry(section, entry, has_entry_flags)) {
01032             return it->second;
01033         }
01034     }
01035     return null;
01036 }
01037 
01038 
01039 bool CCompoundRegistry::x_Empty(TFlags flags) const
01040 {
01041     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01042         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01043             break;
01044         }
01045         if ( !it->second->Empty(flags & ~fJustCore) ) {
01046             return false;
01047         }
01048     }
01049     return true;
01050 }
01051 
01052 
01053 bool CCompoundRegistry::x_Modified(TFlags flags) const
01054 {
01055     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01056         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01057             break;
01058         }
01059         if ( it->second->Modified(flags & ~fJustCore) ) {
01060             return true;
01061         }
01062     }
01063     return false;
01064 }
01065 
01066 
01067 void CCompoundRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01068 {
01069     _ASSERT( !modified );
01070     for (TPriorityMap::reverse_iterator it = m_PriorityMap.rbegin();
01071          it != m_PriorityMap.rend();  ++it) {
01072         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01073             break;
01074         }
01075         it->second->SetModifiedFlag(modified, flags & ~fJustCore);
01076     }
01077 }
01078 
01079 
01080 const string& CCompoundRegistry::x_Get(const string& section,
01081                                        const string& name,
01082                                        TFlags flags) const
01083 {
01084     CConstRef<IRegistry> reg = FindByContents(section, name,
01085                                               flags & ~fJustCore);
01086     return reg ? reg->Get(section, name, flags & ~fJustCore) : kEmptyStr;
01087 }
01088 
01089 
01090 bool CCompoundRegistry::x_HasEntry(const string& section, const string& name,
01091                                    TFlags flags) const
01092 {
01093     return FindByContents(section, name, flags).NotEmpty();
01094 }
01095 
01096 
01097 const string& CCompoundRegistry::x_GetComment(const string& section,
01098                                               const string& name, TFlags flags)
01099     const
01100 {
01101     if ( m_PriorityMap.empty() ) {
01102         return kEmptyStr;
01103     }
01104 
01105     CConstRef<IRegistry> reg;
01106     if (section.empty()) {
01107         reg = m_PriorityMap.rbegin()->second;
01108     } else {
01109         reg = FindByContents(section, name, flags);
01110     }
01111     return reg ? reg->GetComment(section, name, flags & ~fJustCore)
01112         : kEmptyStr;
01113 }
01114 
01115 
01116 void CCompoundRegistry::x_Enumerate(const string& section,
01117                                     list<string>& entries, TFlags flags) const
01118 {
01119     set<string> accum;
01120     REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
01121         if ((flags & fJustCore)  &&  (it->first < m_CoreCutoff)) {
01122             break;
01123         }
01124         list<string> tmp;
01125         it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
01126         ITERATE (list<string>, it2, tmp) {
01127             accum.insert(*it2);
01128         }
01129     }
01130     ITERATE (set<string>, it, accum) {
01131         entries.push_back(*it);
01132     }
01133 }
01134 
01135 
01136 void CCompoundRegistry::x_ChildLockAction(FLockAction action)
01137 {
01138     NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
01139         ((*it->second).*action)();
01140     }
01141 }
01142 
01143 
01144 //////////////////////////////////////////////////////////////////////
01145 //
01146 // CTwoLayerRegistry
01147 
01148 CTwoLayerRegistry::CTwoLayerRegistry(IRWRegistry* persistent, TFlags flags)
01149     : m_Transient(CRegRef(new CMemoryRegistry(flags))),
01150       m_Persistent(CRegRef(persistent ? persistent
01151                            : new CMemoryRegistry(flags)))
01152 {
01153 }
01154 
01155 
01156 bool CTwoLayerRegistry::x_Empty(TFlags flags) const
01157 {
01158     // mask out fTPFlags whe 
01159     if (flags & fTransient  &&  !m_Transient->Empty(flags | fTPFlags) ) {
01160         return false;
01161     } else if (flags & fPersistent
01162                &&  !m_Persistent->Empty(flags | fTPFlags) ) {
01163         return false;
01164     } else {
01165         return true;
01166     }
01167 }
01168 
01169 
01170 bool CTwoLayerRegistry::x_Modified(TFlags flags) const
01171 {
01172     if (flags & fTransient  &&  m_Transient->Modified(flags | fTPFlags)) {
01173         return true;
01174     } else if (flags & fPersistent
01175                &&  m_Persistent->Modified(flags | fTPFlags)) {
01176         return true;
01177     } else {
01178         return false;
01179     }
01180 }
01181 
01182 
01183 void CTwoLayerRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01184 {
01185     if (flags & fTransient) {
01186         m_Transient->SetModifiedFlag(modified, flags | fTPFlags);
01187     }
01188     if (flags & fPersistent) {
01189         m_Persistent->SetModifiedFlag(modified, flags | fTPFlags);
01190     }
01191 }
01192 
01193 
01194 const string& CTwoLayerRegistry::x_Get(const string& section,
01195                                        const string& name, TFlags flags) const
01196 {
01197     if (flags & fTransient) {
01198         const string& result = m_Transient->Get(section, name,
01199                                                 flags & ~fTPFlags);
01200         if ( !result.empty()  ||  !(flags & fPersistent) ) {
01201             return result;
01202         }
01203     }
01204     return m_Persistent->Get(section, name, flags & ~fTPFlags);
01205 }
01206 
01207 
01208 bool CTwoLayerRegistry::x_HasEntry(const string& section, const string& name,
01209                                    TFlags flags) const
01210 {
01211     return (((flags & fTransient)
01212              &&  m_Transient->HasEntry(section, name, flags & ~fTPFlags))  ||
01213             ((flags & fPersistent)
01214              &&  m_Persistent->HasEntry(section, name, flags & ~fTPFlags)));
01215 }
01216 
01217 
01218 const string& CTwoLayerRegistry::x_GetComment(const string& section,
01219                                               const string& name,
01220                                               TFlags flags) const
01221 {
01222     if (flags & fTransient) {
01223         const string& result = m_Transient->GetComment(section, name,
01224                                                        flags & ~fTPFlags);
01225         if ( !result.empty()  ||  !(flags & fPersistent) ) {
01226             return result;
01227         }
01228     }
01229     return m_Persistent->GetComment(section, name, flags & ~fTPFlags);
01230 }
01231 
01232 
01233 void CTwoLayerRegistry::x_Enumerate(const string& section,
01234                                     list<string>& entries, TFlags flags) const
01235 {
01236     switch (flags & fTPFlags) {
01237     case fTransient:
01238         m_Transient->EnumerateEntries(section, &entries, flags | fTPFlags);
01239         break;
01240     case fPersistent:
01241         m_Persistent->EnumerateEntries(section, &entries, flags | fTPFlags);
01242         break;
01243     case fTPFlags:
01244     {
01245         list<string> tl, pl;
01246         m_Transient ->EnumerateEntries(section, &tl, flags | fTPFlags);
01247         m_Persistent->EnumerateEntries(section, &pl, flags | fTPFlags);
01248         set_union(pl.begin(), pl.end(), tl.begin(), tl.end(),
01249                   back_inserter(entries), PNocase());
01250         break;
01251     }
01252     default:
01253         _TROUBLE;
01254     }
01255 }
01256 
01257 
01258 void CTwoLayerRegistry::x_ChildLockAction(FLockAction action)
01259 {
01260     ((*m_Transient).*action)();
01261     ((*m_Persistent).*action)();
01262 }
01263 
01264 
01265 void CTwoLayerRegistry::x_Clear(TFlags flags)
01266 {
01267     if (flags & fTransient) {
01268         m_Transient->Clear(flags | fTPFlags);
01269     }
01270     if (flags & fPersistent) {
01271         m_Persistent->Clear(flags | fTPFlags);
01272     }
01273 }
01274 
01275 
01276 bool CTwoLayerRegistry::x_Set(const string& section, const string& name,
01277                               const string& value, TFlags flags,
01278                               const string& comment)
01279 {
01280     if (flags & fPersistent) {
01281         return m_Persistent->Set(section, name, value, flags & ~fTPFlags,
01282                                  comment);
01283     } else {
01284         return m_Transient->Set(section, name, value, flags & ~fTPFlags,
01285                                 comment);
01286     }
01287 }
01288 
01289 
01290 bool CTwoLayerRegistry::x_SetComment(const string& comment,
01291                                      const string& section, const string& name,
01292                                      TFlags flags)
01293 {
01294     if (flags & fTransient) {
01295         return m_Transient->SetComment(comment, section, name,
01296                                        flags & ~fTPFlags);
01297     } else {
01298         return m_Persistent->SetComment(comment, section, name,
01299                                         flags & ~fTPFlags);
01300     }
01301 }
01302 
01303 
01304 
01305 //////////////////////////////////////////////////////////////////////
01306 //
01307 // CNcbiRegistry -- compound R/W registry with extra policy and
01308 // compatibility features.  (See below for CCompoundRWRegistry,
01309 // which has been factored out.)
01310 
01311 const char* CNcbiRegistry::sm_EnvRegName      = ".env";
01312 const char* CNcbiRegistry::sm_FileRegName     = ".file";
01313 const char* CNcbiRegistry::sm_OverrideRegName = ".overrides";
01314 const char* CNcbiRegistry::sm_SysRegName      = ".ncbirc";
01315 
01316 inline
01317 void CNcbiRegistry::x_Init(void)
01318 {
01319     CNcbiApplication* app = CNcbiApplication::Instance();
01320     TFlags            cf  = m_Flags & fCaseFlags;
01321     if (app) {
01322         m_EnvRegistry.Reset(new CEnvironmentRegistry(app->SetEnvironment(),
01323                                                      eNoOwnership, cf));
01324     } else {
01325         m_EnvRegistry.Reset(new CEnvironmentRegistry(cf));
01326     }
01327     x_Add(*m_EnvRegistry, ePriority_Environment, sm_EnvRegName);
01328 
01329     m_FileRegistry.Reset(new CTwoLayerRegistry(NULL, cf));
01330     x_Add(*m_FileRegistry, ePriority_File, sm_FileRegName);
01331 
01332     m_SysRegistry.Reset(new CTwoLayerRegistry(NULL, cf));
01333     x_Add(*m_SysRegistry, ePriority_Default - 1, sm_SysRegName);
01334 
01335     const char* override_path = getenv("NCBI_CONFIG_OVERRIDES");
01336     if (override_path  &&  *override_path) {
01337         m_OverrideRegistry.Reset(new CCompoundRWRegistry(cf));
01338         CMetaRegistry::SEntry entry
01339             = CMetaRegistry::Load(override_path, CMetaRegistry::eName_AsIs,
01340                                   0, cf, m_OverrideRegistry.GetPointer());
01341         if (entry.registry) {
01342             if (entry.registry != m_OverrideRegistry) {
01343                 ERR_POST_X(5, Warning << "Resetting m_OverrideRegistry");
01344                 m_OverrideRegistry.Reset(entry.registry);
01345             }
01346             x_Add(*m_OverrideRegistry, ePriority_Overrides, sm_OverrideRegName);
01347         } else {
01348             ERR_POST_ONCE(Warning
01349                           << "NCBI_CONFIG_OVERRIDES names nonexistent file "
01350                           << override_path);
01351             m_OverrideRegistry.Reset();
01352         }
01353     }
01354 }
01355 
01356 
01357 CNcbiRegistry::CNcbiRegistry(TFlags flags)
01358     : m_RuntimeOverrideCount(0), m_Flags(flags)
01359 {
01360     x_Init();
01361 }
01362 
01363 
01364 CNcbiRegistry::CNcbiRegistry(CNcbiIstream& is, TFlags flags)
01365     : m_RuntimeOverrideCount(0), m_Flags(flags)
01366 {
01367     x_CheckFlags("CNcbiRegistry::CNcbiRegistry", flags,
01368                  fTransient | fInternalSpaces | fWithNcbirc | fCaseFlags);
01369     x_Init();
01370     m_FileRegistry->Read(is, flags & ~(fWithNcbirc | fCaseFlags));
01371     IncludeNcbircIfAllowed(flags & ~fCaseFlags);
01372 }
01373 
01374 
01375 CNcbiRegistry::~CNcbiRegistry()
01376 {
01377 }
01378 
01379 
01380 bool CNcbiRegistry::IncludeNcbircIfAllowed(TFlags flags)
01381 {
01382     if (flags & fWithNcbirc) {
01383         flags &= ~fWithNcbirc;
01384     } else {
01385         return false;
01386     }
01387 
01388     if (getenv("NCBI_DONT_USE_NCBIRC")) {
01389         return false;
01390     }
01391 
01392     if (HasEntry("NCBI", "DONT_USE_NCBIRC")) {
01393         return false;
01394     }
01395 
01396     try {
01397         CMetaRegistry::SEntry entry
01398             = CMetaRegistry::Load("ncbi", CMetaRegistry::eName_RcOrIni,
01399                                   0, flags, m_SysRegistry.GetPointer());
01400         if (entry.registry  &&  entry.registry != m_SysRegistry) {
01401             ERR_POST_X(5, Warning << "Resetting m_SysRegistry");
01402             m_SysRegistry.Reset(entry.registry);
01403         }
01404     } catch (CRegistryException& e) {
01405         ERR_POST_X(6, Critical << "CNcbiRegistry: "
01406                       "Syntax error in system-wide configuration file: "
01407                       << e.what());
01408         return false;
01409     }
01410 
01411     if ( !m_SysRegistry->Empty() ) {
01412         return true;
01413     }
01414 
01415     return false;
01416 }
01417 
01418 
01419 void CNcbiRegistry::x_Clear(TFlags flags) // XXX - should this do more?
01420 {
01421     CCompoundRWRegistry::x_Clear(flags);
01422     m_FileRegistry->Clear(flags);
01423 }
01424 
01425 
01426 IRWRegistry* CNcbiRegistry::x_Read(CNcbiIstream& is, TFlags flags)
01427 {
01428     // Normally, all settings should go to the main portion.  However,
01429     // loading an initial configuration file should instead go to the
01430     // file portion so that environment settings can take priority.
01431     CConstRef<IRegistry> main_reg(FindByName(sm_MainRegName));
01432     if (main_reg->Empty()  &&  m_FileRegistry->Empty()) {
01433         m_FileRegistry->Read(is, flags);
01434         LoadBaseRegistries(flags);
01435         IncludeNcbircIfAllowed(flags);
01436         return NULL;
01437     } else if ((flags & fNoOverride) == 0) { // ensure proper layering
01438         CRef<CCompoundRWRegistry> crwreg
01439             (new CCompoundRWRegistry(m_Flags & fCaseFlags));
01440         crwreg->Read(is, flags);
01441         // Allow contents to override anything previously Set() directly.
01442         IRWRegistry& nc_main_reg
01443             = dynamic_cast<IRWRegistry&>(const_cast<IRegistry&>(*main_reg));
01444         if ((flags & fTransient) == 0) {
01445             flags |= fPersistent;
01446         }
01447         list<string> sections;
01448         crwreg->EnumerateSections(&sections, flags | fCountCleared);
01449         ITERATE (list<string>, sit, sections) {
01450             list<string> entries;
01451             crwreg->EnumerateEntries(*sit, &entries, flags | fCountCleared);
01452             ITERATE (list<string>, eit, entries) {
01453                 // In principle, it should be possible to clear the setting
01454                 // in nc_main_reg rather than duplicating it; however,
01455                 // letting the entry in crwreg be visible would require
01456                 // having CCompoundRegistry::FindByContents no longer force
01457                 // fCountCleared, which breaks other corner cases (as shown
01458                 // by test_sub_reg). :-/
01459                 if (nc_main_reg.HasEntry(*sit, *eit, flags | fCountCleared)) {
01460                     nc_main_reg.Set(*sit, *eit, crwreg->Get(*sit, *eit), flags);
01461                 }
01462             }
01463         }
01464         ++m_RuntimeOverrideCount;
01465         x_Add(*crwreg, ePriority_RuntimeOverrides + m_RuntimeOverrideCount,
01466               sm_OverrideRegName + NStr::IntToString(m_RuntimeOverrideCount));
01467         return crwreg.GetPointer();
01468     } else {
01469         // This will only affect the main registry, but still needs to
01470         // go through CCompoundRWRegistry::x_Set.
01471         return CCompoundRWRegistry::x_Read(is, flags);
01472     }
01473 }
01474 
01475 
01476 //////////////////////////////////////////////////////////////////////
01477 //
01478 // CCompoundRWRegistry -- general-purpose setup
01479 
01480 const char* CCompoundRWRegistry::sm_MainRegName       = ".main";
01481 const char* CCompoundRWRegistry::sm_BaseRegNamePrefix = ".base:";
01482 
01483 
01484 CCompoundRWRegistry::CCompoundRWRegistry(TFlags flags)
01485     : m_MainRegistry(new CTwoLayerRegistry),
01486       m_AllRegistries(new CCompoundRegistry),
01487       m_Flags(flags)
01488 {
01489     x_Add(*m_MainRegistry, CCompoundRegistry::ePriority_Max - 1,
01490           sm_MainRegName);
01491 }
01492 
01493 
01494 CCompoundRWRegistry::~CCompoundRWRegistry()
01495 {
01496 }
01497 
01498 
01499 CCompoundRWRegistry::TPriority CCompoundRWRegistry::GetCoreCutoff(void) const
01500 {
01501     return m_AllRegistries->GetCoreCutoff();
01502 }
01503 
01504 
01505 void CCompoundRWRegistry::SetCoreCutoff(TPriority prio)
01506 {
01507     m_AllRegistries->SetCoreCutoff(prio);
01508 }
01509 
01510 
01511 void CCompoundRWRegistry::Add(const IRegistry& reg, TPriority prio,
01512                               const string& name)
01513 {
01514     if (name.size() > 1  &&  name[0] == '.') {
01515         NCBI_THROW2(CRegistryException, eErr,
01516                     "The sub-registry name " + name + " is reserved.", 0);
01517     }
01518     if (prio > ePriority_MaxUser) {
01519         ERR_POST_X(7, Warning
01520                       << "Reserved priority value automatically downgraded.");
01521         prio = ePriority_MaxUser;
01522     }
01523     x_Add(reg, prio, name);
01524 }
01525 
01526 
01527 void CCompoundRWRegistry::Remove(const IRegistry& reg)
01528 {
01529     if (&reg == m_MainRegistry.GetPointer()) {
01530         NCBI_THROW2(CRegistryException, eErr,
01531                     "The primary portion of the registry may not be removed.",
01532                     0);
01533     } else {
01534         m_AllRegistries->Remove(reg);
01535     }
01536 }
01537 
01538 
01539 CConstRef<IRegistry> CCompoundRWRegistry::FindByName(const string& name) const
01540 {
01541     return m_AllRegistries->FindByName(name);
01542 }
01543 
01544 
01545 CConstRef<IRegistry> CCompoundRWRegistry::FindByContents(const string& section,
01546                                                          const string& entry,
01547                                                          TFlags flags) const
01548 {
01549     return m_AllRegistries->FindByContents(section, entry, flags);
01550 }
01551 
01552 
01553 bool CCompoundRWRegistry::LoadBaseRegistries(TFlags flags, int metareg_flags)
01554 {
01555     if (flags & fJustCore) {
01556         return false;
01557     }
01558 
01559     list<string> names;
01560     {{
01561         string s = m_MainRegistry->Get("NCBI", ".Inherits");
01562         if (s.empty()) {
01563             if (dynamic_cast<CNcbiRegistry*>(this) != NULL) {
01564                 _TRACE("LoadBaseRegistries(" << this
01565                        << "): trying file registry");
01566                 s = FindByName(CNcbiRegistry::sm_FileRegName)
01567                     ->Get("NCBI", ".Inherits");
01568             }
01569             if (s.empty()) {
01570                 return false;
01571             }
01572         }
01573         _TRACE("LoadBaseRegistries(" << this << "): using " << s);
01574         NStr::Split(s, ", ", names);
01575     }}
01576 
01577     typedef pair<string, CRef<IRWRegistry> > TNewBase;
01578     typedef vector<TNewBase> TNewBases;
01579     TNewBases bases;
01580     SIZE_TYPE initial_num_bases = m_BaseRegNames.size();
01581 
01582     ITERATE (list<string>, it, names) {
01583         if (m_BaseRegNames.find(*it) != m_BaseRegNames.end()) {
01584             continue;
01585         }
01586         CMetaRegistry::ENameStyle style
01587             = ((it->find('.') == NPOS)
01588                ? CMetaRegistry::eName_Ini : CMetaRegistry::eName_AsIs);
01589         CRef<CCompoundRWRegistry> reg2
01590             (new CCompoundRWRegistry(m_Flags & fCaseFlags));
01591         CMetaRegistry::SEntry entry2
01592             = CMetaRegistry::Load(*it, style, metareg_flags, flags,
01593                                   reg2.GetPointer());
01594         if (entry2.registry) {
01595             m_BaseRegNames.insert(*it);
01596             bases.push_back(TNewBase(*it, entry2.registry));
01597         }
01598     }
01599 
01600     for (SIZE_TYPE i = 0;  i < bases.size();  ++i) {
01601         x_Add(*bases[i].second, ePriority_MaxUser - initial_num_bases - i,
01602               sm_BaseRegNamePrefix + bases[i].first);
01603     }
01604 
01605     return !bases.empty();
01606 }
01607 
01608 
01609 bool CCompoundRWRegistry::x_Empty(TFlags flags) const
01610 {
01611     return m_AllRegistries->Empty(flags);
01612 }
01613 
01614 
01615 bool CCompoundRWRegistry::x_Modified(TFlags flags) const
01616 {
01617     return m_AllRegistries->Modified(flags);
01618 }
01619 
01620 
01621 void CCompoundRWRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
01622 {
01623     if (modified) {
01624         m_MainRegistry->SetModifiedFlag(modified, flags);
01625     } else {
01626         // CCompoundRegistry only permits clearing...
01627         m_AllRegistries->SetModifiedFlag(modified, flags);
01628     }
01629 }
01630 
01631 
01632 const string& CCompoundRWRegistry::x_Get(const string& section,
01633                                          const string& name,
01634                                          TFlags flags) const
01635 {
01636     TClearedEntries::const_iterator it
01637         = m_ClearedEntries.find(s_FlatKey(section, name));
01638     if (it != m_ClearedEntries.end()) {
01639         flags &= ~it->second;
01640         if ( !(flags & ~fJustCore) ) {
01641             return kEmptyStr;
01642         }
01643     }
01644     return m_AllRegistries->Get(section, name, flags);
01645 }
01646 
01647 
01648 bool CCompoundRWRegistry::x_HasEntry(const string& section, const string& name,
01649                                      TFlags flags) const
01650 {
01651     TClearedEntries::const_iterator it
01652         = m_ClearedEntries.find(s_FlatKey(section, name));
01653     if (it != m_ClearedEntries.end()) {
01654         if ((flags & fCountCleared)  &&  (flags & it->second)) {
01655             return true;
01656         }
01657         flags &= ~it->second;
01658         if ( !(flags & ~fJustCore) ) {
01659             return false;
01660         }
01661     }
01662     return m_AllRegistries->HasEntry(section, name, flags);
01663 }
01664 
01665 
01666 const string& CCompoundRWRegistry::x_GetComment(const string& section,
01667                                                 const string& name,
01668                                                 TFlags flags) const
01669 {
01670     return m_AllRegistries->GetComment(section, name, flags);
01671 }
01672 
01673 
01674 void CCompoundRWRegistry::x_Enumerate(const string& section,
01675                                       list<string>& entries,
01676                                       TFlags flags) const
01677 {
01678     set<string> accum;
01679     REVERSE_ITERATE (CCompoundRegistry::TPriorityMap, it,
01680                      m_AllRegistries->m_PriorityMap) {
01681         if ((flags & fJustCore)  &&  (it->first < GetCoreCutoff())) {
01682             break;
01683         }
01684         list<string> tmp;
01685         it->second->EnumerateEntries(section, &tmp, flags & ~fJustCore);
01686         ITERATE (list<string>, it2, tmp) {
01687             // avoid reporting cleared entries
01688             TClearedEntries::const_iterator ceci
01689                 = (flags & fCountCleared) ? m_ClearedEntries.end() 
01690                 : m_ClearedEntries.find(s_FlatKey(section, *it2));
01691             if (ceci == m_ClearedEntries.end()
01692                 ||  (flags & ~fJustCore & ~ceci->second)) {
01693                 accum.insert(*it2);
01694             }
01695         }
01696     }
01697     ITERATE (set<string>, it, accum) {
01698         entries.push_back(*it);
01699     }
01700 }
01701 
01702 
01703 void CCompoundRWRegistry::x_ChildLockAction(FLockAction action)
01704 {
01705     ((*m_AllRegistries).*action)();
01706 }
01707 
01708 
01709 void CCompoundRWRegistry::x_Clear(TFlags flags) // XXX - should this do more?
01710 {
01711     m_MainRegistry->Clear(flags);
01712 
01713     ITERATE (set<string>, it, m_BaseRegNames) {
01714         Remove(*FindByName(sm_BaseRegNamePrefix + *it));
01715     }
01716     m_BaseRegNames.clear();
01717 }
01718 
01719 
01720 bool CCompoundRWRegistry::x_Set(const string& section, const string& name,
01721                                 const string& value, TFlags flags,
01722                                 const string& comment)
01723 {
01724     TFlags flags2 = (flags & fPersistent) ? flags : (flags | fTransient);
01725     flags2 &= fLayerFlags;
01726     _TRACE('[' << section << ']' << name << " = " << value);
01727     if ((flags & fNoOverride)  &&  HasEntry(section, name, flags)) {
01728         return false;
01729     }
01730     if (value.empty()) {
01731         bool was_empty = Get(section, name, flags).empty();
01732         m_MainRegistry->Set(section, name, value, flags, comment);
01733         m_ClearedEntries[s_FlatKey(section, name)] |= flags2;
01734         return !was_empty;
01735     } else {
01736         TClearedEntries::iterator it
01737             = m_ClearedEntries.find(s_FlatKey(section, name));
01738         if (it != m_ClearedEntries.end()) {
01739             if ((it->second &= ~flags2) == 0) {
01740                 m_ClearedEntries.erase(it);
01741             }
01742         }
01743     }
01744     return m_MainRegistry->Set(section, name, value, flags, comment);
01745 }
01746 
01747 
01748 bool CCompoundRWRegistry::x_SetComment(const string& comment,
01749                                        const string& section,
01750                                        const string& name, TFlags flags)
01751 {
01752     return m_MainRegistry->SetComment(comment, section, name, flags);
01753 }
01754 
01755 
01756 IRWRegistry* CCompoundRWRegistry::x_Read(CNcbiIstream& in, TFlags flags)
01757 {
01758     TFlags lbr_flags = flags;
01759     if ((flags & fNoOverride) == 0  &&  !Empty(fPersistent) ) {
01760         lbr_flags |= fOverride;
01761     } else {
01762         lbr_flags &= ~fOverride;
01763     }
01764     IRWRegistry::x_Read(in, flags);
01765     LoadBaseRegistries(lbr_flags);
01766     return NULL;
01767 }
01768 
01769 
01770 void CCompoundRWRegistry::x_Add(const IRegistry& reg, TPriority prio,
01771                                 const string& name)
01772 {
01773     m_AllRegistries->Add(reg, prio, name);
01774 }
01775 
01776 
01777 //////////////////////////////////////////////////////////////////////
01778 //
01779 // CRegistryException -- error reporting
01780 
01781 const char* CRegistryException::GetErrCodeString(void) const
01782 {
01783     switch (GetErrCode()) {
01784     case eSection: return "eSection";
01785     case eEntry:   return "eEntry";
01786     case eValue:   return "eValue";
01787     case eErr:     return "eErr";
01788     default:       return CException::GetErrCodeString();
01789     }
01790 }
01791 
01792 
01793 END_NCBI_SCOPE
01794 
01795 

Generated on Sun Nov 22 23:35:59 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Nov 23 19:53:55 2009 by modify_doxy.py rev. 173732