1
0
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:
David Steele
2017-12-22 22:55:17 -05:00
parent 69488ca8ff
commit d8c575801d
7 changed files with 385 additions and 3 deletions

View File

@@ -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>

View File

@@ -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
View 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
View 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

View File

@@ -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 =>

View File

@@ -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';
####################################################################################################################################

View 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");
}
}