Build Version : T3.0.0.27786 Firebird 3.0 Unstable (writeBuildNum.sh,v 1.27961 2010/02/28 18:00:43 alexpeshkof ) Update of /cvsroot/firebird/firebird2/src/common/config In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv14999/src/common/config Modified Files: config.cpp config.h config_file.cpp config_file.h Added Files: ConfigCache.cpp ConfigCache.h Removed Files: config_impl.h Log Message: Extend format of firebird configuration file with a set of additional parameters for any parameter. Use traditional firebird configuration files format in all places (intl, trace, etc.). Add per-database configuration facility to aliases.conf (currently 15 parameters from firebird.conf). --- ConfigCache.cpp ADDED --- --- ConfigCache.h ADDED --- Index: config.cpp =================================================================== RCS file: /cvsroot/firebird/firebird2/src/common/config/config.cpp,v retrieving revision 1.106 retrieving revision 1.107 diff -b -U3 -r1.106 -r1.107 --- config.cpp 28 Dec 2009 13:25:46 -0000 1.106 +++ config.cpp 28 Feb 2010 18:00:40 -0000 1.107 @@ -22,17 +22,78 @@ #include "firebird.h" -#include "../../common/config/config.h" -#include "../../common/config/config_impl.h" -#include "../../common/config/config_file.h" -#include "../../common/classes/init.h" +#include "../common/config/config.h" +#include "../common/config/config_file.h" +#include "../jrd/os/config_root.h" +#include "../common/classes/init.h" +#include "../jrd/os/fbsyslog.h" #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif // config_file works with OS case-sensitivity -typedef Firebird::PathName string; +typedef Firebird::PathName String; + +namespace { + +/****************************************************************************** + * + * firebird.conf implementation + */ + +class ConfigImpl : public ConfigRoot +{ +public: + explicit ConfigImpl(Firebird::MemoryPool& p) : ConfigRoot(p) + { + try + { + root_dir = getRootDirectory(); + + ConfigFile file(getConfigFilePath(), ConfigFile::EXCEPTION_ON_ERROR | ConfigFile::NO_MACRO); + defaultConfig = new Config(file); + } + catch (const Firebird::fatal_exception& ex) + { + Firebird::Syslog::Record(Firebird::Syslog::Error, ex.what()); + (Firebird::Arg::Gds(isc_random) << "Problems with master configuration file - " + "inform server admin please").raise(); + } + } + +/* void changeDefaultConfig(Config* newConfig) + { + defaultConfig = newConfig; + } + */ + Firebird::RefPtr<Config> getDefaultConfig() const + { + return defaultConfig; + } + + const char* getCachedRootDir() + { + return root_dir; + } + +private: + const char* root_dir; + Firebird::RefPtr<Config> defaultConfig; + + ConfigImpl(const ConfigImpl&); + void operator=(const ConfigImpl&); + +}; + +/****************************************************************************** + * + * Static instance of the system configuration file + */ + +Firebird::InitInstance<ConfigImpl> firebirdConf; + +} // anonymous namespace /****************************************************************************** * @@ -52,7 +113,7 @@ const char* AmTrusted = "trusted"; const char* AmMixed = "mixed"; -const ConfigImpl::ConfigEntry ConfigImpl::entries[] = +const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] = { {TYPE_STRING, "RootDirectory", (ConfigValue) 0}, {TYPE_INTEGER, "TempBlockSize", (ConfigValue) 1048576}, // bytes @@ -127,43 +188,44 @@ /****************************************************************************** * - * Static instance of the system configuration file + * Config routines */ -static Firebird::InitInstance<ConfigImpl> sysConfig; +Config::Config(const ConfigFile& file) +{ + // Iterate through the known configuration entries -/****************************************************************************** - * - * Implementation interface - */ + for (unsigned int i = 0; i < MAX_CONFIG_KEY; i++) + { + values[i] = entries[i].default_value; + } + + loadValues(file); +} -ConfigImpl::ConfigImpl(MemoryPool& p) : ConfigRoot(p) +Config::Config(const ConfigFile& file, const Config& base) { - // Prepare some stuff + // Iterate through the known configuration entries - ConfigFile file(p, true); - root_dir = getRootDirectory(); - const int size = FB_NELEM(entries); - values = FB_NEW(p) ConfigValue[size]; + for (unsigned int i = 0; i < MAX_CONFIG_KEY; i++) + { + values[i] = base.values[i]; + } - //string val_sep = ","; - file.setConfigFilePath(getConfigFilePath()); + loadValues(file); +} +void Config::loadValues(const ConfigFile& file) +{ // Iterate through the known configuration entries - for (int i = 0; i < size; i++) + for (int i = 0; i < MAX_CONFIG_KEY; i++) { - const ConfigEntry entry = entries[i]; - const string value = getValue(file, entries[i].key); + const ConfigEntry& entry = entries[i]; + const String value = getValue(file, entry.key); - if (!value.length()) + if (value.length()) { - // Assign the default value - - values[i] = entries[i].default_value; - continue; - } - // Assign the actual value switch (entry.data_type) @@ -175,26 +237,28 @@ values[i] = (ConfigValue) asInteger(value); break; case TYPE_STRING: + values[i] = (ConfigValue) asString(value); + break; + //case TYPE_STRING_VECTOR: + // break; + } + + if (entry.data_type == TYPE_STRING && values[i] != entry.default_value) { - const char* src = asString(value); - char* dst = FB_NEW(p) char[strlen(src) + 1]; + const char* src = (const char*) values[i]; + char* dst = FB_NEW(getPool()) char[strlen(src) + 1]; strcpy(dst, src); values[i] = (ConfigValue) dst; } - break; - //case TYPE_STRING_VECTOR: - // break; } } } -ConfigImpl::~ConfigImpl() +Config::~Config() { - const int size = FB_NELEM(entries); - // Free allocated memory - for (int i = 0; i < size; i++) + for (int i = 0; i < MAX_CONFIG_KEY; i++) { if (values[i] == entries[i].default_value) continue; @@ -208,37 +272,48 @@ // break; } } - delete[] values; } -string ConfigImpl::getValue(ConfigFile& file, const ConfigKey key) +String Config::getValue(const ConfigFile& file, ConfigName key) { - return file.doesKeyExist(key) ? file.getString(key) : ""; + const ConfigFile::Parameter* p = file.findParameter(key); + return p ? p->value : ""; } -int ConfigImpl::asInteger(const string &value) +int Config::asInteger(const String &value) { return atoi(value.data()); } -bool ConfigImpl::asBoolean(const string &value) +bool Config::asBoolean(const String &value) { return (atoi(value.data()) != 0); } -const char* ConfigImpl::asString(const string &value) +const char* Config::asString(const String &value) { return value.c_str(); } +template <typename T> +T Config::get(Config::ConfigKey key) const +{ + return (T) values[key]; +} + /****************************************************************************** * * Public interface */ +const Firebird::RefPtr<Config> Config::getDefaultConfig() +{ + return firebirdConf().getDefaultConfig(); +} + const char* Config::getInstallDirectory() { - return sysConfig().getInstallDirectory(); + return firebirdConf().getInstallDirectory(); } static Firebird::PathName* rootFromCommandLine = 0; @@ -263,39 +338,39 @@ return rootFromCommandLine->c_str(); } - const char* result = (char*) sysConfig().values[KEY_ROOT_DIRECTORY]; - return result ? result : sysConfig().root_dir; + const char* result = (char*) getDefaultConfig()->values[KEY_ROOT_DIRECTORY]; + return result ? result : firebirdConf().getCachedRootDir(); } -int Config::getTempBlockSize() +int Config::getTempBlockSize() const { - return (int) sysConfig().values[KEY_TEMP_BLOCK_SIZE]; + return get<int>(KEY_TEMP_BLOCK_SIZE); } -int Config::getTempCacheLimit() +int Config::getTempCacheLimit() const { - int v = (int) sysConfig().values[KEY_TEMP_CACHE_LIMIT]; + int v = get<int>(KEY_TEMP_CACHE_LIMIT); return v < 0 ? 0 : v; } bool Config::getRemoteFileOpenAbility() { - return (bool) sysConfig().values[KEY_REMOTE_FILE_OPEN_ABILITY]; + return (bool) getDefaultConfig()->values[KEY_REMOTE_FILE_OPEN_ABILITY]; } int Config::getGuardianOption() { - return (int) sysConfig().values[KEY_GUARDIAN_OPTION]; + return (int) getDefaultConfig()->values[KEY_GUARDIAN_OPTION]; } int Config::getCpuAffinityMask() { - return (int) sysConfig().values[KEY_CPU_AFFINITY_MASK]; + return (int) getDefaultConfig()->values[KEY_CPU_AFFINITY_MASK]; } int Config::getTcpRemoteBufferSize() { - int rc = (int) sysConfig().values[KEY_TCP_REMOTE_BUFFER_SIZE]; + int rc = (int) getDefaultConfig()->values[KEY_TCP_REMOTE_BUFFER_SIZE]; if (rc < 1448) rc = 1448; if (rc > MAX_SSHORT) @@ -305,57 +380,57 @@ bool Config::getTcpNoNagle() { - return (bool) sysConfig().values[KEY_TCP_NO_NAGLE]; + return (bool) getDefaultConfig()->values[KEY_TCP_NO_NAGLE]; } -int Config::getDefaultDbCachePages() +int Config::getDefaultDbCachePages() const { - return (int) sysConfig().values[KEY_DEFAULT_DB_CACHE_PAGES]; + return get<int>(KEY_DEFAULT_DB_CACHE_PAGES); } int Config::getConnectionTimeout() { - return (int) sysConfig().values[KEY_CONNECTION_TIMEOUT]; + return (int) getDefaultConfig()->values[KEY_CONNECTION_TIMEOUT]; } int Config::getDummyPacketInterval() { - return (int) sysConfig().values[KEY_DUMMY_PACKET_INTERVAL]; + return (int) getDefaultConfig()->values[KEY_DUMMY_PACKET_INTERVAL]; } -int Config::getLockMemSize() +int Config::getLockMemSize() const { - return (int) sysConfig().values[KEY_LOCK_MEM_SIZE]; + return get<int>(KEY_LOCK_MEM_SIZE); } -bool Config::getLockGrantOrder() +bool Config::getLockGrantOrder() const { - return (bool) sysConfig().values[KEY_LOCK_GRANT_ORDER]; + return get<bool>(KEY_LOCK_GRANT_ORDER); } -int Config::getLockHashSlots() +int Config::getLockHashSlots() const { - return (int) sysConfig().values[KEY_LOCK_HASH_SLOTS]; + return get<int>(KEY_LOCK_HASH_SLOTS); } -int Config::getLockAcquireSpins() +int Config::getLockAcquireSpins() const { - return (int) sysConfig().values[KEY_LOCK_ACQUIRE_SPINS]; + return get<int>(KEY_LOCK_ACQUIRE_SPINS); } -int Config::getEventMemSize() +int Config::getEventMemSize() const { - return (int) sysConfig().values[KEY_EVENT_MEM_SIZE]; + return get<int>(KEY_EVENT_MEM_SIZE); } -int Config::getDeadlockTimeout() +int Config::getDeadlockTimeout() const { - return (int) sysConfig().values[KEY_DEADLOCK_TIMEOUT]; + return get<int>(KEY_DEADLOCK_TIMEOUT); } int Config::getPrioritySwitchDelay() { - int rc = (int) sysConfig().values[KEY_PRIORITY_SWITCH_DELAY]; + int rc = (int) getDefaultConfig()->values[KEY_PRIORITY_SWITCH_DELAY]; if (rc < 1) rc = 1; return rc; @@ -363,7 +438,7 @@ int Config::getPriorityBoost() { - int rc = (int) sysConfig().values[KEY_PRIORITY_BOOST]; + int rc = (int) getDefaultConfig()->values[KEY_PRIORITY_BOOST]; if (rc < 1) rc = 1; if (rc > 1000) @@ -373,62 +448,62 @@ bool Config::getUsePriorityScheduler() { - return (bool) sysConfig().values[KEY_USE_PRIORITY_SCHEDULER]; + return (bool) getDefaultConfig()->values[KEY_USE_PRIORITY_SCHEDULER]; } const char *Config::getRemoteServiceName() { - return (const char*) sysConfig().values[KEY_REMOTE_SERVICE_NAME]; + return (const char*) getDefaultConfig()->values[KEY_REMOTE_SERVICE_NAME]; } unsigned short Config::getRemoteServicePort() { - return (unsigned short) sysConfig().values[KEY_REMOTE_SERVICE_PORT]; + return (unsigned short) getDefaultConfig()->values[KEY_REMOTE_SERVICE_PORT]; } const char *Config::getRemotePipeName() { - return (const char*) sysConfig().values[KEY_REMOTE_PIPE_NAME]; + return (const char*) getDefaultConfig()->values[KEY_REMOTE_PIPE_NAME]; } const char *Config::getIpcName() { - return (const char*) sysConfig().values[KEY_IPC_NAME]; + return (const char*) getDefaultConfig()->values[KEY_IPC_NAME]; } -int Config::getMaxUnflushedWrites() +int Config::getMaxUnflushedWrites() const { - return (int) sysConfig().values[KEY_MAX_UNFLUSHED_WRITES]; + return get<int>(KEY_MAX_UNFLUSHED_WRITES); } -int Config::getMaxUnflushedWriteTime() +int Config::getMaxUnflushedWriteTime() const { - return (int) sysConfig().values[KEY_MAX_UNFLUSHED_WRITE_TIME]; + return get<int>(KEY_MAX_UNFLUSHED_WRITE_TIME); } int Config::getProcessPriorityLevel() { - return (int) sysConfig().values[KEY_PROCESS_PRIORITY_LEVEL]; + return (int) getDefaultConfig()->values[KEY_PROCESS_PRIORITY_LEVEL]; } int Config::getRemoteAuxPort() { - return (int) sysConfig().values[KEY_REMOTE_AUX_PORT]; + return (int) getDefaultConfig()->values[KEY_REMOTE_AUX_PORT]; } const char *Config::getRemoteBindAddress() { - return (const char*) sysConfig().values[KEY_REMOTE_BIND_ADDRESS]; + return (const char*) getDefaultConfig()->values[KEY_REMOTE_BIND_ADDRESS]; } -const char *Config::getExternalFileAccess() +const char *Config::getExternalFileAccess() const { - return (const char*) sysConfig().values[KEY_EXTERNAL_FILE_ACCESS]; + return get<const char*>(KEY_EXTERNAL_FILE_ACCESS); } const char *Config::getDatabaseAccess() { - return (const char*) sysConfig().values[KEY_DATABASE_ACCESS]; + return (const char*) getDefaultConfig()->values[KEY_DATABASE_ACCESS]; } const char *Config::getUdfAccess() @@ -449,7 +524,7 @@ return value; } - const char* v = (const char*) sysConfig().values[KEY_UDF_ACCESS]; + const char* v = (const char*) getDefaultConfig()->values[KEY_UDF_ACCESS]; if (CASE_SENSITIVITY ? (! strcmp(v, UDF_DEFAULT_CONFIG_VALUE) && FB_UDFDIR[0]) : (! fb_utils::stricmp(v, UDF_DEFAULT_CONFIG_VALUE) && FB_UDFDIR[0])) { @@ -465,66 +540,66 @@ const char *Config::getTempDirectories() { - return (const char*) sysConfig().values[KEY_TEMP_DIRECTORIES]; + return (const char*) getDefaultConfig()->values[KEY_TEMP_DIRECTORIES]; } bool Config::getBugcheckAbort() { - return (bool) sysConfig().values[KEY_BUGCHECK_ABORT]; + return (bool) getDefaultConfig()->values[KEY_BUGCHECK_ABORT]; } bool Config::getLegacyHash() { - return (bool) sysConfig().values[KEY_LEGACY_HASH]; + return (bool) getDefaultConfig()->values[KEY_LEGACY_HASH]; } -const char *Config::getGCPolicy() +const char *Config::getGCPolicy() const { - return (const char*) sysConfig().values[KEY_GC_POLICY]; + return get<const char*>(KEY_GC_POLICY); } bool Config::getRedirection() { - return (bool) sysConfig().values[KEY_REDIRECTION]; + return (bool) getDefaultConfig()->values[KEY_REDIRECTION]; } const char *Config::getAuthMethod() { - return (const char*) sysConfig().values[KEY_AUTH_METHOD]; + return (const char*) getDefaultConfig()->values[KEY_AUTH_METHOD]; } -int Config::getDatabaseGrowthIncrement() +int Config::getDatabaseGrowthIncrement() const { - return (int) sysConfig().values[KEY_DATABASE_GROWTH_INCREMENT]; + return get<int>(KEY_DATABASE_GROWTH_INCREMENT); } -int Config::getFileSystemCacheThreshold() +int Config::getFileSystemCacheThreshold() const { - int rc = (int) sysConfig().values[KEY_FILESYSTEM_CACHE_THRESHOLD]; + int rc = get<int>(KEY_FILESYSTEM_CACHE_THRESHOLD); return rc < 0 ? 0 : rc; } bool Config::getRelaxedAliasChecking() { - return (bool) sysConfig().values[KEY_RELAXED_ALIAS_CHECKING]; + return (bool) getDefaultConfig()->values[KEY_RELAXED_ALIAS_CHECKING]; } bool Config::getOldSetClauseSemantics() { - return (bool) sysConfig().values[KEY_OLD_SET_CLAUSE_SEMANTICS]; + return (bool) getDefaultConfig()->values[KEY_OLD_SET_CLAUSE_SEMANTICS]; } int Config::getFileSystemCacheSize() { - return (int) sysConfig().values[KEY_FILESYSTEM_CACHE_SIZE]; + return (int) getDefaultConfig()->values[KEY_FILESYSTEM_CACHE_SIZE]; } const char *Config::getAuditTraceConfigFile() { - return (const char*) sysConfig().values[KEY_TRACE_CONFIG]; + return (const char*) getDefaultConfig()->values[KEY_TRACE_CONFIG]; } int Config::getMaxUserTraceLogSize() { - return (int) sysConfig().values[KEY_MAX_TRACELOG_SIZE]; + return (int) getDefaultConfig()->values[KEY_MAX_TRACELOG_SIZE]; } Index: config.h =================================================================== RCS file: /cvsroot/firebird/firebird2/src/common/config/config.h,v retrieving revision 1.59 retrieving revision 1.60 diff -b -U3 -r1.59 -r1.60 --- config.h 28 Dec 2009 13:25:46 -0000 1.59 +++ config.h 28 Feb 2010 18:00:40 -0000 1.60 @@ -23,8 +23,9 @@ #ifndef COMMON_CONFIG_H #define COMMON_CONFIG_H +#include "../common/classes/alloc.h" #include "../common/classes/fb_string.h" -#include "../jrd/os/path_utils.h" +#include "../common/classes/RefCounted.h" /** Since the original (isc.cpp) code wasn't able to provide powerful and @@ -68,8 +69,13 @@ enum AmCache {AM_UNKNOWN, AM_DISABLED, AM_ENABLED}; -class Config +class ConfigFile; + +class Config : public Firebird::RefCounted, public Firebird::GlobalStorage { +public: + typedef IPTR ConfigValue; + enum ConfigKey { KEY_ROOT_DIRECTORY, @@ -116,13 +122,45 @@ KEY_OLD_SET_CLAUSE_SEMANTICS, KEY_TRACE_CONFIG, KEY_MAX_TRACELOG_SIZE, - KEY_FILESYSTEM_CACHE_SIZE + KEY_FILESYSTEM_CACHE_SIZE, + MAX_CONFIG_KEY // keep it last + }; + + +private: + enum ConfigType + { + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_STRING + //TYPE_STRING_VECTOR // CVC: Unused + }; + + typedef const char* ConfigName; + struct ConfigEntry + { + ConfigType data_type; + ConfigName key; + ConfigValue default_value; }; + static Firebird::PathName getValue(const ConfigFile&, ConfigName); + + static int asInteger(const Firebird::PathName&); + static bool asBoolean(const Firebird::PathName&); + static const char* asString(const Firebird::PathName&); + void loadValues(const ConfigFile& file); + template <typename T> T get(Config::ConfigKey key) const; + + static const ConfigEntry entries[MAX_CONFIG_KEY]; + ConfigValue values[MAX_CONFIG_KEY]; + public: + Config(const ConfigFile& file); // use to build default config + Config(const ConfigFile& file, const Config& base); // use to build db-specific config + ~Config(); // Interface to support command line root specification. - // This ugly solution was required to make it possible to specify root // in command line to load firebird.conf from that root, though in other // cases firebird.conf may be also used to specify root. @@ -130,6 +168,12 @@ static void setRootDirectoryFromCommandLine(const Firebird::PathName& newRoot); static const Firebird::PathName* getCommandLineRootDirectory(); + // Master config - needed to provide per-database config + static const Firebird::RefPtr<Config> getDefaultConfig(); + + // Static functions apply to instance-wide values, + // non-static may be specified per database. + // Installation directory static const char* getInstallDirectory(); @@ -137,10 +181,10 @@ static const char* getRootDirectory(); // Allocation chunk for the temporary spaces - static int getTempBlockSize(); + int getTempBlockSize() const; // Caching limit for the temporary data - static int getTempCacheLimit(); + int getTempCacheLimit() const; // Whether remote (NFS) files can be opened static bool getRemoteFileOpenAbility(); @@ -158,7 +202,7 @@ static bool getTcpNoNagle(); // Default database cache size - static int getDefaultDbCachePages(); + int getDefaultDbCachePages() const; // Connection timeout static int getConnectionTimeout(); @@ -167,22 +211,22 @@ static int getDummyPacketInterval(); // Lock manager memory size - static int getLockMemSize(); + int getLockMemSize() const; // Lock manager grant order - static bool getLockGrantOrder(); + bool getLockGrantOrder() const; // Lock manager hash slots - static int getLockHashSlots(); + int getLockHashSlots() const; // Lock manager acquire spins - static int getLockAcquireSpins(); + int getLockAcquireSpins() const; // Event manager memory size - static int getEventMemSize(); + int getEventMemSize() const; // Deadlock timeout - static int getDeadlockTimeout(); + int getDeadlockTimeout() const; // Priority switch delay static int getPrioritySwitchDelay(); @@ -206,10 +250,10 @@ static const char *getIpcName(); // Unflushed writes number - static int getMaxUnflushedWrites(); + int getMaxUnflushedWrites() const; // Unflushed write time - static int getMaxUnflushedWriteTime(); + int getMaxUnflushedWriteTime() const; // Process priority level static int getProcessPriorityLevel(); @@ -221,7 +265,7 @@ static const char *getRemoteBindAddress(); // Directory list for external tables - static const char *getExternalFileAccess(); + const char *getExternalFileAccess() const; // Directory list for databases static const char *getDatabaseAccess(); @@ -239,7 +283,7 @@ static bool getLegacyHash(); // GC policy - static const char *getGCPolicy(); + const char *getGCPolicy() const; // Redirection static bool getRedirection(); @@ -247,9 +291,9 @@ // Use native, trusted or mixed authentication static const char *getAuthMethod(); - static int getDatabaseGrowthIncrement(); + int getDatabaseGrowthIncrement() const; - static int getFileSystemCacheThreshold(); + int getFileSystemCacheThreshold() const; static int getFileSystemCacheSize(); Index: config_file.cpp =================================================================== RCS file: /cvsroot/firebird/firebird2/src/common/config/config_file.cpp,v retrieving revision 1.36 retrieving revision 1.37 diff -b -U3 -r1.36 -r1.37 --- config_file.cpp 9 Apr 2009 16:44:45 -0000 1.36 +++ config_file.cpp 28 Feb 2010 18:00:40 -0000 1.37 @@ -22,176 +22,371 @@ #include "firebird.h" -#include "../../common/classes/alloc.h" -#include "../../common/classes/auto.h" -#include "../../common/config/config_file.h" -#include "../jrd/os/fbsyslog.h" +#include "../common/classes/alloc.h" +#include "../common/classes/auto.h" +#include "../common/config/config_file.h" +#include "../common/config/config.h" +#include "../jrd/os/path_utils.h" #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif -// Invalid or missing CONF_FILE may lead to severe errors -// in applications. That's why for regular SERVER builds -// it's better to exit with appropriate diags rather continue -// with missing / wrong configuration. -#if (! defined(BOOT_BUILD)) && (! defined(EMBEDDED)) && (! defined(SUPERCLIENT)) -#define EXCEPTION_ON_NO_CONF -#else -#undef EXCEPTION_ON_NO_CONF -#endif - -// config_file works with OS case-sensitivity -typedef Firebird::PathName string; +using namespace Firebird; -/****************************************************************************** - * - * Strip any comments - */ +namespace { -bool ConfigFile::stripComments(string& s) const +class MainStream : public ConfigFile::Stream { - if (!parsingAliases) +public: + MainStream(const char* fname, bool fExceptionOnError) + : file(fopen(fname, "rt")), l(0) + { + if ((!file) && fExceptionOnError) + { + // config file does not exist + fatal_exception::raiseFmt("Missing configuration file: ", fname); + } + } + + bool getLine(ConfigFile::String& input, unsigned int& line) + { + input = ""; + if (!file) + { + return false; + } + + // this loop efficiently skips almost all comment lines + do { - // Simple, fast processing for firebird.conf - // Note that this is only a hack. It won't work in case inputLine - // contains hash-marks embedded in quotes! Not that I know if we - // should care about that case. - const string::size_type commentPos = s.find('#'); - if (commentPos != string::npos) + if (feof(file)) { - s = s.substr(0, commentPos); + return false; } + input.LoadFromFile(file); + ++l; + input.alltrim(" \t\r"); + } while (input.isEmpty() || input[0] == '#'); + line = l; return true; } - // Paranoid, slow processing for aliases.conf - bool equalSeen = false, inString = false; - const char* iter = s.begin(); - const char* end = s.end(); +private: + AutoPtr<FILE, FileClose> file; + unsigned int l; +}; - while (iter < end) +class TextStream : public ConfigFile::Stream +{ +public: + TextStream(const char* configText) + : s(configText), l(0) { - switch (*iter) + if (s && !*s) { - case '"': - if (!equalSeen) // quoted string to the left of = doesn't make sense + s = NULL; + } + } + + bool getLine(ConfigFile::String& input, unsigned int& line) + { + do + { + if (!s) + { + input = ""; return false; - inString = !inString; // We don't support embedded quotes - if (!inString) // we finished a quoted string + } + const char* ptr = strchr(s, '\n'); + if (!ptr) + { + input.assign(s); + s = NULL; + } + else { - // We don't want trash after closing the quoted string, except comments - const string::size_type startPos = s.find_first_not_of(" \t\r", iter + 1 - s.begin()); - if (startPos == string::npos || s[startPos] == '#') + input.assign(s, ptr - s); + s = ptr + 1; + if (!*s) { - s = s.substr(0, iter + 1 - s.begin()); + s = NULL; + } + } + ++l; + input.alltrim(" \t\r"); + } while (input.isEmpty() || input[0] == '#'); + line = l; return true; } + +private: + const char* s; + unsigned int l; +}; + +class SubStream : public ConfigFile::Stream +{ +public: + SubStream() + : cnt(0) + { } + + bool getLine(ConfigFile::String& input, unsigned int& line) + { + if (cnt >= data.getCount()) + { + input = ""; return false; } + + input = data[cnt].first; + line = data[cnt].second; + ++cnt; + return true; + } + + void putLine(const ConfigFile::String& input, unsigned int line) + { + data.push(Line(input, line)); + } + +private: + typedef Pair<Left<ConfigFile::String, unsigned int> > Line; + ObjectsArray<Line> data; + size_t cnt; +}; + +} // anonymous namespace + + +ConfigFile::ConfigFile(const ConfigFile::String& file, USHORT fl) + : AutoStorage(), configFile(getPool(), file), parameters(getPool()), flags(fl) +{ + MainStream s(configFile.c_str(), flags & EXCEPTION_ON_ERROR); + parse(&s); +} + +ConfigFile::ConfigFile(const char* file, USHORT fl) + : AutoStorage(), configFile(getPool(), String(file)), parameters(getPool()), flags(fl) +{ + MainStream s(configFile.c_str(), flags & EXCEPTION_ON_ERROR); + parse(&s); +} + +ConfigFile::ConfigFile(UseText, const char* configText, USHORT fl) + : AutoStorage(), configFile(getPool()), parameters(getPool()), flags(fl) +{ + TextStream s(configText); + parse(&s); +} + +ConfigFile::ConfigFile(MemoryPool& p, ConfigFile::Stream* s, USHORT fl, const String& file) + : AutoStorage(p), configFile(getPool(), file), parameters(getPool()), flags(fl) +{ + parse(s); +} + +ConfigFile::Stream::~Stream() { } + +/****************************************************************************** + * + * Parse line, taking quotes into an account + */ + +ConfigFile::LineType ConfigFile::parseLine(const String& input, String& key, String& value) +{ + int inString = 0; + String::size_type valStart = 0; + String::size_type eol = String::npos; + bool hasSub = false; + + for (String::size_type n=0; n < input.length(); ++n) + { + switch (input[n]) + { + case '"': + if (key.isEmpty()) // quoted string to the left of = doesn't make sense + return LINE_BAD; + if (inString >= 2) // one more quote after quoted string doesn't make sense + return LINE_BAD; + inString++; break; + case '=': - equalSeen = true; + key = input.substr(0, n); + key.rtrim(" \t\r"); + if (key.isEmpty()) // not good - no key + return LINE_BAD; + valStart = n + 1; break; + case '#': - if (!inString) + if (inString != 1) { - s = s.substr(0, iter - s.begin()); - return true; + eol = n; + n = input.length(); // skip the rest of symbols } break; + + case ' ': + case '\t': + case '\r': + break; + + case '{': + case '}': + if (flags & HAS_SUB_CONF) + { + if (inString != 1) { + if (input[n] == '}') // Subconf close mark not expected + { + return LINE_BAD; } - ++iter; + hasSub = true; + inString = 2; + eol = n; + } + break; } + // fall through .... - return !inString; // If we are still inside a string, it's error -} + default: + if (inString >= 2) // Something after the end of line + return LINE_BAD; + break; + } + } -/****************************************************************************** - * - * Check whether the given key exists or not - */ + if (inString == 1) // If we are still inside a string, it's error + return LINE_BAD; -bool ConfigFile::doesKeyExist(const string& key) -{ - checkLoadConfig(); + if (key.isEmpty()) + { + key = input.substr(0, eol); + key.rtrim(" \t\r"); + } + else + { + value = input.substr(valStart, eol - valStart); + value.alltrim(" \t\r"); + value.alltrim("\""); + } - const string data = getString(key); + // Now expand macros in value + String::size_type subFrom; + while ((subFrom = value.find("$(")) != String::npos) + { + String::size_type subTo = value.find(")", subFrom); + if (subTo != String::npos) + { + String macro; + String m = value.substr(subFrom + 2, subTo - (subFrom + 2)); + if (! translate(m, macro)) + { + return LINE_BAD; + } + value.replace(subFrom, subTo + 1 - subFrom, macro); + } + else + { + return LINE_BAD; + } + } - return !data.empty(); + return hasSub ? LINE_START_SUB : LINE_REGULAR; } /****************************************************************************** * - * Return string value corresponding the given key + * Find macro value */ -string ConfigFile::getString(const string& key) +bool ConfigFile::translate(const String& from, String& to) { - checkLoadConfig(); + if (flags & NO_MACRO) + { + return false; + } - size_t pos; - return parameters.find(key, pos) ? parameters[pos].second : string(); + if (from == "root") + { + to = Config::getRootDirectory(); + } + else if (from == "install") + { + to = Config::getInstallDirectory(); + } + else if (from == "this") + { + if (configFile.isEmpty()) + { + return false; + } + PathName file; + PathUtils::splitLastComponent(to, file, configFile); + } +/* else if (!substituteOneOfStandardFirebirdDirs(from, to)) + { + return false; + } */ + else + { + return false; + } + + return true; } /****************************************************************************** * - * Parse key + * Return parameter corresponding the given key */ -string ConfigFile::parseKeyFrom(const string& inputLine, string::size_type& endPos) +const ConfigFile::Parameter* ConfigFile::findParameter(const String& name) const { - endPos = inputLine.find_first_of("="); - if (endPos == string::npos) - { - return inputLine; - } - - return inputLine.substr(0, endPos); + size_t pos; + return parameters.find(name, pos) ? ¶meters[pos] : NULL; } /****************************************************************************** * - * Parse value + * Return parameter corresponding the given key and value */ -string ConfigFile::parseValueFrom(string inputLine, string::size_type initialPos) +const ConfigFile::Parameter* ConfigFile::findParameter(const String& name, const String& value) const { - if (initialPos == string::npos) + size_t pos; + if (!parameters.find(name, pos)) { - return string(); + return NULL; } - // skip leading white spaces - const string::size_type startPos = inputLine.find_first_not_of("= \t", initialPos); - if (startPos == string::npos) + while(pos < parameters.getCount() && parameters[pos].name == name) { - return string(); - } - - inputLine.rtrim(" \t\r"); - // stringComments demands paired quotes but trimming \r may render startPos invalid. - if (parsingAliases && inputLine.length() > startPos + 1 && - inputLine[startPos] == '"' && inputLine.end()[-1] == '"') + if (parameters[pos].value == value) { - return inputLine.substr(startPos + 1, inputLine.length() - startPos - 2); + return ¶meters[pos]; } - - return inputLine.substr(startPos); + ++pos; + } + return NULL; } /****************************************************************************** * - * Load file, if necessary + * Take into an account fault line */ -void ConfigFile::checkLoadConfig() +void ConfigFile::badLine(const String& line) { - if (!isLoadedFlg) + if (flags & EXCEPTION_ON_ERROR) { - loadConfig(); + fatal_exception::raiseFmt("%s: illegal line <%s>" , + configFile.hasData() ? configFile.c_str() : "Passed text", + line.c_str()); } } @@ -200,73 +395,61 @@ * Load file immediately */ -void ConfigFile::loadConfig() +void ConfigFile::parse(Stream* stream) { - isLoadedFlg = true; - - parameters.clear(); + String inputLine; + Parameter* previous = NULL; + unsigned int line; - Firebird::AutoPtr<FILE, Firebird::FileClose> ifile(fopen(configFile.c_str(), "rt")); - -#ifdef EXCEPTION_ON_NO_CONF - int BadLinesCount = 0; -#endif - if (!ifile) + while (stream->getLine(inputLine, line)) { - // config file does not exist -#ifdef EXCEPTION_ON_NO_CONF - if (fExceptionOnError) + Parameter current; + current.line = line; + + switch(parseLine(inputLine, current.name, current.value)) { - const Firebird::string msg = - "Missing configuration file: " + configFile.ToString() + ", exiting"; - Firebird::Syslog::Record(Firebird::Syslog::Error, msg.c_str()); - Firebird::fatal_exception::raise(msg.c_str()); - } -#endif //EXCEPTION_ON_NO_CONF - return; - } - string inputLine; + case LINE_BAD: + badLine(inputLine); + break; - while (!feof(ifile)) + case LINE_REGULAR: + if (current.name.isEmpty()) { - inputLine.LoadFromFile(ifile); + badLine(inputLine); + break; + } - const bool goodLine = stripComments(inputLine); - inputLine.ltrim(" \t\r"); + previous = ¶meters[parameters.add(current)]; + break; - if (!inputLine.size()) + case LINE_START_SUB: + if (current.name.hasData()) { - continue; // comment-line or empty line + size_t n = parameters.add(current); + previous = ¶meters[n]; } - - if (!goodLine || inputLine.find('=') == string::npos) + { // subconf scope + SubStream subStream; + while (stream->getLine(inputLine, line)) { - const Firebird::string msg = - (configFile + ": illegal line \"" + inputLine + "\"").ToString(); - Firebird::Syslog::Record(fExceptionOnError ? - Firebird::Syslog::Error : Firebird::Syslog::Warning, - msg.c_str()); -#ifdef EXCEPTION_ON_NO_CONF - BadLinesCount++; -#endif + if (inputLine[0] == '}') + { + String s = inputLine.substr(1); + s.ltrim(" \t\r"); + if (s.hasData() && s[0] != '#') + { + badLine(s); continue; } + break; + } + subStream.putLine(inputLine, line); + } - string::size_type endPos; - - string key = parseKeyFrom(inputLine, endPos); - key.rtrim(" \t\r"); - // TODO: here we must check for correct parameter spelling ! - const string value = parseValueFrom(inputLine, endPos); - - parameters.add(Parameter(getPool(), key, value)); + previous->sub = FB_NEW(getPool()) ConfigFile(getPool(), &subStream, + flags & ~HAS_SUB_CONF, configFile); + } + break; } -#ifdef EXCEPTION_ON_NO_CONF - if (BadLinesCount && fExceptionOnError) - { - Firebird::fatal_exception::raise("Bad lines in firebird.conf"); } -#endif } - - Index: config_file.h =================================================================== RCS file: /cvsroot/firebird/firebird2/src/common/config/config_file.h,v retrieving revision 1.16 retrieving revision 1.17 diff -b -U3 -r1.16 -r1.17 --- config_file.h 5 Dec 2008 00:55:52 -0000 1.16 +++ config_file.h 28 Feb 2010 18:00:40 -0000 1.17 @@ -23,10 +23,11 @@ #ifndef CONFIG_CONFIG_FILE_H #define CONFIG_CONFIG_FILE_H -#include "../../common/classes/alloc.h" -#include "../../common/classes/fb_pair.h" -#include "../../common/classes/objects_array.h" +#include "../common/classes/alloc.h" +#include "../common/classes/fb_pair.h" +#include "../common/classes/objects_array.h" #include "../common/classes/fb_string.h" +#include "../common/classes/auto.h" /** Since the original (isc.cpp) code wasn't able to provide powerful and @@ -45,56 +46,82 @@ (common/config/config.cpp) and server-side alias manager (jrd/db_alias.cpp). **/ -class ConfigFile : public Firebird::AutoStorage +class ConfigFile : public Firebird::AutoStorage, public Firebird::RefCounted { +public: + // flags for config file + static const USHORT EXCEPTION_ON_ERROR = 0x01; + static const USHORT HAS_SUB_CONF = 0x02; + static const USHORT NO_MACRO = 0x04; + + // enum to distinguish ctors + enum UseText {USE_TEXT}; + // config_file works with OS case-sensitivity - typedef Firebird::PathName string; + typedef Firebird::PathName String; - typedef Firebird::Pair<Firebird::Full<string, string> > Parameter; + class Stream + { + public: + virtual ~Stream(); + virtual bool getLine(String&, unsigned int&) = 0; + }; + + struct Parameter : public AutoStorage + { + Parameter(MemoryPool& p, const Parameter& par) + : AutoStorage(p), name(getPool(), par.name), value(getPool(), par.value), + sub(par.sub), line(par.line) + { } + Parameter() + : AutoStorage(), name(getPool()), value(getPool()), sub(0), line(0) + { } + + String name; + String value; + Firebird::RefPtr<ConfigFile> sub; + unsigned int line; + + static const String* generate(const void* /*sender*/, const Parameter* item) + { + return &item->name; + } + }; + + typedef Firebird::SortedObjectsArray <Parameter, Firebird::InlineStorage<Parameter*, 100>, + String, Parameter> Parameters; + + ConfigFile(const String& file, USHORT fl); + ConfigFile(const char* file, USHORT fl); + ConfigFile(UseText, const char* configText, USHORT fl); - typedef Firebird::SortedObjectsArray <Parameter, - Firebird::InlineStorage<Parameter *, 100>, - string, Firebird::FirstPointerKey<Parameter> > mymap_t; +private: + ConfigFile(MemoryPool& p, ConfigFile::Stream* s, USHORT fl, const String& file); public: - ConfigFile(MemoryPool& p, bool ExceptionOnError) - : AutoStorage(p), isLoadedFlg(false), - fExceptionOnError(ExceptionOnError), parsingAliases(false), - parameters(getPool()) {} - ConfigFile(bool ExceptionOnError, bool useForAliases) - : AutoStorage(), isLoadedFlg(false), - fExceptionOnError(ExceptionOnError), parsingAliases(useForAliases), - parameters(getPool()) {} - - explicit ConfigFile(bool ExceptionOnError) - : AutoStorage(), isLoadedFlg(false), - fExceptionOnError(ExceptionOnError), parsingAliases(false), - parameters(getPool()) {} - - // configuration file management - const string getConfigFilePath() const { return configFile; } - void setConfigFilePath(const string& newFile) { configFile = newFile; } + // key and value management + const Parameter* findParameter(const String& name) const; + const Parameter* findParameter(const String& name, const String& value) const; - bool isLoaded() const { return isLoadedFlg; } + // all parameters access + const Parameters& getParameters() const + { + return parameters; + } - void loadConfig(); - void checkLoadConfig(); +private: + enum LineType {LINE_BAD, LINE_REGULAR, LINE_START_SUB}; - // key and value management - bool doesKeyExist(const string&); - string getString(const string&); + String configFile; + Parameters parameters; + USHORT flags; + USHORT badLinesCount; // utilities - bool stripComments(string&) const; - static string parseKeyFrom(const string&, string::size_type&); - string parseValueFrom(string, string::size_type); - -private: - string configFile; - bool isLoadedFlg; - const bool fExceptionOnError; - const bool parsingAliases; - mymap_t parameters; + void parse(Stream* stream); + LineType parseLine(const String& input, String& key, String& value); + bool translate(const String& from, String& to); + void badLine(const String& line); }; #endif // CONFIG_CONFIG_FILE_H --- config_impl.h DELETED --- |