Skip to content

Commit

Permalink
Merge pull request #4979 from flo91/improve-c-plugin
Browse files Browse the repository at this point in the history
Plugins: Improve c plugin (doc, tests, refactor)
  • Loading branch information
atmaxinger committed Jul 28, 2023
2 parents 49b53f4 + 159e559 commit 76276dd
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 51 deletions.
2 changes: 1 addition & 1 deletion doc/CONTRACT.ini
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ type = vector<long enum>
{"default", 64000}, ; will be added by the build system for KDB_DEFAULT_STORAGE and KDB_DEFAULT_RESOLVER
{"recommended", 32000}, ; in case of doubt, use this plugin
{"productive", 8000}, ; actively used in productive environments, not only by maintainer
{"maintained", 4000}, ; actively used and fixed by maintainer (infos/author)
{"maintained", 4000}, ; actively used and fixed by maintainer (infos/maintainer)
{"reviewed", 4000}, ; actively reviewed on every change and not by maintainer
{"conformant", 2000}, ; to indicate that e.g. a storage plugin fulfils all requirements of a storage plugin
{"compatible", 2000}, ; to indicate it will be compatible with its later versions
Expand Down
8 changes: 7 additions & 1 deletion doc/news/_preparation_next_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ docker run -it elektra/elektra
- <<HIGHLIGHT>>
- <<HIGHLIGHT>>
- New Changetracking API
- ODBC Backend _(Florian Lindner @flo91)_
- ODBC Backend
- <<HIGHLIGHT>>

### <<HIGHLIGHT>>
Expand Down Expand Up @@ -152,6 +152,12 @@ The following text lists news about the [plugins](https://www.libelektra.org/plu
- <<TODO>>
- <<TODO>>

### c

- Improve the c plugin: some refactoring, add documentation, extend README.md, add unit tests. _(Florian Lindner @flo91)_
- <<TODO>>
- <<TODO>>

### <<Plugin>>

- <<TODO>>
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ include (LibAddMacros)

add_plugin (
c
SOURCES c.h c.c COMPONENT libelektra${SO_VERSION})
SOURCES c.h c.c COMPONENT libelektra${SO_VERSION}
ADD_TEST COMPONENT libelektra${SO_VERSION})
49 changes: 45 additions & 4 deletions src/plugins/c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,56 @@
- infos/provides = storage/c
- infos/recommends =
- infos/placements = getstorage setstorage
- infos/status = maintained nodep libc writeonly preview nodoc
- infos/status = maintained reviewed unittest nodep libc writeonly preview
- infos/metadata =
- infos/description = C-struct exports for Elektra
- infos/description = C code KeySet exports for Elektra

## Usage

Export Elektra’s C-structs (e.g. `ksNew(.. keyNew(`). This is
useful for generating test data, e.g.:
Export a subset of the KDB as C code which creates a KeySet (`ksNew(<number of keys>, keyNew(...), ...)`).
This can be useful for generating test data or help to generate code for already defined configuration data.

```sh
kdb export user:/testdata c
```

The output can be used in C code for generating KeySets and the keys inside them.
So one use case is to extract a part of the KDB and copy the generated code
to create the exported part of the KDB programmatically, e.g. for another Elektra installation.

It can also be useful if you develop your own application and first create the necessary
keys manually. Then you can export them from the KDB and just paste the generated code
in the right place of the codebase of your application.

> Please note that if you use this plugin for creating a mountpoint (e.g. by calling `kdb mount <filename> <key> c`),
> only the content written by the last `kdbSet (...)` which includes the mountpoint will be inside the file.
> If you call `kdbSet (...)` for such a mountpoint, the previous content of the file is erased.
>
> This is because the c plugin is implemented as a write-only plugin.
> It's recommended to only use it for exporting parts of the KDB by calling `kdb export <parent key>`.
## Example

In this example, we add some keys and metakeys to the KDB and export them with the c plugin.
The output can directly be used inside C source code which uses Elektra.

```sh
kdb set user:/tests/cplugin/key1 value1
#> Create a new key user:/tests/cplugin/key1 with string "value1"

kdb set user:/tests/cplugin/key2 value2
#> Create a new key user:/tests/cplugin/key2 with string "value2"

kdb meta-set user:/tests/cplugin/key2 metakey2.1 metaval2.1
kdb set user:/tests/cplugin/key2 metakey2.2 metaval2.2

kdb set user:/tests/cplugin/key3 value3
#> Create a new key user:/tests/cplugin/key3 with string "value3"

kdb export user:/tests/cplugin c
#> ksNew (3,
#> keyNew ("user:/tests/cplugin/key1", KEY_VALUE, "value1", KEY_END),
#> keyNew ("user:/tests/cplugin/key2", KEY_VALUE, "value2", KEY_META, "metakey2.1", "metaval2.1", KEY_META, "metakey2.2", "metaval2.2", KEY_END),
#> keyNew ("user:/tests/cplugin/key3", KEY_VALUE, "value3", KEY_END),
#> KS_END);
```
105 changes: 66 additions & 39 deletions src/plugins/c/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ static const char * const escapes = "\"'\\?nrt";

static const char * const hex = "0123456789abcdef";

/**
* @internal
*
* @brief The function escapes the string @p str and replaces non-printable characters with their HEX values.
*
* @param str The string to escape
* Please note that the given pointer points to a different memory area after the function was executed.
* New memory is allocated for the escaped string and *str points to it then.
* The memory for the old string is freed by this function.
* You must free the returned string.
* @return The escaped string. (the same pointer as @p *str is set to)
*/
static char * escapeString (char ** str)
{
size_t size = 0;
Expand All @@ -36,7 +48,7 @@ static char * escapeString (char ** str)
++size;
}
else
{
{ /* convert to hex value, needs 4 chars */
size += 4;
}
}
Expand All @@ -55,6 +67,7 @@ static char * escapeString (char ** str)

if (e != NULL)
{
/* add '\' and escaped character */
char escaped = escapes[e - toEscape];
*newCur = '\\';
++newCur;
Expand All @@ -63,11 +76,13 @@ static char * escapeString (char ** str)
}
else if (isprint (c))
{
/* just copy the printable character */
*newCur = c;
++newCur;
}
else
{
/* convert the character to a HEX value */
*newCur = '\\';
++newCur;
*newCur = 'x';
Expand All @@ -85,17 +100,22 @@ static char * escapeString (char ** str)
}

/**
* Generate a C-Style key and stream it.
*
* This keyset can be used to include as c-code for
* applikations using elektra.
* @internal
*
* @brief Generate a C-style key and stream it.
*
* The result can be included in C-code for applications using Elektra.
*
* @param key The key to work with
* @param stream The file pointer where to send the stream to
*
* @param key the key object to work with
* @param stream the file pointer where to send the stream
* @retval 1 on success
* @retval -1 on error
*
* @ingroup stream
*/
int keyGenerate (const Key * key, FILE * stream)
static int keyGenerate (const Key * key, FILE * stream)
{
size_t n = keyGetNameSize (key);
if (n > 1)
Expand All @@ -106,7 +126,7 @@ int keyGenerate (const Key * key, FILE * stream)
fprintf (stream, "\tkeyNew (\"%s\"", escapeString (&nam));
elektraFree (nam);
}
else if (n == 1)
else if (n == 1) /* size 1 because of \0 */
{
fprintf (stream, "\tkeyNew(\"\"");
}
Expand All @@ -129,13 +149,15 @@ int keyGenerate (const Key * key, FILE * stream)
elektraFree (str);
}

const Key * meta;
/* Dup key because keyMeta() needs a non-const key */
Key * dup = keyDup (key, KEY_CP_ALL);
KeySet * metaKeys = keyMeta (dup);

for (elektraCursor it = 0; it < ksGetSize (metaKeys); ++it)
{
meta = ksAtCursor (metaKeys, it);
const Key * meta = ksAtCursor (metaKeys, it);

/* Dup the key-name without "meta:/" prefix (remove namespace) */
char * metaName = elektraStrDup (keyName (meta) + sizeof ("meta:/") - 1);
char * metaStr = elektraStrDup (keyString (meta));
fprintf (stream, ", KEY_META, \"%s\", \"%s\"", escapeString (&metaName), escapeString (&metaStr));
Expand All @@ -150,58 +172,75 @@ int keyGenerate (const Key * key, FILE * stream)


/**
* Generate a C-Style keyset and stream it.
* @internal
*
* This keyset can be used to include as c-code for
* applikations using elektra.
* Generate a C-Style KeySet and stream it.
*
* The result can be included in C-code for applications using Elektra.
*
* @param ks The KeySet to work with
* @param stream The file pointer where to send the stream to
*
* @param ks the keyset to work with
* @param stream the file pointer where to send the stream
* @retval 1 on success
* @retval -1 on error
*
* @ingroup stream
*/
int ksGenerate (const KeySet * ks, FILE * stream)
static int ksGenerate (const KeySet * ks, FILE * stream)
{
Key * key;
KeySet * cks = ksDup (ks);
fprintf (stream, "ksNew (%d,\n", (int) ksGetSize (ks));

fprintf (stream, "ksNew (%d,\n", (int) ksGetSize (cks));

for (elektraCursor it = 0; it < ksGetSize (cks); ++it)
for (elektraCursor it = 0; it < ksGetSize (ks); ++it)
{
key = ksAtCursor (cks, it);
Key * key = ksAtCursor (ks, it);
keyGenerate (key, stream);
fprintf (stream, ",\n");
}

fprintf (stream, "\tKS_END);\n");

ksDel (cks);
return 1;
}

int elektraCGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
/**
* @brief The get function of this plugin is only used to define the contract.
*
* @retval 1 on success (always as parsing is not yet implemented)
*/
int elektraCGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
{
if (!elektraStrCmp (keyName (parentKey), "system:/elektra/modules/c"))
{
KeySet * contract = ksNew (30, keyNew ("system:/elektra/modules/c", KEY_VALUE, "c plugin waits for your orders", KEY_END),
keyNew ("system:/elektra/modules/c/exports", KEY_END),
keyNew ("system:/elektra/modules/c/exports/get", KEY_FUNC, elektraCGet, KEY_END),
keyNew ("system:/elektra/modules/c/exports/set", KEY_FUNC, elektraCSet, KEY_END),
keyNew ("system:/elektra/modules/c/exports/checkconf", KEY_FUNC, elektraCCheckConf, KEY_END),
#include ELEKTRA_README
keyNew ("system:/elektra/modules/c/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
ksAppend (returned, contract);
ksDel (contract);

return 1; // success
}
// get all keys

// no action if no contract is requested (write-only plugin)
return 1; // success
}

int elektraCSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
/**
* @brief Generate the C-code and put it in the file whose name is defined as the value of @p parentKey.
*
* @post The given KeySet and its keys are converted into C-code that can be used with Elektra
* and the generated code is written into the file defined by the string-value of parent key.
* Please note that any existing content in the given file is deleted.
*
* @param returned The KeySet for which the C-code should be generated
* @param parentKey The value of the key is the name of the file where the generated code gets written to
*
* @retval 1 on success
* @retval -1 on error
*/
int elektraCSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
{
FILE * fp = fopen (keyString (parentKey), "w");

Expand All @@ -217,18 +256,6 @@ int elektraCSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSE
return 1; // success
}

int elektraCCheckConf (Key * errorKey ELEKTRA_UNUSED, KeySet * conf ELEKTRA_UNUSED)
{
// validate plugin configuration
// this function is optional

// the return codes have the following meaning:
// 0: The configuration was OK and has not been changed
// 1: The configuration has been changed and now it is OK
// -1: The configuration was not OK and could not be fixed. An error has to be set to errorKey.
return 0;
}

Plugin * ELEKTRA_PLUGIN_EXPORT
{
// clang-format off
Expand Down
5 changes: 0 additions & 5 deletions src/plugins/c/c.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,8 @@

#include <kdbplugin.h>


int elektraCOpen (Plugin * handle, Key * errorKey);
int elektraCClose (Plugin * handle, Key * errorKey);
int elektraCGet (Plugin * handle, KeySet * ks, Key * parentKey);
int elektraCSet (Plugin * handle, KeySet * ks, Key * parentKey);
int elektraCError (Plugin * handle, KeySet * ks, Key * parentKey);
int elektraCCheckConf (Key * errorKey, KeySet * conf);

Plugin * ELEKTRA_PLUGIN_EXPORT;

Expand Down

0 comments on commit 76276dd

Please sign in to comment.