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