Skip to content

Commit

Permalink
Implement first version of KConfig plugin set function
Browse files Browse the repository at this point in the history
  • Loading branch information
darddan committed Dec 1, 2019
1 parent 68d4bb6 commit 610b454
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/plugins/kconfig/CMakeLists.txt
Expand Up @@ -6,4 +6,5 @@ add_plugin (
kconfig_delegate.cpp
kconfig_parser.cpp
kconfig_parser_exception.cpp
kconfig_serializer.cpp
file_utility.cpp)
28 changes: 26 additions & 2 deletions src/plugins/kconfig/kconfig.cpp
Expand Up @@ -9,7 +9,9 @@

#include "kconfig.hpp"
#include "kconfig_delegate.hpp"
#include "kconfig_serializer.hpp"

#include <fstream>
#include <kdberrors.h>
#include <kdbhelper.h>

Expand Down Expand Up @@ -117,9 +119,31 @@ int elektraKconfigGet (Plugin * handle, KeySet * returned, Key * parentKey)
}

/** @see elektraDocSet */
int elektraKconfigSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
int elektraKconfigSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
{
return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
CppKeySet keys{ returned };
CppKey parent{ parentKey };

ELEKTRA_LOG_DEBUG ("Save `%s` using the kconfig plugin", parent.getFullName ().c_str ());
auto filePtr = new std::ofstream{ parent.getString () };
if (!filePtr->is_open ())
{
ELEKTRA_SET_RESOURCE_ERRORF (parent.getKey (), "Unable to save data to file '%s'. Reason: %s", parent.getString ().c_str (),
"Could not open the file.");
parent.release ();
keys.release ();
return ELEKTRA_PLUGIN_STATUS_ERROR;
}
ELEKTRA_LOG_DEBUG ("File opened successfully, start saving the data.");

KConfigSerializer serializer{ keys, parent, std::unique_ptr<std::ofstream> (filePtr) };
serializer.save ();

ELEKTRA_LOG_DEBUG ("Data succesfully stored into `%s`.", parent.getFullName ().c_str ());

parent.release ();
keys.release ();
return ELEKTRA_PLUGIN_STATUS_SUCCESS;
}

/** @see elektraDocError */
Expand Down
236 changes: 236 additions & 0 deletions src/plugins/kconfig/kconfig_serializer.cpp
@@ -0,0 +1,236 @@
#include "kconfig_serializer.hpp"
#include "base.hpp"
#include <algorithm>
#include <vector>

KConfigSerializer::KConfigSerializer (CppKeySet & keySetParam, CppKey & parentParam, std::unique_ptr<std::ostream> oParam)
: o{ std::move (oParam) }, keySet{ keySetParam }, parent{ parentParam }, parentKeyNameSize{ parentParam.getName ().size () + 1 },
lastPrintedGroup{ "" }, isFirstKey{ true }
{
}

void KConfigSerializer::save ()
{
std::vector<CppKey> keys{ keySet.begin (), keySet.end () };
std::sort (keys.begin (), keys.end (), KConfigSerializer::KeyNameComparator{ parent });

const CppKey * groupCandidate{ nullptr };

for (const auto & k : keys)
{
if (groupCandidate != nullptr)
{
if (k.getName ().rfind (groupCandidate->getName (), 0) == 0)
{
saveGroupKeyOut (groupCandidate->getName ());
lastPrintedGroup = groupCandidate->getName ();
}
else
{
saveLeafKeyWithGroupCandidate (*groupCandidate);
}
groupCandidate = nullptr;
}

if (!k.getString ().empty ())
{
saveLeafKeyWithGroupCandidate (k);
}
else
{
groupCandidate = &k;
}
}
}

void KConfigSerializer::saveAndEscapeString (const std::string & val, bool isGroupKey)
{
std::ostream & out = *o;

char currentChar;
for (std::size_t i{ 0 }; i < val.size (); ++i)
{
currentChar = val[i];
switch (currentChar)
{
case '\\':
if (isGroupKey && i < val.size () && val[i + 1] == '/')
{
// Forward slash is parsed as "\/"
out << '/';
++i;
}
else
{
out << "\\\\";
}
break;
case '/':
if (isGroupKey)
{
out << "][";
}
else
{
out << '/';
}
break;
case '\n':
out << "\\n";
break;
case '\r':
out << "\\r";
break;
case '\t':
out << "\\t";
break;
default:
out << currentChar;
}
}
}

void KConfigSerializer::saveGroupKeyOut (std::string const & group)
{
std::ostream & out = *o;
std::size_t skipChars = parentKeyNameSize;

if (group.size () > skipChars)
{
std::string outstr{ group.substr (skipChars) };

if (!isFirstKey)
{
out << character_newline;
}
else
{
isFirstKey = false;
}

out << character_open_bracket;
saveAndEscapeString (outstr, true);
out << character_close_bracket << character_newline;
}
}

void KConfigSerializer::saveLeafKeyWithGroupCandidate (CppKey const & k)
{
std::string currentGroupName{ groupNameFromLeaf (k.getName ()) };
if (lastPrintedGroup != currentGroupName)
{
saveGroupKeyOut (currentGroupName);
lastPrintedGroup = currentGroupName;
}
saveLeafKeyOut (k);
}

void KConfigSerializer::saveLeafKeyOut (CppKey const & key)
{
std::ostream & out = *o;
isFirstKey = false;
saveAndEscapeString (key.getBaseName (), false);

std::string meta = key.getMeta<std::string> (KCONFIG_METADATA_KEY);
for (char c : meta)
{
out << character_open_bracket;
out << character_dollar_sign;
out << c;
out << character_close_bracket;
}

out << character_equals_sign;
saveAndEscapeString (key.getString (), false);
out << character_newline;
}

std::size_t KConfigSerializer::findLastSlash (std::string const & s)
{
std::size_t count{ 0 };

for (std::size_t i{ 0 }; i < s.size (); i++)
{
switch (s[i])
{
case '/':
count = i;
break;
case '\\':
++i;
}
}

return count;
}

std::string KConfigSerializer::groupNameFromLeaf (std::string const & leafKeyName)
{
return leafKeyName.substr (0, findLastSlash (leafKeyName));
}

KConfigSerializer::KeyNameComparator::KeyNameComparator (CppKey const & parent)
{
auto iter{ parent.begin () };
parentKeyCount = 0;

while (iter != parent.end ())
{
iter++;
parentKeyCount++;
}
}

bool KConfigSerializer::KeyNameComparator::operator() (CppKey const & keyA, CppKey const & keyB)
{
auto itA{ keyA.begin () };
auto itB{ keyB.begin () };

// Don't compare the parent
skipParent (itA);
skipParent (itB);

auto endA{ keyA.end () };
auto endB{ keyB.end () };

while (true)
{
// pointer to where the current keys are stored
const char * strPosA = itA.pos ();
const char * strPosB = itB.pos ();

++itA;
++itB;

// Check if they're group keys, or leaf keys
bool endsA{ itA == endA };
bool endsB{ itB == endB };

if (endsA || endsB)
{
// If one of them is a leaf key, we don't need to iterate further
if (endsA && endsB)
{
// If both are leaf keys, return the compared value
return strcmp (strPosA, strPosB) < 0;
}
return endsA;
}
// Compare the current branch key of a to that of b
int compareAtoB{ strcmp (strPosA, strPosB) };

if (compareAtoB != 0)
{
// If the names don't match, don't continue iterating and return
return compareAtoB < 0;
}
}
}

void KConfigSerializer::KeyNameComparator::skipParent (CppKey::iterator & it)
{
for (std::size_t i{ 0 }; i < parentKeyCount; ++i)
{
it++;
}
}

0 comments on commit 610b454

Please sign in to comment.