diff --git a/cmake/ElektraCache.cmake b/cmake/ElektraCache.cmake index 57e389756e8..2959dad07f7 100644 --- a/cmake/ElektraCache.cmake +++ b/cmake/ElektraCache.cmake @@ -48,6 +48,8 @@ set (PLUGINS_LIST_COMPILE set (PLUGINS_LIST_NODEP ccode fstab + csvstorage + lineendings hexcode hidden ni @@ -71,6 +73,7 @@ set (PLUGINS_LIST_POSIX iconv network path + enum keytometa rename syslog diff --git a/doc/METADATA.ini b/doc/METADATA.ini index 1e31797ddac..e7901fad1e3 100644 --- a/doc/METADATA.ini +++ b/doc/METADATA.ini @@ -302,7 +302,25 @@ description= range checks, from-to, or arbitrary sets of numbers see= check/type/min and check/type/max for already existing implementations +[check/enum] +status= implemented +usedby/plugin= enum +description= List of apostrophe enclosed values separated by + commas to check against +[check/calculate] +status= implemented +usedby/plugin= calculate +type= string +description= A string starting with a logical comparator symbol + followed by a Polish prefix notation to calculate a value + to check against +[check/condition] +status= implemented +usedby/plugin= conditionals +type= string +description= A ternary conditional style operator to define + relations between keys # # Other information about keys # @@ -471,3 +489,14 @@ type= string description= the space used between a comment and the beginning of the line or after end of payload data (for inline comments) If no start token was found, only space and no comment is present. + + +# +# Plugin specific Metadata +# + +[csv/order] +status= implemented +usedby/plugins= csvstorage +type= string +description= used to manage the column order. diff --git a/src/liberror/specification b/src/liberror/specification index 79344df9d36..7f82f633235 100644 --- a/src/liberror/specification +++ b/src/liberror/specification @@ -660,3 +660,75 @@ description:python warning severity:warning ingroup:plugin module:python + +number:113 +description:couldnt open file +severity:error +ingroup:plugin +module:lineendings + +number:114 +description:invalid line ending +severity:error +ingroup:plugin +module:lineendings + +number:115 +description:inconsistent line endings +severity:error +ingroup:plugin +module:lineendings + +number:116 +description: couldn't open file +severity:error +ingroup:plugin +module:csvstorage + +number:117 +description:invalid number of columns +severity:error +ingroup:plugin +module:csvstorage + +number:118 +description:problem during parsing of csvfile (see reason) +severity:warning +ingroup:plugin +module:csvstorage + +number:119 +description:out of memory +severity:error +ingroup:plugin +module:csvstorage + +number:120 +description: Couldn't compile regex +severity: error +ingroup:plugin +module:enum + +number:121 +description:Validation failed +severity:error +ingroup:plugin +module:enum + +number:122 +description:not a valid Polish prefix notation syntax +severity:error +ingroup:plugin +module:calculate + +number:123 +description:invalid value +severity:error +ingroup:plugin +module:calculate + +number:124 +description:key doesn't exist +severity:error +ingroup:plugin +module:calculate diff --git a/src/plugins/README.md b/src/plugins/README.md index bdd09e23170..3e94e34604a 100644 --- a/src/plugins/README.md +++ b/src/plugins/README.md @@ -56,7 +56,7 @@ productive use: - [fstab](fstab) reads fstab files. - [regexstore](regexstore) - [simpleini](simpleini) is ini without sections - +- [csvstorage](csvstorage) for csv files ## System Information ## @@ -126,7 +126,7 @@ copied by another plugin just before): - [network](network) by using network APIs - [path](path) by checking files on filesystem - [type](type) using runtime type checking (CORBA types) - +- [enum](enum) compares the keyvalue against a list of valid values ## Interpreter ## @@ -142,6 +142,7 @@ These plugins start an interpreter and allow you to use a bindings. - [doc](doc) contains the documentation of the plugin interface - [error](error) yields errors as described in metadata - [template](template) to be copied for new plugins +- [lineendings](lineendings) tests file for consistent line endings To add a new plugin you can copy the template plugin. Please make sure to add your plugin: diff --git a/src/plugins/csvstorage/CMakeLists.txt b/src/plugins/csvstorage/CMakeLists.txt new file mode 100644 index 00000000000..a182ffe3090 --- /dev/null +++ b/src/plugins/csvstorage/CMakeLists.txt @@ -0,0 +1,11 @@ +include (LibAddMacros) + +add_plugin(csvstorage + SOURCES + csvstorage.h + csvstorage.c + ) + +install(DIRECTORY csvstorage DESTINATION ${TARGET_TEST_DATA_FOLDER}) + +add_plugintest(csvstorage) diff --git a/src/plugins/csvstorage/README.md b/src/plugins/csvstorage/README.md new file mode 100644 index 00000000000..6768a0483a2 --- /dev/null +++ b/src/plugins/csvstorage/README.md @@ -0,0 +1,26 @@ +- infos = Information about the csvstorage plugin is in keys below +- infos/author = Thomas Waser +- infos/licence = BSD +- infos/needs = +- infos/provides = storage +- infos/placements = getstorage setstorage +- infos/description = parses csv files + +## Introduction ## + +This plugin allowes Elektra to read and write CSV files. + +## Configuration ## + +`delimiter` +Tells the plugin what delimiter is used in the file. +The default delimiter is `;` and will be used if `delimiter` is not set. + +`useheader` +Tells the plugin to use the first line as a header if it's set to "1". The columns will get the coresponding names. +If useheader is not set, the columns get namend #0,#1,... + +## Example ## + +`kdb mount test.csv /csv csvstorage delimiter=";" useheader="1"` + diff --git a/src/plugins/csvstorage/csvstorage.c b/src/plugins/csvstorage/csvstorage.c new file mode 100644 index 00000000000..172982dbaa5 --- /dev/null +++ b/src/plugins/csvstorage/csvstorage.c @@ -0,0 +1,425 @@ +/** + * @file + * + * @brief Source for csvstorage plugin + * + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + */ + + +#ifndef HAVE_KDBCONFIG +# include "kdbconfig.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "csvstorage.h" + +#define INTSTR_MAX 15 +static char *parseLine(char *origLine, char delim, unsigned long offset) +{ + char *line = (origLine + offset); + if(*line == '\0') + return NULL; + char *ptr = strchr(line, delim); + + if(ptr == NULL) + { + line[elektraStrLen(line)-2]='\0'; + } + else + { + *ptr = '\0'; + } + return line; +} + +static unsigned long getLineLength(FILE *fp) +{ + int startPos = ftell(fp); + char c; + while((c = fgetc(fp)) && (!feof(fp))) + { + if(c == '\n') + break; + } + int endPos = ftell(fp); + fseek(fp, startPos, SEEK_SET); + if((endPos - startPos) == 0) + return 0; + else + return (endPos - startPos)+1; +} + +static unsigned long getColumnCount(char *lineBuffer, char delim) +{ + char *ptr = lineBuffer; + unsigned long counter = 0; + while(*ptr != '\0') + { + if(*ptr == delim) + ++counter; + ++ptr; + } + ++counter; + return counter; +} + +static char *itostr(char *buf, unsigned long i, uint8_t len) +{ + snprintf(buf, len, "%lu", i); + return buf; +} + +static Key *getKeyByOrderNr(KeySet *ks, unsigned long n) +{ + Key *cur; + char buf[INTSTR_MAX]; + ksRewind(ks); + while((cur = ksNext(ks)) != NULL) + { + if(!strcmp(keyString(keyGetMeta(cur, "csv/order")), itostr(buf, n, sizeof(buf)-1))) + return cur; + } + return NULL; +} + +static int csvRead(KeySet *returned, Key *parentKey, char delim, short useHeader) +{ + const char *fileName; + fileName = keyString(parentKey); + FILE *fp = NULL; + fp = fopen(fileName, "rb"); + if(!fp) + { + ELEKTRA_SET_ERRORF(116, parentKey, "couldn't open file %s\n", fileName); + return -1; + } + + unsigned long length = 0; + length = getLineLength(fp); + if(length == 0) + { + ELEKTRA_ADD_WARNING(118, parentKey, "Empty file"); + fclose(fp); + return -2; + } + + char *lineBuffer; + lineBuffer = elektraMalloc((length * sizeof(char))+1); + if(!lineBuffer) + { + ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); + return -1; + } + if(!fgets(lineBuffer, length, fp)) + { + ELEKTRA_SET_ERROR(116, parentKey, "Cant read File"); + return -1; + } + + unsigned long columns = 0; + columns = getColumnCount(lineBuffer, delim); + + unsigned long colCounter = 0; + unsigned long lineCounter = 0; + unsigned long offset = 0; + char *col; + char buf[INTSTR_MAX]; + int nr_keys = 1; + KeySet *header = ksNew(0, KS_END); + Key *key; + + if(useHeader == 1) + { + colCounter = 0; + offset = 0; + while((col = parseLine(lineBuffer, delim, offset)) != NULL) + { + offset += elektraStrLen(col); + key = keyDup(parentKey); + keyAddBaseName(key, col); + keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); + ksAppendKey(header, key); + ++colCounter; + } + } + else + { + colCounter = 0; + //if no headerline exists name the columns 0..N where N is the number of columns + key = keyDup(parentKey); + keyAddName(key, "#"); + while(colCounter < columns) + { + if(elektraArrayIncName(key) == -1) + { + elektraFree(lineBuffer); + keyDel(key); + ksDel(header); + fclose(fp); + return -1; + } + keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); + ksAppendKey(header, keyDup(key)); + ++colCounter; + } + keyDel(key); + fseek(fp, 0, SEEK_SET); + } + Key *dirKey; + Key *cur; + dirKey = keyDup(parentKey); + keyAddName(dirKey, "#"); + while(!feof(fp)) + { + length = getLineLength(fp); + if(length == 0) + break; + if(elektraRealloc((void **)&lineBuffer, (length * sizeof(char))+1) < 0) + { + fclose(fp); + elektraFree(lineBuffer); + ksDel(header); + keyDel(dirKey); + ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); + return -1; + } + fgets(lineBuffer, length, fp); + if(elektraArrayIncName(dirKey) == -1) + { + elektraFree(lineBuffer); + keyDel(dirKey); + ksDel(header); + fclose(fp); + return -1; + } + ++nr_keys; + offset = 0; + colCounter = 0; + char *lastIndex = "#0"; + while((col = parseLine(lineBuffer, delim, offset)) != NULL) + { + cur = getKeyByOrderNr(header, colCounter); + offset += elektraStrLen(col); + key = keyDup(dirKey); + keyAddBaseName(key, keyBaseName(cur)); + keySetString(key, col); + keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); + ksAppendKey(returned, key); + lastIndex = (char *)keyString(key); + ++nr_keys; + ++colCounter; + } + keySetString(dirKey, lastIndex); + ksAppendKey(returned, keyDup(dirKey)); + if(colCounter != columns) + { + ELEKTRA_ADD_WARNING(118, parentKey, "illegal number of columns"); + } + ++lineCounter; + } + key = keyDup(parentKey); + keySetString(key, keyBaseName(dirKey)); + ksAppendKey(returned, key); + keyDel(dirKey); + fclose(fp); + elektraFree(lineBuffer); + ksDel(header); + return nr_keys; +} + +int elektraCsvstorageGet(Plugin *handle, KeySet *returned, Key *parentKey) +{ + if (!strcmp(keyName(parentKey), "system/elektra/modules/csvstorage")) + { + KeySet *contract = ksNew (30, + keyNew ("system/elektra/modules/csvstorage", + KEY_VALUE, "csvstorage plugin waits for your orders", KEY_END), + keyNew ("system/elektra/modules/csvstorage/exports", KEY_END), + keyNew ("system/elektra/modules/csvstorage/exports/get", + KEY_FUNC, elektraCsvstorageGet, KEY_END), + keyNew ("system/elektra/modules/csvstorage/exports/set", + KEY_FUNC, elektraCsvstorageSet, KEY_END), +#include ELEKTRA_README(csvstorage) + keyNew ("system/elektra/modules/csvstorage/infos/version", + KEY_VALUE, PLUGINVERSION, KEY_END), + KS_END); + ksAppend (returned, contract); + ksDel (contract); + + return 1; /* success */ + } + + KeySet *config = elektraPluginGetConfig(handle); + Key *delimKey = ksLookupByName(config, "/delimiter", 0); + char delim = ';'; + if(delimKey) + { + const char *delimString = keyString(delimKey); + delim = delimString[0]; + } + + Key *readHeaderKey = ksLookupByName(config, "/useheader", 0); + short useHeader = 0; + if(readHeaderKey) + { + const char *printHeaderString = keyString(readHeaderKey); + if((printHeaderString[0] - '0') == 1) + { + useHeader = 1; + } + } + int nr_keys; + nr_keys = csvRead(returned, parentKey, delim, useHeader); + + switch(nr_keys) + { + case (-2): + return 1; + break; + case (-1): + return -1; + break; + default: + return 1; + break; + } +} + +static int csvWrite(KeySet *returned, Key *parentKey, char delim, short printHeader) +{ + FILE *fp; + fp = fopen(keyString(parentKey), "w"); + if(!fp) + { + ELEKTRA_SET_ERROR_SET(parentKey); + return -1; + } + + keyDel(ksLookup(returned, parentKey, KDB_O_POP)); + + unsigned long colCounter = 0; + unsigned long columns = 0; + Key *cur; + KeySet *toWriteKS; + Key *toWrite; + while((cur = ksNext(returned)) != NULL) + { + if(keyRel(parentKey, cur) != 1) + continue; + toWriteKS = ksCut(returned, cur); + ksRewind(toWriteKS); + colCounter = 0; + if(printHeader) + { + while(1) + { + if(colCounter == ULONG_MAX) + { + ELEKTRA_SET_ERROR(117, parentKey, "number of columns exceeds ULONG_MAX"); + fclose(fp); + return -1; + } + toWrite = getKeyByOrderNr(toWriteKS, colCounter); + if(!toWrite) + break; + if(colCounter) + fprintf(fp, "%c", delim); + ++colCounter; + fprintf(fp, "%s", keyBaseName(toWrite)); + } + fprintf(fp, "\n"); + if(!colCounter) + { + ELEKTRA_SET_ERROR(117, parentKey, "no columns"); + fclose(fp); + return -1; + } + columns = colCounter; + colCounter = 0; + printHeader = 0; + } + else + { + while(1) + { + toWrite = getKeyByOrderNr(toWriteKS, colCounter); + if(!toWrite) + break; + if(colCounter) + fprintf(fp, "%c", delim); + ++colCounter; + fprintf(fp, "%s", keyString(toWrite)); + + } + ksDel(toWriteKS); + fprintf(fp, "\n"); + } + if(columns == 0) + columns = colCounter; + if(colCounter != columns) + { + ELEKTRA_SET_ERROR(117, parentKey, "illegal number of columns\n"); + fclose(fp); + return -1; + } + } + fclose(fp); + return 1; +} + +int elektraCsvstorageSet(Plugin *handle, KeySet *returned, Key *parentKey) +{ + KeySet *config = elektraPluginGetConfig(handle); + Key *delimKey = ksLookupByName(config, "/delimiter", 0); + char outputDelim; + if(delimKey) + { + const char *delimString = keyString(delimKey); + outputDelim = delimString[0]; + } + else + { + outputDelim = ';'; + } + + Key *printHeaderKey = ksLookupByName(config, "/useheader", 0); + short printHeader = 0; + if(printHeaderKey) + { + const char *printHeaderString = keyString(printHeaderKey); + if((printHeaderString[0] - '0') == 1) + printHeader = 1; + else + printHeader = 0; + } + else + { + printHeader = 0; + } + + if(csvWrite(returned, parentKey, outputDelim, printHeader) == -1) + { + return -1; + } + else + { + return 1; /* success */ + } +} + +Plugin *ELEKTRA_PLUGIN_EXPORT(csvstorage) +{ + return elektraPluginExport("csvstorage", + ELEKTRA_PLUGIN_GET, &elektraCsvstorageGet, + ELEKTRA_PLUGIN_SET, &elektraCsvstorageSet, + ELEKTRA_PLUGIN_END); +} + diff --git a/src/plugins/csvstorage/csvstorage.h b/src/plugins/csvstorage/csvstorage.h new file mode 100644 index 00000000000..63f5b343681 --- /dev/null +++ b/src/plugins/csvstorage/csvstorage.h @@ -0,0 +1,21 @@ +/** +* @file +* +* @brief Header for csvstorage plugin +* +* @copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + +#ifndef ELEKTRA_PLUGIN_CSVSTORAGE_H +#define ELEKTRA_PLUGIN_CSVSTORAGE_H + +#include + + +int elektraCsvstorageGet(Plugin *handle, KeySet *ks, Key *parentKey); +int elektraCsvstorageSet(Plugin *handle, KeySet *ks, Key *parentKey); + +Plugin *ELEKTRA_PLUGIN_EXPORT(csvstorage); + +#endif diff --git a/src/plugins/csvstorage/csvstorage/invalid_columns.csv b/src/plugins/csvstorage/csvstorage/invalid_columns.csv new file mode 100644 index 00000000000..f22cd8bac87 --- /dev/null +++ b/src/plugins/csvstorage/csvstorage/invalid_columns.csv @@ -0,0 +1,3 @@ +l1c1;l1c2;l1c3 +l2c1;l2c2 +l3c1;l3c2;l3c3 diff --git a/src/plugins/csvstorage/csvstorage/valid.csv b/src/plugins/csvstorage/csvstorage/valid.csv new file mode 100644 index 00000000000..e35b02a5299 --- /dev/null +++ b/src/plugins/csvstorage/csvstorage/valid.csv @@ -0,0 +1,3 @@ +col1;col2 +l1c1;l2c2 +l2c1;l2c2 diff --git a/src/plugins/csvstorage/testmod_csvstorage.c b/src/plugins/csvstorage/testmod_csvstorage.c new file mode 100644 index 00000000000..adeb3a5a7ab --- /dev/null +++ b/src/plugins/csvstorage/testmod_csvstorage.c @@ -0,0 +1,73 @@ +/** +* \file +* +* \brief Tests for csvstorage plugin +* +* \copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + +#include +#include + +#include + +#include + +static void testread(const char *file) +{ + printf("testing on %s:%s\n", srcdir_file(file), file); + Key * parentKey = keyNew ("user/tests/csvstorage", KEY_VALUE, srcdir_file(file), KEY_END); + KeySet *conf = ksNew (20, + keyNew ("system/useheader", KEY_VALUE, "1", KEY_END), KS_END); + PLUGIN_OPEN("csvstorage"); + KeySet *ks = ksNew(0, KS_END); + succeed_if (plugin->kdbGet(plugin, ks, parentKey) >= 1, "call to kdbGet was not successful"); + output_keyset(ks); + Key *key; + key = ksLookupByName(ks, "user/tests/csvstorage/#0/col1", 0); + exit_if_fail(key, "key not found"); + succeed_if(strcmp(keyString(key), "l1c1") == 0, "wrong key"); + key = ksLookupByName(ks, "user/tests/csvstorage/#1/col2", 0); + exit_if_fail(key, "key not found"); + succeed_if(strcmp(keyString(key), "l2c2") == 0, "wrong key"); + + ksDel(ks); + keyDel(parentKey); + + PLUGIN_CLOSE(); +} + +static void testreadwriteinvalid(const char *file) +{ + + Key * parentKey = keyNew ("user/tests/csvstorage", KEY_VALUE, srcdir_file(file), KEY_END); + KeySet *conf = 0; + KeySet *ks = ksNew(0, KS_END); + PLUGIN_OPEN("csvstorage"); + succeed_if (plugin->kdbGet(plugin, ks, parentKey) >0, "call to kdbGet was not successful"); + output_keyset(ks); + succeed_if(!output_warnings(parentKey), "no warnings in kdbGet"); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (-1), "error: wrote invalid data"); + ksDel(ks); + keyDel(parentKey); + PLUGIN_CLOSE(); +} + + +int main(int argc, char** argv) +{ + printf ("CSVSTORAGE TESTS\n"); + printf ("==================\n\n"); + + init (argc, argv); + + testread("csvstorage/valid.csv"); + testreadwriteinvalid("csvstorage/invalid_columns.csv"); + + printf ("\ntestmod_csvstorage RESULTS: %d test(s) done. %d error(s).\n", + nbTest, nbError); + + return nbError; +} + diff --git a/src/plugins/enum/CMakeLists.txt b/src/plugins/enum/CMakeLists.txt new file mode 100644 index 00000000000..c30660e5fbf --- /dev/null +++ b/src/plugins/enum/CMakeLists.txt @@ -0,0 +1,9 @@ +include (LibAddMacros) + +add_plugin(enum + SOURCES + enum.h + enum.c + ) + +add_plugintest(enum) diff --git a/src/plugins/enum/README.md b/src/plugins/enum/README.md new file mode 100644 index 00000000000..b5e16b1dd8b --- /dev/null +++ b/src/plugins/enum/README.md @@ -0,0 +1,17 @@ +- infos = Information about the enum plugin is in keys below +- infos/author = Thomas Waser +- infos/licence = BSD +- infos/needs = +- infos/provides = check +- infos/placements = presetstorage +- infos/description = + +## Introduction ## + +The Enum plugin checks string values of Keys by comparing it against a list of valid values. + +## Usage ## + +The plugin checks every Key in the Keyset for the Metakey `check/enum` containing a list + with the syntax `'string1', 'string2', 'string3', ..., 'stringN'` and compares each +value with the string value of the Key. If no match is found an error is returned. diff --git a/src/plugins/enum/enum.c b/src/plugins/enum/enum.c new file mode 100644 index 00000000000..e66939991dc --- /dev/null +++ b/src/plugins/enum/enum.c @@ -0,0 +1,116 @@ +/** +* @file +* +* @brief Source for enum plugin +* +* @copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + + +#ifndef HAVE_KDBCONFIG +# include "kdbconfig.h" +#endif + +#include +#include +#include +#include +#include +#include "enum.h" + +int elektraEnumGet(Plugin *handle ELEKTRA_UNUSED, KeySet *returned ELEKTRA_UNUSED, Key *parentKey ELEKTRA_UNUSED) +{ + if (!strcmp(keyName(parentKey), "system/elektra/modules/enum")) + { + KeySet *contract = ksNew (30, + keyNew ("system/elektra/modules/enum", + KEY_VALUE, "enum plugin waits for your orders", KEY_END), + keyNew ("system/elektra/modules/enum/exports", KEY_END), + keyNew ("system/elektra/modules/enum/exports/get", + KEY_FUNC, elektraEnumGet, KEY_END), + keyNew ("system/elektra/modules/enum/exports/set", + KEY_FUNC, elektraEnumSet, KEY_END), +#include ELEKTRA_README(enum) + keyNew ("system/elektra/modules/enum/infos/version", + KEY_VALUE, PLUGINVERSION, KEY_END), + KS_END); + ksAppend (returned, contract); + ksDel (contract); + + return 1; /* success */ + } + /* get all keys */ + + return 1; /* success */ +} + +static int validateKey(Key *key) +{ + const Key *meta = keyGetMeta(key, "check/enum"); + if(!meta) + return 1; + const char *validValues = keyString(meta); + const char *regexString = "'([^']*)'\\s*(,|$)"; + regex_t regex; + if(regcomp(®ex, regexString, REG_EXTENDED|REG_NEWLINE)) + { + ELEKTRA_SET_ERROR(120, key, "regcomp failed"); + return -1; + } + const char *ptr = validValues; + const short submatches = 3; //first submatch is the string we want, second submatch , or EOL + regmatch_t match[submatches]; + char *value = NULL; + int nomatch; + int start; + int end; + while(1) + { + nomatch = regexec(®ex, ptr, submatches, match, 0); + if(nomatch) + break; + + start = match[1].rm_so + (ptr - validValues); + end = match[1].rm_eo + (ptr - validValues); + elektraRealloc((void **)&value, (end - start)+1); + strncpy(value, validValues+start, end-start); + value[(end-start)] = '\0'; + if(strcmp(keyString(key), value) == 0) + { + regfree(®ex); + elektraFree(value); + return 1; + } + ptr += match[0].rm_eo; + } + if(value) + elektraFree(value); + regfree(®ex); + return 0; +} + +int elektraEnumSet(Plugin *handle ELEKTRA_UNUSED, KeySet *returned ELEKTRA_UNUSED, Key *parentKey ELEKTRA_UNUSED) +{ + /* set all keys */ + Key *cur; + while((cur = ksNext(returned)) != NULL) + { + if(!validateKey(cur)) + { + ELEKTRA_SET_ERROR(121, parentKey, "Validation failed"); + return -1; + } + } + + return 1; /* success */ +} + +Plugin *ELEKTRA_PLUGIN_EXPORT(enum) +{ + return elektraPluginExport("enum", + ELEKTRA_PLUGIN_GET, &elektraEnumGet, + ELEKTRA_PLUGIN_SET, &elektraEnumSet, + ELEKTRA_PLUGIN_END); +} + diff --git a/src/plugins/enum/enum.h b/src/plugins/enum/enum.h new file mode 100644 index 00000000000..856a0e51522 --- /dev/null +++ b/src/plugins/enum/enum.h @@ -0,0 +1,21 @@ +/** +* @file +* +* @brief Header for enum plugin +* +* @copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + +#ifndef ELEKTRA_PLUGIN_ENUM_H +#define ELEKTRA_PLUGIN_ENUM_H + +#include + + +int elektraEnumGet(Plugin *handle, KeySet *ks, Key *parentKey); +int elektraEnumSet(Plugin *handle, KeySet *ks, Key *parentKey); + +Plugin *ELEKTRA_PLUGIN_EXPORT(enum); + +#endif diff --git a/src/plugins/enum/testmod_enum.c b/src/plugins/enum/testmod_enum.c new file mode 100644 index 00000000000..5b148e657d5 --- /dev/null +++ b/src/plugins/enum/testmod_enum.c @@ -0,0 +1,80 @@ +/** +* \file +* +* \brief Tests for enum plugin +* +* \copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + +#include +#include + +#include +#include + +#include +#include + + +static void test() +{ + Key *parentKey = keyNew("user/tests/enum", KEY_VALUE, "", KEY_END); + Key *k1 = keyNew("user/tests/enum/valid1", KEY_VALUE, "TRUE", KEY_META, "check/enum", "'TRUE','FALSE'", KEY_END); + Key *k2 = keyNew("user/tests/enum/valid2", KEY_VALUE, "FALSE", KEY_META, "check/enum", "'TRUE','FALSE'", KEY_END); + Key *k3 = keyNew("user/tests/enum/invalid1", KEY_VALUE, "BLA", KEY_META, "check/enum", "'TRUE','FALSE'", KEY_END); + Key *k4 = keyNew("user/tests/enum/invalid2", KEY_VALUE, "", KEY_META, "check/enum", "'TRUE','FALSE'", KEY_END); + KeySet *conf = ksNew(0, KS_END); + KeySet *ks; + PLUGIN_OPEN("enum"); + + ks = ksNew(20, KS_END); + ksAppendKey(ks, k1); + output_keyset(ks); + ksRewind(ks); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (1), "kdbSet failed"); + ksDel(ks); + + ks = ksNew(20, KS_END); + ksAppendKey(ks, k2); + output_keyset(ks); + ksRewind(ks); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (1), "kdbSet failed"); + ksDel(ks); + + ks = ksNew(20, KS_END); + ksAppendKey(ks, k3); + output_keyset(ks); + ksRewind(ks); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (-1), "kdbSet failed"); + ksDel(ks); + + ks = ksNew(20, KS_END); + ksAppendKey(ks, k4); + output_keyset(ks); + ksRewind(ks); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (-1), "kdbSet failed"); + ksDel(ks); + + keyDel(parentKey); + PLUGIN_CLOSE(); + +} + + + +int main(int argc, char** argv) +{ + printf ("ENUM TESTS\n"); + printf ("==================\n\n"); + + init (argc, argv); + + test(); + + printf ("\ntestmod_enum RESULTS: %d test(s) done. %d error(s).\n", + nbTest, nbError); + + return nbError; +} + diff --git a/src/plugins/lineendings/CMakeLists.txt b/src/plugins/lineendings/CMakeLists.txt new file mode 100644 index 00000000000..14e31661093 --- /dev/null +++ b/src/plugins/lineendings/CMakeLists.txt @@ -0,0 +1,9 @@ +include (LibAddMacros) + +add_plugin(lineendings + SOURCES + lineendings.h + lineendings.c + ) +install(DIRECTORY lineendings DESTINATION ${TARGET_TEST_DATA_FOLDER}) +add_plugintest(lineendings) diff --git a/src/plugins/lineendings/README.md b/src/plugins/lineendings/README.md new file mode 100644 index 00000000000..3378ac95cce --- /dev/null +++ b/src/plugins/lineendings/README.md @@ -0,0 +1,19 @@ +- infos = Information about the lineendings plugin is in keys below +- infos/author = Thomas Waser +- infos/licence = BSD +- infos/needs = +- infos/provides = +- infos/placements = pregetstorage precommit +- infos/description = + +## Introduction ## + +The Lineendings Plugin verifies the Lineendings of a file. +If inconsistent lineendings or lineendings that don't match `valid` are detected the plugin yields an error. + +## Configuration ## + +`valid` +The key tells the plugin to reject all lineendings other than specified in this key. Valid options: CRLF, LFCR, CR, LF +If the key doesn't exist only inconsistent lineendings get rejected. + diff --git a/src/plugins/lineendings/lineendings.c b/src/plugins/lineendings/lineendings.c new file mode 100644 index 00000000000..ece83f0a2b0 --- /dev/null +++ b/src/plugins/lineendings/lineendings.c @@ -0,0 +1,182 @@ +/** + * @file + * + * @brief Source for lineendings plugin + * + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + */ + + +#ifndef HAVE_KDBCONFIG +# include "kdbconfig.h" +#endif + +#include +#include +#include +#include +#include "lineendings.h" + +#define LF_BYTE 0x0A +#define CR_BYTE 0x0D + +typedef enum {NA, CR, LF, CRLF, LFCR, NUM_TYPES}Lineending; + +static inline char *LEString(Lineending index) +{ + static char *strings[] = {"NA", "CR", "LF", "CRLF", "LFCR"}; + if(index > NUM_TYPES) + return NULL; + return strings[index]; +} +static Lineending strToLE(const char *str) +{ + uint8_t counter = 0; + for(; counter < NUM_TYPES; ++counter) + { + if(!strcmp(LEString(counter), str)) + return counter; + } + return NA; +} +static int checkLineEndings(const char *fileName, Lineending validLineEnding, Key *parentKey) +{ + FILE *fp; + fp = fopen(fileName, "rb"); + if(fp == NULL) + { + return -1; + } + + Lineending lineEnding = NA; + Lineending found = NA; + uint8_t fc, sc; + unsigned long line = 1; + fc = sc = 0; + fread(&fc, 1, 1, fp); + while(!feof(fp)) + { + fread(&sc, 1, 1, fp); + switch(fc) + { + case LF_BYTE: + if(sc == CR_BYTE) + found = LFCR; + else if(sc == LF_BYTE) + found = LF; + else if(sc) + found = LF; + break; + case CR_BYTE: + if(sc == LF_BYTE) + found = CRLF; + else if(sc == CR_BYTE) + found = CR; + else if(sc) + found = CR; + break; + } + if(found == CRLF || found == LFCR) + { + fread(&sc, 1, 1, fp); + } + if(lineEnding == NA && found != NA) + { + lineEnding = found; + if(validLineEnding != NA && lineEnding != validLineEnding) + { + fclose(fp); + ELEKTRA_SET_ERRORF(114, parentKey, "Invalid line ending at line %lu", line); + return -2; + } + found = NA; + } + else if(lineEnding != found && found != NA) + { + fclose(fp); + ELEKTRA_SET_ERRORF(115, parentKey, "inconsistent line endings at line %lu", line); + return -3; + } + else + { + ++line; + } + fc = sc; + found = NA; + } + fclose(fp); + return 0; +} + +int elektraLineendingsGet(Plugin *handle ELEKTRA_UNUSED, KeySet *returned ELEKTRA_UNUSED, Key *parentKey ELEKTRA_UNUSED) +{ + if (!strcmp(keyName(parentKey), "system/elektra/modules/lineendings")) + { + KeySet *contract = ksNew (30, + keyNew ("system/elektra/modules/lineendings", + KEY_VALUE, "lineendings plugin waits for your orders", KEY_END), + keyNew ("system/elektra/modules/lineendings/exports", KEY_END), + keyNew ("system/elektra/modules/lineendings/exports/get", + KEY_FUNC, elektraLineendingsGet, KEY_END), + keyNew ("system/elektra/modules/lineendings/exports/set", + KEY_FUNC, elektraLineendingsSet, KEY_END), +#include ELEKTRA_README(lineendings) + keyNew ("system/elektra/modules/lineendings/infos/version", + KEY_VALUE, PLUGINVERSION, KEY_END), + KS_END); + ksAppend (returned, contract); + ksDel (contract); + + return 1; /* success */ + } + /* get all keys */ + KeySet *config = elektraPluginGetConfig(handle); + Key *valid = ksLookupByName(config, "/valid", 0); + Lineending validLineEnding = strToLE(keyString(valid)); + int ret; + ret = checkLineEndings(keyString(parentKey), validLineEnding, parentKey); + if(ret == (-3)) + { + return -1; + } + else + return 1; +} + +int elektraLineendingsSet(Plugin *handle, KeySet *returned ELEKTRA_UNUSED, Key *parentKey) +{ + KeySet *config = elektraPluginGetConfig(handle); + Key *valid = ksLookupByName(config, "/valid", 0); + Lineending validLineEnding = strToLE(keyString(valid)); + int ret; + ret = checkLineEndings(keyString(parentKey), validLineEnding, parentKey); + switch(ret) + { + case (-1): + ELEKTRA_SET_ERRORF(113, parentKey, "Couldn't open file %s\n", keyString(parentKey)); + return 1; + break; + case (-2): + return -1; + break; + case (-3): + return -1; + break; + case 0: + default: + return 1; + break; + } + +} + +Plugin *ELEKTRA_PLUGIN_EXPORT(lineendings) +{ + return elektraPluginExport("lineendings", + ELEKTRA_PLUGIN_GET, &elektraLineendingsGet, + ELEKTRA_PLUGIN_SET, &elektraLineendingsSet, + // ELEKTRA_PLUGIN_ERROR, &elektraLineendingsError, + ELEKTRA_PLUGIN_END); +} + diff --git a/src/plugins/lineendings/lineendings.h b/src/plugins/lineendings/lineendings.h new file mode 100644 index 00000000000..68bf3122186 --- /dev/null +++ b/src/plugins/lineendings/lineendings.h @@ -0,0 +1,21 @@ +/** +* @file +* +* @brief Header for lineendings plugin +* +* @copyright BSD License (see doc/COPYING or http://www.libelektra.org) +* +*/ + +#ifndef ELEKTRA_PLUGIN_LINEENDINGS_H +#define ELEKTRA_PLUGIN_LINEENDINGS_H + +#include + + +int elektraLineendingsGet(Plugin *handle, KeySet *ks, Key *parentKey); +int elektraLineendingsSet(Plugin *handle, KeySet *ks, Key *parentKey); + +Plugin *ELEKTRA_PLUGIN_EXPORT(lineendings); + +#endif diff --git a/src/plugins/lineendings/lineendings/inconsistent b/src/plugins/lineendings/lineendings/inconsistent new file mode 100644 index 00000000000..d923dc3c90f --- /dev/null +++ b/src/plugins/lineendings/lineendings/inconsistent @@ -0,0 +1,2 @@ +r-n: +n: diff --git a/src/plugins/lineendings/lineendings/invalid b/src/plugins/lineendings/lineendings/invalid new file mode 100644 index 00000000000..873bca2eaaa --- /dev/null +++ b/src/plugins/lineendings/lineendings/invalid @@ -0,0 +1,2 @@ +r-n: +r-n2: diff --git a/src/plugins/lineendings/lineendings/valid1 b/src/plugins/lineendings/lineendings/valid1 new file mode 100644 index 00000000000..b250dd0ebed --- /dev/null +++ b/src/plugins/lineendings/lineendings/valid1 @@ -0,0 +1,2 @@ +r-n: +r-n2: diff --git a/src/plugins/lineendings/lineendings/valid2 b/src/plugins/lineendings/lineendings/valid2 new file mode 100644 index 00000000000..873bca2eaaa --- /dev/null +++ b/src/plugins/lineendings/lineendings/valid2 @@ -0,0 +1,2 @@ +r-n: +r-n2: diff --git a/src/plugins/lineendings/testmod_lineendings.c b/src/plugins/lineendings/testmod_lineendings.c new file mode 100644 index 00000000000..103e5e57cb0 --- /dev/null +++ b/src/plugins/lineendings/testmod_lineendings.c @@ -0,0 +1,76 @@ +/** + * \file + * + * \brief Tests for lineendings plugin + * + * \copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + */ + +#include +#include + +#include + +#include + +void testvalid(const char *file) +{ + Key * parentKey = keyNew ("user/tests/lineendings", KEY_VALUE, srcdir_file(file), KEY_END); + KeySet *conf = 0; + PLUGIN_OPEN("lineendings"); + KeySet *ks = ksNew(0, KS_END); + succeed_if(plugin->kdbGet(plugin, ks, parentKey) == 1, "kdbget failed"); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == 1, "kdbset failed"); + ksDel(ks); + keyDel(parentKey); + PLUGIN_CLOSE(); + +} + +void testinconsistent(const char *file) +{ + Key * parentKey = keyNew ("user/tests/lineendings", KEY_VALUE, srcdir_file(file), KEY_END); + KeySet *conf = 0; + PLUGIN_OPEN("lineendings"); + KeySet *ks = ksNew(0, KS_END); + succeed_if(plugin->kdbGet(plugin, ks, parentKey) == (-1), "should have failed"); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (-1), "should have failed"); + ksDel(ks); + keyDel(parentKey); + PLUGIN_CLOSE(); + +} + +void testinvalid(const char *file) +{ + Key * parentKey = keyNew ("user/tests/lineendings", KEY_VALUE, srcdir_file(file), KEY_END); + KeySet *conf = ksNew(20, keyNew("system/valid", KEY_VALUE, "CRLF", KEY_END), KS_END); + PLUGIN_OPEN("lineendings"); + KeySet *ks = ksNew(0, KS_END); + succeed_if(plugin->kdbGet(plugin, ks, parentKey) == 1, "kdbget failed"); + succeed_if(plugin->kdbSet(plugin, ks, parentKey) == (-1), "should have failed"); + ksDel(ks); + keyDel(parentKey); + PLUGIN_CLOSE(); + +} + + + +int main(int argc, char** argv) +{ + printf ("LINEENDINGS TESTS\n"); + printf ("==================\n\n"); + + init (argc, argv); + + testvalid("lineendings/valid1"); + testinconsistent("lineendings/inconsistent"); + testinvalid("lineendings/invalid"); + printf ("\ntestmod_lineendings RESULTS: %d test(s) done. %d error(s).\n", + nbTest, nbError); + + return nbError; +} + diff --git a/src/plugins/ni/nickel-1.1.0/src/tests/ni_parse_oddities.ini b/src/plugins/ni/nickel-1.1.0/src/tests/ni_parse_oddities.ini deleted file mode 100644 index a90c4bb17a9..00000000000 --- a/src/plugins/ni/nickel-1.1.0/src/tests/ni_parse_oddities.ini +++ /dev/null @@ -1,14 +0,0 @@ -; ignored -also ignored -=not ignored ; but I am -also not ignored= -a=multi \ - line -"con" "cat"=enated -multi="line -inside quotes" ; with comments after -escapes=\\\a\r\n\x11\b\056\t\\t\xj -\==\; not a comment -\[not a section\]=yeah -loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggggggg=truncated -something = ; nothing diff --git a/tests/shell/include_common.sh.in b/tests/shell/include_common.sh.in index ec34ff791c3..bb835c3792e 100644 --- a/tests/shell/include_common.sh.in +++ b/tests/shell/include_common.sh.in @@ -206,7 +206,8 @@ is_not_rw_storage() -o "x$PLUGIN" = "xuname" \ -o "x$PLUGIN" = "xconstants" \ -o "x$PLUGIN" = "xaugeas" \ - -o "x$PLUGIN" = "xregexstore" + -o "x$PLUGIN" = "xcsvstorage" \ + -o "x$PLUGIN" = "xregexstore" } is_plugin_available()