Add type.c/.h, value.c/.h, value_dict.c/.h

- none of this is plugged in yet
- expr.c/.h is not in yet, so we still express array length with len_spec
  integer.  Some function are mocked out so the thing compiles
diff --git a/ChangeLog b/ChangeLog
index 0dffe52..51b9f9c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2012-04-30  Petr Machata  <pmachata@redhat.com>
 
+	* type.c, type.h: New module with routines related to handling of
+	types.  Some of this was moved over from common.h.
+	* common.h: Adjust for this change.
+	* read_config_file.c: Likewise.
+	* display_args.c: Likewise.
+	* output.c: Likewise.
+	* sysdeps/linux-gnu/*/trace.c: Likewise.
+	* value.c, value.h: New module for values of arbitrary length.
+	* value_dict.c, value_dict.h: New module for value dictionaries.
+
+2012-04-30  Petr Machata  <pmachata@redhat.com>
+
 	* library.h (arch_translate_address): First argument is ltelf.
 	(arch_translate_address_dyn): New function.
 	* sysdeps/linux-gnu/ppc/plt.c: Implement above, use .opd to
diff --git a/Makefile.am b/Makefile.am
index 0e91f71..ad93ef6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,10 @@
 	library.c \
 	filter.c \
 	glob.c \
-	vect.c
+	vect.c \
+	type.c \
+	value.c \
+	value_dict.c
 
 libltrace_la_LIBADD = \
 	$(libelf_LIBS) \
@@ -67,7 +70,10 @@
 	library.h \
 	filter.h \
 	glob.h \
-	vect.h
+	vect.h \
+	type.h \
+	value.h \
+	value_dict.h
 
 dist_man1_MANS = \
 	ltrace.1
diff --git a/common.h b/common.h
index 621aa13..9db7e83 100644
--- a/common.h
+++ b/common.h
@@ -15,6 +15,7 @@
 #include "ltrace-elf.h"
 #include "read_config_file.h"
 #include "proc.h"
+#include "forward.h"
 
 #if defined HAVE_LIBSUPC__ || defined HAVE_LIBSTDC__
 # define USE_CXA_DEMANGLE
@@ -27,77 +28,6 @@
 
 extern int exiting;  /* =1 if we have to exit ASAP */
 
-enum arg_type {
-	ARGTYPE_UNKNOWN = -1,
-	ARGTYPE_VOID,
-	ARGTYPE_INT,
-	ARGTYPE_UINT,
-	ARGTYPE_LONG,
-	ARGTYPE_ULONG,
-	ARGTYPE_OCTAL,
-	ARGTYPE_CHAR,
-	ARGTYPE_SHORT,
-	ARGTYPE_USHORT,
-	ARGTYPE_FLOAT,		/* float value, may require index */
-	ARGTYPE_DOUBLE,		/* double value, may require index */
-	ARGTYPE_ADDR,
-	ARGTYPE_FILE,
-	ARGTYPE_FORMAT,		/* printf-like format */
-	ARGTYPE_STRING,		/* NUL-terminated string */
-	ARGTYPE_STRING_N,	/* String of known maxlen */
-	ARGTYPE_ARRAY,		/* Series of values in memory */
-	ARGTYPE_ENUM,		/* Enumeration */
-	ARGTYPE_STRUCT,		/* Structure of values */
-	ARGTYPE_POINTER,	/* Pointer to some other type */
-	ARGTYPE_COUNT		/* number of ARGTYPE_* values */
-};
-
-typedef struct arg_type_info_t {
-	enum arg_type type;
-	union {
-		/* ARGTYPE_ENUM */
-		struct {
-			size_t entries;
-			char ** keys;
-			int * values;
-		} enum_info;
-
-		/* ARGTYPE_ARRAY */
-		struct {
-			struct arg_type_info_t * elt_type;
-			size_t elt_size;
-			int len_spec;
-		} array_info;
-
-		/* ARGTYPE_STRING_N */
-		struct {
-			int size_spec;
-		} string_n_info;
-
-		/* ARGTYPE_STRUCT */
-		struct {
-			struct arg_type_info_t ** fields;	/* NULL-terminated */
-			size_t * offset;
-			size_t size;
-		} struct_info;
-
-		/* ARGTYPE_POINTER */
-		struct {
-			struct arg_type_info_t * info;
-		} ptr_info;
-
-		/* ARGTYPE_FLOAT */
-		struct {
-			size_t float_index;
-		} float_info;
-
-		/* ARGTYPE_DOUBLE */
-		struct {
-			size_t float_index;
-		} double_info;
-	} u;
-} arg_type_info;
-
 enum tof {
 	LT_TOF_NONE = 0,
 	LT_TOF_FUNCTION,	/* A real library function */
@@ -110,9 +40,9 @@
 typedef struct Function Function;
 struct Function {
 	const char * name;
-	arg_type_info * return_info;
+	struct arg_type_info *return_info;
 	int num_params;
-	arg_type_info * arg_info[MAX_ARGS];
+	struct arg_type_info *arg_info[MAX_ARGS];
 	int params_right;
 	Function * next;
 };
@@ -157,11 +87,10 @@
 extern void handle_event(Event * event);
 
 extern pid_t execute_program(const char * command, char ** argv);
-extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+extern int display_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info);
 extern void disable_all_breakpoints(Process * proc);
 
 extern void show_summary(void);
-extern arg_type_info * lookup_prototype(enum arg_type at);
 
 struct breakpoint;
 struct library_symbol;
@@ -193,10 +122,12 @@
 extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
 extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
 extern void continue_after_vfork(Process * proc);
-extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+extern long gimme_arg(enum tof type, Process *proc, int arg_num,
+		      struct arg_type_info *info);
 extern void save_register_args(enum tof type, Process * proc);
 extern int umovestr(Process * proc, void * addr, int len, void * laddr);
-extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info);
+extern int umovelong(Process *proc, void *addr, long *result,
+		     struct arg_type_info *info);
 extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
 extern int ffcheck(void * maddr);
 extern void * sym2addr(Process *, struct library_symbol *);
diff --git a/display_args.c b/display_args.c
index 5df34ca..872b1c9 100644
--- a/display_args.c
+++ b/display_args.c
@@ -6,13 +6,14 @@
 
 #include "common.h"
 #include "proc.h"
+#include "type.h"
 
 static int display_char(int what);
 static int display_string(enum tof type, Process *proc,
 			  void* addr, size_t maxlen);
 static int display_value(enum tof type, Process *proc,
-			 long value, arg_type_info *info,
-			 void *st, arg_type_info* st_info);
+			 long value, struct arg_type_info *info,
+			 void *st, struct arg_type_info *st_info);
 static int display_unknown(enum tof type, Process *proc, long value);
 static int display_format(enum tof type, Process *proc, int arg_num);
 
@@ -21,15 +22,16 @@
 
 static long
 get_length(enum tof type, Process *proc, int len_spec,
-		       void *st, arg_type_info* st_info) {
+	   void *st, struct arg_type_info *st_info)
+{
 	long len;
-	arg_type_info info;
+	struct arg_type_info info;
 
 	if (len_spec > 0)
 		return len_spec;
 	if (type == LT_TOF_STRUCT) {
-		umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1],
-			   &len, st_info->u.struct_info.fields[-len_spec-1]);
+		umovelong(proc, st + st_info->u.struct_info.offset[-len_spec-1],
+			  &len, st_info->u.struct_info.fields[-len_spec-1]);
 		return len;
 	}
 
@@ -39,9 +41,9 @@
 
 static int
 display_ptrto(enum tof type, Process *proc, long item,
-			 arg_type_info * info,
-			 void *st, arg_type_info* st_info) {
-	arg_type_info temp;
+			 struct arg_type_info * info,
+			 void *st, struct arg_type_info *st_info) {
+	struct arg_type_info temp;
 	temp.type = ARGTYPE_POINTER;
 	temp.u.ptr_info.info = info;
 	return display_value(type, proc, item, &temp, st, st_info);
@@ -57,8 +59,8 @@
  */
 static int
 display_arrayptr(enum tof type, Process *proc,
-			    void *addr, arg_type_info * info,
-			    void *st, arg_type_info* st_info) {
+			    void *addr, struct arg_type_info * info,
+			    void *st, struct arg_type_info *st_info) {
 	int len = 0;
 	size_t i;
 	size_t array_len;
@@ -70,7 +72,7 @@
 			st, st_info);
 	len += fprintf(options.output, "[ ");
 	for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) {
-		arg_type_info *elt_type = info->u.array_info.elt_type;
+		struct arg_type_info *elt_type = info->u.array_info.elt_type;
 		size_t elt_size = info->u.array_info.elt_size;
 		if (i != 0)
 			len += fprintf(options.output, ", ");
@@ -91,9 +93,9 @@
  */
 static int
 display_structptr(enum tof type, Process *proc,
-			     void *addr, arg_type_info * info) {
+			     void *addr, struct arg_type_info * info) {
 	int i;
-	arg_type_info *field;
+	struct arg_type_info *field;
 	int len = 0;
 
 	if (addr == NULL)
@@ -119,10 +121,10 @@
 
 static int
 display_pointer(enum tof type, Process *proc, long value,
-			   arg_type_info * info,
-			   void *st, arg_type_info* st_info) {
+			   struct arg_type_info * info,
+			   void *st, struct arg_type_info *st_info) {
 	long pointed_to;
-	arg_type_info *inner = info->u.ptr_info.info;
+	struct arg_type_info *inner = info->u.ptr_info.info;
 
 	if (inner->type == ARGTYPE_ARRAY) {
 		return display_arrayptr(type, proc, (void*) value, inner,
@@ -143,7 +145,7 @@
 
 static int
 display_enum(enum tof type, Process *proc,
-		arg_type_info* info, long value) {
+		struct arg_type_info *info, long value) {
 	size_t ii;
 	for (ii = 0; ii < info->u.enum_info.entries; ++ii) {
 		if (info->u.enum_info.values[ii] == value)
@@ -166,8 +168,8 @@
 */
 int
 display_value(enum tof type, Process *proc,
-		long value, arg_type_info *info,
-		void *st, arg_type_info* st_info) {
+		long value, struct arg_type_info *info,
+		void *st, struct arg_type_info *st_info) {
 	int tmp;
 
 	switch (info->type) {
@@ -239,7 +241,9 @@
 }
 
 int
-display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) {
+display_arg(enum tof type, Process *proc, int arg_num,
+	    struct arg_type_info * info)
+{
 	long arg;
 
 	if (info->type == ARGTYPE_VOID) {
@@ -330,7 +334,7 @@
 	unsigned char *str1;
 	int i;
 	size_t len = 0;
-	arg_type_info info;
+	struct arg_type_info info;
 
 	info.type = ARGTYPE_POINTER;
 	addr = (void *)gimme_arg(type, proc, arg_num, &info);
diff --git a/output.c b/output.c
index db6e93e..265993c 100644
--- a/output.c
+++ b/output.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "proc.h"
 #include "library.h"
+#include "type.h"
 
 /* TODO FIXME XXX: include in common.h: */
 extern struct timeval current_time_spent;
@@ -161,7 +162,7 @@
 {
 	const char *function_name = libsym->name;
 	Function *func;
-	static arg_type_info *arg_unknown = NULL;
+	static struct arg_type_info *arg_unknown = NULL;
 	if (arg_unknown == NULL)
 	    arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
 
@@ -225,7 +226,7 @@
 {
 	const char *function_name = libsym->name;
 	Function *func = name2func(function_name);
-	static arg_type_info *arg_unknown = NULL;
+	static struct arg_type_info *arg_unknown = NULL;
 	if (arg_unknown == NULL)
 	    arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
 
diff --git a/read_config_file.c b/read_config_file.c
index b4b1b56..af19512 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -5,12 +5,13 @@
 #include <ctype.h>
 
 #include "common.h"
+#include "type.h"
 
 static int line_no;
 static char *filename;
 static int error_count = 0;
 
-static arg_type_info *parse_type(char **str);
+static struct arg_type_info *parse_type(char **str);
 
 Function *list_of_functions = NULL;
 
@@ -45,7 +46,7 @@
 /* Array of prototype objects for each of the types. The order in this
  * array must exactly match the list of enumerated values in
  * common.h */
-static arg_type_info arg_type_prototypes[] = {
+static struct arg_type_info arg_type_prototypes[] = {
 	{ ARGTYPE_VOID },
 	{ ARGTYPE_INT },
 	{ ARGTYPE_UINT },
@@ -69,7 +70,7 @@
 	{ ARGTYPE_UNKNOWN }
 };
 
-arg_type_info *
+struct arg_type_info *
 lookup_prototype(enum arg_type at) {
 	if (at >= 0 && at <= ARGTYPE_COUNT)
 		return &arg_type_prototypes[at];
@@ -77,7 +78,7 @@
 		return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
 }
 
-static arg_type_info *
+static struct arg_type_info *
 str2type(char **str) {
 	struct list_of_pt_t *tmp = &list_of_pt[0];
 
@@ -208,11 +209,11 @@
 
 struct typedef_node_t {
 	char *name;
-	arg_type_info *info;
+	struct arg_type_info *info;
 	struct typedef_node_t *next;
 } *typedefs = NULL;
 
-static arg_type_info *
+static struct arg_type_info *
 lookup_typedef(char **str) {
 	struct typedef_node_t *node;
 	char *end = *str;
@@ -234,7 +235,7 @@
 static void
 parse_typedef(char **str) {
 	char *name;
-	arg_type_info *info;
+	struct arg_type_info *info;
 	struct typedef_node_t *binding;
 
 	(*str) += strlen("typedef");
@@ -267,7 +268,7 @@
 }
 
 static size_t
-arg_sizeof(arg_type_info * arg) {
+arg_sizeof(struct arg_type_info * arg) {
 	if (arg->type == ARGTYPE_CHAR) {
 		return sizeof(char);
 	} else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) {
@@ -295,7 +296,7 @@
 #undef alignof
 #define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
 static size_t
-arg_align(arg_type_info * arg) {
+arg_align(struct arg_type_info * arg) {
 	struct { char c; char C; } cC;
 	struct { char c; short s; } cs;
 	struct { char c; int i; } ci;
@@ -355,7 +356,7 @@
 /* I'm sure this isn't completely correct, but just try to get most of
  * them right for now. */
 static void
-align_struct(arg_type_info* info) {
+align_struct(struct arg_type_info* info) {
 	size_t offset;
 	int i;
 
@@ -366,7 +367,7 @@
 	// various types.
 	offset = 0;
 	for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
-		arg_type_info *field = info->u.struct_info.fields[i];
+		struct arg_type_info *field = info->u.struct_info.fields[i];
 		offset += align_skip(arg_align(field), offset);
 		info->u.struct_info.offset[i] = offset;
 		offset += arg_sizeof(field);
@@ -375,10 +376,10 @@
 	info->u.struct_info.size = offset;
 }
 
-static arg_type_info *
+static struct arg_type_info *
 parse_nonpointer_type(char **str) {
-	arg_type_info *simple;
-	arg_type_info *info;
+	struct arg_type_info *simple;
+	struct arg_type_info *info;
 
 	if (strncmp(*str, "typedef", 7) == 0) {
 		parse_typedef(str);
@@ -556,11 +557,11 @@
 	}
 }
 
-static arg_type_info *
+static struct arg_type_info *
 parse_type(char **str) {
-	arg_type_info *info = parse_nonpointer_type(str);
+	struct arg_type_info *info = parse_nonpointer_type(str);
 	while (**str == '*') {
-		arg_type_info *outer = malloc(sizeof(*info));
+		struct arg_type_info *outer = malloc(sizeof(*info));
 		outer->type = ARGTYPE_POINTER;
 		outer->u.ptr_info.info = info;
 		(*str)++;
diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c
index 18fe395..3c15664 100644
--- a/sysdeps/linux-gnu/alpha/trace.c
+++ b/sysdeps/linux-gnu/alpha/trace.c
@@ -48,7 +48,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	if (arg_num == -1) {	/* return value */
 		return ptrace(PTRACE_PEEKUSER, proc->pid, 0 /* REG_R0 */ , 0);
 	}
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
index 768e1a8..8cf5560 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -82,7 +82,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	proc_archdep *a = (proc_archdep *) proc->arch_ptr;
 
 	if (arg_num == -1) {	/* return value */
diff --git a/sysdeps/linux-gnu/cris/trace.c b/sysdeps/linux-gnu/cris/trace.c
index b2b1ba8..2e7c586 100644
--- a/sysdeps/linux-gnu/cris/trace.c
+++ b/sysdeps/linux-gnu/cris/trace.c
@@ -51,7 +51,8 @@
 	return 0;
 }
 
-long gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info)
+long gimme_arg(enum tof type, Process *proc, int arg_num,
+	       struct arg_type_info *info)
 {
 	int pid = proc->pid;
 
diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c
index f0c1e50..d024bc9 100644
--- a/sysdeps/linux-gnu/i386/trace.c
+++ b/sysdeps/linux-gnu/i386/trace.c
@@ -57,7 +57,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	if (arg_num == -1) {	/* return value */
 		return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0);
 	}
diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c
index 385fac1..0ef1e60 100644
--- a/sysdeps/linux-gnu/ia64/trace.c
+++ b/sysdeps/linux-gnu/ia64/trace.c
@@ -13,6 +13,7 @@
 
 #include "proc.h"
 #include "common.h"
+#include "type.h"
 
 /* What we think of as a bundle, ptrace thinks of it as two unsigned
  * longs */
@@ -245,7 +246,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	union {
 		long l;
 		float f;
diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c
index c63702d..b15e47d 100644
--- a/sysdeps/linux-gnu/m68k/trace.c
+++ b/sysdeps/linux-gnu/m68k/trace.c
@@ -47,7 +47,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	if (arg_num == -1) {	/* return value */
 		return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0);
 	}
diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c
index 4b999e4..2c509d9 100644
--- a/sysdeps/linux-gnu/mipsel/trace.c
+++ b/sysdeps/linux-gnu/mipsel/trace.c
@@ -9,6 +9,7 @@
 #include "proc.h"
 #include "common.h"
 #include "mipsel.h"
+#include "type.h"
 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
 #endif
@@ -118,7 +119,8 @@
 
 */
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	long ret;
 	long addr;
 	debug(2,"type %d arg %d",type,arg_num);
diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
index 742785a..18eb92e 100644
--- a/sysdeps/linux-gnu/ppc/trace.c
+++ b/sysdeps/linux-gnu/ppc/trace.c
@@ -13,6 +13,7 @@
 #include "common.h"
 #include "ptrace.h"
 #include "breakpoint.h"
+#include "type.h"
 
 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -67,7 +68,8 @@
 }
 
 static long
-gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info,
+gimme_arg_regset(enum tof type, Process *proc, int arg_num,
+		 struct arg_type_info *info,
 		 gregset_t *regs, fpregset_t *fpregs)
 {
 	union { long val; float fval; double dval; } cvt;
@@ -110,7 +112,7 @@
 }
 
 static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
+gimme_retval(Process *proc, int arg_num, struct arg_type_info *info,
 	     gregset_t *regs, fpregset_t *fpregs)
 {
 	union { long val; float fval; double dval; } cvt;
@@ -130,7 +132,7 @@
 
 /* Grab functions arguments based on the PPC64 ABI.  */
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info)
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
 {
 	proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
 	if (arch == NULL || !arch->valid)
@@ -173,7 +175,9 @@
 
 /* Read a single long from the process's memory address 'addr'.  */
 int
-arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+arch_umovelong (Process *proc, void *addr, long *result,
+		struct arg_type_info *info)
+{
 	long pointed_to;
 
 	errno = 0;
diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c
index 8c08f1f..6660b18 100644
--- a/sysdeps/linux-gnu/s390/trace.c
+++ b/sysdeps/linux-gnu/s390/trace.c
@@ -161,7 +161,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	long ret;
 
 	switch (arg_num) {
diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c
index e05c4d3..04efdf2 100644
--- a/sysdeps/linux-gnu/sparc/trace.c
+++ b/sysdeps/linux-gnu/sparc/trace.c
@@ -45,7 +45,8 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
 	proc_archdep *a = (proc_archdep *) proc->arch_ptr;
 	if (!a->valid) {
 		fprintf(stderr, "Could not get child registers\n");
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index bd5d826..f573f22 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -19,6 +19,7 @@
 #include "breakpoint.h"
 #include "proc.h"
 #include "linux-gnu/trace.h"
+#include "type.h"
 
 /* If the system headers did not provide the constants, hard-code the normal
    values.  */
@@ -50,13 +51,14 @@
 #ifdef ARCH_HAVE_UMOVELONG
 extern int arch_umovelong (Process *, void *, long *, arg_type_info *);
 int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+umovelong(Process *proc, void *addr, long *result, arg_type_info *info) {
 	return arch_umovelong (proc, addr, result, info);
 }
 #else
 /* Read a single long from the process's memory address 'addr' */
 int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+umovelong(Process *proc, void *addr, long *result, struct arg_type_info *info)
+{
 	long pointed_to;
 
 	errno = 0;
@@ -66,7 +68,7 @@
 
 	*result = pointed_to;
 	if (info) {
-		switch(info->type) {
+		switch (info->type) {
 			case ARGTYPE_INT:
 				*result &= 0x00000000ffffffffUL;
 			default:
diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c
index 0d3f693..ec53bfc 100644
--- a/sysdeps/linux-gnu/x86_64/trace.c
+++ b/sysdeps/linux-gnu/x86_64/trace.c
@@ -13,6 +13,7 @@
 #include "common.h"
 #include "ptrace.h"
 #include "proc.h"
+#include "type.h"
 
 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -112,7 +113,7 @@
 }
 
 static long
-gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info,
+gimme_arg_regset(Process *proc, int arg_num, struct arg_type_info *info,
                  struct user_regs_struct *regs,
 		 struct user_fpregs_struct *fpregs)
 {
@@ -148,7 +149,7 @@
 	}
 }
 static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
+gimme_retval(Process *proc, int arg_num, struct arg_type_info *info,
              struct user_regs_struct *regs, struct user_fpregs_struct *fpregs)
 {
         if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
@@ -158,7 +159,9 @@
 }
 
 long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num,
+	  struct arg_type_info *info)
+{
 	if (proc->mask_32bit)
 		return (unsigned int)gimme_arg32(type, proc, arg_num);
 
diff --git a/type.c b/type.c
new file mode 100644
index 0000000..08d37e2
--- /dev/null
+++ b/type.c
@@ -0,0 +1,557 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2007,2008 Juan Cespedes
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "type.h"
+#include "sysdep.h"
+
+static int
+expr_is_compile_constant(int len_spec)
+{
+	return len_spec > 0;
+}
+
+static int
+expr_eval_constant(int len_spec, long *result)
+{
+	if (len_spec <= 0)
+		return -1;
+	*result = len_spec;
+	return 0;
+}
+
+struct arg_type_info *
+type_get_simple(enum arg_type type)
+{
+#define HANDLE(T) {					\
+		static struct arg_type_info t = { T };	\
+	case T:						\
+		return &t;				\
+	}
+
+	switch (type) {
+	HANDLE(ARGTYPE_UNKNOWN)
+	HANDLE(ARGTYPE_VOID)
+	HANDLE(ARGTYPE_INT)
+	HANDLE(ARGTYPE_UINT)
+	HANDLE(ARGTYPE_LONG)
+	HANDLE(ARGTYPE_ULONG)
+	HANDLE(ARGTYPE_OCTAL)
+	HANDLE(ARGTYPE_CHAR)
+	HANDLE(ARGTYPE_SHORT)
+	HANDLE(ARGTYPE_USHORT)
+	HANDLE(ARGTYPE_FLOAT)
+	HANDLE(ARGTYPE_DOUBLE)
+
+	  HANDLE(ARGTYPE_ADDR)
+	  HANDLE(ARGTYPE_FILE)
+	  HANDLE(ARGTYPE_FORMAT)
+
+#undef HANDLE
+
+	case ARGTYPE_STRING:
+	case ARGTYPE_STRING_N:
+	case ARGTYPE_COUNT:
+
+	case ARGTYPE_ARRAY:
+	case ARGTYPE_ENUM:
+	case ARGTYPE_STRUCT:
+	case ARGTYPE_POINTER:
+		assert(!"Not a simple type!");
+	};
+	abort();
+}
+
+struct enum_entry {
+	char *key;
+	int own_key;
+	int value;
+};
+
+void
+type_init_enum(struct arg_type_info *info)
+{
+	info->type = ARGTYPE_ENUM;
+	VECT_INIT(&info->u.entries, struct enum_entry);
+}
+
+int
+type_enum_add(struct arg_type_info *info,
+	      const char *key, int own_key, int value)
+{
+	assert(info->type == ARGTYPE_ENUM);
+	struct enum_entry entry = { (char *)key, own_key, value };
+	return VECT_PUSHBACK(&info->u.entries, &entry);
+}
+
+size_t
+type_enum_size(struct arg_type_info *info)
+{
+	assert(info->type == ARGTYPE_ENUM);
+	return vect_size(&info->u.entries);
+}
+
+const char *
+type_enum_get(struct arg_type_info *info, int value)
+{
+	assert(info->type == ARGTYPE_ENUM);
+	size_t i;
+	for (i = 0; i < vect_size(&info->u.entries); ++i) {
+		struct enum_entry *entry = VECT_ELEMENT(&info->u.entries,
+							struct enum_entry, i);
+		if (value == entry->value)
+			return entry->key;
+	}
+	return NULL;
+}
+
+static void
+enum_entry_dtor(struct enum_entry *entry, void *data)
+{
+	if (entry->own_key)
+		free(entry->key);
+}
+
+static void
+type_enum_destroy(struct arg_type_info *info)
+{
+	VECT_DESTROY(&info->u.entries, struct enum_entry,
+		     enum_entry_dtor, NULL);
+}
+
+struct struct_field {
+	struct arg_type_info *info;
+	int own_info;
+};
+
+void
+type_init_struct(struct arg_type_info *info)
+{
+	info->type = ARGTYPE_STRUCT;
+	VECT_INIT(&info->u.entries, struct struct_field);
+}
+
+int
+type_struct_add(struct arg_type_info *info,
+		struct arg_type_info *field_info, int own)
+{
+	assert(info->type == ARGTYPE_STRUCT);
+	struct struct_field field = { field_info, own };
+	return VECT_PUSHBACK(&info->u.entries, &field);
+}
+
+struct arg_type_info *
+type_struct_get(struct arg_type_info *info, size_t idx)
+{
+	assert(info->type == ARGTYPE_STRUCT);
+	struct struct_field *field = VECT_ELEMENT(&info->u.entries,
+						  struct struct_field, idx);
+	if (field == NULL)
+		return NULL;
+	return field->info;
+}
+
+size_t
+type_struct_size(struct arg_type_info *info)
+{
+	assert(info->type == ARGTYPE_STRUCT);
+	return vect_size(&info->u.entries);
+}
+
+static void
+struct_field_dtor(struct struct_field *field, void *data)
+{
+	if (field->own_info) {
+		type_destroy(field->info);
+		free(field->info);
+	}
+}
+
+static void
+type_struct_destroy(struct arg_type_info *info)
+{
+	VECT_DESTROY(&info->u.entries, struct struct_field,
+		     struct_field_dtor, NULL);
+}
+
+static int
+layout_struct(struct Process *proc, struct arg_type_info *info,
+	      size_t *sizep, size_t *alignmentp, size_t *offsetofp)
+{
+	size_t sz = 0;
+	size_t max_alignment = 0;
+	size_t i;
+	size_t offsetof_field = (size_t)-1;
+	if (offsetofp != NULL)
+		offsetof_field = *offsetofp;
+
+	assert(info->type == ARGTYPE_STRUCT);
+	for (i = 0; i < vect_size(&info->u.entries); ++i) {
+		struct struct_field *field
+			= VECT_ELEMENT(&info->u.entries,
+				       struct struct_field, i);
+
+		size_t alignment = type_alignof(proc, field->info);
+		if (alignment == (size_t)-1)
+			return -1;
+
+		/* Add padding to SZ to align the next element.  */
+		sz = align(sz, alignment);
+		if (i == offsetof_field) {
+			*offsetofp = sz;
+			if (sizep == NULL && alignmentp == NULL)
+				return 0;
+		}
+
+		size_t size = type_sizeof(proc, field->info);
+		if (size == (size_t)-1)
+			return -1;
+		sz += size;
+
+		if (alignment > max_alignment)
+			max_alignment = alignment;
+	}
+
+	if (max_alignment > 0)
+		sz = align(sz, max_alignment);
+
+	if (sizep != NULL)
+		*sizep = sz;
+
+	if (alignmentp != NULL)
+		*alignmentp = max_alignment;
+
+	return 0;
+}
+
+void
+type_init_array(struct arg_type_info *info,
+		struct arg_type_info *element_info, int own_info,
+		int len_spec)
+{
+	info->type = ARGTYPE_ARRAY;
+	info->u.array_info.elt_type = element_info;
+	info->u.array_info.own_info = own_info;
+	info->u.array_info.len_spec = len_spec;
+}
+
+static void
+type_array_destroy(struct arg_type_info *info)
+{
+	if (info->u.array_info.own_info) {
+		type_destroy(info->u.array_info.elt_type);
+		free(info->u.array_info.elt_type);
+	}
+	/*
+	if (info->u.array_info.own_length) {
+		expr_destroy(info->u.array_info.length);
+		free(info->u.array_info.length);
+	}
+	*/
+}
+
+void
+type_init_pointer(struct arg_type_info *info,
+		  struct arg_type_info *pointee_info, int own_info)
+{
+	info->type = ARGTYPE_POINTER;
+	info->u.ptr_info.info = pointee_info;
+	info->u.ptr_info.own_info = own_info;
+}
+
+static void
+type_pointer_destroy(struct arg_type_info *info)
+{
+	if (info->u.ptr_info.own_info) {
+		type_destroy(info->u.ptr_info.info);
+		free(info->u.ptr_info.info);
+	}
+}
+
+void
+type_destroy(struct arg_type_info *info)
+{
+	if (info == NULL)
+		return;
+
+	switch (info->type) {
+	case ARGTYPE_ENUM:
+		return type_enum_destroy(info);
+
+	case ARGTYPE_STRUCT:
+		type_struct_destroy(info);
+		break;
+
+	case ARGTYPE_ARRAY:
+		type_array_destroy(info);
+		break;
+
+	case ARGTYPE_POINTER:
+		type_pointer_destroy(info);
+		break;
+
+	case ARGTYPE_UNKNOWN:
+	case ARGTYPE_VOID:
+	case ARGTYPE_INT:
+	case ARGTYPE_UINT:
+	case ARGTYPE_LONG:
+	case ARGTYPE_ULONG:
+	case ARGTYPE_OCTAL:
+	case ARGTYPE_CHAR:
+	case ARGTYPE_SHORT:
+	case ARGTYPE_USHORT:
+	case ARGTYPE_FLOAT:
+	case ARGTYPE_DOUBLE:
+		break;
+
+	case ARGTYPE_ADDR:
+	case ARGTYPE_FILE:
+	case ARGTYPE_FORMAT:
+	case ARGTYPE_STRING:
+	case ARGTYPE_STRING_N:
+	case ARGTYPE_COUNT:
+		break;
+	}
+}
+
+#ifdef ARCH_HAVE_SIZEOF
+size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_sizeof(struct Process *proc, struct arg_type_info * arg)
+{
+	/* Use default value.  */
+	return (size_t)-2;
+}
+#endif
+
+#ifdef ARCH_HAVE_ALIGNOF
+size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_alignof(struct Process *proc, struct arg_type_info * arg)
+{
+	/* Use default value.  */
+	return (size_t)-2;
+}
+#endif
+
+/* We need to support alignments that are not power of two.  E.g. long
+ * double on x86 has alignment of 12.  */
+size_t
+align(size_t sz, size_t alignment)
+{
+	assert(alignment != 0);
+
+	if ((sz % alignment) != 0)
+		sz = ((sz / alignment) + 1) * alignment;
+
+	return sz;
+}
+
+size_t
+type_sizeof(struct Process *proc, struct arg_type_info *type)
+{
+	size_t arch_size = arch_type_sizeof(proc, type);
+	if (arch_size != (size_t)-2)
+		return arch_size;
+
+	switch (type->type) {
+		size_t size;
+	case ARGTYPE_CHAR:
+		return sizeof(char);
+
+	case ARGTYPE_SHORT:
+	case ARGTYPE_USHORT:
+		return sizeof(short);
+
+	case ARGTYPE_INT:
+	case ARGTYPE_UINT:
+	case ARGTYPE_ENUM:
+		return sizeof(int);
+
+	case ARGTYPE_LONG:
+	case ARGTYPE_ULONG:
+		return sizeof(long);
+
+	case ARGTYPE_FLOAT:
+		return sizeof(float);
+
+	case ARGTYPE_DOUBLE:
+		return sizeof(double);
+
+	case ARGTYPE_STRUCT:
+		if (layout_struct(proc, type, &size, NULL, NULL) < 0)
+			return (size_t)-1;
+		return size;
+
+	case ARGTYPE_POINTER:
+		return sizeof(void *);
+
+	case ARGTYPE_ARRAY:
+		if (expr_is_compile_constant(type->u.array_info.len_spec)) {
+			long l;
+			if (expr_eval_constant(type->u.array_info.len_spec,
+					       &l) < 0)
+				return -1;
+
+			struct arg_type_info *elt_ti
+				= type->u.array_info.elt_type;
+
+			size_t elt_size = type_sizeof(proc, elt_ti);
+			if (elt_size == (size_t)-1)
+				return (size_t)-1;
+
+			return ((size_t)l) * elt_size;
+
+		} else {
+			/* Flexible arrays don't count into the
+			 * sizeof.  */
+			return 0;
+		}
+
+	case ARGTYPE_VOID:
+		return 0;
+
+	/* XXX these are in fact formatting conventions, not
+	 * data types.  They should be handled differently.  */
+	case ARGTYPE_OCTAL:
+	case ARGTYPE_UNKNOWN:
+		return sizeof(long);
+
+	case ARGTYPE_ADDR:
+	case ARGTYPE_FILE:
+	case ARGTYPE_FORMAT:
+	case ARGTYPE_STRING:
+	case ARGTYPE_STRING_N:
+	case ARGTYPE_COUNT:
+		return -1;
+	}
+
+	abort();
+}
+
+#undef alignof
+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
+
+size_t
+type_alignof(struct Process *proc, struct arg_type_info *type)
+{
+	size_t arch_alignment = arch_type_alignof(proc, type);
+	if (arch_alignment != (size_t)-2)
+		return arch_alignment;
+
+	struct { char c; char C; } cC;
+	struct { char c; short s; } cs;
+	struct { char c; int i; } ci;
+	struct { char c; long l; } cl;
+	struct { char c; void* p; } cp;
+	struct { char c; float f; } cf;
+	struct { char c; double d; } cd;
+
+	static size_t char_alignment = alignof(C, cC);
+	static size_t short_alignment = alignof(s, cs);
+	static size_t int_alignment = alignof(i, ci);
+	static size_t long_alignment = alignof(l, cl);
+	static size_t ptr_alignment = alignof(p, cp);
+	static size_t float_alignment = alignof(f, cf);
+	static size_t double_alignment = alignof(d, cd);
+
+	switch (type->type) {
+		size_t alignment;
+	case ARGTYPE_LONG:
+	case ARGTYPE_ULONG:
+		return long_alignment;
+	case ARGTYPE_CHAR:
+		return char_alignment;
+	case ARGTYPE_SHORT:
+	case ARGTYPE_USHORT:
+		return short_alignment;
+	case ARGTYPE_FLOAT:
+		return float_alignment;
+	case ARGTYPE_DOUBLE:
+		return double_alignment;
+	case ARGTYPE_POINTER:
+		return ptr_alignment;
+
+	case ARGTYPE_ARRAY:
+		return type_alignof(proc, type->u.array_info.elt_type);
+
+	case ARGTYPE_STRUCT:
+		if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
+			return (size_t)-1;
+		return alignment;
+
+	default:
+		return int_alignment;
+	}
+}
+
+size_t
+type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
+{
+	assert(type->type == ARGTYPE_STRUCT
+	       || type->type == ARGTYPE_ARRAY);
+
+	switch (type->type) {
+		size_t alignment;
+		size_t size;
+	case ARGTYPE_ARRAY:
+		alignment = type_alignof(proc, type->u.array_info.elt_type);
+		if (alignment == (size_t)-1)
+			return (size_t)-1;
+
+		size = type_sizeof(proc, type->u.array_info.elt_type);
+		if (size == (size_t)-1)
+			return (size_t)-1;
+
+		return emt * align(size, alignment);
+
+	case ARGTYPE_STRUCT:
+		if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
+			return (size_t)-1;
+		return emt;
+
+	default:
+		abort ();
+	}
+}
+
+struct arg_type_info *
+type_element(struct arg_type_info *info, size_t emt)
+{
+	assert(info->type == ARGTYPE_STRUCT
+	       || info->type == ARGTYPE_ARRAY);
+
+	switch (info->type) {
+	case ARGTYPE_ARRAY:
+		return info->u.array_info.elt_type;
+
+	case ARGTYPE_STRUCT:
+		assert(emt < type_struct_size(info));
+		return type_struct_get(info, emt);
+
+	default:
+		abort ();
+	}
+}
diff --git a/type.h b/type.h
new file mode 100644
index 0000000..e6e8b74
--- /dev/null
+++ b/type.h
@@ -0,0 +1,181 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1997-2009 Juan Cespedes
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef TYPE_H
+#define TYPE_H
+
+#include <stddef.h>
+#include "forward.h"
+#include "vect.h"
+
+enum arg_type {
+	ARGTYPE_UNKNOWN = -1,
+	ARGTYPE_VOID,
+	ARGTYPE_INT,
+	ARGTYPE_UINT,
+	ARGTYPE_LONG,
+	ARGTYPE_ULONG,
+	ARGTYPE_OCTAL,
+	ARGTYPE_CHAR,
+	ARGTYPE_SHORT,
+	ARGTYPE_USHORT,
+	ARGTYPE_FLOAT,		/* float value, may require index */
+	ARGTYPE_DOUBLE,		/* double value, may require index */
+	ARGTYPE_ADDR,
+	ARGTYPE_FILE,
+	ARGTYPE_FORMAT,		/* printf-like format */
+	ARGTYPE_STRING,		/* NUL-terminated string */
+	ARGTYPE_STRING_N,	/* String of known maxlen */
+	ARGTYPE_ARRAY,		/* Series of values in memory */
+	ARGTYPE_ENUM,		/* Enumeration */
+	ARGTYPE_STRUCT,		/* Structure of values */
+	ARGTYPE_POINTER,	/* Pointer to some other type */
+	ARGTYPE_COUNT		/* number of ARGTYPE_* values */
+};
+
+struct arg_type_info {
+	enum arg_type type;
+	union {
+		struct vect entries;
+
+		/* ARGTYPE_ENUM */
+		struct {
+			size_t entries;
+			char **keys;
+			int *values;
+		} enum_info;
+
+		/* ARGTYPE_ARRAY */
+		struct {
+			struct arg_type_info *elt_type;
+			size_t elt_size;
+			int len_spec;
+			int own_info:1;
+		} array_info;
+
+		/* ARGTYPE_STRING_N */
+		struct {
+			int size_spec;
+		} string_n_info;
+
+		/* ARGTYPE_STRUCT */
+		struct {
+			struct arg_type_info **fields;
+			size_t * offset;
+			size_t size;
+		} struct_info;
+
+		/* ARGTYPE_POINTER */
+		struct {
+			struct arg_type_info *info;
+			int own_info:1;
+		} ptr_info;
+
+		/* ARGTYPE_FLOAT */
+		struct {
+			size_t float_index;
+		} float_info;
+
+		/* ARGTYPE_DOUBLE */
+		struct {
+			size_t float_index;
+		} double_info;
+	} u;
+};
+
+/* Return a type info for simple type TYPE (which shall not be array,
+ * struct, enum or pointer.  Each call with the same TYPE yields the
+ * same arg_type_info pointer.  */
+struct arg_type_info *type_get_simple(enum arg_type type);
+
+/* Initialize INFO so it becomes ARGTYPE_ENUM.  Returns 0 on success
+ * or negative value on failure.  */
+void type_init_enum(struct arg_type_info *info);
+
+/* Push another member of the enumeration, named KEY, with given
+ * VALUE.  If OWN_KEY, KEY is owned and released after the type is
+ * destroyed.  KEY is typed as const char *, but note that if
+ * OWN_KEY, this value will be freed.  */
+int type_enum_add(struct arg_type_info *info,
+		  const char *key, int own_key, int value);
+
+/* Return number of enum elements of type INFO.  */
+size_t type_enum_size(struct arg_type_info *info);
+
+/* Look up enum key with given VALUE in INFO.  */
+const char *type_enum_get(struct arg_type_info *info, int value);
+
+/* Initialize INFO so it becomes ARGTYPE_STRUCT.  The created
+ * structure contains no fields.  Use type_struct_add to populate the
+ * structure.  */
+void type_init_struct(struct arg_type_info *info);
+
+/* Add a new field of type FIELD_INFO to a structure INFO.  If OWN,
+ * the field type is owned and destroyed together with INFO.  */
+int type_struct_add(struct arg_type_info *info,
+		    struct arg_type_info *field_info, int own);
+
+/* Get IDX-th field of structure type INFO.  */
+struct arg_type_info *type_struct_get(struct arg_type_info *info, size_t idx);
+
+/* Return number of fields of structure type INFO.  */
+size_t type_struct_size(struct arg_type_info *info);
+
+/* Initialize INFO so it becomes ARGTYPE_ARRAY.  The element type is
+ * passed in ELEMENT_INFO, and array length in LENGTH_EXPR.  If,
+ * respectively, OWN_INFO and OWN_LENGTH are true, the pointee and
+ * length are owned and destroyed together with INFO.  */
+void type_init_array(struct arg_type_info *info,
+		     struct arg_type_info *element_info, int own_info,
+		     int len_spec);
+
+/* Initialize INFO so it becomes ARGTYPE_POINTER.  The pointee type is
+ * passed in POINTEE_INFO.  If OWN_INFO, the pointee type is owned and
+ * destroyed together with INFO.  */
+void type_init_pointer(struct arg_type_info *info,
+		       struct arg_type_info *pointee_info, int own_info);
+
+/* Release any memory associated with INFO.  Doesn't free INFO
+ * itself.  */
+void type_destroy(struct arg_type_info *info);
+
+/* Compute a size of given type.  Return (size_t)-1 for error.  */
+size_t type_sizeof(struct Process *proc, struct arg_type_info *type);
+
+/* Compute an alignment necessary for elements of this type.  Return
+ * (size_t)-1 for error.  */
+size_t type_alignof(struct Process *proc, struct arg_type_info *type);
+
+/* Align value SZ to ALIGNMENT and return the result.  */
+size_t align(size_t sz, size_t alignment);
+
+/* Return ELT-th element of compound type TYPE.  This is useful for
+ * arrays and structures.  */
+struct arg_type_info *type_element(struct arg_type_info *type, size_t elt);
+
+/* Compute an offset of EMT-th element of type TYPE.  This works for
+ * arrays and structures.  Return (size_t)-1 for error.  */
+size_t type_offsetof(struct Process *proc,
+		     struct arg_type_info *type, size_t elt);
+
+struct arg_type_info *lookup_prototype(enum arg_type at);
+
+#endif /* TYPE_H */
diff --git a/value.c b/value.c
new file mode 100644
index 0000000..3e4e8d6
--- /dev/null
+++ b/value.c
@@ -0,0 +1,372 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "value.h"
+#include "type.h"
+#include "common.h"
+
+static void
+value_common_init(struct value *valp, struct Process *inferior,
+		  struct value *parent, struct arg_type_info *type,
+		  int own_type)
+{
+	valp->type = type;
+	valp->own_type = own_type;
+	valp->inferior = inferior;
+	memset(&valp->u, 0, sizeof(valp->u));
+	valp->where = VAL_LOC_NODATA;
+	valp->parent = parent;
+	valp->size = (size_t)-1;
+}
+
+void
+value_init(struct value *valp, struct Process *inferior, struct value *parent,
+	   struct arg_type_info *type, int own_type)
+{
+	assert(inferior != NULL);
+	value_common_init(valp, inferior, parent, type, own_type);
+}
+
+void
+value_init_detached(struct value *valp, struct value *parent,
+		    struct arg_type_info *type, int own_type)
+{
+	value_common_init(valp, NULL, parent, type, own_type);
+}
+
+void
+value_set_type(struct value *value, struct arg_type_info *type, int own_type)
+{
+	if (value->own_type)
+		type_destroy(value->type);
+	value->type = type;
+	value->own_type = own_type;
+}
+
+void
+value_take_type(struct value *value, struct arg_type_info **type,
+		int *own_type)
+{
+	*type = value->type;
+	*own_type = value->own_type;
+	value->own_type = 0;
+}
+
+void
+value_release(struct value *val)
+{
+	if (val == NULL)
+		return;
+	if (val->where == VAL_LOC_COPY) {
+		free(val->u.address);
+		val->where = VAL_LOC_NODATA;
+	}
+}
+
+void
+value_destroy(struct value *val)
+{
+	if (val == NULL)
+		return;
+	value_release(val);
+	value_set_type(val, NULL, 0);
+}
+
+void
+value_set_long(struct value *valp, long value)
+{
+	valp->where = VAL_LOC_WORD;
+	valp->u.value = value;
+}
+
+unsigned char *
+value_reserve(struct value *valp, size_t size)
+{
+	if (size <= sizeof(valp->u.value)) {
+		value_set_long(valp, 0);
+	} else {
+		valp->where = VAL_LOC_COPY;
+		valp->u.address = calloc(size, 1);
+		if (valp->u.address == 0)
+			return NULL;
+	}
+	return value_get_raw_data(valp);
+}
+
+int
+value_reify(struct value *val, struct value_dict *arguments)
+{
+	if (val->where != VAL_LOC_INFERIOR)
+		return 0;
+	assert(val->inferior != NULL);
+
+	size_t size = value_size(val, arguments);
+	if (size == (size_t)-1)
+		return -1;
+
+	void *data;
+	enum value_location_t nloc;
+	if (size <= sizeof(val->u.value)) {
+		data = &val->u.value;
+		nloc = VAL_LOC_WORD;
+	} else {
+		data = malloc(size);
+		if (data == NULL)
+			return -1;
+		nloc = VAL_LOC_COPY;
+	}
+
+	if (umovebytes(val->inferior, val->u.address, data, size) < size) {
+		if (nloc == VAL_LOC_COPY)
+			free(data);
+		return -1;
+	}
+
+	val->where = nloc;
+	if (nloc == VAL_LOC_COPY)
+		val->u.address = data;
+
+	return 0;
+}
+
+unsigned char *
+value_get_data(struct value *val, struct value_dict *arguments)
+{
+	if (value_reify(val, arguments) < 0)
+		return NULL;
+	return value_get_raw_data(val);
+}
+
+unsigned char *
+value_get_raw_data(struct value *val)
+{
+	switch (val->where) {
+	case VAL_LOC_INFERIOR:
+		abort();
+	case VAL_LOC_NODATA:
+		return NULL;
+	case VAL_LOC_COPY:
+	case VAL_LOC_SHARED:
+		return val->u.address;
+	case VAL_LOC_WORD:
+		return val->u.buf;
+	}
+
+	assert(!"Unexpected value of val->where");
+	abort();
+}
+
+int
+value_clone(struct value *retp, struct value *val)
+{
+	*retp = *val;
+	if (val->where == VAL_LOC_COPY) {
+		assert(val->inferior != NULL);
+		size_t size = type_sizeof(val->inferior, val->type);
+		if (size == (size_t)-1)
+			return -1;
+
+		retp->u.address = malloc(size);
+		if (retp->u.address == NULL)
+			return -1;
+
+		memcpy(retp->u.address, val->u.address, size);
+	}
+
+	return 0;
+}
+
+#include "value_dict.h"
+static int
+expr_eval(int len_spec, struct value *val, struct value_dict *arguments,
+	  struct value *result)
+{
+	if (len_spec > 0) {
+		value_init(result, val->inferior, NULL,
+			   lookup_prototype(ARGTYPE_UINT), 0);
+		value_set_long(result, len_spec);
+		return 0;
+	}
+
+	struct value *v;
+	if (len_spec < 0)
+		v = val_dict_get_num(arguments, -len_spec);
+	else
+		v = val_dict_get_name(arguments, "retval");
+
+	if (v == NULL)
+		return -1;
+
+	*result = *v;
+	return 0;
+}
+
+size_t
+value_size(struct value *val, struct value_dict *arguments)
+{
+	if (val->size != (size_t)-1)
+		return val->size;
+
+	if (val->type->type != ARGTYPE_ARRAY)
+		return val->size = type_sizeof(val->inferior, val->type);
+
+	struct value length;
+	if (expr_eval(val->type->u.array_info.len_spec, val,
+		      arguments, &length) < 0)
+		return (size_t)-1;
+
+	size_t l;
+	int o = value_extract_word(&length, (long *)&l, arguments);
+	value_destroy(&length);
+
+	if (o < 0)
+		return (size_t)-1;
+
+	struct arg_type_info *elt_type = val->type->u.array_info.elt_type;
+	size_t elt_size = type_sizeof(val->inferior, elt_type);
+	if (elt_size == (size_t)-1)
+		return (size_t)-1;
+
+	return val->size = elt_size * l;
+}
+
+int
+value_init_element(struct value *ret_val, struct value *val, size_t element)
+{
+	size_t off = type_offsetof(val->inferior, val->type, element);
+	if (off == (size_t)-1)
+		return -1;
+
+	struct arg_type_info *e_info = type_element(val->type, element);
+	if (e_info == NULL)
+		return -1;
+
+	value_common_init(ret_val, val->inferior, val, e_info, 0);
+
+	switch (val->where) {
+	case VAL_LOC_COPY:
+	case VAL_LOC_SHARED:
+		ret_val->u.address = val->u.address + off;
+		ret_val->where = VAL_LOC_SHARED;
+		return 0;
+
+	case VAL_LOC_WORD:
+		ret_val->u.address = value_get_raw_data(val) + off;
+		ret_val->where = VAL_LOC_SHARED;
+		return 0;
+
+	case VAL_LOC_INFERIOR:
+		ret_val->u.address = val->u.address + off;
+		ret_val->where = VAL_LOC_INFERIOR;
+		return 0;
+
+	case VAL_LOC_NODATA:
+		assert(!"Can't offset NODATA.");
+		abort();
+	}
+	abort();
+}
+
+int
+value_init_deref(struct value *ret_val, struct value *valp)
+{
+	assert(valp->type->type == ARGTYPE_POINTER);
+
+	/* Note: extracting a pointer value should not need value_dict
+	 * with function arguments.  */
+	long l;
+	if (value_extract_word(valp, &l, NULL) < 0)
+		return -1;
+
+	/* We need "long" to be long enough to hold platform
+	 * pointers.  */
+	typedef char assert__long_enough_long[-(sizeof(l) < sizeof(void *))];
+
+	value_common_init(ret_val, valp->inferior, valp,
+			  valp->type->u.ptr_info.info, 0);
+	ret_val->u.value = l; /* Set the address.  */
+	ret_val->where = VAL_LOC_INFERIOR;
+	return 0;
+}
+
+int
+value_extract_word(struct value *value, long *retp, struct value_dict *arguments)
+{
+	union {
+		long val;
+		unsigned char buf[0];
+	} u;
+	memset(u.buf, 0, sizeof(u));
+	if (value_extract_buf(value, u.buf, arguments) < 0)
+		return -1;
+	*retp = u.val;
+	return 0;
+}
+
+int
+value_extract_buf(struct value *value, unsigned char *tgt,
+		  struct value_dict *arguments)
+{
+	unsigned char *data = value_get_data(value, arguments);
+	if (data == NULL)
+		return -1;
+
+	size_t sz = type_sizeof(value->inferior, value->type);
+	if (sz == (size_t)-1)
+		return -1;
+
+	memcpy(tgt, data, sz);
+	return 0;
+}
+
+struct value *
+value_get_parental_struct(struct value *val)
+{
+	struct value *parent;
+	for (parent = val->parent; parent != NULL; parent = parent->parent)
+		if (parent->type->type == ARGTYPE_STRUCT)
+			return parent;
+	return NULL;
+}
+
+int
+value_is_zero(struct value *val, struct value_dict *arguments)
+{
+	unsigned char *data = value_get_data(val, arguments);
+	if (data == NULL)
+		return -1;
+	size_t sz = type_sizeof(val->inferior, val->type);
+	if (sz == (size_t)-1)
+		return -1;
+
+	int zero = 1;
+	size_t j;
+	for (j = 0; j < sz; ++j) {
+		if (data[j] != 0) {
+			zero = 0;
+			break;
+		}
+	}
+	return zero;
+}
diff --git a/value.h b/value.h
new file mode 100644
index 0000000..e88b72a
--- /dev/null
+++ b/value.h
@@ -0,0 +1,152 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef VALUE_H
+#define VALUE_H
+
+#include "forward.h"
+
+/* Values are objects that capture data fetched from an inferior.
+ * Typically a value is attached to a single inferior where it was
+ * extracted from, but it is possible to create a detached value.
+ * Each value is typed.  Values support a number of routines, such as
+ * dereferencing if the value is of pointer type, array or structure
+ * access, etc.
+ *
+ * A value can be uninitialized, abstract or reified.  Abstract values
+ * are just references into inferior, no transfer has taken place yet.
+ * Reified values have been copied out of the corresponding inferior,
+ * or otherwise set to some value.  */
+
+enum value_location_t {
+	VAL_LOC_NODATA = 0,	/* Uninitialized.  */
+	VAL_LOC_INFERIOR,	/* Value is in the inferior process.  */
+	VAL_LOC_COPY,		/* Value was copied out of the inferior.  */
+	VAL_LOC_SHARED,		/* Like VAL_LOC_COPY, but don't free.  */
+	VAL_LOC_WORD,		/* Like VAL_LOC_COPY, but small enough.  */
+};
+
+struct value {
+	struct arg_type_info *type;
+	struct Process *inferior;
+	struct value *parent;
+	size_t size;
+	union {
+		void *address;  /* VAL_LOC_CLIENT, VAL_LOC_COPY,
+				   VAL_LOC_SHARED */
+		long value;     /* VAL_LOC_WORD */
+		unsigned char buf[0];
+	} u;
+	enum value_location_t where;
+	int own_type;
+};
+
+/* Initialize VALUE.  INFERIOR must not be NULL.  PARENT is parental
+ * value, in case of compound types.  It may be NULL.  TYPE is a type
+ * of the value.  It may be NULL if the type is not yet known.  If
+ * OWN_TYPE, the passed-in type is owned and released by value.  */
+void value_init(struct value *value, struct Process *inferior,
+		struct value *parent, struct arg_type_info *type,
+		int own_type);
+
+/* Initialize VALUE.  This is like value_init, except that inferior is
+ * NULL.  VALP is initialized as a detached value, without assigned
+ * process.  You have to be careful not to use VAL_LOC_INFERIOR
+ * values if the value is detached.  */
+void value_init_detached(struct value *value, struct value *parent,
+			 struct arg_type_info *type, int own_type);
+
+/* Set TYPE.  This releases old type if it was owned.  TYPE is owned
+ * and released if OWN_TYPE.  */
+void value_set_type(struct value *value,
+		    struct arg_type_info *type, int own_type);
+
+/* Transfers the ownership of VALUE's type, if any, to the caller.
+ * This doesn't reset the VALUE's type, but gives up ownership if
+ * there was one.  Previous ownership is passed in OWN_TYPE.  */
+void value_take_type(struct value *value,
+		     struct arg_type_info **type, int *own_type);
+
+/* Release the data held by VALP, if any, but not the type.  */
+void value_release(struct value *valp);
+
+/* Destroy the value.  This is like value_release, but it additionally
+ * frees the value type, if it's own_type.  It doesn't free the VAL
+ * pointer itself.  */
+void value_destroy(struct value *val);
+
+/* Set the data held by VALP to VALUE.  This also sets the value's
+ * where to VAL_LOC_WORD.  */
+void value_set_long(struct value *valp, long value);
+
+/* Set the data held by VALP to a buffer of size SIZE.  This buffer
+ * may be allocated by malloc.  Returns NULL on failure.  */
+unsigned char *value_reserve(struct value *valp, size_t size);
+
+/* Access ELEMENT-th field of the compound value VALP, and store the
+ * result into the value RET_VAL.  Returns 0 on success, or negative
+ * value on failure.  */
+int value_init_element(struct value *ret_val, struct value *valp, size_t element);
+
+/* De-reference pointer value, and store the result into the value
+ * RET_VAL.  Returns 0 on success, or negative value on failure.  */
+int value_init_deref(struct value *ret_val, struct value *valp);
+
+/* If value is in inferior, copy it over to ltrace.  Return 0 for
+ * success or negative value for failure.  */
+int value_reify(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the data of the value.  This copies the data
+ * from the inferior to the tracer.  Returns NULL on failure.  */
+unsigned char *value_get_data(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the raw data of the value.  This shall not be
+ * called on a VAL_LOC_INFERIOR value.  */
+unsigned char *value_get_raw_data(struct value *val);
+
+/* Copy value VAL into the area pointed-to by RETP.  Return 0 on
+ * success or a negative value on failure.  */
+int value_clone(struct value *retp, struct value *val);
+
+/* Give a size of given value.  Return (size_t)-1 for error.  This is
+ * a full size of the value.  In particular for arrays, it returns
+ * actual length of the array, as computed by the length
+ * expression.  */
+size_t value_size(struct value *val, struct value_dict *arguments);
+
+/* Extract at most word-sized datum from the value.  Return 0 on
+ * success or negative value on failure.  */
+int value_extract_word(struct value *val, long *retp,
+		       struct value_dict *arguments);
+
+/* Copy contents of VAL to DATA.  The buffer must be large enough to
+ * hold all the data inside.  */
+int value_extract_buf(struct value *val, unsigned char *data,
+		      struct value_dict *arguments);
+
+/* Find the most enclosing parental value that is a struct.  Return
+ * NULL when there is no such parental value.  */
+struct value *value_get_parental_struct(struct value *val);
+
+/* Determine whether this is all-zero value.  Returns >0 if it is, ==0
+ * if it isn't, <0 on error.  */
+int value_is_zero(struct value *val, struct value_dict *arguments);
+
+#endif /* VALUE_H */
diff --git a/value_dict.c b/value_dict.c
new file mode 100644
index 0000000..3f3880a
--- /dev/null
+++ b/value_dict.c
@@ -0,0 +1,147 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "value_dict.h"
+#include "value.h"
+
+struct named_value
+{
+	const char *name;
+	struct value value;
+	int own_name;
+};
+
+void
+val_dict_init(struct value_dict *dict)
+{
+	VECT_INIT(&dict->numbered, struct value);
+	VECT_INIT(&dict->named, struct named_value);
+}
+
+static int
+value_clone_cb(struct value *tgt, struct value *src, void *data)
+{
+	return value_clone(tgt, src);
+}
+
+static void
+value_dtor(struct value *val, void *data)
+{
+	value_destroy(val);
+}
+
+static int
+named_value_clone(struct named_value *tgt, struct named_value *src, void *data)
+{
+	tgt->name = strdup(src->name);
+	if (tgt->name == NULL)
+		return -1;
+	tgt->own_name = 1;
+	if (value_clone(&tgt->value, &src->value) < 0) {
+		free((char *)tgt->name);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+named_value_dtor(struct named_value *named, void *data)
+{
+	if (named->own_name)
+		free((char *)named->name);
+	value_destroy(&named->value);
+}
+
+int
+val_dict_clone(struct value_dict *target, struct value_dict *source)
+{
+	if (VECT_CLONE(&target->numbered, &source->numbered, struct value,
+		       value_clone_cb, value_dtor, NULL) < 0)
+		return -1;
+
+	if (VECT_CLONE(&target->named, &source->named, struct named_value,
+		       named_value_clone, named_value_dtor, NULL) < 0) {
+		VECT_DESTROY(&target->numbered, struct value, value_dtor, NULL);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+val_dict_push_next(struct value_dict *dict, struct value *val)
+{
+	return VECT_PUSHBACK(&dict->numbered, val);
+}
+
+int
+val_dict_push_named(struct value_dict *dict, struct value *val,
+		    const char *name, int own_name)
+{
+	if (own_name && (name = strdup(name)) == NULL)
+		return -1;
+	struct named_value element = { name, *val, own_name };
+	if (VECT_PUSHBACK(&dict->named, &element) < 0) {
+		if (own_name)
+			free((char *)name);
+		return -1;
+	}
+	return 0;
+}
+
+size_t
+val_dict_count(struct value_dict *dict)
+{
+	return vect_size(&dict->numbered);
+}
+
+struct value *
+val_dict_get_num(struct value_dict *dict, size_t num)
+{
+	assert(num < vect_size(&dict->numbered));
+	return VECT_ELEMENT(&dict->numbered, struct value, num);
+}
+
+struct value *
+val_dict_get_name(struct value_dict *dict, const char *name)
+{
+	size_t i;
+	for (i = 0; i < vect_size(&dict->named); ++i) {
+		struct named_value *element
+			= VECT_ELEMENT(&dict->named, struct named_value, i);
+		if (strcmp(element->name, name) == 0)
+			return &element->value;
+	}
+	return NULL;
+}
+
+void
+val_dict_destroy(struct value_dict *dict)
+{
+	if (dict == NULL)
+		return;
+
+	VECT_DESTROY(&dict->numbered, struct value, value_dtor, NULL);
+	VECT_DESTROY(&dict->named, struct named_value, named_value_dtor, NULL);
+}
diff --git a/value_dict.h b/value_dict.h
new file mode 100644
index 0000000..f75ee37
--- /dev/null
+++ b/value_dict.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef ARGS_H
+#define ARGS_H
+
+#include "forward.h"
+#include "vect.h"
+
+/* Value dictionary is used to store actual function arguments.  It
+ * supports both numbered and named arguments.  */
+struct value_dict
+{
+	struct vect numbered;
+	struct vect named;
+};
+
+/* Initialize DICT.  */
+void val_dict_init(struct value_dict *dict);
+
+/* Clone SOURCE into TARGET.  Return 0 on success or a negative value
+ * on failure.  */
+int val_dict_clone(struct value_dict *target, struct value_dict *source);
+
+/* Push next numbered value, VAL.  The value is copied over and the
+ * dictionary becomes its owner, and is responsible for destroying it
+ * later.  Returns 0 on success and a negative value on failure.  */
+int val_dict_push_next(struct value_dict *dict, struct value *val);
+
+/* Return count of numbered arguments.  */
+size_t val_dict_count(struct value_dict *dict);
+
+/* Push value VAL named NAME.  See notes at val_dict_push_next about
+ * value ownership.  The name is owned and freed if OWN_NAME is
+ * non-zero.  */
+int val_dict_push_named(struct value_dict *dict, struct value *val,
+			const char *name, int own_name);
+
+/* Get NUM-th numbered argument, or NULL if there's not that much
+ * arguments.  */
+struct value *val_dict_get_num(struct value_dict *dict, size_t num);
+
+/* Get argument named NAME, or NULL if there's no such argument.  */
+struct value *val_dict_get_name(struct value_dict *dict, const char *name);
+
+/* Destroy the dictionary and all the values in it.  Note that DICT
+ * itself (the pointer) is not freed.  */
+void val_dict_destroy(struct value_dict *dict);
+
+#endif /* ARGS_H */