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