1 /* $Id: metareg.cpp 593248 2019-09-16 12:18:31Z grichenk $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Aaron Ucko
27 *
28 * File Description:
29 * CMetaRegistry
30 *
31 * ===========================================================================
32 */
33
34 #include <ncbi_pch.hpp>
35 #include <corelib/metareg.hpp>
36 #include <corelib/ncbifile.hpp>
37 #include <corelib/ncbi_safe_static.hpp>
38 #include "ncbisys.hpp"
39
40 // strstream (aka CNcbiStrstream) remains the default for historical
41 // reasons; however, MIPSpro's implementation is buggy, yielding
42 // truncated results in some cases. :-/
43 #ifdef NCBI_COMPILER_MIPSPRO
44 # include <sstream>
45 typedef std::stringstream TStrStream;
46 #else
47 typedef ncbi::CNcbiStrstream TStrStream;
48 #endif
49
50 BEGIN_NCBI_SCOPE
51
52
53 #ifdef NCBI_OS_MSWIN
54 static const CTempString kConfigPathDelim = ";";
55 #else
56 static const CTempString kConfigPathDelim = ":;";
57 #endif
58
59 static CSafeStatic<CMetaRegistry> s_Instance;
60
61
Reload(CMetaRegistry::TFlags reload_flags)62 bool CMetaRegistry::SEntry::Reload(CMetaRegistry::TFlags reload_flags)
63 {
64 CFile file(actual_name);
65 if ( !file.Exists() ) {
66 _TRACE("No such registry file " << actual_name);
67 return false;
68 }
69 CMutexGuard GUARD(s_Instance->m_Mutex);
70 Int8 new_length = file.GetLength();
71 CTime new_timestamp;
72 file.GetTime(&new_timestamp);
73 if ( ((reload_flags & fAlwaysReload) != fAlwaysReload)
74 && new_length == length && new_timestamp == timestamp ) {
75 _TRACE("Registry file " << actual_name
76 << " appears not to have changed since last loaded");
77 return false;
78 }
79 CNcbiIfstream ifs(actual_name.c_str(), IOS_BASE::in | IOS_BASE::binary);
80 if ( !ifs.good() ) {
81 _TRACE("Unable to (re)open registry file " << actual_name);
82 return false;
83 }
84 IRWRegistry* dest = NULL;
85 if (registry) {
86 CRegistryWriteGuard REG_GUARD(*registry);
87 TRegFlags rflags = IRWRegistry::AssessImpact(reg_flags,
88 IRWRegistry::eRead);
89 if ((reload_flags & fKeepContents) || registry->Empty(rflags)) {
90 dest = registry->Read(ifs, reg_flags | IRegistry::fJustCore);
91 } else {
92 // Go through a temporary so errors (exceptions) won't
93 // cause *registry to be incomplete.
94 CMemoryRegistry tmp_reg(reg_flags & IRegistry::fCaseFlags);
95 TStrStream str;
96 tmp_reg.Read(ifs, reg_flags);
97 tmp_reg.Write(str, reg_flags);
98 str.seekg(0);
99 bool was_modified = registry->Modified(rflags);
100 registry->Clear(rflags);
101 dest = registry->Read(str, reg_flags | IRegistry::fJustCore);
102 if ( !was_modified ) {
103 registry->SetModifiedFlag(false, rflags);
104 }
105 }
106
107 if (dest) {
108 dest->WriteLock();
109 } else {
110 dest = registry.GetPointer();
111 }
112 } else {
113 registry.Reset(new CNcbiRegistry(ifs, reg_flags, file.GetDir()));
114 }
115
116 CCompoundRWRegistry* crwreg = dynamic_cast<CCompoundRWRegistry*>(dest);
117 if (crwreg) {
118 crwreg->LoadBaseRegistries(reg_flags, reload_flags, file.GetDir());
119 }
120
121 timestamp = new_timestamp;
122 length = new_length;
123 return true;
124 }
125
126
Instance(void)127 CMetaRegistry& CMetaRegistry::Instance(void)
128 {
129 return *s_Instance;
130 }
131
132
~CMetaRegistry()133 CMetaRegistry::~CMetaRegistry()
134 {
135 // XX - optionally save modified registries?
136 }
137
138
Load(const string & name,CMetaRegistry::ENameStyle style,CMetaRegistry::TFlags flags,IRegistry::TFlags reg_flags,IRWRegistry * reg,const string & path)139 CMetaRegistry::SEntry CMetaRegistry::Load(const string& name,
140 CMetaRegistry::ENameStyle style,
141 CMetaRegistry::TFlags flags,
142 IRegistry::TFlags reg_flags,
143 IRWRegistry* reg,
144 const string& path)
145 {
146 SEntry scratch_entry;
147 if ( reg && !reg->Empty() ) { // shouldn't share
148 flags |= fPrivate;
149 }
150 const SEntry& entry = Instance().x_Load(name, style, flags, reg_flags, reg,
151 name, style, scratch_entry, path);
152 if (reg && entry.registry && reg != entry.registry.GetPointer()) {
153 _ASSERT( !(flags & fPrivate) );
154 // Copy the relevant data in
155 if (&entry != &scratch_entry) {
156 scratch_entry = entry;
157 }
158 TRegFlags rflags = IRWRegistry::AssessImpact(reg_flags,
159 IRWRegistry::eRead);
160 TStrStream str;
161 entry.registry->Write(str, rflags);
162 str.seekg(0);
163 CRegistryWriteGuard REG_GUARD(*reg);
164 if ( !(flags & fKeepContents) ) {
165 bool was_modified = reg->Modified(rflags);
166 reg->Clear(rflags);
167 if ( !was_modified ) {
168 reg->SetModifiedFlag(false, rflags);
169 }
170 }
171 reg->Read(str, reg_flags | IRegistry::fJustCore);
172 scratch_entry.registry.Reset(reg);
173 CCompoundRWRegistry* crwreg = dynamic_cast<CCompoundRWRegistry*>(reg);
174 if (crwreg) {
175 REG_GUARD.Release();
176 string dir;
177 CDirEntry::SplitPath(scratch_entry.actual_name, &dir);
178 crwreg->LoadBaseRegistries(reg_flags, flags, dir);
179 }
180 return scratch_entry;
181 }
182 return entry;
183 }
184
185
186 const CMetaRegistry::SEntry&
x_Load(const string & name,CMetaRegistry::ENameStyle style,CMetaRegistry::TFlags flags,IRegistry::TFlags reg_flags,IRWRegistry * reg,const string & name0,CMetaRegistry::ENameStyle style0,CMetaRegistry::SEntry & scratch_entry,const string & path)187 CMetaRegistry::x_Load(const string& name, CMetaRegistry::ENameStyle style,
188 CMetaRegistry::TFlags flags,
189 IRegistry::TFlags reg_flags, IRWRegistry* reg,
190 const string& name0, CMetaRegistry::ENameStyle style0,
191 CMetaRegistry::SEntry& scratch_entry, const string& path)
192 {
193 _TRACE("CMetaRegistry::Load: looking for " << name);
194
195 CMutexGuard GUARD(m_Mutex);
196
197 if (flags & fPrivate) {
198 GUARD.Release();
199 }
200 else { // see if we already have it
201 TIndex::const_iterator iit
202 = m_Index.find(SKey(name, style, flags, reg_flags));
203 if (iit != m_Index.end()) {
204 _TRACE("found in cache");
205 _ASSERT(iit->second < m_Contents.size());
206 SEntry& result = m_Contents[iit->second];
207 result.Reload(flags);
208 return result;
209 }
210
211 NON_CONST_ITERATE (vector<SEntry>, it, m_Contents) {
212 if (it->flags != flags || it->reg_flags != reg_flags)
213 continue;
214
215 if (style == eName_AsIs && it->actual_name == name) {
216 _TRACE("found in cache");
217 it->Reload(flags);
218 return *it;
219 }
220 }
221 }
222
223 scratch_entry.actual_name = x_FindRegistry(name, style, path);
224 scratch_entry.flags = flags;
225 scratch_entry.reg_flags = reg_flags;
226 scratch_entry.registry.Reset(reg);
227 scratch_entry.length = 0;
228 if (scratch_entry.actual_name.empty()
229 || !scratch_entry.Reload(flags | fAlwaysReload | fKeepContents) ) {
230 scratch_entry.registry.Reset();
231 return scratch_entry;
232 } else if (flags & fPrivate) {
233 return scratch_entry;
234 } else {
235 m_Contents.push_back(scratch_entry);
236 m_Index[SKey(name0, style0, flags, reg_flags)]
237 = m_Contents.size() - 1;
238 return m_Contents.back();
239 }
240 }
241
242
x_FindRegistry(const string & name,ENameStyle style,const string & path)243 string CMetaRegistry::x_FindRegistry(const string& name, ENameStyle style,
244 const string& path)
245 {
246 _TRACE("CMetaRegistry::FindRegistry: looking for " << name);
247
248 if ( !path.empty() && !CDirEntry::IsAbsolutePath(name) ) {
249 const string& result
250 = x_FindRegistry(CDirEntry::ConcatPath(path, name), style);
251 if ( !result.empty() ) {
252 return result;
253 }
254 }
255
256 string dir;
257 CDirEntry::SplitPath(name, &dir, 0, 0);
258 if ( dir.empty() ) {
259 ITERATE (TSearchPath, it, m_SearchPath) {
260 const string& result
261 = x_FindRegistry(CDirEntry::MakePath(*it, name), style);
262 if ( !result.empty() ) {
263 return result;
264 }
265 }
266 } else {
267 switch (style) {
268 case eName_AsIs:
269 if (CFile(name).Exists()) {
270 string abs_name;
271 if ( CDirEntry::IsAbsolutePath(name) ) {
272 abs_name = name;
273 } else {
274 abs_name = CDirEntry::ConcatPath(CDir::GetCwd(), name);
275 }
276 return CDirEntry::NormalizePath(abs_name);
277 }
278 break;
279 case eName_Ini:
280 for (string name2(name); ; ) {
281 string result = x_FindRegistry(name2 + ".ini", eName_AsIs);
282 if ( !result.empty() ) {
283 return result;
284 }
285
286 string base, ext; // dir already known
287 CDirEntry::SplitPath(name2, 0, &base, &ext);
288 if ( ext.empty() ) {
289 break;
290 }
291 name2 = CDirEntry::MakePath(dir, base);
292 }
293 break;
294 case eName_DotRc: {
295 string base, ext;
296 CDirEntry::SplitPath(name, 0, &base, &ext);
297 return x_FindRegistry(CDirEntry::MakePath(dir, '.' + base, ext)
298 + "rc", eName_AsIs);
299 }
300 } // switch (style)
301 }
302 return kEmptyStr;
303 }
304
305
x_Reload(const string & path,IRWRegistry & reg,TFlags flags,TRegFlags reg_flags)306 bool CMetaRegistry::x_Reload(const string& path, IRWRegistry& reg,
307 TFlags flags, TRegFlags reg_flags)
308 {
309 SEntry* entryp = 0;
310 NON_CONST_ITERATE (vector<SEntry>, it, m_Contents) {
311 if (it->registry == ® || it->actual_name == path) {
312 entryp = &*it;
313 break;
314 }
315 }
316 if (entryp) {
317 return entryp->Reload(flags);
318 } else {
319 SEntry entry = Load(path, eName_AsIs, flags, reg_flags, ®);
320 _ASSERT(entry.registry.IsNull() || entry.registry == ®);
321 return !entry.registry.IsNull();
322 }
323 }
324
325
GetDefaultSearchPath(CMetaRegistry::TSearchPath & path)326 void CMetaRegistry::GetDefaultSearchPath(CMetaRegistry::TSearchPath& path)
327 {
328 path.clear();
329
330 const TXChar* cfg_path = NcbiSys_getenv(_TX("NCBI_CONFIG_PATH"));
331 TSearchPath path_tail;
332 if (cfg_path) {
333 NStr::Split(_T_STDSTRING(cfg_path), kConfigPathDelim, path);
334 TSearchPath::iterator it = find(path.begin(), path.end(), kEmptyStr);
335 if (it == path.end()) {
336 return;
337 } else {
338 path_tail.assign(it + 1, path.end());
339 path.erase(it, path.end());
340 }
341 }
342
343 if (NcbiSys_getenv(_TX("NCBI_DONT_USE_LOCAL_CONFIG")) == NULL) {
344 path.push_back(".");
345 string home = CDir::GetHome();
346 if ( !home.empty() ) {
347 path.push_back(home);
348 }
349 }
350
351 {{
352 const TXChar* ncbi = NcbiSys_getenv(_TX("NCBI"));
353 if (ncbi && *ncbi) {
354 path.push_back(_T_STDSTRING(ncbi));
355 }
356 }}
357
358 #ifdef NCBI_OS_MSWIN
359 {{
360 const TXChar* sysroot = NcbiSys_getenv(_TX("SYSTEMROOT"));
361 if (sysroot && *sysroot) {
362 path.push_back(_T_STDSTRING(sysroot));
363 }
364 }}
365 #else
366 path.push_back("/etc");
367 #endif
368
369 {{
370 CNcbiApplicationGuard the_app = CNcbiApplication::InstanceGuard();
371 if ( the_app ) {
372 const CNcbiArguments& args = the_app->GetArguments();
373 string dir = args.GetProgramDirname(eIgnoreLinks);
374 string dir2 = args.GetProgramDirname(eFollowLinks);
375 if (dir.size()) {
376 path.push_back(dir);
377 }
378 if (dir2.size() && dir2 != dir) {
379 path.push_back(dir2);
380 }
381 }
382 }}
383
384 if ( !path_tail.empty() ) {
385 ITERATE (TSearchPath, it, path_tail) {
386 if ( !it->empty() ) {
387 path.push_back(*it);
388 }
389 }
390 }
391 }
392
393
operator <(const SKey & k) const394 bool CMetaRegistry::SKey::operator <(const SKey& k) const
395 {
396 if (requested_name < k.requested_name) {
397 return true;
398 } else if (requested_name > k.requested_name) {
399 return false;
400 }
401
402 if (style < k.style) {
403 return true;
404 } else if (style > k.style) {
405 return false;
406 }
407
408 if (flags < k.flags) {
409 return true;
410 } else if (flags > k.flags) {
411 return false;
412 }
413
414 if (reg_flags < k.reg_flags) {
415 return true;
416 } else if (reg_flags > k.reg_flags) {
417 return false;
418 }
419
420 return false;
421 }
422
423
424 END_NCBI_SCOPE
425