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