diff options
Diffstat (limited to 'source4/lib/genparser.c')
-rw-r--r-- | source4/lib/genparser.c | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/source4/lib/genparser.c b/source4/lib/genparser.c new file mode 100644 index 0000000000..39c455def4 --- /dev/null +++ b/source4/lib/genparser.c @@ -0,0 +1,786 @@ +/* + Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + automatic marshalling/unmarshalling system for C structures +*/ + +#include "includes.h" + +/* see if a range of memory is all zero. Used to prevent dumping of zero elements */ +static int all_zero(const char *ptr, unsigned size) +{ + int i; + if (!ptr) return 1; + for (i=0;i<size;i++) { + if (ptr[i]) return 0; + } + return 1; +} + +/* encode a buffer of bytes into a escaped string */ +static char *encode_bytes(TALLOC_CTX *mem_ctx, const char *ptr, unsigned len) +{ + const char *hexdig = "0123456789abcdef"; + char *ret, *p; + unsigned i; + ret = talloc(mem_ctx, len*3 + 1); /* worst case size */ + if (!ret) return NULL; + for (p=ret,i=0;i<len;i++) { + if (isalnum(ptr[i]) || isspace(ptr[i]) || + (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) { + *p++ = ptr[i]; + } else { + unsigned char c = *(const unsigned char *)(ptr+i); + if (c == 0 && all_zero(ptr+i, len-i)) break; + p[0] = '\\'; + p[1] = hexdig[c>>4]; + p[2] = hexdig[c&0xF]; + p += 3; + } + } + + *p = 0; + + return ret; +} + +/* decode an escaped string from encode_bytes() into a buffer */ +static char *decode_bytes(TALLOC_CTX *mem_ctx, const char *s, unsigned *len) +{ + char *ret, *p; + unsigned i; + int slen = strlen(s) + 1; + + ret = talloc(mem_ctx, slen); /* worst case length */ + if (!ret) + return NULL; + memset(ret, 0, slen); + + if (*s == '{') s++; + + for (p=ret,i=0;s[i];i++) { + if (s[i] == '}') { + break; + } else if (s[i] == '\\') { + unsigned v; + if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) { + return NULL; + } + *(unsigned char *)p = v; + p++; + i += 2; + } else { + *p++ = s[i]; + } + } + *p = 0; + + (*len) = (unsigned)(p - ret); + + return ret; +} + +/* the add*() functions deal with adding things to a struct + parse_string */ + +/* allocate more space if needed */ +static int addgen_alloc(TALLOC_CTX *mem_ctx, struct parse_string *p, int n) +{ + if (p->length + n <= p->allocated) return 0; + p->allocated = p->length + n + 200; + p->s = talloc_realloc(mem_ctx, p->s, p->allocated); + if (!p->s) { + errno = ENOMEM; + return -1; + } + return 0; +} + +/* add a character to the buffer */ +static int addchar(TALLOC_CTX *mem_ctx, struct parse_string *p, char c) +{ + if (addgen_alloc(mem_ctx, p, 2) != 0) { + return -1; + } + p->s[p->length++] = c; + p->s[p->length] = 0; + return 0; +} + +/* add a string to the buffer */ +int addstr(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s) +{ + int len = strlen(s); + if (addgen_alloc(mem_ctx, p, len+1) != 0) { + return -1; + } + memcpy(p->s + p->length, s, len+1); + p->length += len; + return 0; +} + +/* add a string to the buffer with a tab prefix */ +static int addtabbed(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s, unsigned indent) +{ + int len = strlen(s); + if (addgen_alloc(mem_ctx, p, indent+len+1) != 0) { + return -1; + } + while (indent--) { + p->s[p->length++] = '\t'; + } + memcpy(p->s + p->length, s, len+1); + p->length += len; + return 0; +} + +/* note! this can only be used for results up to 60 chars wide! */ +int addshort(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...) +{ + char buf[60]; + int n; + va_list ap; + va_start(ap, fmt); + n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (addgen_alloc(mem_ctx, p, n + 1) != 0) { + return -1; + } + if (n != 0) { + memcpy(p->s + p->length, buf, n); + } + p->length += n; + p->s[p->length] = 0; + return 0; +} + +/* + this is here to make it easier for people to write dump functions + for their own types + */ +int gen_addgen(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...) +{ + char *buf = NULL; + int n; + va_list ap; + va_start(ap, fmt); + n = vasprintf(&buf, fmt, ap); + va_end(ap); + if (addgen_alloc(mem_ctx, p, n + 1) != 0) { + if (buf) free(buf); + return -1; + } + if (n != 0) { + memcpy(p->s + p->length, buf, n); + } + p->length += n; + p->s[p->length] = 0; + if (buf) free(buf); + return 0; +} + +/* dump a enumerated type */ +int gen_dump_enum(TALLOC_CTX *mem_ctx, + const struct enum_struct *einfo, + struct parse_string *p, + const char *ptr, + unsigned indent) +{ + unsigned v = *(const unsigned *)ptr; + int i; + for (i=0;einfo[i].name;i++) { + if (v == einfo[i].value) { + addstr(mem_ctx, p, einfo[i].name); + return 0; + } + } + /* hmm, maybe we should just fail? */ + return gen_dump_unsigned(mem_ctx, p, ptr, indent); +} + +/* dump a single non-array element, hanlding struct and enum */ +static int gen_dump_one(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *ptr, + unsigned indent) +{ + if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) { + char *s = encode_bytes(mem_ctx, ptr, strlen(ptr)); + if (addchar(mem_ctx, p,'{') || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}")) { + return -1; + } + return 0; + } + + return pinfo->dump_fn(mem_ctx, p, ptr, indent); +} + +/* handle dumping of an array of arbitrary type */ +static int gen_dump_array(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *ptr, + int array_len, + int indent) +{ + int i, count=0; + + /* special handling of fixed length strings */ + if (array_len != 0 && + pinfo->ptr_count == 0 && + pinfo->dump_fn == gen_dump_char) { + char *s = encode_bytes(mem_ctx, ptr, array_len); + if (!s) return -1; + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addstr(mem_ctx, p, " = {") || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}\n")) { + return -1; + } + free(s); + return 0; + } + + for (i=0;i<array_len;i++) { + const char *p2 = ptr; + unsigned size = pinfo->size; + + /* generic pointer dereference */ + if (pinfo->ptr_count) { + p2 = *(const char **)ptr; + size = sizeof(void *); + } + + if ((count || pinfo->ptr_count) && + !(pinfo->flags & FLAG_ALWAYS) && + all_zero(ptr, size)) { + ptr += size; + continue; + } + if (count == 0) { + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addshort(mem_ctx, p, " = %u:", i)) { + return -1; + } + } else { + if (addshort(mem_ctx, p, ", %u:", i) != 0) { + return -1; + } + } + if (gen_dump_one(mem_ctx, p, pinfo, p2, indent) != 0) { + return -1; + } + ptr += size; + count++; + } + if (count) { + return addstr(mem_ctx, p, "\n"); + } + return 0; +} + +/* find a variable by name in a loaded structure and return its value + as an integer. Used to support dynamic arrays */ +static int find_var(const struct parse_struct *pinfo, + const char *data, + const char *var) +{ + int i; + const char *ptr; + + /* this allows for constant lengths */ + if (isdigit(*var)) { + return atoi(var); + } + + for (i=0;pinfo[i].name;i++) { + if (strcmp(pinfo[i].name, var) == 0) break; + } + if (!pinfo[i].name) return -1; + + ptr = data + pinfo[i].offset; + + switch (pinfo[i].size) { + case sizeof(int): + return *(const int *)ptr; + case sizeof(char): + return *(const char *)ptr; + } + + return -1; +} + + +int gen_dump_struct(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + struct parse_string *p, + const char *ptr, + unsigned indent) +{ + char *s = gen_dump(mem_ctx, pinfo, ptr, indent+1); + if (!s) return -1; + if (addstr(mem_ctx, p, "{\n") || + addstr(mem_ctx, p, s) || + addtabbed(mem_ctx, p, "}", indent)) { + return -1; + } + return 0; +} + +static int gen_dump_string(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *data, + unsigned indent) +{ + const char *ptr = *(const char **)data; + char *s = encode_bytes(mem_ctx, ptr, strlen(ptr)); + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addstr(mem_ctx, p, " = ") || + addchar(mem_ctx, p, '{') || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}\n")) { + return -1; + } + return 0; +} + +/* + find the length of a nullterm array +*/ +static int len_nullterm(const char *ptr, int size, int array_len) +{ + int len; + + if (size == 1) { + len = strnlen(ptr, array_len); + } else { + for (len=0; len < array_len; len++) { + if (all_zero(ptr+len*size, size)) break; + } + } + + if (len == 0) len = 1; + + return len; +} + + +/* the generic dump routine. Scans the parse information for this structure + and processes it recursively */ +char *gen_dump(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + const char *data, + unsigned indent) +{ + struct parse_string p; + int i; + + p.length = 0; + p.allocated = 0; + p.s = NULL; + + if (addstr(mem_ctx, &p, "") != 0) { + return NULL; + } + + for (i=0;pinfo[i].name;i++) { + const char *ptr = data + pinfo[i].offset; + unsigned size = pinfo[i].size; + + if (pinfo[i].ptr_count) { + size = sizeof(void *); + } + + /* special handling for array types */ + if (pinfo[i].array_len) { + unsigned len = pinfo[i].array_len; + if (pinfo[i].flags & FLAG_NULLTERM) { + len = len_nullterm(ptr, size, len); + } + if (gen_dump_array(mem_ctx, &p, &pinfo[i], ptr, + len, indent)) { + goto failed; + } + continue; + } + + /* and dynamically sized arrays */ + if (pinfo[i].dynamic_len) { + int len = find_var(pinfo, data, pinfo[i].dynamic_len); + struct parse_struct p2 = pinfo[i]; + if (len < 0) { + goto failed; + } + if (len > 0) { + if (pinfo[i].flags & FLAG_NULLTERM) { + len = len_nullterm(*(const char **)ptr, + pinfo[i].size, len); + } + p2.ptr_count--; + p2.dynamic_len = NULL; + if (gen_dump_array(mem_ctx, &p, &p2, + *(const char **)ptr, + len, indent) != 0) { + goto failed; + } + } + continue; + } + + /* don't dump zero elements */ + if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue; + + /* assume char* is a null terminated string */ + if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 && + pinfo[i].dump_fn == gen_dump_char) { + if (gen_dump_string(mem_ctx, &p, &pinfo[i], ptr, indent) != 0) { + goto failed; + } + continue; + } + + /* generic pointer dereference */ + if (pinfo[i].ptr_count) { + ptr = *(const char **)ptr; + } + + if (addtabbed(mem_ctx, &p, pinfo[i].name, indent) || + addstr(mem_ctx, &p, " = ") || + gen_dump_one(mem_ctx, &p, &pinfo[i], ptr, indent) || + addstr(mem_ctx, &p, "\n")) { + goto failed; + } + } + return p.s; + +failed: + return NULL; +} + +/* search for a character in a string, skipping over sections within + matching braces */ +static char *match_braces(char *s, char c) +{ + int depth = 0; + while (*s) { + switch (*s) { + case '}': + depth--; + break; + case '{': + depth++; + break; + } + if (depth == 0 && *s == c) { + return s; + } + s++; + } + return s; +} + +/* parse routine for enumerated types */ +int gen_parse_enum(TALLOC_CTX *mem_ctx, + const struct enum_struct *einfo, + char *ptr, + const char *str) +{ + unsigned v; + int i; + + if (isdigit(*str)) { + if (sscanf(str, "%u", &v) != 1) { + errno = EINVAL; + return -1; + } + *(unsigned *)ptr = v; + return 0; + } + + for (i=0;einfo[i].name;i++) { + if (strcmp(einfo[i].name, str) == 0) { + *(unsigned *)ptr = einfo[i].value; + return 0; + } + } + + /* unknown enum value?? */ + return -1; +} + + +/* parse all base types */ +static int gen_parse_base(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + char *ptr, + const char *str) +{ + if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) { + unsigned len; + char *s = decode_bytes(mem_ctx, str, &len); + if (!s) return -1; + *(char **)ptr = s; + return 0; + } + + if (pinfo->ptr_count) { + unsigned size = pinfo->ptr_count>1?sizeof(void *):pinfo->size; + struct parse_struct p2 = *pinfo; + *(void **)ptr = talloc(mem_ctx, size); + if (! *(void **)ptr) { + return -1; + } + memset(*(void **)ptr, 0, size); + ptr = *(char **)ptr; + p2.ptr_count--; + return gen_parse_base(mem_ctx, &p2, ptr, str); + } + + return pinfo->parse_fn(mem_ctx, ptr, str); +} + +/* parse a generic array */ +static int gen_parse_array(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + char *ptr, + const char *str, + int array_len) +{ + char *p, *p2; + unsigned size = pinfo->size; + + /* special handling of fixed length strings */ + if (array_len != 0 && + pinfo->ptr_count == 0 && + pinfo->dump_fn == gen_dump_char) { + unsigned len = 0; + char *s = decode_bytes(mem_ctx, str, &len); + if (!s || (len > array_len)) return -1; + memset(ptr, 0, array_len); + memcpy(ptr, s, len); + return 0; + } + + if (pinfo->ptr_count) { + size = sizeof(void *); + } + + while (*str) { + unsigned idx; + int done; + + idx = atoi(str); + p = strchr(str,':'); + if (!p) break; + p++; + p2 = match_braces(p, ','); + done = (*p2 != ','); + *p2 = 0; + + if (*p == '{') { + p++; + p[strlen(p)-1] = 0; + } + + if (gen_parse_base(mem_ctx, pinfo, ptr + idx*size, p) != 0) { + return -1; + } + + if (done) break; + str = p2+1; + } + + return 0; +} + +/* parse one element, hanlding dynamic and static arrays */ +static int gen_parse_one(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + const char *name, + char *data, + const char *str) +{ + int i; + for (i=0;pinfo[i].name;i++) { + if (strcmp(pinfo[i].name, name) == 0) { + break; + } + } + if (pinfo[i].name == NULL) { + return 0; + } + + if (pinfo[i].array_len) { + return gen_parse_array(mem_ctx, &pinfo[i], + data+pinfo[i].offset, + str, pinfo[i].array_len); + } + + if (pinfo[i].dynamic_len) { + int len = find_var(pinfo, data, pinfo[i].dynamic_len); + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len > 0) { + struct parse_struct p2 = pinfo[i]; + char *ptr; + unsigned size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size; + ptr = talloc(mem_ctx, len*size); + if (!ptr) { + errno = ENOMEM; + return -1; + } + memset(ptr, 0, len*size); + *((char **)(data + pinfo[i].offset)) = ptr; + p2.ptr_count--; + p2.dynamic_len = NULL; + return gen_parse_array(mem_ctx, &p2, ptr, str, len); + } + return 0; + } + + return gen_parse_base(mem_ctx, &pinfo[i], data + pinfo[i].offset, str); +} + +int gen_parse_struct(TALLOC_CTX * mem_ctx, const struct parse_struct *pinfo, char *ptr, const char *str) +{ + return gen_parse(mem_ctx, pinfo, ptr, str); +} + +/* the main parse routine */ +int gen_parse(TALLOC_CTX *mem_ctx, const struct parse_struct *pinfo, char *data, const char *s) +{ + char *str, *s0; + + s0 = strdup(s); + str = s0; + + while (*str) { + char *p; + char *name; + char *value; + + /* skip leading whitespace */ + while (isspace(*str)) str++; + + p = strchr(str, '='); + if (!p) break; + value = p+1; + while (p > str && isspace(*(p-1))) { + p--; + } + + *p = 0; + name = str; + + while (isspace(*value)) value++; + + if (*value == '{') { + str = match_braces(value, '}'); + value++; + } else { + str = match_braces(value, '\n'); + } + + *str++ = 0; + + if (gen_parse_one(mem_ctx, pinfo, name, data, value) != 0) { + free(s0); + return -1; + } + } + + free(s0); + return 0; +} + + + +/* for convenience supply some standard dumpers and parsers here */ + +int gen_parse_char(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(unsigned char *)ptr = atoi(str); + return 0; +} + +int gen_parse_int(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(int *)ptr = atoi(str); + return 0; +} + +int gen_parse_unsigned(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(unsigned *)ptr = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_time_t(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(time_t *)ptr = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_double(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(double *)ptr = atof(str); + return 0; +} + +int gen_parse_float(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(float *)ptr = atof(str); + return 0; +} + +int gen_dump_char(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(unsigned char *)(ptr)); +} + +int gen_dump_int(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%d", *(int *)(ptr)); +} + +int gen_dump_unsigned(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(unsigned *)(ptr)); +} + +int gen_dump_time_t(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(time_t *)(ptr)); +} + +int gen_dump_double(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%lg", *(double *)(ptr)); +} + +int gen_dump_float(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%g", *(float *)(ptr)); +} |