Add expr_clone, type_clone; call from value_clone

- This fixes several memory errors in trace_clone and hello_vfork test
  cases.
diff --git a/expr.c b/expr.c
index 552a53c..4059a32 100644
--- a/expr.c
+++ b/expr.c
@@ -1,6 +1,6 @@
 /*
  * This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 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
@@ -171,6 +171,85 @@
 	abort();
 }
 
+static int
+expr_alloc_and_clone(struct expr_node **retpp, struct expr_node *node, int own)
+{
+	*retpp = node;
+	if (own) {
+		*retpp = malloc(sizeof **retpp);
+		if (*retpp == NULL || expr_clone(*retpp, node) < 0) {
+			free(*retpp);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int
+expr_clone(struct expr_node *retp, const struct expr_node *node)
+{
+	*retp = *node;
+
+	switch (node->kind) {
+		struct expr_node *nlhs;
+		struct expr_node *nrhs;
+
+	case EXPR_OP_ARGNO:
+	case EXPR_OP_SELF:
+		return 0;
+
+	case EXPR_OP_CONST:
+		return value_clone(&retp->u.value, &node->u.value);
+
+	case EXPR_OP_NAMED:
+		if (node->u.name.own
+		    && (retp->u.name.s = strdup(node->u.name.s)) == NULL)
+			return -1;
+		return 0;
+
+	case EXPR_OP_INDEX:
+		if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0)
+			return -1;
+
+		if (expr_alloc_and_clone(&nrhs, node->u.node.n,
+					 node->u.node.own) < 0) {
+			if (nlhs != node->lhs) {
+				expr_destroy(nlhs);
+				free(nlhs);
+			}
+			return -1;
+		}
+
+		retp->lhs = nlhs;
+		retp->u.node.n = nrhs;
+		return 0;
+
+	case EXPR_OP_CALL2:
+		if (expr_alloc_and_clone(&nrhs, node->u.call.rhs,
+					 node->u.call.own_rhs) < 0)
+			return -1;
+		retp->u.call.rhs = nrhs;
+		/* Fall through.  */
+
+	case EXPR_OP_UP:
+	case EXPR_OP_CALL1:
+		if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0) {
+			if (node->kind == EXPR_OP_CALL2
+			    && node->u.call.own_rhs) {
+				expr_destroy(nrhs);
+				free(nrhs);
+				return -1;
+			}
+		}
+
+		retp->lhs = nlhs;
+		return 0;
+	}
+
+	assert(!"Invalid value of node kind");
+	abort();
+}
+
 int
 expr_is_compile_constant(struct expr_node *node)
 {
diff --git a/expr.h b/expr.h
index 53b75b7..ef49c25 100644
--- a/expr.h
+++ b/expr.h
@@ -1,6 +1,6 @@
 /*
  * This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 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
@@ -125,6 +125,10 @@
 /* Release the data inside NODE.  Doesn't free NODE itself.  */
 void expr_destroy(struct expr_node *node);
 
+/* Copy expression NODE into the area pointed to by RETP.  Return 0 on
+ * success or a negative value on failure.  */
+int expr_clone(struct expr_node *retp, const struct expr_node *node);
+
 /* Evaluate the expression NODE in context of VALUE.  ARGUMENTS is a
  * dictionary of named and numbered values that NODE may use.  Returns
  * 0 in case of success or a negative value on error.  CONTEXT and
diff --git a/type.c b/type.c
index d5bc98f..458da4b 100644
--- a/type.c
+++ b/type.c
@@ -249,6 +249,95 @@
 	}
 }
 
+static int
+type_alloc_and_clone(struct arg_type_info **retpp,
+		     struct arg_type_info *info, int own)
+{
+	*retpp = info;
+	if (own) {
+		*retpp = malloc(sizeof **retpp);
+		if (*retpp == NULL || type_clone(*retpp, info) < 0) {
+			free(*retpp);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static enum callback_status
+clone_struct_add_field(const struct struct_field *field, void *data)
+{
+	struct arg_type_info *retp = data;
+	struct arg_type_info *info;
+	if (type_alloc_and_clone(&info, field->info, field->own_info) < 0) {
+	fail:
+		if (info != field->info)
+			free(info);
+		return CBS_STOP;
+	}
+
+	if (type_struct_add(retp, info, field->own_info) < 0) {
+		if (field->own_info)
+			type_destroy(info);
+		goto fail;
+	}
+
+	return CBS_CONT;
+}
+
+int
+type_clone(struct arg_type_info *retp, const struct arg_type_info *info)
+{
+	switch (info->type) {
+	case ARGTYPE_STRUCT:
+		type_init_struct(retp);
+		if (VECT_EACH_CST(&info->u.entries, struct struct_field, NULL,
+				  clone_struct_add_field, retp) != NULL) {
+			type_destroy(retp);
+			return -1;
+		}
+		break;
+
+	case ARGTYPE_ARRAY:;
+		struct arg_type_info *elt_type;
+		if (type_alloc_and_clone(&elt_type, info->u.array_info.elt_type,
+					 info->u.array_info.own_info) < 0)
+			return -1;
+
+		assert(!info->u.array_info.own_length); // XXXXXXX
+		type_init_array(retp, elt_type, info->u.array_info.own_info,
+				info->u.array_info.length,
+				info->u.array_info.own_length);
+		break;
+
+	case ARGTYPE_POINTER:;
+		struct arg_type_info *ninfo;
+		if (type_alloc_and_clone(&ninfo, info->u.ptr_info.info,
+					 info->u.ptr_info.own_info) < 0)
+			return -1;
+		type_init_pointer(retp, ninfo, info->u.ptr_info.own_info);
+		break;
+
+	case ARGTYPE_VOID:
+	case ARGTYPE_INT:
+	case ARGTYPE_UINT:
+	case ARGTYPE_LONG:
+	case ARGTYPE_ULONG:
+	case ARGTYPE_CHAR:
+	case ARGTYPE_SHORT:
+	case ARGTYPE_USHORT:
+	case ARGTYPE_FLOAT:
+	case ARGTYPE_DOUBLE:
+		*retp = *info;
+		break;
+	}
+
+	assert(!info->own_lens);
+	retp->lens = info->lens;
+	retp->own_lens = info->own_lens;
+	return 0;
+}
+
 #ifdef ARCH_HAVE_SIZEOF
 size_t arch_type_sizeof(struct process *proc, struct arg_type_info *arg);
 #else
diff --git a/type.h b/type.h
index 3210677..0ffbd98 100644
--- a/type.h
+++ b/type.h
@@ -110,6 +110,10 @@
  * itself.  */
 void type_destroy(struct arg_type_info *info);
 
+/* Copy type INFO into the area pointed to by RETP.  Return 0 on
+ * success or a negative value on failure.  */
+int type_clone(struct arg_type_info *retp, const 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);
 
diff --git a/value.c b/value.c
index 8caf98c..2125ba9 100644
--- a/value.c
+++ b/value.c
@@ -189,15 +189,30 @@
 value_clone(struct value *retp, const struct value *val)
 {
 	*retp = *val;
+
+	if (val->own_type) {
+		retp->type = malloc(sizeof(struct arg_type_info));
+		if (type_clone (retp->type, val->type) < 0) {
+			free(retp->type);
+			return -1;
+		}
+	}
+
 	if (val->where == VAL_LOC_COPY) {
 		assert(val->inferior != NULL);
 		size_t size = type_sizeof(val->inferior, val->type);
-		if (size == (size_t)-1)
+		if (size == (size_t)-1) {
+		fail:
+			if (retp->own_type) {
+				type_destroy(retp->type);
+				free(retp->type);
+			}
 			return -1;
+		}
 
 		retp->u.address = malloc(size);
 		if (retp->u.address == NULL)
-			return -1;
+			goto fail;
 
 		memcpy(retp->u.address, val->u.address, size);
 	}