Skip to content

Commit

Permalink
Add Ini object.
Browse files Browse the repository at this point in the history
  • Loading branch information
dwsteele committed Dec 23, 2017
1 parent 69488ca commit d8c5758
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 3 deletions.
2 changes: 1 addition & 1 deletion doc/xml/release.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down
249 changes: 249 additions & 0 deletions src/common/ini.c
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions src/common/ini.h
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions test/lib/pgBackRestTest/Common/DefineTest.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

####################################################################################################################################
Expand Down
Loading

0 comments on commit d8c5758

Please sign in to comment.