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