]>
git.proxmox.com Git - mirror_frr.git/blob - lib/csv.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2013,2020 Cumulus Networks, Inc.
17 #include <sys/queue.h>
25 #define log_error(fmt, ...) \
28 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
29 __LINE__, __func__, ##__VA_ARGS__); \
32 #define log_verbose(fmt, ...) \
35 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
36 __LINE__, __func__, __VA_ARGS__); \
39 struct _csv_field_t_
{
40 TAILQ_ENTRY(_csv_field_t_
) next_field
;
45 struct _csv_record_t_
{
46 TAILQ_HEAD(, _csv_field_t_
) fields
;
47 TAILQ_ENTRY(_csv_record_t_
) next_record
;
53 TAILQ_HEAD(, _csv_record_t_
) records
;
62 int csvlen(csv_t
*csv
)
64 return (csv
->csv_len
);
67 csv_t
*csv_init(csv_t
*csv
, char *buf
, int buflen
)
70 csv
= malloc(sizeof(csv_t
));
72 log_error("CSV Malloc failed\n");
76 memset(csv
, 0, sizeof(csv_t
));
80 TAILQ_INIT(&(csv
->records
));
84 void csv_clean(csv_t
*csv
)
89 rec
= TAILQ_FIRST(&(csv
->records
));
91 rec_n
= TAILQ_NEXT(rec
, next_record
);
92 csv_remove_record(csv
, rec
);
97 void csv_free(csv_t
*csv
)
104 static void csv_init_record(csv_record_t
*record
)
106 TAILQ_INIT(&(record
->fields
));
110 csv_record_t
*csv_record_iter(csv_t
*csv
)
112 return (TAILQ_FIRST(&(csv
->records
)));
115 csv_record_t
*csv_record_iter_next(csv_record_t
*rec
)
119 return (TAILQ_NEXT(rec
, next_record
));
122 char *csv_field_iter(csv_record_t
*rec
, csv_field_t
**fld
)
126 *fld
= TAILQ_FIRST(&(rec
->fields
));
127 return ((*fld
)->field
);
130 char *csv_field_iter_next(csv_field_t
**fld
)
132 *fld
= TAILQ_NEXT(*fld
, next_field
);
133 if ((*fld
) == NULL
) {
136 return ((*fld
)->field
);
139 int csv_field_len(csv_field_t
*fld
)
142 return fld
->field_len
;
147 static void csv_decode_record(csv_record_t
*rec
)
149 char *curr
= rec
->record
;
153 field
= strpbrk(curr
, ",");
154 while (field
!= NULL
) {
155 fld
= malloc(sizeof(csv_field_t
));
157 TAILQ_INSERT_TAIL(&(rec
->fields
), fld
, next_field
);
159 fld
->field_len
= field
- curr
;
162 field
= strpbrk(curr
, ",");
164 field
= strstr(curr
, "\n");
168 fld
= malloc(sizeof(csv_field_t
));
171 fld
->field_len
= field
- curr
;
172 TAILQ_INSERT_TAIL(&(rec
->fields
), fld
, next_field
);
176 static csv_field_t
*csv_add_field_to_record(csv_t
*csv
, csv_record_t
*rec
,
180 char *str
= rec
->record
;
181 int rlen
= rec
->rec_len
;
182 int blen
= csv
->buflen
;
184 fld
= malloc(sizeof(csv_field_t
));
186 log_error("field malloc failed\n");
187 /* more cleanup needed */
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
;
198 csv_record_t
*csv_encode(csv_t
*csv
, int count
, ...)
202 char *buf
= csv
->buf
;
203 int len
= csv
->buflen
;
204 int pointer
= csv
->pointer
;
213 /* allocate sufficient buffer */
214 str
= (char *)malloc(csv
->buflen
);
216 log_error("field str malloc failed\n");
221 va_start(list
, count
);
222 rec
= malloc(sizeof(csv_record_t
));
224 log_error("record malloc failed\n");
230 csv_init_record(rec
);
232 TAILQ_INSERT_TAIL(&(csv
->records
), rec
, next_record
);
236 * Iterate through the fields passed as a variable list and add them
238 for (tempc
= 0; tempc
< count
; tempc
++) {
239 col
= va_arg(list
, char *);
240 fld
= csv_add_field_to_record(csv
, rec
, col
);
242 log_error("fld malloc failed\n");
243 csv_remove_record(csv
, rec
);
247 if (tempc
< (count
- 1)) {
248 rec
->rec_len
+= snprintf((str
+ rec
->rec_len
),
249 (len
- rec
->rec_len
), ",");
253 snprintf((str
+ rec
->rec_len
), (len
- rec
->rec_len
), "\n");
255 csv
->csv_len
+= rec
->rec_len
;
256 csv
->pointer
+= rec
->rec_len
;
260 int csv_num_records(csv_t
*csv
)
263 return csv
->num_recs
;
268 csv_record_t
*csv_encode_record(csv_t
*csv
, csv_record_t
*rec
, int count
, ...)
274 csv_field_t
*fld
= NULL
;
277 va_start(list
, count
);
278 str
= csv_field_iter(rec
, &fld
);
284 for (tempc
= 0; tempc
< count
; tempc
++) {
285 col
= va_arg(list
, char *);
286 for (i
= 0; i
< fld
->field_len
; i
++) {
289 str
= csv_field_iter_next(&fld
);
295 csv_record_t
*csv_append_record(csv_t
*csv
, csv_record_t
*rec
, int count
, ...)
299 int len
= csv
->buflen
, tlen
;
305 /* not only works with discrete bufs */
310 /* create a new rec */
311 rec
= calloc(1, sizeof(csv_record_t
));
313 log_error("record malloc failed\n");
316 csv_init_record(rec
);
317 rec
->record
= calloc(1, csv
->buflen
);
319 log_error("field str malloc failed\n");
323 csv_insert_record(csv
, rec
);
328 va_start(list
, count
);
330 if (rec
->rec_len
&& (str
[rec
->rec_len
- 1] == '\n'))
331 str
[rec
->rec_len
- 1] = ',';
334 * Iterate through the fields passed as a variable list and add them
337 for (tempc
= 0; tempc
< count
; tempc
++) {
338 col
= va_arg(list
, char *);
339 fld
= csv_add_field_to_record(csv
, rec
, col
);
341 log_error("fld malloc failed\n");
344 if (tempc
< (count
- 1)) {
345 rec
->rec_len
+= snprintf((str
+ rec
->rec_len
),
346 (len
- rec
->rec_len
), ",");
350 snprintf((str
+ rec
->rec_len
), (len
- rec
->rec_len
), "\n");
352 csv
->csv_len
+= (rec
->rec_len
- tlen
);
353 csv
->pointer
+= (rec
->rec_len
- tlen
);
357 int csv_serialize(csv_t
*csv
, char *msgbuf
, int msglen
)
365 rec
= csv_record_iter(csv
);
366 while (rec
!= NULL
) {
367 if ((offset
+ rec
->rec_len
) >= msglen
)
369 offset
+= sprintf(&msgbuf
[offset
], "%s", rec
->record
);
370 rec
= csv_record_iter_next(rec
);
376 void csv_clone_record(csv_t
*csv
, csv_record_t
*in_rec
, csv_record_t
**out_rec
)
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");
387 /* only works with csv with discrete bufs */
390 "un-supported for this csv type - single buf detected\n");
394 /* create a new rec */
395 rec
= calloc(1, sizeof(csv_record_t
));
397 log_error("record malloc failed\n");
400 csv_init_record(rec
);
401 curr
= calloc(1, csv
->buflen
);
403 log_error("field str malloc failed\n");
408 rec
->rec_len
= in_rec
->rec_len
;
409 strlcpy(rec
->record
, in_rec
->record
, csv
->buflen
);
411 /* decode record into fields */
412 csv_decode_record(rec
);
417 void csv_remove_record(csv_t
*csv
, csv_record_t
*rec
)
419 csv_field_t
*fld
= NULL
, *p_fld
;
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");
428 csv_field_iter(rec
, &fld
);
431 csv_field_iter_next(&fld
);
432 TAILQ_REMOVE(&(rec
->fields
), p_fld
, next_field
);
436 TAILQ_REMOVE(&(csv
->records
), rec
, next_record
);
439 csv
->csv_len
-= rec
->rec_len
;
440 csv
->pointer
-= rec
->rec_len
;
446 void csv_insert_record(csv_t
*csv
, csv_record_t
*rec
)
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");
454 /* we can only insert records if no buf was supplied during csv init */
457 "un-supported for this csv type - single buf detected\n");
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");
467 TAILQ_INSERT_TAIL(&(csv
->records
), rec
, next_record
);
469 csv
->csv_len
+= rec
->rec_len
;
470 csv
->pointer
+= rec
->rec_len
;
473 csv_record_t
*csv_concat_record(csv_t
*csv
, csv_record_t
*rec1
,
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");
487 /* we can only concat records if no buf was supplied during csv init */
490 "un-supported for this csv type - single buf detected\n");
494 /* create a new rec */
495 rec
= calloc(1, sizeof(csv_record_t
));
497 log_error("record malloc failed\n");
500 csv_init_record(rec
);
502 curr
= (char *)calloc(1, csv
->buflen
);
504 log_error("field str malloc failed\n");
509 /* concat the record string */
510 ret
= strstr(rec1
->record
, "\n");
512 log_error("rec1 str not properly formatted\n");
516 snprintf(curr
, (int)(ret
- rec1
->record
+ 1), "%s", rec1
->record
);
519 ret
= strstr(rec2
->record
, "\n");
521 log_error("rec2 str not properly formatted\n");
525 snprintf((curr
+ strlen(curr
)), (int)(ret
- rec2
->record
+ 1), "%s",
528 rec
->rec_len
= strlen(curr
);
532 > (csv
->csv_len
- rec1
->rec_len
- rec2
->rec_len
+ rec
->rec_len
));
534 /* decode record into fields */
535 csv_decode_record(rec
);
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
);
551 void csv_decode(csv_t
*csv
, char *inbuf
)
557 buf
= (inbuf
) ? inbuf
: csv
->buf
;
560 pos
= strpbrk(buf
, "\n");
561 while (pos
!= NULL
) {
562 rec
= calloc(1, sizeof(csv_record_t
));
565 csv_init_record(rec
);
566 TAILQ_INSERT_TAIL(&(csv
->records
), rec
, next_record
);
571 rec
->record
= calloc(1, csv
->buflen
);
573 log_error("field str malloc failed\n");
576 strncpy(rec
->record
, buf
, pos
- buf
+ 1);
578 rec
->rec_len
= pos
- buf
+ 1;
579 /* decode record into fields */
580 csv_decode_record(rec
);
582 pos
= strpbrk(buf
, "\n");
586 int csv_is_record_valid(csv_t
*csv
, csv_record_t
*in_rec
)
591 rec
= csv_record_iter(csv
);
597 rec
= csv_record_iter_next(rec
);
603 void csv_dump(csv_t
*csv
)
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
);
616 rec
= csv_record_iter_next(rec
);
622 static int get_memory_usage(pid_t pid
)
625 char buf
[4096], status_child
[PATH_MAX
];
628 snprintf(status_child
, sizeof(status_child
), "/proc/%d/status", pid
);
629 fd
= open(status_child
, O_RDONLY
);
639 vm
= strstr(buf
, "VmData:");
641 sscanf(vm
, "%*s %d", &data
);
643 vm
= strstr(buf
, "VmStk:");
645 sscanf(vm
, "%*s %d", &stack
);
657 char hdr1
[32], hdr2
[32];
659 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
660 csv_init(&csv
, buf
, 256);
661 snprintf(hdr1
, sizeof(hdr1
), "%4d", 0);
662 snprintf(hdr2
, sizeof(hdr2
), "%4d", 1);
663 log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1
), strlen(hdr2
), atoi(hdr1
),
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");
672 csv_encode(&csv
, 2, "pdfadfadfadsadsaddfdfdsfdsd", "35444554545454545");
673 log_verbose("%s\n", buf
);
674 snprintf(hdr1
, sizeof(hdr1
), "%4d", csv
.csv_len
);
675 snprintf(hdr2
, sizeof(hdr2
), "%4d", 1);
676 log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1
), strlen(hdr2
), atoi(hdr1
),
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
);
681 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
683 log_verbose("Mem: %d\n", get_memory_usage(getpid()));
684 csv_init(&csv
, buf
, 256);
685 csv_decode(&csv
, NULL
);
686 log_verbose("%s", "AFTER DECODE\n");
689 log_verbose("Mem: %d\n", get_memory_usage(getpid()));