2 Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 automatic marshalling/unmarshalling system for C structures
33 #include "genparser.h"
38 /* see if a range of memory is all zero. Used to prevent dumping of zero elements */
39 static int all_zero(const char *ptr, unsigned size)
43 for (i=0;i<size;i++) {
49 /* encode a buffer of bytes into a escaped string */
50 static char *encode_bytes(const char *ptr, unsigned len)
52 const char *hexdig = "0123456789abcdef";
55 ret = malloc(len*3 + 1); /* worst case size */
56 if (!ret) return NULL;
57 for (p=ret,i=0;i<len;i++) {
58 if (isalnum(ptr[i]) || isspace(ptr[i]) ||
59 (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
62 unsigned char c = *(unsigned char *)(ptr+i);
63 if (c == 0 && all_zero(ptr+i, len-i)) break;
76 /* decode an escaped string from encode_bytes() into a buffer */
77 static char *decode_bytes(const char *s, unsigned *len)
81 ret = calloc(1, strlen(s)+1); /* worst case length */
85 for (p=ret,i=0;s[i];i++) {
88 } else if (s[i] == '\\') {
90 if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
94 *(unsigned char *)p = v;
103 (*len) = (unsigned)(p - ret);
108 /* the add*() functions deal with adding things to a struct
111 /* allocate more space if needed */
112 static int addgen_alloc(struct parse_string *p, int n)
114 if (p->length + n <= p->allocated) return 0;
115 p->allocated = p->length + n + 200;
116 p->s = realloc(p->s, p->allocated);
124 /* add a character to the buffer */
125 static int addchar(struct parse_string *p, char c)
127 if (addgen_alloc(p, 2) != 0) {
130 p->s[p->length++] = c;
135 /* add a string to the buffer */
136 static int addstr(struct parse_string *p, const char *s)
139 if (addgen_alloc(p, len+1) != 0) {
142 memcpy(p->s + p->length, s, len+1);
147 /* add a string to the buffer with a tab prefix */
148 static int addtabbed(struct parse_string *p, const char *s, unsigned indent)
151 if (addgen_alloc(p, indent+len+1) != 0) {
155 p->s[p->length++] = '\t';
157 memcpy(p->s + p->length, s, len+1);
162 /* note! this can only be used for results up to 60 chars wide! */
163 static int addshort(struct parse_string *p, const char *fmt, ...)
169 n = vsnprintf(buf, sizeof(buf), fmt, ap);
171 if (addgen_alloc(p, n + 1) != 0) {
175 memcpy(p->s + p->length, buf, n);
183 this is here to make it easier for people to write dump functions
186 int gen_addgen(struct parse_string *p, const char *fmt, ...)
192 n = vasprintf(&buf, fmt, ap);
194 if (addgen_alloc(p, n + 1) != 0) {
199 memcpy(p->s + p->length, buf, n);
207 /* dump a enumerated type */
208 int gen_dump_enum(const struct enum_struct *einfo,
209 struct parse_string *p,
213 unsigned v = *(unsigned *)ptr;
215 for (i=0;einfo[i].name;i++) {
216 if (v == einfo[i].value) {
217 addstr(p, einfo[i].name);
221 /* hmm, maybe we should just fail? */
222 return gen_dump_unsigned(p, ptr, indent);
225 /* dump a single non-array element, hanlding struct and enum */
226 static int gen_dump_one(struct parse_string *p,
227 const struct parse_struct *pinfo,
231 if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) {
232 char *s = encode_bytes(ptr, strlen(ptr));
233 if (addchar(p,'{') ||
242 return pinfo->dump_fn(p, ptr, indent);
245 /* handle dumping of an array of arbitrary type */
246 static int gen_dump_array(struct parse_string *p,
247 const struct parse_struct *pinfo,
254 /* special handling of fixed length strings */
255 if (array_len != 0 &&
256 pinfo->ptr_count == 0 &&
257 pinfo->dump_fn == gen_dump_char) {
258 char *s = encode_bytes(ptr, array_len);
260 if (addtabbed(p, pinfo->name, indent) ||
271 for (i=0;i<array_len;i++) {
272 const char *p2 = ptr;
273 unsigned size = pinfo->size;
275 /* generic pointer dereference */
276 if (pinfo->ptr_count) {
277 p2 = *(const char **)ptr;
278 size = sizeof(void *);
281 if ((count || pinfo->ptr_count) &&
282 !(pinfo->flags & FLAG_ALWAYS) &&
283 all_zero(ptr, size)) {
288 if (addtabbed(p, pinfo->name, indent) ||
289 addshort(p, " = %u:", i)) {
293 if (addshort(p, ", %u:", i) != 0) {
297 if (gen_dump_one(p, pinfo, p2, indent) != 0) {
304 return addstr(p, "\n");
309 /* find a variable by name in a loaded structure and return its value
310 as an integer. Used to support dynamic arrays */
311 static int find_var(const struct parse_struct *pinfo,
318 /* this allows for constant lengths */
323 for (i=0;pinfo[i].name;i++) {
324 if (strcmp(pinfo[i].name, var) == 0) break;
326 if (!pinfo[i].name) return -1;
328 ptr = data + pinfo[i].offset;
330 switch (pinfo[i].size) {
341 int gen_dump_struct(const struct parse_struct *pinfo,
342 struct parse_string *p,
346 char *s = gen_dump(pinfo, ptr, indent+1);
348 if (addstr(p, "{\n") ||
350 addtabbed(p,"}", indent)) {
358 static int gen_dump_string(struct parse_string *p,
359 const struct parse_struct *pinfo,
363 const char *ptr = *(char **)data;
364 char *s = encode_bytes(ptr, strlen(ptr));
365 if (addtabbed(p, pinfo->name, indent) ||
377 find the length of a nullterm array
379 static int len_nullterm(const char *ptr, int size, int array_len)
384 len = strnlen(ptr, array_len);
386 for (len=0;len<array_len;len++) {
387 if (all_zero(ptr+len*size, size)) break;
391 if (len == 0) len = 1;
397 /* the generic dump routine. Scans the parse information for this structure
398 and processes it recursively */
399 char *gen_dump(const struct parse_struct *pinfo,
403 struct parse_string p;
410 if (addstr(&p, "") != 0) {
414 for (i=0;pinfo[i].name;i++) {
415 const char *ptr = data + pinfo[i].offset;
416 unsigned size = pinfo[i].size;
418 if (pinfo[i].ptr_count) {
419 size = sizeof(void *);
422 /* special handling for array types */
423 if (pinfo[i].array_len) {
424 unsigned len = pinfo[i].array_len;
425 if (pinfo[i].flags & FLAG_NULLTERM) {
426 len = len_nullterm(ptr, size, len);
428 if (gen_dump_array(&p, &pinfo[i], ptr,
435 /* and dynamically sized arrays */
436 if (pinfo[i].dynamic_len) {
437 int len = find_var(pinfo, data, pinfo[i].dynamic_len);
438 struct parse_struct p2 = pinfo[i];
443 if (pinfo[i].flags & FLAG_NULLTERM) {
444 len = len_nullterm(*(char **)ptr,
448 p2.dynamic_len = NULL;
449 if (gen_dump_array(&p, &p2, *(char **)ptr,
457 /* don't dump zero elements */
458 if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue;
460 /* assume char* is a null terminated string */
461 if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 &&
462 pinfo[i].dump_fn == gen_dump_char) {
463 if (gen_dump_string(&p, &pinfo[i], ptr, indent) != 0) {
469 /* generic pointer dereference */
470 if (pinfo[i].ptr_count) {
471 ptr = *(const char **)ptr;
474 if (addtabbed(&p, pinfo[i].name, indent) ||
476 gen_dump_one(&p, &pinfo[i], ptr, indent) ||
488 /* search for a character in a string, skipping over sections within
490 static char *match_braces(char *s, char c)
502 if (depth == 0 && *s == c) {
510 /* parse routine for enumerated types */
511 int gen_parse_enum(const struct enum_struct *einfo,
519 if (sscanf(str, "%u", &v) != 1) {
523 *(unsigned *)ptr = v;
527 for (i=0;einfo[i].name;i++) {
528 if (strcmp(einfo[i].name, str) == 0) {
529 *(unsigned *)ptr = einfo[i].value;
534 /* unknown enum value?? */
539 /* parse all base types */
540 static int gen_parse_base(const struct parse_struct *pinfo,
544 if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) {
546 char *s = decode_bytes(str, &len);
552 if (pinfo->ptr_count) {
553 struct parse_struct p2 = *pinfo;
554 *(void **)ptr = calloc(1, pinfo->ptr_count>1?sizeof(void *):pinfo->size);
555 if (! *(void **)ptr) {
560 return gen_parse_base(&p2, ptr, str);
563 return pinfo->parse_fn(ptr, str);
566 /* parse a generic array */
567 static int gen_parse_array(const struct parse_struct *pinfo,
573 unsigned size = pinfo->size;
575 /* special handling of fixed length strings */
576 if (array_len != 0 &&
577 pinfo->ptr_count == 0 &&
578 pinfo->dump_fn == gen_dump_char) {
580 char *s = decode_bytes(str, &len);
582 memset(ptr, 0, array_len);
588 if (pinfo->ptr_count) {
589 size = sizeof(void *);
600 p2 = match_braces(p, ',');
609 if (gen_parse_base(pinfo, ptr + idx*size, p) != 0) {
620 /* parse one element, hanlding dynamic and static arrays */
621 static int gen_parse_one(const struct parse_struct *pinfo,
627 for (i=0;pinfo[i].name;i++) {
628 if (strcmp(pinfo[i].name, name) == 0) {
632 if (pinfo[i].name == NULL) {
636 if (pinfo[i].array_len) {
637 return gen_parse_array(&pinfo[i], data+pinfo[i].offset,
638 str, pinfo[i].array_len);
641 if (pinfo[i].dynamic_len) {
642 int len = find_var(pinfo, data, pinfo[i].dynamic_len);
649 struct parse_struct p2 = pinfo[i];
651 size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size;
652 ptr = calloc(len, size);
657 *((char **)(data + pinfo[i].offset)) = ptr;
659 p2.dynamic_len = NULL;
660 return gen_parse_array(&p2, ptr, str, len);
665 return gen_parse_base(&pinfo[i], data + pinfo[i].offset, str);
668 int gen_parse_struct(const struct parse_struct *pinfo, char *ptr, const char *str)
670 return gen_parse(pinfo, ptr, str);
673 /* the main parse routine */
674 int gen_parse(const struct parse_struct *pinfo, char *data, const char *s)
686 /* skip leading whitespace */
687 while (isspace(*str)) str++;
689 p = strchr(str, '=');
692 while (p > str && isspace(*(p-1))) {
699 while (isspace(*value)) value++;
702 str = match_braces(value, '}');
705 str = match_braces(value, '\n');
710 if (gen_parse_one(pinfo, name, data, value) != 0) {
722 /* for convenience supply some standard dumpers and parsers here */
724 int gen_parse_char(char *ptr, const char *str)
726 *(unsigned char *)ptr = atoi(str);
730 int gen_parse_int(char *ptr, const char *str)
732 *(int *)ptr = atoi(str);
736 int gen_parse_unsigned(char *ptr, const char *str)
738 *(unsigned *)ptr = strtoul(str, NULL, 10);
742 int gen_parse_time_t(char *ptr, const char *str)
744 *(time_t *)ptr = strtoul(str, NULL, 10);
748 int gen_parse_double(char *ptr, const char *str)
750 *(double *)ptr = atof(str);
754 int gen_parse_float(char *ptr, const char *str)
756 *(float *)ptr = atof(str);
760 int gen_dump_char(struct parse_string *p, const char *ptr, unsigned indent)
762 return addshort(p, "%u", *(unsigned char *)(ptr));
765 int gen_dump_int(struct parse_string *p, const char *ptr, unsigned indent)
767 return addshort(p, "%d", *(int *)(ptr));
770 int gen_dump_unsigned(struct parse_string *p, const char *ptr, unsigned indent)
772 return addshort(p, "%u", *(unsigned *)(ptr));
775 int gen_dump_time_t(struct parse_string *p, const char *ptr, unsigned indent)
777 return addshort(p, "%u", *(time_t *)(ptr));
780 int gen_dump_double(struct parse_string *p, const char *ptr, unsigned indent)
782 return addshort(p, "%lg", *(double *)(ptr));
785 int gen_dump_float(struct parse_string *p, const char *ptr, unsigned indent)
787 return addshort(p, "%g", *(float *)(ptr));