1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/lib/genparser.c
Andrew Tridgell ef2e26c91b first public release of samba4 code
(This used to be commit b0510b5428)
2003-08-13 01:53:07 +00:00

787 lines
17 KiB
C

/*
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));
}