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