LCOV - code coverage report
Current view: top level - src/plugins/csvstorage - csvstorage.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 314 409 76.8 %
Date: 2019-09-12 12:28:41 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for csvstorage plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : 
      11             : #ifndef HAVE_KDBCONFIG
      12             : #include "kdbconfig.h"
      13             : #endif
      14             : 
      15             : #include "csvstorage.h"
      16             : #include <errno.h>
      17             : #include <kdbassert.h>
      18             : #include <kdbease.h>
      19             : #include <kdberrors.h>
      20             : #include <kdbhelper.h>
      21             : #include <kdbproposal.h> // for ksRenameKeys
      22             : #include <stdio.h>
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : 
      26             : 
      27             : #define PARSE 1
      28             : #define COLCOUNT 2
      29             : #define READLINE 3
      30             : 
      31        8808 : static char * parseRecord (char ** ptr, char delim, int * isQuoted, int * isCol, int * hasUnescapedDQuote, unsigned long * counter,
      32             :                            unsigned short mode)
      33             : {
      34        8808 :         ELEKTRA_NOT_NULL (ptr);
      35        8808 :         ELEKTRA_NOT_NULL (*ptr);
      36        8808 :         if (**ptr == '"')
      37             :         {
      38         120 :                 if (!(*isCol) && !(*isQuoted))
      39             :                 {
      40          40 :                         *isCol = 1;
      41          40 :                         *isQuoted = 1;
      42             :                 }
      43          80 :                 else if (*isCol && *isQuoted)
      44             :                 {
      45          56 :                         if (*(*ptr + 1) == '"')
      46             :                         {
      47          12 :                                 ++(*ptr);
      48             :                         }
      49          44 :                         else if (*(*ptr + 1) == delim)
      50             :                         {
      51          40 :                                 *isQuoted = 0;
      52          40 :                                 *isCol = 0;
      53          40 :                                 ++(*ptr);
      54          40 :                                 ++(*counter);
      55          40 :                                 if (mode == PARSE) return NULL;
      56             :                         }
      57           4 :                         else if (*(*ptr + 1) == '\n' || *(*ptr + 1) == '\r')
      58             :                         {
      59           0 :                                 *isQuoted = 0;
      60           0 :                                 *isCol = 0;
      61           0 :                                 ++(*counter);
      62             :                         }
      63             :                         else
      64             :                         {
      65           4 :                                 *hasUnescapedDQuote = 1;
      66             :                         }
      67             :                 }
      68             :         }
      69        8688 :         else if (**ptr == delim)
      70             :         {
      71         696 :                 if (!(*isQuoted))
      72             :                 {
      73         688 :                         *isCol = 0;
      74         688 :                         ++(*counter);
      75         688 :                         if (mode == PARSE) return NULL;
      76             :                 }
      77             :         }
      78        7992 :         else if (**ptr != '\n' && **ptr != '\r')
      79             :         {
      80        7578 :                 if (!(*isCol))
      81             :                 {
      82        1066 :                         *isCol = 1;
      83             :                 }
      84             :         }
      85             :         else // it's \n or \r
      86             :         {
      87         414 :                 if (mode == READLINE)
      88             :                 {
      89           0 :                         if (*isQuoted && *isCol)
      90             :                         {
      91             :                                 return *ptr;
      92             :                         }
      93             :                         else
      94             :                         {
      95           0 :                                 *isCol = 0;
      96           0 :                                 *isQuoted = 0;
      97             :                         }
      98             :                 }
      99             :                 else
     100             :                 {
     101             :                         // last column is empty
     102         414 :                         if (!(*isQuoted))
     103             :                         {
     104         402 :                                 *isCol = 0;
     105         402 :                                 ++(*counter);
     106         402 :                                 if (mode == PARSE) return NULL;
     107             :                         }
     108             :                 }
     109             :         }
     110        8329 :         ++(*ptr);
     111        8329 :         return *ptr;
     112             : }
     113             : 
     114             : // ignore record and field separators in quoted fields according to RFC 4180
     115             : // @returns next field in record
     116             : 
     117         650 : static char * parseLine (char * origLine, char delim, unsigned long offset, Key * parentKey, unsigned long lineNr, int lastLine)
     118             : {
     119         650 :         char * line = (origLine + offset);
     120             : 
     121         650 :         if (*line == '\0') return NULL;
     122             : 
     123         479 :         char * ptr = line;
     124         479 :         int isQuoted = 0;
     125         479 :         int isCol = 0;
     126         479 :         int hasUnescapedDQuote = 0;
     127        4170 :         while (*ptr)
     128             :         {
     129        3691 :                 char * ret = parseRecord (&ptr, delim, &isQuoted, &isCol, &hasUnescapedDQuote, &(unsigned long){ 0 }, PARSE);
     130        3691 :                 if (!ret) break;
     131             :         }
     132         479 :         if (!(*ptr))
     133             :         {
     134           0 :                 if (!isQuoted && isCol)
     135             :                 {
     136           0 :                         isCol = 0;
     137           0 :                         if (!lastLine)
     138             :                         {
     139           0 :                                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (
     140             :                                         parentKey, "Unexpected end of line(%lu), all records except the last must and with a newline",
     141             :                                         lineNr);
     142             :                         }
     143             :                 }
     144             :         }
     145         479 :         unsigned long len = elektraStrLen (line);
     146         479 :         if (isQuoted)
     147             :         {
     148           0 :                 if (line[len - 2] == '\n' || line[len - 2] == '\r')
     149             :                 {
     150           0 :                         line[len - 2] = '\0';
     151             :                 }
     152           0 :                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (
     153             :                         parentKey, "Unexpected end of line(%lu). unbalanced number of double-quotes in (%s)", lineNr, line);
     154             :         }
     155         479 :         else if (isCol)
     156             :         {
     157           0 :                 if (line[len - 2] == '\n' || line[len - 2] == '\r')
     158             :                 {
     159           0 :                         line[len - 2] = '\0';
     160             :                 }
     161           0 :                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (parentKey, "Unexpected end of line(%lu): (%s)", lineNr, line);
     162             :         }
     163             :         else
     164             :         {
     165         479 :                 *ptr = '\0';
     166             :         }
     167         479 :         if (hasUnescapedDQuote)
     168             :         {
     169           2 :                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (parentKey, "Quoted field in line(%lu) has an unescaped double-quote: (%s)",
     170             :                                                            lineNr, line);
     171             :         }
     172             : 
     173             :         return line;
     174             : }
     175             : 
     176         235 : static unsigned long getLineLength (FILE * fp)
     177             : {
     178         235 :         int startPos = ftell (fp);
     179             :         char c;
     180        4505 :         while ((c = fgetc (fp)) && (!feof (fp)))
     181             :         {
     182        4227 :                 if (c == '\n') break;
     183             :         }
     184         235 :         int endPos = ftell (fp);
     185         235 :         fseek (fp, startPos, SEEK_SET);
     186         235 :         if ((endPos - startPos) == 0)
     187             :                 return 0;
     188             :         else
     189         192 :                 return (endPos - startPos) + 1;
     190             : }
     191             : 
     192             : // count columns in lineBuffer
     193             : // ignore record and field separators in quoted fields
     194             : 
     195          45 : static unsigned long getColumnCount (char * lineBuffer, char delim)
     196             : {
     197          45 :         char * ptr = lineBuffer;
     198          45 :         unsigned long counter = 0;
     199          45 :         int isQuoted = 0;
     200          45 :         int isCol = 0;
     201        1006 :         while (*ptr)
     202             :         {
     203         916 :                 parseRecord (&ptr, delim, &isQuoted, &isCol, &(int){ 0 }, &counter, COLCOUNT);
     204             :         }
     205             :         if (!(*ptr))
     206             :         {
     207          45 :                 if (!isQuoted && isCol)
     208             :                 {
     209           0 :                         ++counter;
     210             :                 }
     211             :         }
     212          45 :         return counter;
     213             : }
     214             : 
     215             : // reads next record from file according to RFC 4180
     216             : // if EOL is reached with unbalanced quotes, assume record continues at the next
     217             : // line. append succeeding lines until quotes are balanced or EOF is reached
     218             : 
     219         229 : static char * readNextLine (FILE * fp, char delim, int * lastLine, int * linesRead)
     220             : {
     221         229 :         int done = 0;
     222         229 :         unsigned long bufLen = 0;
     223         229 :         unsigned long offset = 0;
     224         229 :         *linesRead = 0;
     225         229 :         char * lineBuffer = NULL;
     226             :         *linesRead = 0;
     227         229 :         int isQuoted = 0;
     228         229 :         int isCol = 0;
     229         650 :         while (!done)
     230         235 :         {
     231             : 
     232         235 :                 unsigned long len = getLineLength (fp);
     233         235 :                 if (!len)
     234             :                 {
     235          43 :                         if (!lineBuffer)
     236             :                         {
     237          43 :                                 *lastLine = 0;
     238          86 :                                 return NULL;
     239             :                         }
     240             :                         else
     241             :                                 return lineBuffer;
     242             :                 }
     243             :                 else
     244             :                 {
     245         192 :                         ++(*linesRead);
     246             :                 }
     247         192 :                 char buffer[len];
     248         192 :                 fgets (buffer, len, fp);
     249         192 :                 char * ptr = buffer;
     250        4585 :                 while (*ptr)
     251             :                 {
     252        4201 :                         parseRecord (&ptr, delim, &isQuoted, &isCol, &(int){ 0 }, &(unsigned long){ 0 }, COLCOUNT);
     253             :                 }
     254         192 :                 len = elektraStrLen (buffer);
     255         192 :                 bufLen += len;
     256         192 :                 lineBuffer = realloc (lineBuffer, bufLen);
     257             : 
     258         192 :                 memcpy (lineBuffer + offset, buffer, len);
     259         192 :                 offset += (len - 1);
     260         192 :                 if (!isCol && !isQuoted) done = 1;
     261             :         }
     262             :         return lineBuffer;
     263             : }
     264             : 
     265             : 
     266             : /// @returns a newly allocated keyset with the column names
     267          13 : static KeySet * createHeaders (Key * parentKey, int columns, const char ** colNames)
     268             : {
     269          13 :         KeySet * header = ksNew (0, KS_END);
     270          13 :         int colCounter = 0;
     271             :         // if no headerline exists name the columns 0..N where N is the number of columns
     272          13 :         Key * orderKey = keyDup (parentKey);
     273          13 :         keyAddName (orderKey, "#");
     274          69 :         while (colCounter < columns)
     275             :         {
     276          43 :                 if (elektraArrayIncName (orderKey) == -1)
     277             :                 {
     278           0 :                         keyDel (orderKey);
     279           0 :                         ksDel (header);
     280           0 :                         return NULL;
     281             :                 }
     282          43 :                 Key * key = keyDup (orderKey);
     283          43 :                 if (colNames && (colNames + colCounter))
     284           0 :                         keySetString (key, colNames[colCounter]);
     285             :                 else
     286          43 :                         keySetString (key, keyBaseName (key));
     287          43 :                 ksAppendKey (header, key);
     288          43 :                 ++colCounter;
     289             :         }
     290          13 :         keyDel (orderKey);
     291          13 :         return header;
     292             : }
     293             : 
     294             : /// @returns a newly allocated keyset with the column names
     295          30 : static KeySet * readHeaders (Key * parentKey, char * lineBuffer, char delim, int lineCounter, int lastLine, const char ** colNames)
     296             : {
     297          30 :         int colCounter = 0;
     298          30 :         unsigned long offset = 0;
     299             :         char * col;
     300          30 :         offset = 0;
     301          30 :         Key * orderKey = keyDup (parentKey);
     302          30 :         keyAddName (orderKey, "#");
     303          30 :         KeySet * header = ksNew (0, KS_END);
     304         138 :         while ((col = parseLine (lineBuffer, delim, offset, parentKey, lineCounter, lastLine)) != NULL)
     305             :         {
     306          78 :                 offset += elektraStrLen (col);
     307          78 :                 if (elektraArrayIncName (orderKey) == -1)
     308             :                 {
     309           0 :                         elektraFree (lineBuffer);
     310           0 :                         keyDel (orderKey);
     311           0 :                         ksDel (header);
     312           0 :                         return NULL;
     313             :                 }
     314          78 :                 Key * key = keyDup (orderKey);
     315          78 :                 if (colNames && (colNames + colCounter))
     316             :                 {
     317           4 :                         keySetString (key, colNames[colCounter]);
     318             :                 }
     319             :                 else
     320             :                 {
     321          74 :                         keySetString (key, col);
     322             :                 }
     323          78 :                 ksAppendKey (header, key);
     324          78 :                 ++colCounter;
     325             :         }
     326          30 :         keyDel (orderKey);
     327          30 :         return header;
     328             : }
     329             : 
     330          45 : static int csvRead (KeySet * returned, Key * parentKey, char delim, Key * colAsParent, short useHeader, unsigned long fixColumnCount,
     331             :                     const char ** colNames)
     332             : {
     333             :         const char * fileName;
     334          45 :         fileName = keyString (parentKey);
     335          45 :         FILE * fp = fopen (fileName, "rb");
     336          45 :         if (!fp)
     337             :         {
     338           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Couldn't open file %s", fileName);
     339           0 :                 return -1;
     340             :         }
     341          45 :         int lastLine = 0;
     342          45 :         int linesRead = 0;
     343          45 :         char * lineBuffer = readNextLine (fp, delim, &lastLine, &linesRead);
     344          45 :         if (!lineBuffer)
     345             :         {
     346           0 :                 fclose (fp);
     347           0 :                 return 0;
     348             :         }
     349          45 :         unsigned long columns = 0;
     350          45 :         columns = getColumnCount (lineBuffer, delim);
     351          45 :         if (fixColumnCount)
     352             :         {
     353           4 :                 if (columns != fixColumnCount)
     354             :                 {
     355           2 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Illegal number of columns (%lu - %lu) in Header line: %s",
     356             :                                                                  columns, fixColumnCount, lineBuffer);
     357           2 :                         elektraFree (lineBuffer);
     358           2 :                         fclose (fp);
     359           2 :                         return -1;
     360             :                 }
     361             :         }
     362          43 :         unsigned long colCounter = 0;
     363          43 :         unsigned long lineCounter = 0;
     364             : 
     365             :         // TODO: refactoring needed here
     366          43 :         int nr_keys = 1;
     367             :         KeySet * header;
     368             :         Key * key;
     369          43 :         if (useHeader == 1)
     370             :         {
     371          30 :                 header = readHeaders (parentKey, lineBuffer, delim, lineCounter, lastLine, colNames);
     372          30 :                 if (!header)
     373             :                 {
     374           0 :                         fclose (fp);
     375           0 :                         return -1;
     376             :                 }
     377          30 :                 fseek (fp, 0, SEEK_SET);
     378          30 :                 lineCounter += linesRead;
     379             :         }
     380             :         else
     381             :         {
     382          13 :                 header = createHeaders (parentKey, columns, colNames);
     383          13 :                 if (!header)
     384             :                 {
     385           0 :                         elektraFree (lineBuffer);
     386           0 :                         fclose (fp);
     387           0 :                         return -1;
     388             :                 }
     389          13 :                 if (useHeader == 0)
     390             :                 {
     391          13 :                         fseek (fp, 0, SEEK_SET);
     392             :                 }
     393             :                 lineCounter += 1;
     394             :         }
     395             :         Key * dirKey;
     396             :         Key * cur;
     397          43 :         dirKey = keyDup (parentKey);
     398          43 :         keyAddName (dirKey, "#");
     399          43 :         elektraFree (lineBuffer);
     400          43 :         ksRewind (header);
     401             :         while (1)
     402         141 :         {
     403         184 :                 lineBuffer = readNextLine (fp, delim, &lastLine, &linesRead);
     404         184 :                 if (!lineBuffer)
     405             :                 {
     406          43 :                         fclose (fp);
     407          43 :                         keyDel (dirKey);
     408          43 :                         ksDel (header);
     409          43 :                         return (lineCounter > 0) ? 1 : 0;
     410             :                 }
     411             : 
     412         141 :                 if (elektraArrayIncName (dirKey) == -1)
     413             :                 {
     414           0 :                         elektraFree (lineBuffer);
     415           0 :                         keyDel (dirKey);
     416           0 :                         ksDel (header);
     417           0 :                         fclose (fp);
     418           0 :                         return -1;
     419             :                 }
     420         141 :                 ++nr_keys;
     421         141 :                 unsigned long offset = 0;
     422             :                 char * col;
     423         141 :                 colCounter = 0;
     424         141 :                 char * lastIndex = "#0";
     425         141 :                 ksRewind (header);
     426         141 :                 KeySet * tmpKs = ksNew (0, KS_END);
     427         683 :                 while ((col = parseLine (lineBuffer, delim, offset, parentKey, lineCounter, lastLine)) != NULL)
     428             :                 {
     429         401 :                         cur = ksNext (header);
     430         401 :                         offset += elektraStrLen (col);
     431         401 :                         key = keyDup (dirKey);
     432         401 :                         if (col[0] == '"')
     433             :                         {
     434          20 :                                 if (col[elektraStrLen (col) - 2] == '"')
     435             :                                 {
     436          20 :                                         keySetMeta (key, "internal/csvstorage/quoted", "");
     437          20 :                                         ++col;
     438          20 :                                         col[elektraStrLen (col) - 2] = '\0';
     439             :                                 }
     440             :                         }
     441         401 :                         keyAddName (key, keyString (cur));
     442         401 :                         keySetString (key, col);
     443         401 :                         ksAppendKey (tmpKs, key);
     444         401 :                         lastIndex = (char *) keyBaseName (cur);
     445         401 :                         ++nr_keys;
     446         401 :                         ++colCounter;
     447             :                 }
     448         141 :                 if (colAsParent)
     449             :                 {
     450           0 :                         if (!(lineCounter <= 1 && useHeader))
     451             :                         {
     452           0 :                                 keySetString (dirKey, lastIndex);
     453           0 :                                 ksAppendKey (tmpKs, keyDup (dirKey));
     454           0 :                                 Key * lookupKey = keyNew (keyName (dirKey), KEY_END);
     455           0 :                                 keyAddName (lookupKey, keyString (colAsParent));
     456           0 :                                 Key * indexKey = ksLookupByName (tmpKs, keyName (lookupKey), 0);
     457           0 :                                 Key * renameKey = keyNew (keyName (dirKey), 0);
     458           0 :                                 keySetBaseName (renameKey, keyString (indexKey));
     459           0 :                                 ksRewind (tmpKs);
     460           0 :                                 KeySet * renamedKs = ksRenameKeys (tmpKs, renameKey);
     461           0 :                                 ksAppendKey (renamedKs, keyDup (renameKey));
     462           0 :                                 ksRewind (renamedKs);
     463           0 :                                 keyDel (lookupKey);
     464           0 :                                 keyDel (renameKey);
     465           0 :                                 ksRewind (renamedKs);
     466           0 :                                 ksRewind (tmpKs);
     467           0 :                                 ksAppend (returned, renamedKs);
     468           0 :                                 ksDel (renamedKs);
     469             :                         }
     470             :                 }
     471             :                 else
     472             :                 {
     473         141 :                         keySetString (dirKey, lastIndex);
     474         141 :                         ksAppend (returned, tmpKs);
     475         141 :                         ksAppendKey (returned, keyDup (dirKey));
     476             :                 }
     477         141 :                 ksDel (tmpKs);
     478         141 :                 tmpKs = NULL;
     479         141 :                 if (colCounter != columns)
     480             :                 {
     481           4 :                         if (fixColumnCount)
     482             :                         {
     483           0 :                                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Illegal number of columns (%lu - %lu) in line %lu: %s",
     484             :                                                                          colCounter, columns, lineCounter, lineBuffer);
     485           0 :                                 elektraFree (lineBuffer);
     486           0 :                                 fclose (fp);
     487           0 :                                 keyDel (dirKey);
     488           0 :                                 ksDel (header);
     489           0 :                                 return -1;
     490             :                         }
     491           4 :                         ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (parentKey, "Illegal number of columns (%lu - %lu)  in line %lu: %s",
     492             :                                                                    colCounter, columns, lineCounter, lineBuffer);
     493             :                 }
     494         141 :                 lineCounter += linesRead;
     495         141 :                 elektraFree (lineBuffer);
     496         141 :                 ksDel (tmpKs);
     497             :         }
     498             :         key = keyDup (parentKey);
     499             :         keySetString (key, keyBaseName (dirKey));
     500             :         ksAppendKey (returned, key);
     501             :         keyDel (dirKey);
     502             :         fclose (fp);
     503             :         ksDel (header);
     504             :         return 1;
     505             : }
     506             : 
     507          88 : int elektraCsvstorageGet (Plugin * handle, KeySet * returned, Key * parentKey)
     508             : {
     509          88 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/csvstorage"))
     510             :         {
     511          43 :                 KeySet * contract = ksNew (
     512             :                         30, keyNew ("system/elektra/modules/csvstorage", KEY_VALUE, "csvstorage plugin waits for your orders", KEY_END),
     513             :                         keyNew ("system/elektra/modules/csvstorage/exports", KEY_END),
     514             :                         keyNew ("system/elektra/modules/csvstorage/exports/get", KEY_FUNC, elektraCsvstorageGet, KEY_END),
     515             :                         keyNew ("system/elektra/modules/csvstorage/exports/set", KEY_FUNC, elektraCsvstorageSet, KEY_END),
     516             : #include ELEKTRA_README
     517             :                         keyNew ("system/elektra/modules/csvstorage/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     518          43 :                 ksAppend (returned, contract);
     519          43 :                 ksDel (contract);
     520             : 
     521          43 :                 return 1; /* success */
     522             :         }
     523             : 
     524          45 :         KeySet * config = elektraPluginGetConfig (handle);
     525          45 :         Key * delimKey = ksLookupByName (config, "/delimiter", 0);
     526          45 :         char delim = ',';
     527          45 :         if (delimKey)
     528             :         {
     529          18 :                 const char * delimString = keyString (delimKey);
     530          18 :                 delim = delimString[0];
     531             :         }
     532             : 
     533          45 :         Key * readHeaderKey = ksLookupByName (config, "/header", 0);
     534          45 :         short useHeader = 0;
     535          45 :         if (readHeaderKey)
     536             :         {
     537          32 :                 const char * printHeaderString = keyString (readHeaderKey);
     538          32 :                 if (!strcmp (printHeaderString, "colname"))
     539             :                 {
     540             :                         useHeader = 1;
     541             :                 }
     542           0 :                 else if (!(strcmp (printHeaderString, "skip")))
     543             :                 {
     544             :                         useHeader = -1;
     545             :                 }
     546           0 :                 else if (!(strcmp (printHeaderString, "record")))
     547             :                 {
     548             :                         useHeader = 0;
     549             :                 }
     550             :                 else
     551             :                 {
     552             :                         useHeader = 0;
     553             :                 }
     554             :         }
     555          45 :         unsigned long fixColumnCount = 0;
     556          45 :         Key * fixColumnCountKey = ksLookupByName (config, "/columns", 0);
     557          45 :         if (fixColumnCountKey)
     558             :         {
     559           4 :                 if (keyString (fixColumnCountKey))
     560             :                 {
     561           8 :                         fixColumnCount = atol (keyString (fixColumnCountKey));
     562             :                 }
     563             :         }
     564          45 :         Key * colAsParent = ksLookupByName (config, "/columns/index", 0);
     565          45 :         Key * setNamesKey = ksLookupByName (config, "/columns/names", 0);
     566          45 :         char * colNames = NULL;
     567          45 :         if (setNamesKey)
     568             :         {
     569          18 :                 if (fixColumnCountKey)
     570             :                 {
     571           2 :                         KeySet * namesKS = ksCut (config, setNamesKey);
     572           2 :                         unsigned long nrNames = (unsigned long) ksGetSize (namesKS) - 1;
     573           2 :                         if (nrNames == fixColumnCount)
     574             :                         {
     575           2 :                                 colNames = (char *) elektraMalloc (nrNames * sizeof (char *));
     576             :                                 Key * cur;
     577           2 :                                 char ** ptr = (char **) colNames;
     578          10 :                                 while ((cur = ksNext (namesKS)) != NULL)
     579             :                                 {
     580           6 :                                         if (!strcmp (keyName (cur), keyName (setNamesKey))) continue;
     581           4 :                                         if (!strcmp (keyString (cur), ""))
     582           0 :                                                 *ptr = NULL;
     583             :                                         else
     584           4 :                                                 *ptr = (char *) keyString (cur);
     585           4 :                                         ++ptr;
     586             :                                 }
     587             :                         }
     588           2 :                         ksAppend (config, namesKS);
     589           2 :                         ksDel (namesKS);
     590             :                 }
     591             :         }
     592             :         int nr_keys;
     593          45 :         nr_keys = csvRead (returned, parentKey, delim, colAsParent, useHeader, fixColumnCount, (const char **) colNames);
     594          45 :         if (colNames) elektraFree (colNames);
     595          45 :         if (nr_keys == -1) return -1;
     596          43 :         return 1;
     597             : }
     598             : 
     599         158 : static int isExportKey (const Key * key, const Key * parent, KeySet * ks)
     600             : {
     601         158 :         if (!ks) return 1;
     602           6 :         Key * lookupKey = keyNew ("/export", KEY_CASCADING_NAME, KEY_END);
     603           6 :         keyAddName (lookupKey, keyName (key) + strlen (keyName (parent)) + 1);
     604           6 :         if (!ksLookupByName (ks, keyName (lookupKey), KDB_O_NONE))
     605             :         {
     606           2 :                 keyDel (lookupKey);
     607           2 :                 return 0;
     608             :         }
     609             :         else
     610             :         {
     611           4 :                 keyDel (lookupKey);
     612           4 :                 return 1;
     613             :         }
     614             : }
     615             : 
     616          13 : static int csvWrite (KeySet * returned, Key * parentKey, KeySet * exportKS, Key * colAsParent, char delim, short useHeader)
     617             : {
     618             :         FILE * fp;
     619          13 :         fp = fopen (keyString (parentKey), "w");
     620          13 :         if (!fp)
     621             :         {
     622           0 :                 ELEKTRA_SET_ERROR_SET (parentKey);
     623             :                 return -1;
     624             :         }
     625             : 
     626          13 :         keyDel (ksLookup (returned, parentKey, KDB_O_POP));
     627             : 
     628          13 :         unsigned long colCounter = 0;
     629          13 :         unsigned long columns = 0; // TODO: not needed?
     630          13 :         unsigned long lineCounter = 0;
     631             :         Key * cur;
     632             :         KeySet * toWriteKS;
     633             :         Key * toWrite;
     634             : 
     635          13 :         ksRewind (returned);
     636          68 :         while ((cur = ksNext (returned)) != NULL)
     637             :         {
     638          46 :                 if (keyRel (parentKey, cur) != 1) continue;
     639          46 :                 colCounter = 0;
     640          46 :                 if (useHeader)
     641             :                 {
     642           0 :                         useHeader = 0;
     643           0 :                         continue;
     644             :                 }
     645          46 :                 if (colAsParent)
     646             :                 {
     647           0 :                         KeySet * tmpKs = ksDup (returned);
     648           0 :                         ksRewind (tmpKs);
     649           0 :                         KeySet * headerKs = ksCut (tmpKs, cur);
     650           0 :                         ksRewind (headerKs);
     651           0 :                         ksDel (tmpKs);
     652           0 :                         ksNext (headerKs);
     653           0 :                         Key * tmp = ksNext (headerKs);
     654           0 :                         int printDelim = 0;
     655           0 :                         if (isExportKey (tmp, cur, exportKS))
     656             :                         {
     657           0 :                                 fprintf (fp, "%s", keyName (tmp) + strlen (keyName (cur)) + 1);
     658           0 :                                 printDelim = 1;
     659           0 :                                 ++colCounter;
     660             :                         }
     661           0 :                         while ((tmp = ksNext (headerKs)) != NULL)
     662             :                         {
     663           0 :                                 if (!isExportKey (tmp, cur, exportKS)) continue;
     664           0 :                                 ++colCounter;
     665           0 :                                 if (printDelim) fprintf (fp, "%c", delim);
     666           0 :                                 if ((strchr (keyName (tmp), '\n') != NULL) && (keyName (tmp)[0] != '"'))
     667             :                                 {
     668           0 :                                         fprintf (fp, "\"%s\"", keyName (tmp) + strlen (keyName (cur)) + 1);
     669             :                                 }
     670             :                                 else
     671             :                                 {
     672           0 :                                         fprintf (fp, "%s", keyName (tmp) + strlen (keyName (cur)) + 1);
     673             :                                 }
     674             :                                 printDelim = 1;
     675             :                         }
     676           0 :                         fprintf (fp, "\n");
     677           0 :                         if (columns == 0)
     678             :                         {
     679           0 :                                 columns = colCounter;
     680             :                         }
     681           0 :                         colAsParent = NULL;
     682           0 :                         ksDel (headerKs);
     683             :                 }
     684          46 :                 colCounter = 0;
     685          46 :                 toWriteKS = ksCut (returned, cur);
     686          46 :                 ksRewind (toWriteKS);
     687          46 :                 int printDelim = 0;
     688             :                 while (1)
     689             :                 {
     690         250 :                         toWrite = ksNext (toWriteKS);
     691         250 :                         if (!keyCmp (cur, toWrite)) continue;
     692         204 :                         if (!toWrite) break;
     693         158 :                         if (!isExportKey (toWrite, cur, exportKS))
     694             :                         {
     695           2 :                                 continue;
     696             :                         }
     697         156 :                         if (printDelim) fprintf (fp, "%c", delim);
     698         156 :                         ++colCounter;
     699         156 :                         if (keyGetMeta (toWrite, "internal/csvstorage/quoted"))
     700             :                         {
     701          10 :                                 fprintf (fp, "\"%s\"", keyString (toWrite));
     702          10 :                                 printDelim = 1;
     703             :                         }
     704         146 :                         else if ((strchr (keyString (toWrite), '\n') != NULL) && (keyString (toWrite)[0] != '"'))
     705             :                         {
     706           0 :                                 fprintf (fp, "\"%s\"", keyString (toWrite));
     707           0 :                                 printDelim = 1;
     708             :                         }
     709             :                         else
     710             :                         {
     711         146 :                                 fprintf (fp, "%s", keyString (toWrite));
     712         146 :                                 printDelim = 1;
     713             :                         }
     714             :                 }
     715          46 :                 ksDel (toWriteKS);
     716          46 :                 fprintf (fp, "\n");
     717          46 :                 if (columns == 0)
     718             :                 {
     719          13 :                         columns = colCounter;
     720             :                 }
     721          46 :                 if (colCounter != columns)
     722             :                 {
     723           4 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Illegal number of columns (%lu - %lu) in line %lu", colCounter,
     724             :                                                                  columns, lineCounter);
     725           4 :                         fclose (fp);
     726           4 :                         return -1;
     727             :                 }
     728          42 :                 ++lineCounter;
     729             :         }
     730           9 :         fclose (fp);
     731           9 :         return 1;
     732             : }
     733             : 
     734          13 : int elektraCsvstorageSet (Plugin * handle, KeySet * returned, Key * parentKey)
     735             : {
     736          13 :         KeySet * config = elektraPluginGetConfig (handle);
     737          13 :         Key * delimKey = ksLookupByName (config, "/delimiter", 0);
     738             :         char outputDelim;
     739          13 :         if (delimKey)
     740             :         {
     741          10 :                 const char * delimString = keyString (delimKey);
     742          10 :                 outputDelim = delimString[0];
     743             :         }
     744             :         else
     745             :         {
     746             :                 outputDelim = ',';
     747             :         }
     748          13 :         Key * colAsParent = ksLookupByName (config, "/columns/index", 0);
     749          13 :         Key * useHeaderKey = ksLookupByName (config, "/header", 0);
     750          13 :         Key * exportKey = ksLookupByName (config, "/export", 0);
     751          13 :         KeySet * exportKS = NULL;
     752          13 :         if (exportKey)
     753             :         {
     754           2 :                 exportKS = ksCut (config, exportKey);
     755           2 :                 ksAppend (config, exportKS);
     756           2 :                 keyDel (ksLookup (exportKS, exportKey, KDB_O_POP));
     757           2 :                 ksRewind (exportKS);
     758             :         }
     759          13 :         short useHeader = 0;
     760          13 :         if (!strcmp (keyString (useHeaderKey), "skip")) useHeader = -1;
     761          13 :         int rc = csvWrite (returned, parentKey, exportKS, colAsParent, outputDelim, useHeader);
     762          13 :         ksDel (exportKS);
     763          13 :         return rc;
     764             : }
     765             : 
     766         187 : Plugin * ELEKTRA_PLUGIN_EXPORT
     767             : {
     768             :         // clang-format off
     769         187 :         return elektraPluginExport("csvstorage",
     770             :                         ELEKTRA_PLUGIN_GET,     &elektraCsvstorageGet,
     771             :                         ELEKTRA_PLUGIN_SET,     &elektraCsvstorageSet,
     772             :                         ELEKTRA_PLUGIN_END);
     773             : }
     774             : 

Generated by: LCOV version 1.13