diff options
Diffstat (limited to 'tools/npl')
-rw-r--r-- | tools/npl/ast.h | 43 | ||||
-rw-r--r-- | tools/npl/npl.c | 349 | ||||
-rw-r--r-- | tools/npl/parser.l | 97 | ||||
-rw-r--r-- | tools/npl/xmem.h | 2 |
4 files changed, 391 insertions, 100 deletions
diff --git a/tools/npl/ast.h b/tools/npl/ast.h index 8f3b082532..6bc86e1fed 100644 --- a/tools/npl/ast.h +++ b/tools/npl/ast.h @@ -53,6 +53,7 @@ typedef enum { EXPRESSION_STR, EXPRESSION_INDEX, + EXPRESSION_MULTI_INDEX, EXPRESSION_FIELD, EXPRESSION_CALL, @@ -62,6 +63,12 @@ typedef enum { } npl_expression_type_t; +typedef struct _npl_expression_list { + struct _npl_expression_list *next; + + struct _npl_expression *expr; +} npl_expression_list_t; + typedef struct _npl_expression { union { struct { @@ -91,6 +98,13 @@ union { } arr; struct { + npl_expression_type_t type; /* EXPRESSION_MULTI_INDEX */ + + struct _npl_expression *base; + npl_expression_list_t *indexes; + } aarr; + + struct { npl_expression_type_t type; /* EXPRESSION_FIELD */ struct _npl_expression *base; @@ -118,13 +132,7 @@ union { npl_expression_type_t type; /* EXPRESSION_CALL */ struct _npl_expression *fn; - - struct _npl_expression_list { - struct _npl_expression_list *next; - - struct _npl_expression *expr; - - } *args; + npl_expression_list_t *args; } call; @@ -222,7 +230,7 @@ typedef struct _npl_statement { } w; struct { - npl_statement_type_t type; /* STATEMTN_TABLE */ + npl_statement_type_t type; /* STATEMENT_TABLE */ npl_table_t data; } t; @@ -251,7 +259,8 @@ typedef struct _npl_statement { npl_expression_t *format; struct _npl_statements *sts; - struct hfinfo *hfi; /* set by code gen */ + /* after 1st pass of code generator */ + struct hfinfo *hfi; } f; }; @@ -292,12 +301,16 @@ typedef struct { npl_expression_t *display_format; npl_expression_t *size; + /* after 1st pass of code generator */ + const char *hf_type; + } npl_type_t; typedef enum { DECL_INVALID = 0, DECL_ATTR, + DECL_INCLUDE, DECL_STRUCT, DECL_TABLE, DECL_CONST, @@ -306,8 +319,8 @@ typedef enum { } npl_decl_type_t; -typedef struct _npl_attr { - npl_expression_t expr; +typedef struct { + npl_expression_list_t *expr_list; } npl_attr_t; typedef struct { @@ -323,6 +336,12 @@ typedef struct { } a; struct { + npl_decl_type_t type; /* DECL_INCLUDE */ + + char *file; + } i; + + struct { npl_decl_type_t type; /* DECL_STRUCT */ npl_struct_t data; @@ -362,6 +381,4 @@ typedef struct { } *decls; - int parse_ok; - } npl_code_t; diff --git a/tools/npl/npl.c b/tools/npl/npl.c index 1ac324fe03..56b80c94d2 100644 --- a/tools/npl/npl.c +++ b/tools/npl/npl.c @@ -25,7 +25,7 @@ #include "ast.h" #include "xmem.h" -void npl_parse_file(npl_code_t *code, FILE *f, const char *filename); /* parser.l */ +int npl_parse_file(npl_code_t *code, FILE *f, const char *filename); /* parser.l */ static void gen_expr(FILE *f, npl_expression_t *e); static void gen_statements(FILE *f, struct _npl_statements *sts); @@ -39,6 +39,16 @@ struct hfinfo { struct hfinfo *hfs; +static void _fail(const char *file, int line, const char *msg) { + fprintf(stderr, "!!! %s:%d fail(%s)\n", file, line, msg); + abort(); +} + +#define fail(msg) _fail(__FILE__, __LINE__, msg) + +#define xassert(expr) \ + do { if (!(expr)) fail("Assertion failed: " #expr); } while(0); + static struct hfinfo * add_hfi(npl_statement_t *st) @@ -57,8 +67,9 @@ add_hfi(npl_statement_t *st) } static const char * -hfi_name(const struct hfinfo *hfi) +hfi_var(const struct hfinfo *hfi) { + /* XXX nicer name */ static char hf_name[64]; snprintf(hf_name, sizeof(hf_name), "hf_field_%u", hfi->id); @@ -66,6 +77,127 @@ hfi_name(const struct hfinfo *hfi) return hf_name; } +static const char * +hfi_name(const struct hfinfo *hfi) +{ + return hfi->st->f.id; +} + +static const char * +hfi_filter(const struct hfinfo *hfi) +{ + /* TODO stub */ + return ""; +} + +static const char * +hfi_type(const struct hfinfo *hfi) +{ + /* TODO stub */ + return "FT_BYTES"; +} + +static const char * +hfi_display(const struct hfinfo *hfi) +{ + /* TODO stub */ + return "BASE_NONE"; +} + +static unsigned int +hfi_mask(const struct hfinfo *hfi) +{ + /* TODO stub */ + return 0; +} + +static int +expr_to_int(const npl_expression_t *npl, int *val) +{ + if (npl->type == EXPRESSION_INT) { + *val = npl->num.digit; + return 1; + } + if (npl->type == EXPRESSION_UNARY) { + if (!expr_to_int(npl->u.operand, val)) + return 0; + + switch (npl->u.operator) { + case OP1_MINUS: + *val = -(*val); + return 1; + case OP1_NEG: + *val = ~(*val); + return 1; + case OP1_NOT: + *val = !(*val); + return 1; + } + } + return 0; +} + +static int +expr_to_str(const npl_expression_t *npl, const char **val) +{ + if (npl->type == EXPRESSION_STR) { + *val = npl->str.str; + return 1; + } + return 0; +} + +static const char * +type_to_ft(const npl_type_t *t, int size) +{ + switch (t->type) { + case FIELD_DECIMAL: + if (size == 4) + return "FT_FLOAT"; + if (size == 8) + return "FT_DOUBLE"; + + fprintf(stderr, "!!! decimal, size: %d\n", size); + return NULL; + + case FIELD_NUMBER: + if (size == 1) + return "FT_INT8"; + if (size == 2) + return "FT_INT16"; + if (size == 3) + return "FT_INT24"; + if (size == 4) + return "FT_INT32"; + if (size <= 8) + return "FT_INT64"; + + fprintf(stderr, "!!! number, size: %d\n", size); + return NULL; + + case FIELD_UNSIGNED_NUMBER: + if (size == 1) + return "FT_UINT8"; + if (size == 2) + return "FT_UINT16"; + if (size == 3) + return "FT_UINT24"; + if (size == 4) + return "FT_UINT32"; + if (size <= 8) + return "FT_UINT64"; + + fprintf(stderr, "!!! number, size: %d\n", size); + return NULL; + + case FIELD_TIME: + /* XXX, FT_ABSOLUTE_TIME or FT_RELATIVE_TIME? */ + fprintf(stderr, "!!! time, size: %d\n", size); + return "FT_ABSOLUTE_TIME"; + } + fprintf(stderr, "!!! not handled, type: %d, size: %d\n", t->type, size); + return NULL; +} #define gen_fprintf(f, args...) \ do { \ @@ -160,7 +292,7 @@ gen_expr(FILE *f, npl_expression_t *e) case EXPRESSION_CALL: { - struct _npl_expression_list *arg; + npl_expression_list_t *arg; char *ind = ""; gen_expr(f, e->call.fn); @@ -173,12 +305,101 @@ gen_expr(FILE *f, npl_expression_t *e) gen_fprintf(f, ")"); return; } + + case EXPRESSION_FIELD: + gen_expr(f, e->fld.base); + gen_fprintf(f, ".%s ", e->fld.field); + return; } fprintf(stderr, "XXXX expr->type: %d\n", e->type); } +static int +gen_table_struct(FILE *f, npl_table_t *t) +{ + enum { CANT, VALUE_STRING, STRING_STRING } type; + struct npl_table_case *c; + + int all_int = 1; + int all_str = 1; + + if (t->params.count || !t->switch_expr || t->default_expr) + return 0; + + for (c = t->cases; c; c = c->next) { + const char *str; + int val; + + if (!c->return_expr || !expr_to_str(c->return_expr, &str)) + return 0; + + if (all_int && !expr_to_int(&c->e, &val)) + all_int = 0; + if (all_str && !expr_to_str(&c->e, &str)) + all_str = 0; + + if (!all_int && !all_str) + return 0; + } + + if (all_int) + type = VALUE_STRING; + else if (all_str) + type = STRING_STRING; + else + type = CANT; + + /* table can be converted to value_string, generate one */ + if (f && type == VALUE_STRING) { + gen_fprintf(f, + "static const value_string %s[] = {\n", + t->id); + + for (c = t->cases; c; c = c->next) { + const char *str; + int val; + + /* checked above, should not fail now */ + if (!expr_to_str(c->return_expr, &str)) + fail("expr_to_str(str)"); + if (!expr_to_int(&c->e, &val)) + fail("expr_to_int(val)"); + + gen_fprintf(f, "\t{ %x, \"%s\" },\n", val, str); + } + gen_fprintf(f, "\t{ 0, NULL }\n"); + gen_fprintf(f, "};\n"); + return 1; + } + + /* table can be converted to string_string, generate one */ + if (f && type == STRING_STRING) { + gen_fprintf(f, + "static const string_string %s[] = {\n", + t->id); + + for (c = t->cases; c; c = c->next) { + const char *str; + const char *val; + + /* checked above, should not fail now */ + if (!expr_to_str(c->return_expr, &str)) + fail("expr_to_str(str)"); + if (!expr_to_str(&c->e, &val)) + fail("expr_to_str(val)"); + + gen_fprintf(f, "\t{ \"%s\", \"%s\" },\n", val, str); + } + gen_fprintf(f, "\t{ NULL, NULL }\n"); + gen_fprintf(f, "};\n"); + return 1; + } + + return 0; +} + static void -gen_table(FILE *f, npl_table_t *t) +gen_table_func(FILE *f, npl_table_t *t) { struct npl_table_case *c; @@ -215,8 +436,7 @@ again1: if (!c->return_expr) { c = c->next; - if (!c) - abort(); + xassert(c != NULL); gen_fprintf(f, "\n"); goto again1; } else { @@ -244,8 +464,7 @@ again2: if (!c->return_expr) { gen_fprintf(f, " || "); c = c->next; - if (!c) - abort(); + xassert(c != NULL); goto again2; } else { gen_fprintf(f, ")\n"); @@ -265,6 +484,14 @@ again2: gen_fprintf(f, "\treturn \"\";\n"); gen_fprintf(f, "}\n"); +} + +static void +gen_table(FILE *f, npl_table_t *t) +{ + if (!gen_table_struct(f, t)) + gen_table_func(f, t); + gen_fprintf(f, "\n"); } @@ -297,7 +524,7 @@ gen_statement(FILE *f, npl_statement_t *st) if (f) { // XXX, search for st->f.t_id in table. - gen_fprintf(f, "\toffset = dissect_%s(tvb, tree, %s, offset);\n", st->f.t_id, hfi_name(st->f.hfi)); + gen_fprintf(f, "\toffset = dissect_%s(tvb, tree, %s, offset);\n", st->f.t_id, hfi_var(st->f.hfi)); } // XXX st->f.bits, st->f.arr, st->f.format, st->f.sts return; @@ -336,7 +563,6 @@ gen_statement(FILE *f, npl_statement_t *st) return; } - if (c) { npl_statement_t *default_st = st->sw.data.default_st; @@ -413,6 +639,8 @@ gen_protocol(FILE *f, npl_protocol_t *p) gen_fprintf(f, "{\n"); gen_fprintf(f, "\tint offset = 0;\n" + "\tproto_tree *tree = NULL;\n" + "\tproto_item *ti = NULL;\n" "\n" ); @@ -439,8 +667,7 @@ gen_struct(FILE *f, npl_struct_t *s) snprintf(id, sizeof(id), "_noname%u", ++_id); s->id = xstrdup(id); - if (f != NULL) - abort(); + xassert(f == NULL); } if (s->count_expr) { @@ -498,25 +725,40 @@ gen_struct(FILE *f, npl_struct_t *s) static void gen_const(FILE *f, npl_const_t *c) { -// TODO - fprintf(stderr, "gen_const() TODO\n"); + // TODO, later don't do #define, only add to id table with given value. + + gen_fprintf(f, "#define %s", c->id); + gen_expr(f, &c->expr); + gen_fprintf(f, "\n"); } static void gen_type(FILE *f, npl_type_t *t) { + int size = -1; + int byte_order = -1; + const char *hf_type = NULL; + gen_fprintf(f, "static int\n" - "dissect_type_%s(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)\n" + "dissect_type_%s(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int hf, int offset)\n" "{\n", t->name); -#if 0 - npl_params_t params; + if (t->params.count) { + /* XXX */ + fprintf(stderr, "XXX, t->params.count\n"); + } - npl_expression_t *byte_order; - npl_expression_t *display_format; - npl_expression_t *size; -#endif + if (t->size && !expr_to_int(t->size, &size)) + fprintf(stderr, "!!! expr_to_int(size) failed for type: %s\n", t->name); + + if (t->byte_order && !expr_to_int(t->byte_order, &byte_order)) + fprintf(stderr, "!!! expr_to_int(byte_order) failed for type: %s\n", t->name); + + hf_type = type_to_ft(t, size); + /* npl_expression_t *display_format; */ + + t->hf_type = hf_type ? hf_type : "FT_BYTES"; gen_fprintf(f, "\treturn offset;\n"); gen_fprintf(f, "}\n"); @@ -526,8 +768,7 @@ gen_type(FILE *f, npl_type_t *t) static void gen_attr(FILE *f, npl_attr_t *a) { -// TODO - fprintf(stderr, "gen_attr() TODO"); + fprintf(stderr, "gen_attr() TODO\n"); } static void @@ -571,13 +812,11 @@ gen_hf(FILE *f) struct hfinfo *hfi; for (hfi = hfs; hfi; hfi = hfi->next) - gen_fprintf(f, "static int %s = -1;\n", hfi_name(hfi)); + gen_fprintf(f, "static int %s = -1;\n", hfi_var(hfi)); } -static const char proto_name[] = "foo"; /* TODO, hardcoded */ - static void -gen_proto_register(FILE *f) +gen_proto_register(FILE *f, const char *proto_name) { struct hfinfo *hfi; @@ -589,15 +828,14 @@ gen_proto_register(FILE *f) /* hf array */ gen_fprintf(f, "\tstatic hf_register_info hf[] = {\n"); for (hfi = hfs; hfi; hfi = hfi->next) { - npl_statement_t *st = hfi->st; - - gen_fprintf(f, + gen_fprintf(f, "\t\t{ &%s,\n" "\t\t\t{ \"%s\", \"%s\", %s, %s, NULL, 0x%.2x, NULL, HFILL }\n" - "\t\t},\n", hfi_name(hfi), st->f.id, "filtr", "typ", "dec", 0x00 ); + "\t\t},\n", hfi_var(hfi), hfi_name(hfi), hfi_filter(hfi), hfi_type(hfi), hfi_display(hfi), hfi_mask(hfi) ); } gen_fprintf(f, "\t}\n\n"); + /* ett array */ gen_fprintf(f, "\tstatic gint *ett[] = {\n"); #if 0 &ett_foo, @@ -615,14 +853,14 @@ gen_proto_register(FILE *f) } static void -gen_proto_handoff(FILE *f) +gen_proto_handoff(FILE *f, const char *proto_name) { gen_fprintf(f, "void\n" "proto_reg_handoff_%s(void)\n" - "{", proto_name); + "{\n", proto_name); - gen_fprintf(f, "dissector_handle_t %s_handle = new_create_dissector_handle(dissect_%s, proto_%s);\n", proto_name, proto_name, proto_name); + gen_fprintf(f, "\tdissector_handle_t %s_handle = new_create_dissector_handle(dissect_%s, proto_%s);\n", proto_name, proto_name, proto_name); #if 0 dissector_add_uint("REG", XXX, %s_handle); @@ -632,10 +870,24 @@ gen_proto_handoff(FILE *f) gen_fprintf(f, "}\n"); } +static const npl_protocol_t * +get_protocol(npl_code_t *code) +{ + struct _npl_decl_list *decl; + + for (decl = code->decls; decl; decl = decl->next) { + /* XXX, for now return first */ + if (decl->d.type == DECL_PROTOCOL) + return &decl->d.p.data; + } + return NULL; +} + int main(int argc, char **argv) { FILE *f; npl_code_t code; - + int parse_ok; + if (argc != 2) { fprintf(stderr, "usage: %s filename\n", argv[0]); return 1; @@ -647,25 +899,36 @@ int main(int argc, char **argv) { } memset(&code, 0, sizeof(code)); - printf("%s:\n", argv[1]); - npl_parse_file(&code, f, argv[1]); - - if (code.parse_ok) { + parse_ok = npl_parse_file(&code, f, argv[1]); +// parse_ok = 0; + if (parse_ok) { + const npl_protocol_t *proto = get_protocol(&code); + const char *proto_name = (proto) ? proto->id : "noname"; FILE *out; gen_code(NULL, &code); out = fopen("/tmp/npl.c", "w"); - /* TODO declare ett_ */ + /* includes */ + gen_fprintf(out, "#include \"config.h\"\n"); + gen_fprintf(out, "#include <glib.h>\n"); + gen_fprintf(out, "#include <epan/packet.h>\n"); + gen_fprintf(out, "\n"); + + /* TODO declare forward */ + + gen_fprintf(out, "static int proto_%s = -1;\n", proto_name); gen_hf(out); + gen_fprintf(out, "\n"); - gen_fprintf(out, "\n\n"); + /* TODO declare ett_ */ + gen_fprintf(out, "\n"); gen_code(out, &code); - gen_proto_register(out); - gen_proto_handoff(out); + gen_proto_register(out, proto_name); + gen_proto_handoff(out, proto_name); fclose(out); } diff --git a/tools/npl/parser.l b/tools/npl/parser.l index d0d479974b..105b0781aa 100644 --- a/tools/npl/parser.l +++ b/tools/npl/parser.l @@ -485,7 +485,6 @@ parse_params(npl_params_t *p) static void parse_expression(npl_expression_t *expr); static npl_expression_t *xparse_expression(void); -static npl_expression_t g_e; static void parse_primary(npl_expression_t *expr) @@ -531,6 +530,22 @@ parse_primary(npl_expression_t *expr) nomatch(); } +/* ExpressionList = Expression, { ",", Expression } ; */ +static void +parse_expression_list(npl_expression_list_t **ptr) +{ + do { + npl_expression_list_t *cur = xnew(npl_expression_list_t); + + *ptr = cur; + ptr = &(cur->next); + cur->expr = xparse_expression(); + + } while (is_token_accept(TOKEN_COMMA)); + + *ptr = NULL; +} + static void parse_expression1(npl_expression_t *expr) { @@ -539,44 +554,33 @@ parse_expression1(npl_expression_t *expr) do { if (is_token_accept(TOKEN_LPAREN)) { /* foo() */ npl_expression_t *fun = xdup(npl_expression_t, expr); - struct _npl_expression_list **ptr = &(expr->call.args); - - if (!is_token(TOKEN_RPAREN)) { - do { - struct _npl_expression_list *cur = xnew(struct _npl_expression_list); - - *ptr = cur; - ptr = &(cur->next); - cur->expr = xparse_expression(); - - } while (is_token_accept(TOKEN_COMMA)); - } - *ptr = NULL; + npl_expression_list_t *args = NULL; + if (!is_token(TOKEN_RPAREN)) + parse_expression_list(&args); accept(TOKEN_RPAREN); - expr->call.fn = fun; + expr->type = EXPRESSION_CALL; + expr->call.fn = fun; + expr->call.args = args; - } else if (is_token(TOKEN_DOLLAR) || is_token(TOKEN_LBRACKET)) { /* arr$[field1, field2, ...], arr[10] */ + } else if (is_token_accept(TOKEN_DOLLAR)) { /* arr$[field1, field2, ...] */ npl_expression_t *base = xdup(npl_expression_t, expr); - npl_expression_t *idx; - int aa; + npl_expression_list_t *indexes; - aa = is_token_accept(TOKEN_DOLLAR); accept(TOKEN_LBRACKET); + parse_expression_list(&indexes); + accept(TOKEN_RBRACKET); - idx = xparse_expression(); - - if (aa) { - while (is_token_accept(TOKEN_COMMA)) { - // XXX ast - - npl_expression_t *idx2; + expr->type = EXPRESSION_MULTI_INDEX; + expr->aarr.base = base; + expr->aarr.indexes = indexes; - idx2 = xparse_expression(); - } - } + } else if (is_token_accept(TOKEN_LBRACKET)) { /* arr[10] */ + npl_expression_t *base = xdup(npl_expression_t, expr); + npl_expression_t *idx; + idx = xparse_expression(); accept(TOKEN_RBRACKET); expr->type = EXPRESSION_INDEX; @@ -921,21 +925,27 @@ static int is_attribute(void) { return is_token(TOKEN_LBRACKET); } static void parse_attributes(npl_attr_t *attr) { + npl_expression_list_t **ptr = &attr->expr_list; + accept(TOKEN_LBRACKET); do { - parse_expression(&attr->expr); + npl_expression_list_t *cur = xnew(npl_expression_list_t); + + *ptr = cur; + ptr = &(cur->next); + cur->expr = xparse_expression(); if (is_token_accept(TOKEN_SEMICOLON)) { } else if (is_token_accept(TOKEN_COMMA)) { } - } while (!is_token(TOKEN_RBRACKET)); // while (is_token_accept(TOKEN_COMMA)); accept(TOKEN_RBRACKET); + *ptr = NULL; } static void parse_statement(npl_statement_t *st); @@ -1007,8 +1017,6 @@ parse_dynamic_switch(npl_switch_t *sw) is_token_accept(TOKEN_SEMICOLON); } -static void parse_table(npl_table_t *); - static int is_statement(void) { return is_token(TOKEN_WHILE) || @@ -1076,6 +1084,7 @@ xparse_formatting(void) return format; } +static void parse_table(npl_table_t *); static void parse_struct(npl_struct_t *s, int statement); static void @@ -1128,12 +1137,12 @@ parse_statement(npl_statement_t *st) if (is_token_accept(TOKEN_LPAREN)) { /* XXX, WTF: StringTerm(Property.XMLEncoding, "<", true, false, false) Reason; */ + npl_expression_list_t *type_params; - do { - /* XXX, ast */ - parse_expression(&g_e); - } while (is_token_accept(TOKEN_COMMA)); + parse_expression_list(&type_params); accept(TOKEN_RPAREN); + + /* XXX, store type_params in ast */ } st->f.id = accept_id(); @@ -1375,13 +1384,12 @@ parse_decl(npl_decl_t *d) parse_protocol(&d->p.data); } else if (is_type()) { - parse_type(&d->ty.data); d->type = DECL_TYPE; + parse_type(&d->ty.data); } else if (is_token_accept(TOKEN_INCLUDE)) { - /* XXX, ast */ - char *file = accept_str(); - free(file); + d->type = DECL_INCLUDE; + d->i.file = accept_str(); /* XXX, it's C-escaped */ /* XXX, unix / vs dos \\ */ } else nomatch(); @@ -1404,16 +1412,19 @@ parse_npl(npl_code_t *code) *ptr = NULL; } -void +int npl_parse_file(npl_code_t *code, FILE *f, const char *filename) { + int parse_ok = 0; yyfilename = filename; yyin = f; next_token(); parse_npl(code); - code->parse_ok = 1; + parse_ok = 1; yylex_destroy(); + + return parse_ok; } diff --git a/tools/npl/xmem.h b/tools/npl/xmem.h index d42065f040..173a88fd91 100644 --- a/tools/npl/xmem.h +++ b/tools/npl/xmem.h @@ -12,7 +12,7 @@ static inline void *xmalloc(size_t s) { void *ptr = malloc(s); if (!ptr) oom_kil static inline void *xrealloc(void *p, size_t s) { void *ptr = realloc(p, s); if (!ptr) oom_killer(); return ptr; } static inline void *xmemdup(void *p, size_t s) { void *ptr = malloc(s); if (!ptr) oom_killer(); return memcpy(ptr, p, s); } -static inline void *xstrdup(const char *s) { void *ptr = strdup(s); if (!ptr) oom_killer(); return ptr; } +static inline char *xstrdup(const char *s) { void *ptr = strdup(s); if (!ptr) oom_killer(); return ptr; } #define xnew(x) (x *) xmalloc(sizeof(x)) #define xdup(x, y) (x *) xmemdup(y, sizeof(x)) |