]> git.proxmox.com Git - mirror_frr.git/blame - lib/csv.c
*: manual SPDX License ID conversions
[mirror_frr.git] / lib / csv.c
CommitLineData
50e24903 1/* CSV
1120b959 2 * Copyright (C) 2013,2020 Cumulus Networks, Inc.
50e24903
DS
3 *
4 * This file is part of Quagga.
5 *
6 * Quagga is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * Quagga is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
896014f4
DL
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
50e24903 19 */
b45ac5f5
DL
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
1120b959
QY
25#include <zebra.h>
26
c43ed2e4
DS
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdarg.h>
31#include <assert.h>
32#include <sys/queue.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include "csv.h"
36
37#define DEBUG_E 1
38#define DEBUG_V 1
39
d62a17ae 40#define log_error(fmt, ...) \
41 do { \
42 if (DEBUG_E) \
43 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
44 __LINE__, __func__, ##__VA_ARGS__); \
45 } while (0)
46
47#define log_verbose(fmt, ...) \
48 do { \
49 if (DEBUG_V) \
50 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
51 __LINE__, __func__, __VA_ARGS__); \
52 } while (0)
c43ed2e4
DS
53
54struct _csv_field_t_ {
d62a17ae 55 TAILQ_ENTRY(_csv_field_t_) next_field;
56 char *field;
57 int field_len;
c43ed2e4
DS
58};
59
60struct _csv_record_t_ {
d62a17ae 61 TAILQ_HEAD(, _csv_field_t_) fields;
62 TAILQ_ENTRY(_csv_record_t_) next_record;
63 char *record;
64 int rec_len;
c43ed2e4
DS
65};
66
67struct _csv_t_ {
d62a17ae 68 TAILQ_HEAD(, _csv_record_t_) records;
69 char *buf;
70 int buflen;
71 int csv_len;
72 int pointer;
73 int num_recs;
c43ed2e4
DS
74};
75
76
d62a17ae 77int csvlen(csv_t *csv)
c43ed2e4 78{
d62a17ae 79 return (csv->csv_len);
c43ed2e4
DS
80}
81
d62a17ae 82csv_t *csv_init(csv_t *csv, char *buf, int buflen)
c43ed2e4 83{
d62a17ae 84 if (csv == NULL) {
85 csv = malloc(sizeof(csv_t));
86 if (csv == NULL) {
87 log_error("CSV Malloc failed\n");
95f7965d 88 return NULL;
d62a17ae 89 }
90 }
91 memset(csv, 0, sizeof(csv_t));
92
93 csv->buf = buf;
94 csv->buflen = buflen;
95 TAILQ_INIT(&(csv->records));
96 return (csv);
c43ed2e4
DS
97}
98
d62a17ae 99void csv_clean(csv_t *csv)
c43ed2e4 100{
d62a17ae 101 csv_record_t *rec;
102 csv_record_t *rec_n;
103
104 rec = TAILQ_FIRST(&(csv->records));
105 while (rec != NULL) {
106 rec_n = TAILQ_NEXT(rec, next_record);
107 csv_remove_record(csv, rec);
108 rec = rec_n;
109 }
c43ed2e4
DS
110}
111
d62a17ae 112void csv_free(csv_t *csv)
c43ed2e4 113{
d62a17ae 114 if (csv != NULL) {
115 free(csv);
116 }
c43ed2e4
DS
117}
118
d62a17ae 119static void csv_init_record(csv_record_t *record)
c43ed2e4 120{
d62a17ae 121 TAILQ_INIT(&(record->fields));
122 record->rec_len = 0;
c43ed2e4
DS
123}
124
d62a17ae 125csv_record_t *csv_record_iter(csv_t *csv)
c43ed2e4 126{
d62a17ae 127 return (TAILQ_FIRST(&(csv->records)));
c43ed2e4
DS
128}
129
d62a17ae 130csv_record_t *csv_record_iter_next(csv_record_t *rec)
c43ed2e4 131{
d62a17ae 132 if (!rec)
133 return NULL;
134 return (TAILQ_NEXT(rec, next_record));
c43ed2e4
DS
135}
136
d62a17ae 137char *csv_field_iter(csv_record_t *rec, csv_field_t **fld)
c43ed2e4 138{
d62a17ae 139 if (!rec)
140 return NULL;
141 *fld = TAILQ_FIRST(&(rec->fields));
142 return ((*fld)->field);
c43ed2e4
DS
143}
144
d62a17ae 145char *csv_field_iter_next(csv_field_t **fld)
c43ed2e4 146{
d62a17ae 147 *fld = TAILQ_NEXT(*fld, next_field);
148 if ((*fld) == NULL) {
95f7965d 149 return NULL;
d62a17ae 150 }
151 return ((*fld)->field);
c43ed2e4
DS
152}
153
d62a17ae 154int csv_field_len(csv_field_t *fld)
c43ed2e4 155{
d62a17ae 156 if (fld) {
157 return fld->field_len;
158 }
159 return 0;
c43ed2e4
DS
160}
161
d62a17ae 162static void csv_decode_record(csv_record_t *rec)
c43ed2e4 163{
d62a17ae 164 char *curr = rec->record;
165 char *field;
166 csv_field_t *fld;
167
168 field = strpbrk(curr, ",");
169 while (field != NULL) {
170 fld = malloc(sizeof(csv_field_t));
171 if (fld) {
172 TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
173 fld->field = curr;
174 fld->field_len = field - curr;
175 }
176 curr = field + 1;
177 field = strpbrk(curr, ",");
178 }
179 field = strstr(curr, "\n");
180 if (!field)
181 return;
182
183 fld = malloc(sizeof(csv_field_t));
184 if (fld) {
185 fld->field = curr;
186 fld->field_len = field - curr;
187 TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
188 }
c43ed2e4
DS
189}
190
d62a17ae 191static csv_field_t *csv_add_field_to_record(csv_t *csv, csv_record_t *rec,
192 char *col)
c43ed2e4 193{
d62a17ae 194 csv_field_t *fld;
195 char *str = rec->record;
196 int rlen = rec->rec_len;
197 int blen = csv->buflen;
198
199 fld = malloc(sizeof(csv_field_t));
200 if (!fld) {
201 log_error("field malloc failed\n");
202 /* more cleanup needed */
95f7965d 203 return NULL;
d62a17ae 204 }
205 TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
206 fld->field = str + rlen;
207 fld->field_len = snprintf((str + rlen), (blen - rlen), "%s", col);
208 rlen += fld->field_len;
209 rec->rec_len = rlen;
210 return fld;
c43ed2e4
DS
211}
212
d62a17ae 213csv_record_t *csv_encode(csv_t *csv, int count, ...)
c43ed2e4 214{
d62a17ae 215 int tempc;
216 va_list list;
217 char *buf = csv->buf;
218 int len = csv->buflen;
219 int pointer = csv->pointer;
220 char *str = NULL;
221 char *col;
222 csv_record_t *rec;
223 csv_field_t *fld;
224
225 if (buf) {
226 str = buf + pointer;
227 } else {
228 /* allocate sufficient buffer */
229 str = (char *)malloc(csv->buflen);
230 if (!str) {
231 log_error("field str malloc failed\n");
95f7965d 232 return NULL;
d62a17ae 233 }
234 }
235
236 va_start(list, count);
237 rec = malloc(sizeof(csv_record_t));
238 if (!rec) {
239 log_error("record malloc failed\n");
240 if (!buf)
241 free(str);
242 va_end(list);
95f7965d 243 return NULL;
d62a17ae 244 }
245 csv_init_record(rec);
246 rec->record = str;
247 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
248 csv->num_recs++;
249
250 /**
251 * Iterate through the fields passed as a variable list and add them
252 */
253 for (tempc = 0; tempc < count; tempc++) {
254 col = va_arg(list, char *);
255 fld = csv_add_field_to_record(csv, rec, col);
256 if (!fld) {
257 log_error("fld malloc failed\n");
258 csv_remove_record(csv, rec);
259 va_end(list);
95f7965d 260 return NULL;
d62a17ae 261 }
262 if (tempc < (count - 1)) {
263 rec->rec_len += snprintf((str + rec->rec_len),
264 (len - rec->rec_len), ",");
265 }
266 }
267 rec->rec_len +=
268 snprintf((str + rec->rec_len), (len - rec->rec_len), "\n");
269 va_end(list);
270 csv->csv_len += rec->rec_len;
271 csv->pointer += rec->rec_len;
272 return (rec);
c43ed2e4
DS
273}
274
d62a17ae 275int csv_num_records(csv_t *csv)
c43ed2e4 276{
d62a17ae 277 if (csv) {
278 return csv->num_recs;
279 }
280 return 0;
c43ed2e4
DS
281}
282
d62a17ae 283csv_record_t *csv_encode_record(csv_t *csv, csv_record_t *rec, int count, ...)
c43ed2e4 284{
d62a17ae 285 int tempc;
286 va_list list;
287 char *str;
288 char *col;
289 csv_field_t *fld = NULL;
290 int i;
291
292 va_start(list, count);
293 str = csv_field_iter(rec, &fld);
d06542d5
DS
294 if (!fld) {
295 va_end(list);
43b798b7 296 return NULL;
d06542d5
DS
297 }
298
d62a17ae 299 for (tempc = 0; tempc < count; tempc++) {
300 col = va_arg(list, char *);
301 for (i = 0; i < fld->field_len; i++) {
302 str[i] = col[i];
303 }
304 str = csv_field_iter_next(&fld);
305 }
306 va_end(list);
307 return (rec);
c43ed2e4
DS
308}
309
d62a17ae 310csv_record_t *csv_append_record(csv_t *csv, csv_record_t *rec, int count, ...)
c43ed2e4 311{
d62a17ae 312 int tempc;
313 va_list list;
314 int len = csv->buflen, tlen;
315 char *str;
316 csv_field_t *fld;
317 char *col;
318
319 if (csv->buf) {
320 /* not only works with discrete bufs */
321 return NULL;
322 }
323
324 if (!rec) {
325 /* create a new rec */
326 rec = calloc(1, sizeof(csv_record_t));
327 if (!rec) {
328 log_error("record malloc failed\n");
329 return NULL;
330 }
331 csv_init_record(rec);
332 rec->record = calloc(1, csv->buflen);
333 if (!rec->record) {
334 log_error("field str malloc failed\n");
335 free(rec);
336 return NULL;
337 }
338 csv_insert_record(csv, rec);
339 }
340
341 str = rec->record;
342
343 va_start(list, count);
344
345 if (rec->rec_len && (str[rec->rec_len - 1] == '\n'))
346 str[rec->rec_len - 1] = ',';
347
348 /**
349 * Iterate through the fields passed as a variable list and add them
350 */
351 tlen = rec->rec_len;
352 for (tempc = 0; tempc < count; tempc++) {
353 col = va_arg(list, char *);
354 fld = csv_add_field_to_record(csv, rec, col);
355 if (!fld) {
356 log_error("fld malloc failed\n");
357 break;
358 }
359 if (tempc < (count - 1)) {
360 rec->rec_len += snprintf((str + rec->rec_len),
361 (len - rec->rec_len), ",");
362 }
363 }
364 rec->rec_len +=
365 snprintf((str + rec->rec_len), (len - rec->rec_len), "\n");
366 va_end(list);
367 csv->csv_len += (rec->rec_len - tlen);
368 csv->pointer += (rec->rec_len - tlen);
369 return (rec);
c43ed2e4
DS
370}
371
d62a17ae 372int csv_serialize(csv_t *csv, char *msgbuf, int msglen)
c43ed2e4 373{
d62a17ae 374 csv_record_t *rec;
375 int offset = 0;
c43ed2e4 376
d62a17ae 377 if (!csv || !msgbuf)
378 return -1;
c43ed2e4 379
d62a17ae 380 rec = csv_record_iter(csv);
381 while (rec != NULL) {
382 if ((offset + rec->rec_len) >= msglen)
383 return -1;
384 offset += sprintf(&msgbuf[offset], "%s", rec->record);
385 rec = csv_record_iter_next(rec);
386 }
c43ed2e4 387
d62a17ae 388 return 0;
c43ed2e4
DS
389}
390
d62a17ae 391void csv_clone_record(csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec)
c43ed2e4 392{
d62a17ae 393 char *curr;
394 csv_record_t *rec;
395
396 /* first check if rec belongs to this csv */
397 if (!csv_is_record_valid(csv, in_rec)) {
398 log_error("rec not in this csv\n");
399 return;
400 }
401
402 /* only works with csv with discrete bufs */
403 if (csv->buf) {
404 log_error(
405 "un-supported for this csv type - single buf detected\n");
406 return;
407 }
408
409 /* create a new rec */
410 rec = calloc(1, sizeof(csv_record_t));
411 if (!rec) {
412 log_error("record malloc failed\n");
413 return;
414 }
415 csv_init_record(rec);
416 curr = calloc(1, csv->buflen);
417 if (!curr) {
418 log_error("field str malloc failed\n");
419 free(rec);
420 return;
421 }
422 rec->record = curr;
423 rec->rec_len = in_rec->rec_len;
1120b959 424 strlcpy(rec->record, in_rec->record, csv->buflen);
d62a17ae 425
426 /* decode record into fields */
427 csv_decode_record(rec);
428
429 *out_rec = rec;
c43ed2e4
DS
430}
431
d62a17ae 432void csv_remove_record(csv_t *csv, csv_record_t *rec)
c43ed2e4 433{
79e68c7c 434 csv_field_t *fld = NULL, *p_fld;
d62a17ae 435
436 /* first check if rec belongs to this csv */
437 if (!csv_is_record_valid(csv, rec)) {
438 log_error("rec not in this csv\n");
439 return;
440 }
441
442 /* remove fields */
443 csv_field_iter(rec, &fld);
444 while (fld) {
445 p_fld = fld;
446 csv_field_iter_next(&fld);
447 TAILQ_REMOVE(&(rec->fields), p_fld, next_field);
448 free(p_fld);
449 }
450
451 TAILQ_REMOVE(&(csv->records), rec, next_record);
452
453 csv->num_recs--;
454 csv->csv_len -= rec->rec_len;
455 csv->pointer -= rec->rec_len;
456 if (!csv->buf)
457 free(rec->record);
458 free(rec);
c43ed2e4
DS
459}
460
d62a17ae 461void csv_insert_record(csv_t *csv, csv_record_t *rec)
c43ed2e4 462{
d62a17ae 463 /* first check if rec already in csv */
464 if (csv_is_record_valid(csv, rec)) {
465 log_error("rec already in this csv\n");
466 return;
467 }
468
469 /* we can only insert records if no buf was supplied during csv init */
470 if (csv->buf) {
471 log_error(
472 "un-supported for this csv type - single buf detected\n");
473 return;
474 }
475
476 /* do we go beyond the max buf set for this csv ?*/
477 if ((csv->csv_len + rec->rec_len) > csv->buflen) {
478 log_error("cannot insert - exceeded buf size\n");
479 return;
480 }
481
482 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
483 csv->num_recs++;
484 csv->csv_len += rec->rec_len;
485 csv->pointer += rec->rec_len;
c43ed2e4
DS
486}
487
d62a17ae 488csv_record_t *csv_concat_record(csv_t *csv, csv_record_t *rec1,
489 csv_record_t *rec2)
c43ed2e4 490{
d62a17ae 491 char *curr;
492 char *ret;
493 csv_record_t *rec;
494
495 /* first check if rec1 and rec2 belong to this csv */
496 if (!csv_is_record_valid(csv, rec1)
497 || !csv_is_record_valid(csv, rec2)) {
498 log_error("rec1 and/or rec2 invalid\n");
95f7965d 499 return NULL;
d62a17ae 500 }
501
502 /* we can only concat records if no buf was supplied during csv init */
503 if (csv->buf) {
504 log_error(
505 "un-supported for this csv type - single buf detected\n");
95f7965d 506 return NULL;
d62a17ae 507 }
508
509 /* create a new rec */
510 rec = calloc(1, sizeof(csv_record_t));
511 if (!rec) {
512 log_error("record malloc failed\n");
95f7965d 513 return NULL;
d62a17ae 514 }
515 csv_init_record(rec);
516
517 curr = (char *)calloc(1, csv->buflen);
518 if (!curr) {
519 log_error("field str malloc failed\n");
520 goto out_rec;
521 }
522 rec->record = curr;
523
524 /* concat the record string */
525 ret = strstr(rec1->record, "\n");
526 if (!ret) {
527 log_error("rec1 str not properly formatted\n");
528 goto out_curr;
529 }
530
531 snprintf(curr, (int)(ret - rec1->record + 1), "%s", rec1->record);
532 strcat(curr, ",");
533
534 ret = strstr(rec2->record, "\n");
535 if (!ret) {
536 log_error("rec2 str not properly formatted\n");
537 goto out_curr;
538 }
539
540 snprintf((curr + strlen(curr)), (int)(ret - rec2->record + 1), "%s",
541 rec2->record);
542 strcat(curr, "\n");
543 rec->rec_len = strlen(curr);
544
545 /* paranoia */
546 assert(csv->buflen
547 > (csv->csv_len - rec1->rec_len - rec2->rec_len + rec->rec_len));
548
549 /* decode record into fields */
550 csv_decode_record(rec);
551
552 /* now remove rec1 and rec2 and insert rec into this csv */
553 csv_remove_record(csv, rec1);
554 csv_remove_record(csv, rec2);
555 csv_insert_record(csv, rec);
556
557 return rec;
8ac88545
DL
558
559out_curr:
d62a17ae 560 free(curr);
8ac88545 561out_rec:
d62a17ae 562 free(rec);
563 return NULL;
c43ed2e4
DS
564}
565
d62a17ae 566void csv_decode(csv_t *csv, char *inbuf)
c43ed2e4 567{
d62a17ae 568 char *buf;
569 char *pos;
570 csv_record_t *rec;
571
572 buf = (inbuf) ? inbuf : csv->buf;
4f4060f6
DL
573 assert(buf);
574
d62a17ae 575 pos = strpbrk(buf, "\n");
576 while (pos != NULL) {
577 rec = calloc(1, sizeof(csv_record_t));
578 if (!rec)
579 return;
580 csv_init_record(rec);
581 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
582 csv->num_recs++;
583 if (csv->buf)
584 rec->record = buf;
585 else {
586 rec->record = calloc(1, csv->buflen);
587 if (!rec->record) {
588 log_error("field str malloc failed\n");
589 return;
590 }
591 strncpy(rec->record, buf, pos - buf + 1);
592 }
593 rec->rec_len = pos - buf + 1;
594 /* decode record into fields */
595 csv_decode_record(rec);
596 buf = pos + 1;
597 pos = strpbrk(buf, "\n");
598 }
c43ed2e4
DS
599}
600
d62a17ae 601int csv_is_record_valid(csv_t *csv, csv_record_t *in_rec)
c43ed2e4 602{
d62a17ae 603 csv_record_t *rec;
604 int valid = 0;
605
606 rec = csv_record_iter(csv);
607 while (rec) {
608 if (rec == in_rec) {
609 valid = 1;
610 break;
611 }
612 rec = csv_record_iter_next(rec);
613 }
614
615 return valid;
c43ed2e4
DS
616}
617
d62a17ae 618void csv_dump(csv_t *csv)
c43ed2e4 619{
d62a17ae 620 csv_record_t *rec;
621 csv_field_t *fld;
622 char *str;
623
624 rec = csv_record_iter(csv);
625 while (rec != NULL) {
626 str = csv_field_iter(rec, &fld);
627 while (str != NULL) {
628 fprintf(stderr, "%s\n", str);
629 str = csv_field_iter_next(&fld);
630 }
631 rec = csv_record_iter_next(rec);
632 }
c43ed2e4
DS
633}
634
635#ifdef TEST_CSV
636
d62a17ae 637static int get_memory_usage(pid_t pid)
c43ed2e4 638{
d62a17ae 639 int fd, data, stack;
2b7165e7 640 char buf[4096], status_child[PATH_MAX];
d62a17ae 641 char *vm;
642
772270f3 643 snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid);
0e2d7076
DA
644 fd = open(status_child, O_RDONLY);
645 if (fd < 0)
d62a17ae 646 return -1;
647
648 read(fd, buf, 4095);
649 buf[4095] = '\0';
650 close(fd);
651
652 data = stack = 0;
653
654 vm = strstr(buf, "VmData:");
655 if (vm) {
656 sscanf(vm, "%*s %d", &data);
657 }
658 vm = strstr(buf, "VmStk:");
659 if (vm) {
660 sscanf(vm, "%*s %d", &stack);
661 }
662
663 return data + stack;
c43ed2e4
DS
664}
665
d62a17ae 666int main()
c43ed2e4 667{
d62a17ae 668 char buf[10000];
669 csv_t csv;
c7bba448 670 int i;
d62a17ae 671 csv_record_t *rec;
d62a17ae 672 char hdr1[32], hdr2[32];
673
c7bba448 674 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
d62a17ae 675 csv_init(&csv, buf, 256);
772270f3
QY
676 snprintf(hdr1, sizeof(hdr1), "%4d", 0);
677 snprintf(hdr2, sizeof(hdr2), "%4d", 1);
c7bba448 678 log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1),
d62a17ae 679 atoi(hdr2));
680 rec = csv_encode(&csv, 2, hdr1, hdr2);
681 csv_encode(&csv, 4, "name", "age", "sex", "hei");
682 csv_encode(&csv, 3, NULL, "0", NULL);
683 csv_encode(&csv, 2, "p", "35");
684 for (i = 0; i < 50; i++) {
685 csv_encode(&csv, 2, "p", "10");
686 }
687 csv_encode(&csv, 2, "pdfadfadfadsadsaddfdfdsfdsd", "35444554545454545");
688 log_verbose("%s\n", buf);
772270f3
QY
689 snprintf(hdr1, sizeof(hdr1), "%4d", csv.csv_len);
690 snprintf(hdr2, sizeof(hdr2), "%4d", 1);
c7bba448 691 log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1),
d62a17ae 692 atoi(hdr2));
693 rec = csv_encode_record(&csv, rec, 2, hdr1, hdr2);
694 log_verbose("(%d/%d)\n%s\n", rec->rec_len, csv.csv_len, buf);
695
c7bba448 696 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
d62a17ae 697 csv_clean(&csv);
c7bba448 698 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
d62a17ae 699 csv_init(&csv, buf, 256);
700 csv_decode(&csv, NULL);
c7bba448 701 log_verbose("%s", "AFTER DECODE\n");
d62a17ae 702 csv_dump(&csv);
703 csv_clean(&csv);
c7bba448 704 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
c43ed2e4
DS
705}
706#endif