1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/src/ulib/file_config.cpp
stefanocasazza fc24169677 sync
2016-09-15 18:36:41 +02:00

592 lines
13 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// file_config.cpp
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#include <ulib/command.h>
#include <ulib/file_config.h>
#include <ulib/utility/escape.h>
#include <ulib/utility/services.h>
#include <ulib/net/server/server.h>
#include <ulib/utility/string_ext.h>
#if defined(__NetBSD__) || defined(__UNIKERNEL__) || defined(__OSX__)
extern char** environ;
#endif
U_NO_EXPORT void UFileConfig::init()
{
U_TRACE_NO_PARAM(0, "UFileConfig::init()")
UFile::map = (char*)MAP_FAILED;
UFile::st_size = 0;
UFile::map_size = 0;
_start = data.data();
reset();
}
UFileConfig::UFileConfig()
{
U_TRACE_REGISTER_OBJECT(0, UFileConfig, "")
init();
preprocessing = false;
}
UFileConfig::UFileConfig(const UString& _data, bool _preprocessing) : data(_data)
{
U_TRACE_REGISTER_OBJECT(0, UFileConfig, "%V,%b", _data.rep, _preprocessing)
init();
preprocessing = _preprocessing;
}
bool UFileConfig::processData(bool bload)
{
U_TRACE(0, "UFileConfig::processData(%b)", bload)
U_CHECK_MEMORY
// manage if we need preprocessing...
#if defined(HAVE_CPP) || defined(HAVE_MCPP)
if (preprocessing ||
UServices::dosMatch(data, U_CONSTANT_TO_PARAM("*#include *\n*"), 0))
{
static int fd_stderr;
UString command(200U),
_dir = UStringExt::dirname(pathname);
# ifdef DEBUG
# define DBG_DEF " -DDEBUG "
# else
# define DBG_DEF
# endif
if (fd_stderr == 0) fd_stderr = UServices::getDevNull("/tmp/cpp.err");
command.snprintf(U_CONSTANT_TO_PARAM("cpp -undef -nostdinc -w -P -C " DBG_DEF "-I%v -"), _dir.rep);
if (UFile::isOpen())
{
(void) UFile::lseek(U_SEEK_BEGIN, SEEK_SET);
data = UCommand::outputCommand(command, environ, UFile::getFd(), fd_stderr);
# ifdef HAVE_MCPP
if (data.empty())
{
command.snprintf(U_CONSTANT_TO_PARAM("mcpp -P -C " DBG_DEF "-I%v -"), _dir.rep);
(void) UFile::lseek(U_SEEK_BEGIN, SEEK_SET);
data = UCommand::outputCommand(command, environ, UFile::getFd(), fd_stderr);
}
# endif
}
else
{
UCommand cmd(command);
UString output(U_CAPACITY);
(void) cmd.execute(&data, &output, -1, fd_stderr);
# ifndef U_LOG_DISABLE
UServer_Base::logCommandMsgError(cmd.getCommand(), true);
# endif
# ifdef HAVE_MCPP
if (data.empty())
{
UCommand _cmd(command);
command.snprintf(U_CONSTANT_TO_PARAM("mcpp -P -C " DBG_DEF "-I%v -"), _dir.rep);
(void) _cmd.execute(&data, &output, -1, fd_stderr);
# ifndef U_LOG_DISABLE
UServer_Base::logCommandMsgError(_cmd.getCommand(), true);
# endif
}
# endif
data = output;
}
}
#endif
if (data.empty()) U_RETURN(false);
if (bload == false) U_RETURN(true);
_end = data.pend();
_start = data.data();
_size = data.size();
if (UFile::isPath())
{
//------------ -------------------------------------------------------------
// Loads configuration information from the file. The file type is
// determined by the file extension. The following extensions are supported:
// -------------------------------------------------------------------------
// .ini - initialization file (Windows INI)
// .properties - properties file (JAVA Properties)
// -------------------------------------------------------------------------
UString suffix = UFile::getSuffix();
if (suffix)
{
if (suffix.equal(U_CONSTANT_TO_PARAM("ini")))
{
if (loadINI()) U_RETURN(true);
U_RETURN(false);
}
if (suffix.equal(U_CONSTANT_TO_PARAM("properties")))
{
if (loadProperties()) U_RETURN(true);
U_RETURN(false);
}
}
}
if (loadSection(0, 0)) U_RETURN(true);
U_RETURN(false);
}
void UFileConfig::load()
{
U_TRACE_NO_PARAM(0, "UFileConfig::load()")
U_CHECK_MEMORY
U_ASSERT_EQUALS(isLoaded(), false)
if (UFile::open() &&
UFile::size() > 0 &&
UFile::memmap(PROT_READ, &data) &&
processData(true))
{
if (UFile::isOpen()) UFile::close();
}
else
{
U_ERROR("Configuration file %.*S processing failed", U_FILE_TO_TRACE(*this));
}
}
// Perform search of first caracter '{' and check section name before...
bool UFileConfig::searchForObjectStream(const char* section, uint32_t len)
{
U_TRACE(0, "UFileConfig::searchForObjectStream(%.*S,%u)", len, section, len)
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
if (len == 0) section = "{";
bool bretry = len && (_start != data.data());
const char* save_start = _start;
retry:
while (_start < _end)
{
_start = u_skip(_start, _end, 0, '#');
if (_start == _end) break;
U_INTERNAL_ASSERT_EQUALS(u__isspace(_start[0]), false)
U_INTERNAL_DUMP("_start = %.*S", 10, _start)
if (_start[0] != section[0] ||
(len && memcmp(_start, section, len)) ||
(u__isspace(_start[(len ? len : 1)]) == false)) // check for partial match of the name section...
{
while (u__isspace(*_start) == false) ++_start;
continue;
}
_start += (len ? len : 1);
U_INTERNAL_ASSERT(u__isspace(_start[0]))
if (len)
{
// check the caracter after the name of the section...
while (u__isspace(*_start)) ++_start;
if (*_start != '{') continue;
// find the end of the section and consider that as EOF... (call reset() when done with this section)
_end = u_strpend(_start, data.remain(_start), U_CONSTANT_TO_PARAM("{}"), '#');
U_INTERNAL_DUMP("_end = %p", _end)
if (_end == 0) break;
_size = (_end - ++_start); // NB: we advance one char (to call u_skip() after...)
U_INTERNAL_DUMP("_size = %u _end = %.*S", _size, 10, _end)
}
// FOUND
U_RETURN(true);
}
if (bretry)
{
bretry = false;
_start = data.data();
goto retry;
}
_start = save_start;
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
U_RETURN(false);
}
bool UFileConfig::loadTable(UHashMap<UString>& tbl)
{
U_TRACE(0, "UFileConfig::loadTable(%p)", &tbl) // problem with sanitize address
U_CHECK_MEMORY
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
if (_size)
{
uint32_t len = tbl.loadFromData(_start, _size);
_start += len;
_size -= len;
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
if (tbl.empty() == false) U_RETURN(true);
}
U_RETURN(false);
}
bool UFileConfig::loadVector(UVector<UString>& vec, const char* name)
{
U_TRACE(0, "UFileConfig::loadVector(%p,%S)", &vec, name)
U_CHECK_MEMORY
_start = u_skip(_start, _end, 0, '#');
if (_start == _end) U_RETURN(false);
U_INTERNAL_ASSERT_EQUALS(u__isspace(_start[0]), false)
U_INTERNAL_DUMP("_start = %.*S", 10, _start)
uint32_t len = (name ? u__strlen(name, __PRETTY_FUNCTION__) : 0);
if (len)
{
if (_start[0] != name[0] ||
memcmp(_start, name, len))
{
U_RETURN(false);
}
_start += len;
U_INTERNAL_ASSERT(u__isspace(_start[0]))
while (u__isspace(*_start)) ++_start;
}
if (_start[0] == '[' ||
_start[0] == '(')
{
len = vec.loadFromData(_start, _size);
_start += len;
_size -= len;
U_RETURN(true);
}
U_RETURN(false);
}
bool UFileConfig::loadSection(const char* section, uint32_t len)
{
U_TRACE(0, "UFileConfig::loadSection(%.*S,%u)", len, section, len)
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MAJOR(_size, 0)
if (searchForObjectStream(section, len))
{
table.clear();
if (loadTable(table)) U_RETURN(true);
}
U_RETURN(false);
}
bool UFileConfig::loadINI()
{
U_TRACE_NO_PARAM(0, "UFileConfig::loadINI()")
U_CHECK_MEMORY
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
uint32_t len;
const char* ptr;
UString sectionKey, fullKey(U_CAPACITY), key, value;
while (_start < _end)
{
_start = u_skip(_start, _end, 0, ';');
if (_start == _end) break;
U_INTERNAL_ASSERT_EQUALS(u__isspace(_start[0]),false)
U_INTERNAL_DUMP("_start = %.*S", 10, _start)
if (_start[0] == '[') // a line starting with a square bracket denotes a section key [<key>]
{
++_start;
ptr = u__strpbrk(_start, _end - _start, "]\n");
if (ptr == 0)
{
_start = _end;
break;
}
sectionKey = UStringExt::trim(_start, ptr - _start);
_start = ptr;
}
else // every other line denotes a property assignment in the form <value key> = <value>
{
ptr = u__strpbrk(_start, _end - _start, "=\n");
if (ptr == 0)
{
_start = _end;
break;
}
key = UStringExt::trim(_start, ptr - _start);
_start = ptr;
if (*_start == '=')
{
++_start;
ptr = (const char*) memchr(_start, '\n', _end - _start);
if (ptr == 0)
{
_start = _end;
break;
}
value = UStringExt::trim(_start, ptr - _start);
_start = ptr;
}
// The name of a property is composed of the section key and the value key, separated by a period (<section key>.<value key>)
len = sectionKey.size();
fullKey.setBuffer(len + 1 + key.size());
if (len == 0) fullKey.snprintf(U_CONSTANT_TO_PARAM( "%v"), key.rep);
else fullKey.snprintf(U_CONSTANT_TO_PARAM("%v.%v"), sectionKey.rep, key.rep);
(void) fullKey.shrink();
table.insert(fullKey, value);
}
if (_start >= _end) break;
++_start;
}
_size = (_end - _start);
U_INTERNAL_DUMP("_size = %u", _size)
bool result = (table.empty() == false);
U_RETURN(result);
}
bool UFileConfig::loadProperties(UHashMap<UString>& table, const char* _start, const char* _end)
{
U_TRACE(0, "UFileConfig::loadProperties(%p,%p,%p)", &table, _start, _end)
U_INTERNAL_DUMP("_start = %.*S", 10, _start)
char c;
const char* ptr;
UString key, value;
while (_start < _end)
{
// skip white space
if (u__isspace(*_start))
{
++_start;
continue;
}
// a line starting with a hash '#' or exclamation mark '!' is treated as a comment and ignored
c = *_start;
if (c == '#' ||
c == '!')
{
// skip line comment
_start = (const char*) memchr(_start, '\n', _end - _start);
if (_start == 0) _start = _end;
continue;
}
U_INTERNAL_ASSERT_EQUALS(u__isspace(_start[0]),false)
U_INTERNAL_DUMP("_start = %.*S", 10, _start)
// every other line denotes a property assignment in the form <key> = <value>
ptr = u__strpbrk(_start, _end - _start, "=:\r\n");
if (ptr == 0) break;
key = UStringExt::trim(_start, ptr - _start);
_start = ptr;
c = *_start;
if (c == '=' ||
c == ':')
{
++_start;
ptr = (const char*) memchr(_start, '\n', _end - _start);
if (ptr == 0) break;
value = UStringExt::trim(_start, ptr - _start);
// NB: var shell often need to be quoted...
if (c == '=' &&
value.isQuoted())
{
value.unQuote();
}
_start = ptr;
}
table.insert(key, value);
if (_start >= _end) break;
++_start;
}
if (table.empty()) U_RETURN(false);
U_RETURN(true);
}
bool UFileConfig::loadProperties()
{
U_TRACE_NO_PARAM(0, "UFileConfig::loadProperties()")
U_CHECK_MEMORY
U_INTERNAL_DUMP("_size = %u _start = %.*S", _size, 10, _start)
bool result = loadProperties(table, _start, _end);
if (result)
{
_size = (_end - _start);
U_INTERNAL_DUMP("_size = %u", _size)
U_RETURN(true);
}
U_RETURN(false);
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* UFileConfig::dump(bool _reset) const
{
UFile::dump(false);
*UObjectIO::os << '\n'
<< "_end " << (void*)_end << '\n'
<< "_size " << _size << '\n'
<< "_start " << (void*)_start << '\n'
<< "preprocessing " << preprocessing << '\n'
<< "data (UString " << (void*)&data << ")\n"
<< "table (UHashMap<UString> " << (void*)&table << ')';
if (_reset)
{
UObjectIO::output();
return UObjectIO::buffer_output;
}
return 0;
}
#endif