1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-10-30 23:37:45 +02:00
Files
pgbackrest/src/common/ini.c
David Steele da43db3543 Move common/object.h to common/type/object.h.
This header does not contain a type but is used to define types so this seems like a better location.
2020-03-30 20:52:57 -04:00

433 lines
15 KiB
C

/***********************************************************************************************************************************
Ini Handler
***********************************************************************************************************************************/
#include "build.auto.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "common/debug.h"
#include "common/memContext.h"
#include "common/log.h"
#include "common/ini.h"
#include "common/type/keyValue.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct Ini
{
MemContext *memContext; // Context that contains the ini
KeyValue *store; // Key value store that contains the ini data
};
OBJECT_DEFINE_MOVE(INI);
OBJECT_DEFINE_FREE(INI);
/***********************************************************************************************************************************
Create a new Ini object
***********************************************************************************************************************************/
Ini *
iniNew(void)
{
FUNCTION_TEST_VOID();
Ini *this = NULL;
MEM_CONTEXT_NEW_BEGIN("Ini")
{
this = memNew(sizeof(Ini));
*this = (Ini)
{
.memContext = MEM_CONTEXT_NEW(),
.store = kvNew(),
};
}
MEM_CONTEXT_NEW_END();
FUNCTION_TEST_RETURN(this);
}
/***********************************************************************************************************************************
Internal function to get an ini value
***********************************************************************************************************************************/
static const Variant *
iniGetInternal(const Ini *this, const String *section, const String *key, bool required)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_PARAM(BOOL, required);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
const Variant *result = NULL;
// Get the section
KeyValue *sectionKv = varKv(kvGet(this->store, VARSTR(section)));
// Section must exist to get the value
if (sectionKv != NULL)
result = kvGet(sectionKv, VARSTR(key));
// If value is null and required then error
if (result == NULL && required)
THROW_FMT(FormatError, "section '%s', key '%s' does not exist", strPtr(section), strPtr(key));
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Get an ini value -- error if it does not exist
***********************************************************************************************************************************/
const String *
iniGet(const Ini *this, const String *section, const String *key)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
// Get the value
const Variant *result = iniGetInternal(this, section, key, true);
FUNCTION_TEST_RETURN(varStr(result));
}
/***********************************************************************************************************************************
Get an ini value -- if it does not exist then return specified default
***********************************************************************************************************************************/
const String *
iniGetDefault(const Ini *this, const String *section, const String *key, const String *defaultValue)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_PARAM(STRING, defaultValue);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
// Get the value
const Variant *result = iniGetInternal(this, section, key, false);
FUNCTION_TEST_RETURN(result == NULL ? defaultValue : varStr(result));
}
/***********************************************************************************************************************************
Internal function to get an ini value list
***********************************************************************************************************************************/
StringList *
iniGetList(const Ini *this, const String *section, const String *key)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
// Get the value
const Variant *result = iniGetInternal(this, section, key, false);
FUNCTION_TEST_RETURN(result == NULL ? NULL : strLstNewVarLst(varVarLst(result)));
}
/***********************************************************************************************************************************
Internal function to get an ini value list
***********************************************************************************************************************************/
bool
iniSectionKeyIsList(const Ini *this, const String *section, const String *key)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
// Get the value
const Variant *result = iniGetInternal(this, section, key, true);
FUNCTION_TEST_RETURN(varType(result) == varTypeVariantList);
}
/***********************************************************************************************************************************
Get a list of keys for a section
***********************************************************************************************************************************/
StringList *
iniSectionKeyList(const Ini *this, const String *section)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
StringList *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Get the section
KeyValue *sectionKv = varKv(kvGet(this->store, VARSTR(section)));
// Return key list if the section exists
if (sectionKv != NULL)
result = strLstNewVarLst(kvKeyList(sectionKv));
// Otherwise return an empty list
else
result = strLstNew();
strLstMove(result, memContextPrior());
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Get a list of sections
***********************************************************************************************************************************/
StringList *
iniSectionList(const Ini *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
StringList *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Get the sections from the keyList
result = strLstNewVarLst(kvKeyList(this->store));
strLstMove(result, memContextPrior());
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Parse ini from a string
***********************************************************************************************************************************/
void
iniParse(Ini *this, const String *content)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, content);
FUNCTION_TEST_END();
ASSERT(this != NULL);
MEM_CONTEXT_BEGIN(this->memContext)
{
kvFree(this->store);
this->store = kvNew();
if (content != NULL)
{
MEM_CONTEXT_TEMP_BEGIN()
{
// Track the current section
String *section = NULL;
// Split the content into lines and loop
StringList *lines = strLstNewSplitZ(content, "\n");
for (unsigned int lineIdx = 0; lineIdx < strLstSize(lines); lineIdx++)
{
// Get next line
const String *line = strTrim(strLstGet(lines, lineIdx));
const char *linePtr = strPtr(line);
// Only interested in lines that are not blank or comments
if (strSize(line) > 0 && linePtr[0] != '#')
{
// Looks like this line is a section
if (linePtr[0] == '[')
{
// Make sure the section ends with ]
if (linePtr[strSize(line) - 1] != ']')
THROW_FMT(FormatError, "ini section should end with ] at line %u: %s", lineIdx + 1, linePtr);
// Assign section
section = strNewN(linePtr + 1, strSize(line) - 2);
}
// Else it should be a key/value
else
{
if (section == NULL)
THROW_FMT(FormatError, "key/value found outside of section at line %u: %s", lineIdx + 1, linePtr);
// Find the =
const char *lineEqual = strstr(linePtr, "=");
if (lineEqual == NULL)
THROW_FMT(FormatError, "missing '=' in key/value at line %u: %s", lineIdx + 1, linePtr);
// Extract the key
String *key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
if (strSize(key) == 0)
THROW_FMT(FormatError, "key is zero-length at line %u: %s", lineIdx++, linePtr);
// Store the section/key/value
iniSet(this, section, key, strTrim(strNew(lineEqual + 1)));
}
}
}
}
MEM_CONTEXT_TEMP_END();
}
}
MEM_CONTEXT_END();
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Set an ini value
***********************************************************************************************************************************/
void
iniSet(Ini *this, const String *section, const String *key, const String *value)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INI, this);
FUNCTION_TEST_PARAM(STRING, section);
FUNCTION_TEST_PARAM(STRING, key);
FUNCTION_TEST_PARAM(STRING, value);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(section != NULL);
ASSERT(key != NULL);
ASSERT(value != NULL);
MEM_CONTEXT_TEMP_BEGIN()
{
const Variant *sectionKey = VARSTR(section);
KeyValue *sectionKv = varKv(kvGet(this->store, sectionKey));
if (sectionKv == NULL)
sectionKv = kvPutKv(this->store, sectionKey);
kvAdd(sectionKv, VARSTR(key), VARSTR(value));
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Load an ini file and return data to a callback
***********************************************************************************************************************************/
void
iniLoad(
IoRead *read, void (*callbackFunction)(void *data, const String *section, const String *key, const String *value),
void *callbackData)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(IO_READ, read);
FUNCTION_LOG_PARAM(FUNCTIONP, callbackFunction);
FUNCTION_LOG_PARAM_P(VOID, callbackData);
FUNCTION_LOG_END();
ASSERT(read != NULL);
MEM_CONTEXT_TEMP_BEGIN()
{
// Track the current section
String *section = NULL;
// Keep track of the line number for error reporting
unsigned int lineIdx = 0;
MEM_CONTEXT_TEMP_RESET_BEGIN()
{
ioReadOpen(read);
do
{
const String *line = strTrim(ioReadLineParam(read, true));
const char *linePtr = strPtr(line);
// Only interested in lines that are not blank or comments
if (strSize(line) > 0 && linePtr[0] != '#')
{
// Looks like this line is a section
if (linePtr[0] == '[')
{
// Make sure the section ends with ]
if (linePtr[strSize(line) - 1] != ']')
THROW_FMT(FormatError, "ini section should end with ] at line %u: %s", lineIdx + 1, linePtr);
// Assign section
MEM_CONTEXT_PRIOR_BEGIN()
{
section = strNewN(linePtr + 1, strSize(line) - 2);
}
MEM_CONTEXT_PRIOR_END();
}
// Else it should be a key/value
else
{
if (section == NULL)
THROW_FMT(FormatError, "key/value found outside of section at line %u: %s", lineIdx + 1, linePtr);
// Find the =
const char *lineEqual = strstr(linePtr, "=");
if (lineEqual == NULL)
THROW_FMT(FormatError, "missing '=' in key/value at line %u: %s", lineIdx + 1, linePtr);
// Extract the key
String *key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
if (strSize(key) == 0)
THROW_FMT(FormatError, "key is zero-length at line %u: %s", lineIdx++, linePtr);
// Callback with the section/key/value
callbackFunction(callbackData, section, key, strTrim(strNew(lineEqual + 1)));
}
}
lineIdx++;
MEM_CONTEXT_TEMP_RESET(1000);
}
while (!ioReadEof(read));
}
MEM_CONTEXT_TEMP_END();
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}