summaryrefslogtreecommitdiff
path: root/source4/lib/genparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/genparser.c')
-rw-r--r--source4/lib/genparser.c786
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));
+}