1 /* $Id: env_reg.cpp 617973 2020-10-08 18:26:26Z 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 * Authors: Aaron Ucko
27 *
28 * File Description:
29 * Classes to support using environment variables as a backend for
30 * the registry framework.
31 *
32 */
33
34 #include <ncbi_pch.hpp>
35 #include <corelib/env_reg.hpp>
36 #include <corelib/ncbienv.hpp>
37 #include <corelib/error_codes.hpp>
38 #include <set>
39
40
41 #define NCBI_USE_ERRCODE_X Corelib_Env
42
43
44 BEGIN_NCBI_SCOPE
45
46
CEnvironmentRegistry(TFlags flags)47 CEnvironmentRegistry::CEnvironmentRegistry(TFlags flags)
48 : m_Env(new CNcbiEnvironment, eTakeOwnership),
49 m_Modified(false), m_Flags(flags)
50 {
51 AddMapper(*new CNcbiEnvRegMapper);
52 }
53
54
CEnvironmentRegistry(CNcbiEnvironment & env,EOwnership own,TFlags flags)55 CEnvironmentRegistry::CEnvironmentRegistry(CNcbiEnvironment& env,
56 EOwnership own, TFlags flags)
57 : m_Env(&env, own), m_Modified(false), m_Flags(flags)
58 {
59 AddMapper(*new CNcbiEnvRegMapper);
60 }
61
62
~CEnvironmentRegistry()63 CEnvironmentRegistry::~CEnvironmentRegistry()
64 {
65 }
66
67
AddMapper(const IEnvRegMapper & mapper,TPriority prio)68 void CEnvironmentRegistry::AddMapper(const IEnvRegMapper& mapper,
69 TPriority prio)
70 {
71 m_PriorityMap.insert(TPriorityMap::value_type
72 (prio, CConstRef<IEnvRegMapper>(&mapper)));
73 }
74
75
RemoveMapper(const IEnvRegMapper & mapper)76 void CEnvironmentRegistry::RemoveMapper(const IEnvRegMapper& mapper)
77 {
78 NON_CONST_ITERATE (TPriorityMap, it, m_PriorityMap) {
79 if (it->second == &mapper) {
80 m_PriorityMap.erase(it);
81 return; // mappers should be unique
82 }
83 }
84 // already returned if found...
85 NCBI_THROW2(CRegistryException, eErr,
86 "CEnvironmentRegistry::RemoveMapper:"
87 " unknown mapper (already removed?)", 0);
88 }
89
x_Empty(TFlags) const90 bool CEnvironmentRegistry::x_Empty(TFlags /*flags*/) const
91 {
92 // return (flags & fTransient) ? m_PriorityMap.empty() : true;
93 list<string> l;
94 string section, name;
95 ITERATE (TPriorityMap, mapper, m_PriorityMap) {
96 m_Env->Enumerate(l, mapper->second->GetPrefix());
97 ITERATE (list<string>, it, l) {
98 if (mapper->second->EnvToReg(*it, section, name)) {
99 return false;
100 }
101 }
102 }
103 return true;
104 }
105
106
x_Modified(TFlags flags) const107 bool CEnvironmentRegistry::x_Modified(TFlags flags) const
108 {
109 return (flags & fTransient) ? m_Modified : false;
110 }
111
112
x_SetModifiedFlag(bool modified,TFlags flags)113 void CEnvironmentRegistry::x_SetModifiedFlag(bool modified, TFlags flags)
114 {
115 if (flags & fTransient) {
116 m_Modified = modified;
117 }
118 }
119
120
x_Get(const string & section,const string & name,TFlags flags) const121 const string& CEnvironmentRegistry::x_Get(const string& section,
122 const string& name,
123 TFlags flags) const
124 {
125 bool found;
126 return x_Get(section, name, flags, found);
127 }
128
129
x_Get(const string & section,const string & name,TFlags flags,bool & found) const130 const string& CEnvironmentRegistry::x_Get(const string& section,
131 const string& name,
132 TFlags flags,
133 bool& found) const
134 {
135 if ((flags & fTPFlags) == fPersistent) {
136 return kEmptyStr;
137 }
138 REVERSE_ITERATE (TPriorityMap, it, m_PriorityMap) {
139 string var_name = it->second->RegToEnv(section, name);
140 const string* resultp = &m_Env->Get(var_name, &found);
141 if ((m_Flags & fCaseFlags) == 0 && !found) {
142 // try capitalizing the name
143 resultp = &m_Env->Get(NStr::ToUpper(var_name), &found);
144 }
145 if (found) {
146 return *resultp;
147 }
148 }
149 return kEmptyStr;
150 }
151
152
x_HasEntry(const string & section,const string & name,TFlags flags) const153 bool CEnvironmentRegistry::x_HasEntry(const string& section,
154 const string& name,
155 TFlags flags) const
156 {
157 bool found = false;
158 x_Get(section, name, flags, found);
159 return found;
160 }
161
162
x_GetComment(const string &,const string &,TFlags) const163 const string& CEnvironmentRegistry::x_GetComment(const string&, const string&,
164 TFlags) const
165 {
166 return kEmptyStr;
167 }
168
169
x_Enumerate(const string & section,list<string> & entries,TFlags flags) const170 void CEnvironmentRegistry::x_Enumerate(const string& section,
171 list<string>& entries,
172 TFlags flags) const
173 {
174 // Environment does not provide comments, so if we came for in-section
175 // comments, we can just return doing nothing
176 if (flags & fInSectionComments) {
177 return;
178 }
179 if ( !(flags & fTransient) ) {
180 return;
181 }
182
183 typedef set<string, PNocase> TEntrySet;
184
185 list<string> l;
186 TEntrySet entry_set;
187 string parsed_section, parsed_name;
188
189 ITERATE (TPriorityMap, mapper, m_PriorityMap) {
190 m_Env->Enumerate(l, mapper->second->GetPrefix());
191 ITERATE (list<string>, it, l) {
192 if (mapper->second->EnvToReg(*it, parsed_section, parsed_name)) {
193 if (section.empty()) {
194 entry_set.insert(parsed_section);
195 } else if (section == parsed_section) {
196 entry_set.insert(parsed_name);
197 }
198 }
199 }
200 }
201 ITERATE (TEntrySet, it, entry_set) {
202 entries.push_back(*it);
203 }
204 }
205
206
x_ChildLockAction(FLockAction)207 void CEnvironmentRegistry::x_ChildLockAction(FLockAction)
208 {
209 }
210
211
x_Clear(TFlags flags)212 void CEnvironmentRegistry::x_Clear(TFlags flags)
213 {
214 if (flags & fTransient) {
215 // m_Mappers.clear();
216 }
217 }
218
219
x_Set(const string & section,const string & name,const string & value,TFlags flags,const string &)220 bool CEnvironmentRegistry::x_Set(const string& section, const string& name,
221 const string& value, TFlags flags,
222 const string& /*comment*/)
223 {
224 REVERSE_ITERATE (TPriorityMap, it,
225 const_cast<const TPriorityMap&>(m_PriorityMap)) {
226 string var_name = it->second->RegToEnv(section, name);
227 if ( !var_name.empty() ) {
228 string cap_name = var_name;
229 NStr::ToUpper(cap_name);
230 string old_value = m_Env->Get(var_name);
231 if ((m_Flags & fCaseFlags) == 0 && old_value.empty()) {
232 old_value = m_Env->Get(cap_name);
233 }
234 if (MaybeSet(old_value, value, flags)) {
235 m_Env->Set(var_name, value);
236 // m_Env->Set(cap_name, value);
237 return true;
238 }
239 return false;
240 }
241 }
242
243 ERR_POST_X(1, Warning << "CEnvironmentRegistry::x_Set: "
244 "no mapping defined for [" << section << ']' << name);
245 return false;
246 }
247
248
x_Unset(const string & section,const string & name,TFlags)249 bool CEnvironmentRegistry::x_Unset(const string& section, const string& name,
250 TFlags /*flags*/)
251 {
252 bool result = false;
253 ITERATE (TPriorityMap, it, m_PriorityMap) {
254 string var_name = it->second->RegToEnv(section, name);
255 bool found;
256 if (var_name.empty()) {
257 continue;
258 }
259 m_Env->Get(var_name, &found);
260 if (found) {
261 result = true;
262 m_Env->Unset(var_name);
263 }
264 if ((m_Flags & fCaseFlags) == 0) {
265 string cap_name = var_name;
266 NStr::ToUpper(cap_name);
267 m_Env->Get(cap_name, &found);
268 if (found) {
269 result = true;
270 m_Env->Unset(cap_name);
271 }
272 }
273 }
274 return result;
275 }
276
x_SetComment(const string &,const string &,const string &,TFlags)277 bool CEnvironmentRegistry::x_SetComment(const string&, const string&,
278 const string&, TFlags)
279 {
280 ERR_POST_X(2, Warning
281 << "CEnvironmentRegistry::x_SetComment: unsupported operation");
282 return false;
283 }
284
285
CSimpleEnvRegMapper(const string & section,const string & prefix,const string & suffix)286 CSimpleEnvRegMapper::CSimpleEnvRegMapper(const string& section,
287 const string& prefix,
288 const string& suffix)
289 : m_Section(section), m_Prefix(prefix), m_Suffix(suffix)
290 {
291 }
292
293
RegToEnv(const string & section,const string & name) const294 string CSimpleEnvRegMapper::RegToEnv(const string& section, const string& name)
295 const
296 {
297 return (section == m_Section ? (m_Prefix + name + m_Suffix) : kEmptyStr);
298 }
299
300
EnvToReg(const string & env,string & section,string & name) const301 bool CSimpleEnvRegMapper::EnvToReg(const string& env, string& section,
302 string& name) const
303 {
304 SIZE_TYPE plen = m_Prefix.length();
305 SIZE_TYPE tlen = plen + m_Suffix.length();
306 if (env.size() > tlen && NStr::StartsWith(env, m_Prefix, NStr::eNocase)
307 && NStr::EndsWith(name, m_Suffix, NStr::eNocase)) {
308 section = m_Section;
309 name = env.substr(plen, env.length() - tlen);
310 return true;
311 }
312 return false;
313 }
314
315
GetPrefix(void) const316 string CSimpleEnvRegMapper::GetPrefix(void) const
317 {
318 return m_Prefix;
319 }
320
321
322 const char* CNcbiEnvRegMapper::sm_Prefix = "NCBI_CONFIG_";
323
324
RegToEnv(const string & section,const string & name) const325 string CNcbiEnvRegMapper::RegToEnv(const string& section, const string& name)
326 const
327 {
328 string result(sm_Prefix);
329 if (NStr::StartsWith(name, ".")) {
330 result += name.substr(1) + "__" + section;
331 } else {
332 result += "_" + section + "__" + name;
333 }
334 if (result.find_first_of(".-/ ") != NPOS) {
335 NStr::ReplaceInPlace(result, ".", "_DOT_");
336 NStr::ReplaceInPlace(result, "-", "_HYPHEN_");
337 NStr::ReplaceInPlace(result, "/", "_SLASH_");
338 NStr::ReplaceInPlace(result, " ", "_SPACE_");
339 }
340 return result;
341 }
342
343
344 inline
s_IdentifySubstitution(const CTempString & s)345 static char s_IdentifySubstitution(const CTempString& s) {
346 switch (s[0]) {
347 case 'D':
348 if (s.size() == 3 && s == "DOT") {
349 return '.';
350 }
351 break;
352 case 'H':
353 if (s.size() == 6 && s == "HYPHEN") {
354 return '-';
355 }
356 break;
357 case 'S':
358 if (s.size() == 5) {
359 if (s == "SLASH") {
360 return '/';
361 } else if (s == "SPACE") {
362 return ' ';
363 }
364 }
365 break;
366 default:
367 break;
368 }
369 return '\0';
370 }
371
372
EnvToReg(const string & env_in,string & section,string & name) const373 bool CNcbiEnvRegMapper::EnvToReg(const string& env_in, string& section,
374 string& name) const
375 {
376 static const SIZE_TYPE kPfxLen = strlen(sm_Prefix);
377 if (env_in.size() <= kPfxLen || !NStr::StartsWith(env_in, sm_Prefix) ) {
378 return false;
379 }
380 vector<CTempString> v;
381 NStr::Split(env_in, "_", v);
382 string env;
383 env.reserve(env_in.size());
384 for (const auto& it : v) {
385 SIZE_TYPE l = env.size();
386 char c = '\0';
387 if (&it != &v.back() && !env.empty() && env[l - 1] == '_'
388 && !it.empty()) {
389 c = s_IdentifySubstitution(it);
390 }
391 if (c == '\0') {
392 env += it;
393 if (&it != &v.back()) {
394 env += '_';
395 }
396 } else {
397 env[l - 1] = c;
398 }
399 }
400 // Make an offset until the first symbol that could be section name
401 // (alphanumeric character)
402 SIZE_TYPE section_start_pos = kPfxLen;
403 for ( ; section_start_pos < env.size(); section_start_pos++) {
404 if (isalnum(env[section_start_pos])) break;
405 }
406 SIZE_TYPE uu_pos = env.find("__", section_start_pos + 1);
407 if (uu_pos == NPOS || uu_pos == env.size() - 2) {
408 return false;
409 }
410 /* Parse section and entry names from the variable */
411 if (env[kPfxLen] == '_') { // regular entry
412 section = env.substr(kPfxLen + 1, uu_pos - kPfxLen - 1);
413 name = env.substr(uu_pos + 2);
414 } else {
415 name = env.substr(kPfxLen - 1, uu_pos - kPfxLen + 1);
416 _ASSERT(name[0] == '_');
417 name[0] = '.';
418 section = env.substr(uu_pos + 2);
419 }
420 if (!IRegistry::IsNameSection(section, IRegistry::fInternalSpaces)) {
421 ERR_POST(Info << "Invalid registry section name in environment "
422 "variable " << env);
423 }
424 if (!IRegistry::IsNameEntry(name, IRegistry::fInternalSpaces)) {
425 ERR_POST(Info << "Invalid registry entry name in environment "
426 "variable " << env);
427 }
428 return true;
429 }
430
431
GetPrefix(void) const432 string CNcbiEnvRegMapper::GetPrefix(void) const
433 {
434 return sm_Prefix;
435 }
436
437
438 END_NCBI_SCOPE
439