]> git.proxmox.com Git - mirror_frr.git/blob - lib/csv.c
Pre-revert nonmergeable changes
[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 fld = malloc(sizeof(csv_field_t));
181 if (field && fld) {
182 fld->field = curr;
183 fld->field_len = field-curr;
184 TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
185 }
186 }
187
188 static csv_field_t *
189 csv_add_field_to_record(csv_t *csv,
190 csv_record_t *rec,
191 char *col)
192 {
193 csv_field_t *fld;
194 char *str = rec->record;
195 int rlen = rec->rec_len;
196 int blen = csv->buflen;
197
198 fld = malloc(sizeof(csv_field_t));
199 if (!fld) {
200 log_error("field malloc failed\n");
201 /* more cleanup needed */
202 return (NULL);
203 }
204 TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
205 fld->field = str+rlen;
206 fld->field_len = snprintf((str+rlen), (blen - rlen), "%s", col);
207 rlen += fld->field_len;
208 rec->rec_len = rlen;
209 return fld;
210 }
211
212 csv_record_t *
213 csv_encode (csv_t *csv,
214 int count,
215 ...)
216 {
217 int tempc;
218 va_list list;
219 char *buf = csv->buf;
220 int len = csv->buflen;
221 int pointer = csv->pointer;
222 char *str = NULL;
223 char *col;
224 csv_record_t *rec;
225 csv_field_t *fld;
226
227 if (buf) {
228 str = buf + pointer;
229 } else {
230 /* allocate sufficient buffer */
231 str = (char *)malloc(csv->buflen);
232 if (!str) {
233 log_error("field str malloc failed\n");
234 return (NULL);
235 }
236 }
237
238 va_start(list, count);
239 rec = malloc(sizeof(csv_record_t));
240 if (!rec) {
241 log_error("record malloc failed\n");
242 return (NULL);
243 }
244 csv_init_record(rec);
245 rec->record = str;
246 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
247 csv->num_recs++;
248
249 /**
250 * Iterate through the fields passed as a variable list and add them
251 */
252 for (tempc = 0; tempc < count; tempc++) {
253 col = va_arg(list, char *);
254 fld = csv_add_field_to_record(csv, rec, col);
255 if (!fld) {
256 log_error("fld malloc failed\n");
257 csv_remove_record(csv, rec);
258 return (NULL);
259 }
260 if (tempc < (count - 1)) {
261 rec->rec_len += snprintf((str+rec->rec_len), (len - rec->rec_len), ",");
262 }
263 }
264 rec->rec_len += snprintf((str+rec->rec_len), (len - rec->rec_len), "\n");
265 va_end(list);
266 csv->csv_len += rec->rec_len;
267 csv->pointer += rec->rec_len;
268 return (rec);
269 }
270
271 int
272 csv_num_records (csv_t *csv)
273 {
274 if (csv) {
275 return csv->num_recs;
276 }
277 return 0;
278 }
279
280 csv_record_t *
281 csv_encode_record (csv_t *csv,
282 csv_record_t *rec,
283 int count,
284 ...)
285 {
286 int tempc;
287 va_list list;
288 char *str;
289 char *col;
290 csv_field_t *fld = NULL;
291 int i;
292
293 va_start(list, count);
294 str = csv_field_iter(rec, &fld);
295 for (tempc = 0; tempc < count; tempc++) {
296 col = va_arg(list, char *);
297 for (i = 0; i < fld->field_len; i++) {
298 str[i] = col[i];
299 }
300 str = csv_field_iter_next(&fld);
301 }
302 va_end(list);
303 return (rec);
304 }
305
306 csv_record_t *
307 csv_append_record (csv_t *csv,
308 csv_record_t *rec,
309 int count,
310 ...)
311 {
312 int tempc;
313 va_list list;
314 int len = csv->buflen, tlen;
315 char *str;
316 csv_field_t *fld;
317 char *col;
318
319 if (csv->buf) {
320 /* not only works with discrete bufs */
321 return NULL;
322 }
323
324 if (!rec) {
325 /* create a new rec */
326 rec = calloc(1, sizeof(csv_record_t));
327 if (!rec) {
328 log_error("record malloc failed\n");
329 return NULL;
330 }
331 csv_init_record(rec);
332 rec->record = calloc(1, csv->buflen);
333 if (!rec->record) {
334 log_error("field str malloc failed\n");
335 free(rec);
336 return NULL;
337 }
338 csv_insert_record(csv, rec);
339 }
340
341 str = rec->record;
342
343 va_start(list, count);
344
345 if (rec->rec_len && (str[rec->rec_len-1] == '\n'))
346 str[rec->rec_len-1] = ',';
347
348 /**
349 * Iterate through the fields passed as a variable list and add them
350 */
351 tlen = rec->rec_len;
352 for (tempc = 0; tempc < count; tempc++) {
353 col = va_arg(list, char *);
354 fld = csv_add_field_to_record(csv, rec, col);
355 if (!fld) {
356 log_error("fld malloc failed\n");
357 break;
358 }
359 if (tempc < (count - 1)) {
360 rec->rec_len += snprintf((str+rec->rec_len),
361 (len - rec->rec_len), ",");
362 }
363 }
364 rec->rec_len += snprintf((str+rec->rec_len),
365 (len - rec->rec_len), "\n");
366 va_end(list);
367 csv->csv_len += (rec->rec_len - tlen);
368 csv->pointer += (rec->rec_len - tlen);
369 return (rec);
370 }
371
372 int
373 csv_serialize(csv_t *csv, char *msgbuf, int msglen)
374 {
375 csv_record_t *rec;
376 int offset = 0;
377
378 if (!csv || !msgbuf) return -1;
379
380 rec = csv_record_iter(csv);
381 while (rec != NULL) {
382 if ((offset + rec->rec_len) >= msglen)
383 return -1;
384 offset += sprintf(&msgbuf[offset], "%s", rec->record);
385 rec = csv_record_iter_next(rec);
386 }
387
388 return 0;
389 }
390
391 void
392 csv_clone_record (csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec)
393 {
394 char *curr;
395 csv_record_t *rec;
396
397 /* first check if rec belongs to this csv */
398 if(!csv_is_record_valid(csv, in_rec)){
399 log_error("rec not in this csv\n");
400 return;
401 }
402
403 /* only works with csv with discrete bufs */
404 if (csv->buf) {
405 log_error("un-supported for this csv type - single buf detected\n");
406 return;
407 }
408
409 /* create a new rec */
410 rec = calloc(1, sizeof(csv_record_t));
411 if (!rec) {
412 log_error("record malloc failed\n");
413 return;
414 }
415 csv_init_record(rec);
416 curr = calloc(1, csv->buflen);
417 if (!curr) {
418 log_error("field str malloc failed\n");
419 return;
420 }
421 rec->record = curr;
422 rec->rec_len = in_rec->rec_len;
423 strcpy(rec->record, in_rec->record);
424
425 /* decode record into fields */
426 csv_decode_record(rec);
427
428 *out_rec = rec;
429 }
430
431 void
432 csv_remove_record (csv_t *csv, csv_record_t *rec)
433 {
434 csv_field_t *fld, *p_fld;
435
436 /* first check if rec belongs to this csv */
437 if(!csv_is_record_valid(csv, rec)){
438 log_error("rec not in this csv\n");
439 return;
440 }
441
442 /* remove fields */
443 csv_field_iter(rec, &fld);
444 while(fld) {
445 p_fld = fld;
446 csv_field_iter_next(&fld);
447 TAILQ_REMOVE(&(rec->fields), p_fld, next_field);
448 free(p_fld);
449 }
450
451 TAILQ_REMOVE(&(csv->records), rec, next_record);
452
453 csv->num_recs--;
454 csv->csv_len -= rec->rec_len;
455 csv->pointer -= rec->rec_len;
456 if (!csv->buf)
457 free(rec->record);
458 free(rec);
459 }
460
461 void
462 csv_insert_record (csv_t *csv, csv_record_t *rec)
463 {
464 /* first check if rec already in csv */
465 if(csv_is_record_valid(csv, rec)){
466 log_error("rec already in this csv\n");
467 return;
468 }
469
470 /* we can only insert records if no buf was supplied during csv init */
471 if (csv->buf) {
472 log_error("un-supported for this csv type - single buf detected\n");
473 return;
474 }
475
476 /* do we go beyond the max buf set for this csv ?*/
477 if ((csv->csv_len + rec->rec_len) > csv->buflen ) {
478 log_error("cannot insert - exceeded buf size\n");
479 return;
480 }
481
482 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
483 csv->num_recs++;
484 csv->csv_len += rec->rec_len;
485 csv->pointer += rec->rec_len;
486 }
487
488 csv_record_t *
489 csv_concat_record (csv_t *csv,
490 csv_record_t *rec1,
491 csv_record_t *rec2)
492 {
493 char *curr;
494 char *ret;
495 csv_record_t *rec;
496
497 /* first check if rec1 and rec2 belong to this csv */
498 if(!csv_is_record_valid(csv, rec1) ||
499 !csv_is_record_valid(csv, rec2)) {
500 log_error("rec1 and/or rec2 invalid\n");
501 return (NULL);
502 }
503
504 /* we can only concat records if no buf was supplied during csv init */
505 if (csv->buf) {
506 log_error("un-supported for this csv type - single buf detected\n");
507 return (NULL);
508 }
509
510 /* create a new rec */
511 rec = calloc(1, sizeof(csv_record_t));
512 if (!rec) {
513 log_error("record malloc failed\n");
514 return (NULL);
515 }
516 csv_init_record(rec);
517
518 curr = (char *)calloc(1, csv->buflen);
519 if (!curr) {
520 log_error("field str malloc failed\n");
521 return (NULL);
522 }
523 rec->record = curr;
524
525 /* concat the record string */
526 ret = strstr(rec1->record, "\n");
527 if (!ret) {
528 log_error("rec1 str not properly formatted\n");
529 return (NULL);
530 }
531
532 snprintf(curr, (int)(ret - rec1->record + 1), "%s", rec1->record);
533 strcat(curr, ",");
534
535 ret = strstr(rec2->record, "\n");
536 if (!ret) {
537 log_error("rec2 str not properly formatted\n");
538 return (NULL);
539 }
540
541 snprintf((curr+strlen(curr)), (int)(ret - rec2->record + 1), "%s",
542 rec2->record);
543 strcat(curr, "\n");
544 rec->rec_len = strlen(curr);
545
546 /* paranoia */
547 assert(csv->buflen >
548 (csv->csv_len - rec1->rec_len - rec2->rec_len + rec->rec_len));
549
550 /* decode record into fields */
551 csv_decode_record(rec);
552
553 /* now remove rec1 and rec2 and insert rec into this csv */
554 csv_remove_record(csv, rec1);
555 csv_remove_record(csv, rec2);
556 csv_insert_record(csv, rec);
557
558 return rec;
559 }
560
561 void
562 csv_decode (csv_t *csv, char *inbuf)
563 {
564 char *buf;
565 char *pos;
566 csv_record_t *rec;
567
568 buf = (inbuf)? inbuf:csv->buf;
569 pos = strpbrk(buf, "\n");
570 while (pos != NULL) {
571 rec = calloc(1, sizeof(csv_record_t));
572 csv_init_record(rec);
573 TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
574 csv->num_recs++;
575 if (csv->buf)
576 rec->record = buf;
577 else {
578 rec->record = calloc(1, csv->buflen);
579 if (!rec->record) {
580 log_error("field str malloc failed\n");
581 return;
582 }
583 strncpy(rec->record, buf, pos-buf+1);
584 }
585 rec->rec_len = pos-buf+1;
586 /* decode record into fields */
587 csv_decode_record(rec);
588 buf = pos+1;
589 pos = strpbrk(buf, "\n");
590 }
591 }
592
593 int
594 csv_is_record_valid(csv_t *csv, csv_record_t *in_rec)
595 {
596 csv_record_t *rec;
597 int valid = 0;
598
599 rec = csv_record_iter(csv);
600 while (rec) {
601 if(rec == in_rec) {
602 valid = 1;
603 break;
604 }
605 rec = csv_record_iter_next(rec);
606 }
607
608 return valid;
609 }
610
611 void
612 csv_dump (csv_t *csv)
613 {
614 csv_record_t *rec;
615 csv_field_t *fld;
616 char *str;
617
618 rec = csv_record_iter(csv);
619 while (rec != NULL) {
620 str = csv_field_iter(rec, &fld);
621 while (str != NULL) {
622 fprintf(stderr, "%s\n", str);
623 str = csv_field_iter_next(&fld);
624 }
625 rec = csv_record_iter_next(rec);
626 }
627 }
628
629 #ifdef TEST_CSV
630
631 static int
632 get_memory_usage (pid_t pid)
633 {
634 int fd, data, stack;
635 char buf[4096], status_child[BUFSIZ];
636 char *vm;
637
638 sprintf(status_child, "/proc/%d/status", pid);
639 if ((fd = open(status_child, O_RDONLY)) < 0)
640 return -1;
641
642 read(fd, buf, 4095);
643 buf[4095] = '\0';
644 close(fd);
645
646 data = stack = 0;
647
648 vm = strstr(buf, "VmData:");
649 if (vm) {
650 sscanf(vm, "%*s %d", &data);
651 }
652 vm = strstr(buf, "VmStk:");
653 if (vm) {
654 sscanf(vm, "%*s %d", &stack);
655 }
656
657 return data + stack;
658 }
659
660 int main ()
661 {
662 char buf[10000];
663 csv_t csv;
664 int p;
665 int i, j;
666 csv_record_t *rec;
667 csv_field_t *fld;
668 char *str;
669 char hdr1[32], hdr2[32];
670
671 log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
672 csv_init(&csv, buf, 256);
673 sprintf(hdr1, "%4u", 0);
674 sprintf(hdr2, "%4u", 1);
675 log_verbose("(%d/%d/%d/%d)\n", strlen(hdr1),
676 strlen(hdr2), atoi(hdr1), 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",
685 "35444554545454545");
686 log_verbose("%s\n", buf);
687 sprintf(hdr1, "%4u", csv.csv_len);
688 sprintf(hdr2, "%4u", 1);
689 log_verbose("(%d/%d/%d/%d)\n", strlen(hdr1),
690 strlen(hdr2), atoi(hdr1), atoi(hdr2));
691 rec = csv_encode_record(&csv, rec, 2, hdr1, hdr2);
692 log_verbose("(%d/%d)\n%s\n", rec->rec_len, csv.csv_len, buf);
693
694 log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
695 csv_clean(&csv);
696 log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
697 csv_init(&csv, buf, 256);
698 csv_decode(&csv, NULL);
699 log_verbose("AFTER DECODE\n");
700 csv_dump(&csv);
701 csv_clean(&csv);
702 log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
703 }
704 #endif