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 :
|