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