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