You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-09-16 09:06:18 +02:00
Add Ini object.
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Add <code>Buffer</code>, <code>KeyValue</code>, <code>List</code>, <code>Storage</code>, <code>String</code>, <code>StringList</code>, <code>Variant</code>, and <code>VariantList</code> objects.</p>
|
||||
<p>Add <code>Buffer</code>, <code>Ini</code>, <code>KeyValue</code>, <code>List</code>, <code>Storage</code>, <code>String</code>, <code>StringList</code>, <code>Variant</code>, and <code>VariantList</code> objects.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
|
@@ -5,6 +5,7 @@ DESTDIR=
|
||||
pgbackrest: \
|
||||
common/error.o \
|
||||
common/errorType.o \
|
||||
common/ini.o \
|
||||
common/memContext.o \
|
||||
common/type/buffer.o \
|
||||
common/type/keyValue.o \
|
||||
@@ -22,6 +23,7 @@ pgbackrest: \
|
||||
$(CC) $(CFLAGS) -o pgbackrest \
|
||||
common/error.o \
|
||||
common/errorType.o \
|
||||
common/ini.o \
|
||||
common/memContext.o \
|
||||
common/type/buffer.o \
|
||||
common/type/keyValue.o \
|
||||
|
249
src/common/ini.c
Normal file
249
src/common/ini.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/***********************************************************************************************************************************
|
||||
Ini Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/ini.h"
|
||||
#include "common/type/keyValue.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Contains information about the ini
|
||||
***********************************************************************************************************************************/
|
||||
struct Ini
|
||||
{
|
||||
MemContext *memContext; // Context that contains the ini
|
||||
KeyValue *store; // Key value store that contains the ini data
|
||||
String *fileName; // File name (if one has been set)
|
||||
};
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Create a new string from a zero-terminated string
|
||||
***********************************************************************************************************************************/
|
||||
Ini *
|
||||
iniNew()
|
||||
{
|
||||
Ini *this = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("ini")
|
||||
{
|
||||
// Create object
|
||||
this = memNew(sizeof(Ini));
|
||||
this->memContext = MEM_CONTEXT_NEW();
|
||||
|
||||
// Allocate key value store
|
||||
this->store = kvNew();
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
// Return buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Internal function to get an ini value
|
||||
***********************************************************************************************************************************/
|
||||
static const Variant *
|
||||
iniGetInternal(const Ini *this, const String *section, const String *key)
|
||||
{
|
||||
const Variant *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Get the section
|
||||
KeyValue *sectionKv = varKv(kvGet(this->store, varNewStr(section)));
|
||||
|
||||
// Section must exist to get the value
|
||||
if (sectionKv != NULL)
|
||||
result = kvGet(sectionKv, varNewStr(key));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get an ini value -- error if it does not exist
|
||||
***********************************************************************************************************************************/
|
||||
const Variant *iniGet(const Ini *this, const String *section, const String *key)
|
||||
{
|
||||
// Get the value
|
||||
const Variant *result = iniGetInternal(this, section, key);
|
||||
|
||||
// If value is null replace it with default
|
||||
if (result == NULL)
|
||||
THROW(FormatError, "section '%s', key '%s' does not exist", strPtr(section), strPtr(key));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get an ini value -- if it does not exist then return specified default
|
||||
***********************************************************************************************************************************/
|
||||
const Variant *
|
||||
iniGetDefault(const Ini *this, const String *section, const String *key, Variant *defaultValue)
|
||||
{
|
||||
// Get the value
|
||||
const Variant *result = iniGetInternal(this, section, key);
|
||||
|
||||
// If value is null replace it with default
|
||||
if (result == NULL)
|
||||
result = defaultValue;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get a list of keys for a section
|
||||
***********************************************************************************************************************************/
|
||||
StringList *
|
||||
iniSectionKeyList(const Ini *this, const String *section)
|
||||
{
|
||||
StringList *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Get the section
|
||||
KeyValue *sectionKv = varKv(kvGet(this->store, varNewStr(section)));
|
||||
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
|
||||
// Return key list of the section exists
|
||||
if (sectionKv != NULL)
|
||||
result = strLstNewVarLst(kvKeyList(sectionKv));
|
||||
// Otherwise return an empty list
|
||||
else
|
||||
result = strLstNew();
|
||||
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse ini from a string
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
iniParse(Ini *this, const String *content)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
{
|
||||
if (this->store != NULL)
|
||||
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 = strLstNewSplit(content, strNew("\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(FormatError, "ini section should end with ] at line %d: %s", lineIdx + 1, linePtr);
|
||||
|
||||
// Assign section
|
||||
section = strNewSzN(linePtr + 1, strSize(line) - 2);
|
||||
}
|
||||
// Else it should be a key/value
|
||||
else
|
||||
{
|
||||
if (section == NULL)
|
||||
THROW(FormatError, "key/value found outside of section at line %d: %s", lineIdx + 1, linePtr);
|
||||
|
||||
// Find the =
|
||||
const char *lineEqual = strstr(linePtr, "=");
|
||||
|
||||
if (lineEqual == NULL)
|
||||
THROW(FormatError, "missing '=' in key/value at line %d: %s", lineIdx + 1, linePtr);
|
||||
|
||||
// Extract the key
|
||||
String *key = strTrim(strNewSzN(linePtr, lineEqual - linePtr));
|
||||
|
||||
if (strSize(key) == 0)
|
||||
THROW(FormatError, "key is zero-length at line %d: %s", lineIdx++, linePtr);
|
||||
|
||||
// Extract the value
|
||||
Variant *value = varNewStr(strTrim(strNew(lineEqual + 1)));
|
||||
|
||||
// Store the section/key/value
|
||||
iniSet(this, section, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_END()
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Load ini from a file
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
iniLoad(Ini *this, const String *fileName)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
{
|
||||
// Set the filename
|
||||
this->fileName = strDup(fileName);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
iniParse(this, strNewBuf(storageGet(storageLocal(), this->fileName, false)));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
MEM_CONTEXT_END()
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Set an ini value
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
iniSet(Ini *this, const String *section, const String *key, const Variant *value)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
Variant *sectionKey = varNewStr(section);
|
||||
KeyValue *sectionKv = varKv(kvGet(this->store, sectionKey));
|
||||
|
||||
if (sectionKv == NULL)
|
||||
sectionKv = kvPutKv(this->store, sectionKey);
|
||||
|
||||
kvAdd(sectionKv, varNewStr(key), value);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Free the ini
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
iniFree(Ini *this)
|
||||
{
|
||||
memContextFree(this->memContext);
|
||||
}
|
25
src/common/ini.h
Normal file
25
src/common/ini.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/***********************************************************************************************************************************
|
||||
Ini Handler
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMON_INI_H
|
||||
#define COMMON_INI_H
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Ini object
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct Ini Ini;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
Ini *iniNew();
|
||||
const Variant *iniGet(const Ini *this, const String *section, const String *key);
|
||||
const Variant *iniGetDefault(const Ini *this, const String *section, const String *key, Variant *defaultValue);
|
||||
StringList *iniSectionKeyList(const Ini *this, const String *section);
|
||||
void iniParse(Ini *this, const String *content);
|
||||
void iniLoad(Ini *this, const String *fileName);
|
||||
void iniSet(Ini *this, const String *section, const String *key, const Variant *value);
|
||||
|
||||
void iniFree(Ini *this);
|
||||
|
||||
#endif
|
@@ -227,6 +227,16 @@ my $oTestDef =
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'ini',
|
||||
&TESTDEF_TOTAL => 3,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
{
|
||||
'common/ini' => TESTDEF_COVERAGE_FULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'ini-perl',
|
||||
&TESTDEF_TOTAL => 10,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
####################################################################################################################################
|
||||
# InfoIniUnitTest.pm - Unit tests for Ini module
|
||||
# Unit tests for Ini module
|
||||
####################################################################################################################################
|
||||
package pgBackRestTest::Module::Common::CommonIniTest;
|
||||
package pgBackRestTest::Module::Common::CommonIniPerlTest;
|
||||
use parent 'pgBackRestTest::Common::RunTest';
|
||||
|
||||
####################################################################################################################################
|
96
test/src/module/common/iniTest.c
Normal file
96
test/src/module/common/iniTest.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Ini
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void testRun()
|
||||
{
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("iniNew() and iniFree()"))
|
||||
{
|
||||
Ini *ini = NULL;
|
||||
|
||||
TEST_ASSIGN(ini, iniNew(), "new ini");
|
||||
TEST_RESULT_PTR_NE(ini->memContext, NULL, "mem context is set");
|
||||
TEST_RESULT_PTR_NE(ini->store, NULL, "stores is set");
|
||||
TEST_RESULT_VOID(iniFree(ini), "free ini");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("iniSet(), iniGet(), iniGetDefault(), and iniSectionKeyList()"))
|
||||
{
|
||||
Ini *ini = NULL;
|
||||
|
||||
TEST_ASSIGN(ini, iniNew(), "new ini");
|
||||
|
||||
TEST_RESULT_VOID(iniSet(ini, strNew("section1"), strNew("key1"), varNewInt(11)), "set section, key, int");
|
||||
TEST_RESULT_VOID(iniSet(ini, strNew("section1"), strNew("key2"), varNewDbl(1.234)), "set section, key, dbl");
|
||||
TEST_RESULT_INT(varInt(iniGet(ini, strNew("section1"), strNew("key1"))), 11, "get section, key, int");
|
||||
TEST_RESULT_DOUBLE(varDbl(iniGet(ini, strNew("section1"), strNew("key2"))), 1.234, "get section, key, dbl");
|
||||
|
||||
TEST_ERROR(iniGet(ini, strNew("section2"), strNew("key2")), FormatError, "section 'section2', key 'key2' does not exist");
|
||||
|
||||
TEST_RESULT_PTR(iniGetDefault(ini, strNew("section2"), strNew("key2"), NULL), NULL, "get section, key, NULL");
|
||||
TEST_RESULT_BOOL(
|
||||
varBool(iniGetDefault(ini, strNew("section3"), strNew("key3"), varNewBool(true))), true, "get section, key, bool");
|
||||
|
||||
TEST_RESULT_INT(strLstSize(iniSectionKeyList(ini, strNew("bogus"))), 0, "get keys for missing section");
|
||||
TEST_RESULT_STR(strPtr(strLstJoin(iniSectionKeyList(ini, strNew("section1")), "|")), "key1|key2", "get keys for section");
|
||||
|
||||
TEST_RESULT_VOID(iniFree(ini), "free ini");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("iniParse() and iniLoad()"))
|
||||
{
|
||||
Ini *ini = NULL;
|
||||
String *content = NULL;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_ERROR(
|
||||
iniParse(iniNew(), strNew("compress=y\n")), FormatError, "key/value found outside of section at line 1: compress=y");
|
||||
TEST_ERROR(iniParse(iniNew(), strNew("[section\n")), FormatError, "ini section should end with ] at line 1: [section");
|
||||
TEST_ERROR(iniParse(iniNew(), strNew("[section]\nkey")), FormatError, "missing '=' in key/value at line 2: key");
|
||||
TEST_ERROR(iniParse(iniNew(), strNew("[section]\n =value")), FormatError, "key is zero-length at line 1: =value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_ASSIGN(ini, iniNew(), "new ini");
|
||||
|
||||
content = strNew
|
||||
(
|
||||
"[global] \n"
|
||||
"compress=y \n"
|
||||
"\n"
|
||||
" [db]\n"
|
||||
"db-path = /path/to/pg"
|
||||
);
|
||||
|
||||
TEST_RESULT_VOID(iniParse(ini, content), "load ini");
|
||||
|
||||
TEST_RESULT_STR(strPtr(varStr(iniGet(ini, strNew("global"), strNew("compress")))), "y", "get compress");
|
||||
TEST_RESULT_STR(strPtr(varStr(iniGet(ini, strNew("db"), strNew("db-path")))), "/path/to/pg", "get db-path");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_ASSIGN(ini, iniNew(), "new ini");
|
||||
String *fileName = strNewFmt("%s/test.ini", testPath());
|
||||
|
||||
content = strNew
|
||||
(
|
||||
"# Comment\n"
|
||||
" [global]\n"
|
||||
" \n"
|
||||
" compress= y \n"
|
||||
"[db]\t\r\n"
|
||||
" db-path =/path/to/pg\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
TEST_RESULT_VOID(storagePut(storageLocal(), fileName, bufNewStr(content)), "put ini to file");
|
||||
TEST_RESULT_VOID(iniLoad(ini, fileName), "load ini from file");
|
||||
|
||||
TEST_RESULT_STR(strPtr(varStr(iniGet(ini, strNew("global"), strNew("compress")))), "y", "get compress");
|
||||
TEST_RESULT_STR(strPtr(varStr(iniGet(ini, strNew("db"), strNew("db-path")))), "/path/to/pg", "get db-path");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user