]> git.ozlabs.org Git - ccan-lca-2011.git/blob - ccan/cdump/cdump.c
cdump: first cut of translation of Tridge's genstruct junkcode.
[ccan-lca-2011.git] / ccan / cdump / cdump.c
1 /*
2   Based on genstruct by Andrew Tridgell:
3
4    Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stddef.h>
27 #include <stdarg.h>
28 #include <errno.h>
29 #include <ccan/talloc/talloc.h>
30 #include "cdump.h"
31 #include "cdump_internal.h"
32
33 /* intermediate dumps are stored in one of these */
34 struct cdump_string {
35         size_t length;
36         char *s;
37 };
38
39 /* see if a range of memory is all zero. Used to prevent dumping of zero elements */
40 static bool all_zero(const char *ptr, size_t size)
41 {
42         int i;
43
44         for (i=0;i<size;i++) {
45                 if (ptr[i]) return false;
46         }
47         return true;
48 }
49
50 /* encode a buffer of bytes into a escaped string */
51 static char *encode_bytes(const void *ctx, const char *ptr, size_t len)
52 {
53         const char *hexdig = "0123456789abcdef";
54         char *ret, *p;
55         size_t i;
56         ret = talloc_array(ctx, char, len*3 + 1); /* worst case size */
57         if (!ret)
58                 return NULL;
59         for (p=ret,i=0;i<len;i++) {
60                 if (isalnum(ptr[i]) || isspace(ptr[i]) ||
61                     (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
62                         *p++ = ptr[i];
63                 } else {
64                         unsigned char c = *(unsigned char *)(ptr+i);
65                         if (c == 0 && all_zero(ptr+i, len-i)) break;
66                         p[0] = '\\';
67                         p[1] = hexdig[c>>4];
68                         p[2] = hexdig[c&0xF];
69                         p += 3;
70                 }
71         }
72
73         *p = 0;
74
75         return ret;
76 }
77
78 /* decode an escaped string from encode_bytes() into a buffer */
79 static char *decode_bytes(const void *ctx, const char *s)
80 {
81         char *ret, *p;
82         size_t i;
83
84         ret = talloc_array(ctx, char, strlen(s)+1); /* worst case length */
85         if (!ret)
86                 return NULL;
87
88         if (*s == '{') s++;
89
90         for (p=ret,i=0;s[i];i++) {
91                 if (s[i] == '}') {
92                         break;
93                 } else if (s[i] == '\\') {
94                         unsigned v;
95                         if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
96                                 talloc_free(ret);
97                                 return NULL;
98                         }
99                         *(unsigned char *)p = v;
100                         p++;
101                         i += 2;
102                 } else {
103                         *p++ = s[i];
104                 }
105         }
106         /* Nul-terminate in case we're being used as a string. */
107         *p = 0;
108         return ret;
109 }
110
111 static char *bundle(const void *ctx,
112                     const struct cdump_desc *info,
113                     const void *data,
114                     unsigned indent);
115
116 /* the add*() functions deal with adding things to a struct
117    cdump_string */
118
119 /* allocate more space if needed */
120 static bool addgen_alloc(struct cdump_string *p, int n)
121 {
122         if (p->length + n <= talloc_get_size(p->s)) return true;
123         p->s = talloc_realloc(p, p->s, char, p->length + n + 200);
124         return p->s != NULL;
125 }
126
127 /* add a character to the buffer */
128 static bool addchar(struct cdump_string *p, char c)
129 {
130         if (!addgen_alloc(p, 2)) {
131                 return false;
132         }
133         p->s[p->length++] = c;
134         p->s[p->length] = 0;
135         return true;
136 }
137
138 /* add a string to the buffer */
139 static bool addstr(struct cdump_string *p, const char *s)
140 {
141         int len = strlen(s);
142         if (!addgen_alloc(p, len+1)) {
143                 return false;
144         }
145         memcpy(p->s + p->length, s, len+1);
146         p->length += len;
147         return true;
148 }
149
150 /* add a string to the buffer with a tab prefix */
151 static bool addtabbed(struct cdump_string *p, const char *s, unsigned indent)
152 {
153         int len = strlen(s);
154         if (!addgen_alloc(p, indent+len+1)) {
155                 return false;
156         }
157         while (indent--) {
158                 p->s[p->length++] = '\t';
159         }
160         memcpy(p->s + p->length, s, len+1);
161         p->length += len;
162         return true;
163 }
164
165 /* note! this can only be used for results up to 60 chars wide! */
166 static bool addshort(struct cdump_string *p, const char *fmt, ...)
167 {
168         char buf[60];
169         int n;
170         va_list ap;
171         va_start(ap, fmt);
172         n = vsnprintf(buf, sizeof(buf), fmt, ap);
173         va_end(ap);
174         if (!addgen_alloc(p, n + 1)) {
175                 return false;
176         }
177         memcpy(p->s + p->length, buf, n);
178         p->length += n;
179         p->s[p->length] = 0;
180         return true;
181 }
182
183 /*
184    this is here to make it easier for people to write dump functions
185    for their own types
186  */
187 bool cdump_addstr(struct cdump_string *p, const char *fmt, ...)
188 {
189         char *buf;
190         bool ret;
191         va_list ap;
192         va_start(ap, fmt);
193         buf = talloc_vasprintf(NULL, fmt, ap);
194         va_end(ap);
195         if (!buf)
196                 return false;
197         ret = addstr(p, buf);
198         talloc_free(buf);
199         return ret;
200 }
201
202 /* dump a enumerated type */
203 bool cdump_bundle_enum(const struct cdump_enum *einfo,
204                        struct cdump_string *p,
205                        const void *ptr,
206                        unsigned indent)
207 {
208         unsigned v = *(unsigned *)ptr;
209         int i;
210         for (i=0;einfo[i].name;i++) {
211                 if (v == einfo[i].value) {
212                         return addstr(p, einfo[i].name);
213                 }
214         }
215         /* hmm, maybe we should just fail? */
216         return cdump_bundle_unsigned(p, ptr, indent);
217 }
218
219 /* dump a single non-array element, handling struct and enum */
220 static bool bundle_one(struct cdump_string *p,
221                          const struct cdump_desc *info,
222                          const void *ptr,
223                          unsigned indent)
224 {
225         if (info->bundle == cdump_bundle_char && info->ptr_count == 1) {
226                 char *s = encode_bytes(p, ptr, strlen(ptr));
227                 if (!s)
228                         return false;
229                 if (!addchar(p,'{') ||
230                     !addstr(p, s) ||
231                     !addstr(p, "}")) {
232                         return false;
233                 }
234                 return true;
235         }
236
237         return info->bundle(p, ptr, indent);
238 }
239
240 /* handle dumping of an array of arbitrary type */
241 static bool bundle_array(struct cdump_string *p,
242                          const struct cdump_desc *info,
243                          const void *ptr,
244                          size_t array_len,
245                          unsigned indent)
246 {
247         size_t i, count=0;
248
249         /* special handling of fixed length strings */
250         if (array_len != 0 &&
251             info->ptr_count == 0 &&
252             info->bundle == cdump_bundle_char) {
253                 char *s = encode_bytes(p, ptr, array_len);
254                 if (!s) return false;
255                 if (!addtabbed(p, info->name, indent) ||
256                     !addstr(p, " = {") ||
257                     !addstr(p, s) ||
258                     !addstr(p, "}\n")) {
259                         return false;
260                 }
261                 return true;
262         }
263
264         for (i=0;i<array_len;i++) {
265                 const char *p2 = ptr;
266                 size_t size = info->size;
267
268                 /* generic pointer dereference */
269                 if (info->ptr_count) {
270                         p2 = *(const char **)ptr;
271                         size = sizeof(void *);
272                 }
273                 
274                 if ((count || info->ptr_count) &&
275                     !(info->flags & CDUMP_FLAG_ALWAYS) &&
276                     all_zero(ptr, size)) {
277                         ptr += size;
278                         continue;
279                 }
280                 if (count == 0) {
281                         if (!addtabbed(p, info->name, indent) ||
282                             !addshort(p, " = %u:", i)) {
283                                 return false;
284                         }
285                 } else {
286                         if (!addshort(p, ", %u:", i) != 0) {
287                                 return false;
288                         }
289                 }
290                 if (!bundle_one(p, info, p2, indent)) {
291                         return false;
292                 }
293                 ptr += size;
294                 count++;
295         }
296         if (count) {
297                 return addstr(p, "\n");
298         }
299         return true;
300 }
301
302 /* find a variable by name in a loaded structure and return its value
303    as an integer. Used to support dynamic arrays */
304 static ssize_t find_var(const struct cdump_desc *info,
305                         const char *data,
306                         const char *var)
307 {
308         unsigned int i;
309         const char *ptr;
310
311         /* this allows for constant lengths */
312         if (isdigit(*var)) {
313                 return atol(var);
314         }
315
316         for (i=0;info[i].name;i++) {
317                 if (strcmp(info[i].name, var) == 0) break;
318         }
319         if (!info[i].name) return -1;
320
321         ptr = data + info[i].offset;
322
323         if (info[i].size == sizeof(int))
324                 return *(int *)ptr;
325         else if (info[i].size == sizeof(size_t))
326                 return *(ssize_t *)ptr;
327         else if (info[i].size == sizeof(char))
328                 return *(char *)ptr;
329
330         return -1;
331 }
332
333
334 bool cdump_bundle_struct(const struct cdump_desc *info,
335                          struct cdump_string *p,
336                          const void *ptr,
337                          unsigned indent)
338 {
339         char *s = bundle(p, info, ptr, indent+1);
340         if (!s)
341                 return false;
342
343         return addstr(p, "{\n") && addstr(p,s) && addtabbed(p, "}", indent);
344 }
345
346 static bool bundle_string(struct cdump_string *p,
347                           const struct cdump_desc *info,
348                           const char *data,
349                           unsigned indent)
350 {
351         const char *ptr = *(char **)data;
352         char *s = encode_bytes(p, ptr, strlen(ptr));
353         if (!s)
354                 return false;
355
356         return addtabbed(p, info->name, indent)
357                 && addstr(p, " = ")
358                 && addchar(p,'{')
359                 && addstr(p, s)
360                 && addstr(p, "}\n");
361 }
362
363 /* the generic dump routine. Scans the parse information for this structure
364    and processes it recursively */
365 static char *bundle(const void *ctx,
366                     const struct cdump_desc *info,
367                     const void *data,
368                     unsigned indent)
369 {
370         struct cdump_string *p;
371         char *s = NULL;
372         int i;
373
374         p = talloc(ctx, struct cdump_string);
375         if (!p)
376                 return NULL;
377         p->length = 0;
378         p->s = NULL;
379
380         for (i=0;info[i].name;i++) {
381                 const void *ptr = (char *)data + info[i].offset;
382                 unsigned size = info[i].size;
383
384                 if (info[i].ptr_count) {
385                         size = sizeof(void *);
386                 }
387
388                 /* special handling for array types */
389                 if (info[i].array_len) {
390                         unsigned len = info[i].array_len;
391                         if (!bundle_array(p, &info[i], ptr,  len, indent)) {
392                                 goto out;
393                         }
394                         continue;
395                 }
396
397                 /* and dynamically sized arrays */
398                 if (info[i].dynamic_len) {
399                         ssize_t len = find_var(info, data, info[i].dynamic_len);
400                         struct cdump_desc p2 = info[i];
401                         if (len < 0) {
402                                 goto out;
403                         }
404                         if (len > 0) {
405                                 p2.ptr_count--;
406                                 p2.dynamic_len = NULL;
407                                 if (!bundle_array(p, &p2, *(void **)ptr,
408                                                     len, indent)) {
409                                         goto out;
410                                 }
411                         }
412                         continue;
413                 }
414
415                 /* don't dump zero elements */
416                 if (!(info[i].flags & CDUMP_FLAG_ALWAYS) && all_zero(ptr, size))
417                         continue;
418
419                 /* assume char* is a null terminated string */
420                 if (info[i].size == 1 && info[i].ptr_count == 1 &&
421                     info[i].bundle == cdump_bundle_char) {
422                         if (!bundle_string(p, &info[i], ptr, indent)) {
423                                 goto out;
424                         }
425                         continue;
426                 }
427
428                 /* generic pointer dereference */
429                 if (info[i].ptr_count) {
430                         ptr = *(const void **)ptr;
431                 }
432
433                 if (!addtabbed(p, info[i].name, indent) ||
434                     !addstr(p, " = ") ||
435                     !bundle_one(p, &info[i], ptr, indent) ||
436                     !addstr(p, "\n")) {
437                         goto out;
438                 }
439         }
440         s = talloc_steal(ctx, p->s);
441 out:
442         talloc_free(p);
443         return s;
444 }
445
446 char *cdump_bundle(const void *ctx,
447                    const struct cdump_desc *info, const void *data)
448 {
449         return bundle(ctx, info, data, 0);
450 }
451
452 /* parse routine for enumerated types */
453 bool cdump_unbundle_enum(const struct cdump_enum *einfo,
454                          void *ptr,
455                          const char *str)
456 {
457         unsigned v;
458         int i;
459
460         if (isdigit(*str)) {
461                 if (sscanf(str, "%u", &v) != 1) {
462                         errno = EINVAL;
463                         return false;
464                 }
465                 *(unsigned *)ptr = v;
466                 return 0;
467         }
468
469         for (i=0;einfo[i].name;i++) {
470                 if (strcmp(einfo[i].name, str) == 0) {
471                         *(unsigned *)ptr = einfo[i].value;
472                         return true;
473                 }
474         }
475
476         /* unknown enum value?? */
477         return false;
478 }
479
480
481 /* parse all base types */
482 static bool unbundle_base(const void *ctx,
483                           const struct cdump_desc *info,
484                           void *ptr,
485                           const char *str)
486 {
487         if (info->unbundle == cdump_unbundle_char && info->ptr_count==1) {
488                 char *s = decode_bytes(ctx, str);
489                 if (!s)
490                         return false;
491                 *(char **)ptr = s;
492                 return true;
493         }
494
495         if (info->ptr_count) {
496                 struct cdump_desc p2 = *info;
497                 *(void **)ptr = talloc_zero_size(ctx,
498                                                  info->ptr_count>1?sizeof(void *):info->size);
499                 if (! *(void **)ptr) {
500                         return false;
501                 }
502                 ptr = *(char **)ptr;
503                 p2.ptr_count--;
504                 return unbundle_base(ctx, &p2, ptr, str);
505         }
506
507         return info->unbundle(ctx, ptr, str);
508 }
509
510 /* search for a character in a string, skipping over sections within
511    matching braces */
512 static char *match_braces(char *s, char c)
513 {
514         int depth = 0;
515         while (*s) {
516                 switch (*s) {
517                 case '}':
518                         depth--;
519                         break;
520                 case '{':
521                         depth++;
522                         break;
523                 }
524                 if (depth == 0 && *s == c) {
525                         return s;
526                 }
527                 s++;
528         }
529         return s;
530 }
531
532 /* parse a generic array */
533 static bool unbundle_array(const void *ctx,
534                            const struct cdump_desc *info,
535                            char *ptr,
536                            const char *str,
537                            size_t array_len)
538 {
539         char *p, *p2;
540         size_t size = info->size;
541
542         /* special handling of fixed length strings */
543         if (array_len != 0 &&
544             info->ptr_count == 0 &&
545             info->bundle == cdump_bundle_char) {
546                 char *s = decode_bytes(ctx, str);
547                 if (!s)
548                         return false;
549                 memset(ptr, 0, array_len);
550                 memcpy(ptr, s, array_len);
551                 talloc_free(s);
552                 return true;
553         }
554
555         if (info->ptr_count) {
556                 size = sizeof(void *);
557         }
558
559         while (*str) {
560                 size_t idx;
561                 bool done;
562
563                 idx = atol(str);
564                 p = strchr(str,':');
565                 if (!p) break;
566                 p++;
567                 p2 = match_braces(p, ',');
568                 done = (*p2 != ',');
569                 *p2 = 0;
570
571                 if (*p == '{') {
572                         p++;
573                         p[strlen(p)-1] = 0;
574                 }
575
576                 if (!unbundle_base(ctx, info, ptr + idx*size, p)) {
577                         return false;
578                 }
579
580                 if (done)
581                         break;
582                 str = p2+1;
583         }
584
585         return true;
586 }
587
588 /* parse one element, handling dynamic and static arrays */
589 static bool unbundle_one(const void *ctx,
590                          const struct cdump_desc *info,
591                          const char *name,
592                          void *data,
593                          const char *str)
594 {
595         int i;
596         for (i=0;info[i].name;i++) {
597                 if (strcmp(info[i].name, name) == 0) {
598                         break;
599                 }
600         }
601         if (info[i].name == NULL) {
602                 return false;
603         }
604
605         if (info[i].array_len) {
606                 return unbundle_array(ctx, &info[i], data+info[i].offset,
607                                        str, info[i].array_len);
608         }
609
610         if (info[i].dynamic_len) {
611                 ssize_t len = find_var(info, data, info[i].dynamic_len);
612                 if (len < 0) {
613                         errno = EINVAL;
614                         return false;
615                 }
616                 if (len > 0) {
617                         unsigned size;
618                         struct cdump_desc p2 = info[i];
619                         char *ptr;
620                         size = info[i].ptr_count>1?sizeof(void*):info[i].size;
621                         ptr = talloc_zero_size(ctx, len * size);
622                         if (!ptr)
623                                 return false;
624                         *((char **)(data + info[i].offset)) = ptr;
625                         p2.ptr_count--;
626                         p2.dynamic_len = NULL;
627                         return unbundle_array(ctx, &p2, ptr, str, len);
628                 }
629                 return true;
630         }
631
632         return unbundle_base(ctx, &info[i], data + info[i].offset, str);
633 }
634
635 /* the main parse routine */
636 bool cdump_unbundle_struct(const void *ctx,
637                            const struct cdump_desc *info,
638                            void *data, const char *s)
639 {
640         char *str, *s0;
641         
642         s0 = talloc_strdup(ctx, s);
643         str = s0;
644
645         while (*str) {
646                 char *p;
647                 char *name;
648                 char *value;
649
650                 /* skip leading whitespace */
651                 while (isspace(*str)) str++;
652
653                 p = strchr(str, '=');
654                 if (!p) break;
655                 value = p+1;
656                 while (p > str && isspace(*(p-1))) {
657                         p--;
658                 }
659
660                 *p = 0;
661                 name = str;
662
663                 while (isspace(*value)) value++;
664
665                 if (*value == '{') {
666                         str = match_braces(value, '}');
667                         value++;
668                 } else {
669                         str = match_braces(value, '\n');
670                 }
671
672                 *str++ = 0;
673
674                 if (!unbundle_one(ctx, info, name, data, value)) {
675                         talloc_free(s0);
676                         return false;
677                 }
678         }
679
680         talloc_free(s0);
681         return true;
682 }
683
684 bool cdump_unbundle(const void *ctx,
685                     const struct cdump_desc *info,
686                     void *data, const char *s)
687 {
688         return cdump_unbundle_struct(ctx, info, data, s);
689 }
690
691 /* for convenience supply some standard dumpers and parsers here */
692 bool cdump_unbundle_char(const void *ctx, void *ptr, const char *str)
693 {
694         *(unsigned char *)ptr = atoi(str);
695         return true;
696 }
697
698 bool cdump_unbundle_int(const void *ctx, void *ptr, const char *str)
699 {
700         *(int *)ptr = atoi(str);
701         return true;
702 }
703
704 bool cdump_unbundle_unsigned(const void *ctx, void *ptr, const char *str)
705 {
706         *(unsigned *)ptr = strtoul(str, NULL, 10);
707         return true;
708 }
709
710 bool cdump_unbundle_time_t(const void *ctx, void *ptr, const char *str)
711 {
712         *(time_t *)ptr = strtoul(str, NULL, 10);
713         return true;
714 }
715
716 bool cdump_unbundle_double(const void *ctx, void *ptr, const char *str)
717 {
718         *(double *)ptr = atof(str);
719         return true;
720 }
721
722 bool cdump_unbundle_float(const void *ctx, void *ptr, const char *str)
723 {
724         *(float *)ptr = atof(str);
725         return true;
726 }
727
728 bool cdump_bundle_char(struct cdump_string *p, const void *ptr, unsigned indent)
729 {
730         return addshort(p, "%u", *(unsigned char *)ptr);
731 }
732
733 bool cdump_bundle_int(struct cdump_string *p, const void *ptr, unsigned indent)
734 {
735         return addshort(p, "%d", *(int *)ptr);
736 }
737
738 bool cdump_bundle_unsigned(struct cdump_string *p, const void *ptr, unsigned indent)
739 {
740         return addshort(p, "%u", *(unsigned *)ptr);
741 }
742
743 bool cdump_bundle_time_t(struct cdump_string *p, const void *ptr, unsigned indent)
744 {
745         return addshort(p, "%lu", (long int)*(time_t *)ptr);
746 }
747
748 bool cdump_bundle_double(struct cdump_string *p, const void *ptr, unsigned indent)
749 {
750         return addshort(p, "%lg", *(double *)ptr);
751 }
752
753 bool cdump_bundle_float(struct cdump_string *p, const void *ptr, unsigned indent)
754 {
755         return addshort(p, "%g", *(float *)ptr);
756 }