Allow forward declarations of structs

Now ltrace can display singly-linked lists.  Recursion detection was added
to format_pointer.  It relies on the fact that ltrace is not multi-threaded
and doesn't need per-process or per-thread cache of already-displayed
values, because we display one value at a time anyway.

Several test cases added.
diff --git a/lens_default.c b/lens_default.c
index a2233b8..f3e328f 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -27,6 +27,7 @@
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "proc.h"
 #include "lens_default.h"
@@ -250,6 +251,36 @@
 	if (value_is_zero(value, arguments))
 		return fprintf(stream, "nil");
 
+	/* The following is for detecting recursion.  We keep track of
+	 * the values that were already displayed.  Each time a
+	 * pointer should be dereferenced, we compare its value to the
+	 * value of each of the pointers dereferenced so far.  If one
+	 * of them matches, instead of recursing, we just printf which
+	 * superstructure this pointer recurses to.  */
+	static struct vect pointers = {};
+	if (pointers.elt_size == 0)
+		VECT_INIT(&pointers, struct value *);
+
+	size_t len = vect_size(&pointers);
+	size_t i;
+	for (i = len; i-- > 0 ;) {
+		struct value **old = VECT_ELEMENT(&pointers, struct value *, i);
+		int rc = value_equal(value, *old, arguments);
+		if (rc < 0)
+			return -1;
+		if (rc > 0) {
+			size_t reclevel = len - i - 1;
+			char buf[reclevel + 1];
+			memset(buf, '^', sizeof buf);
+			buf[reclevel] = 0;
+			return fprintf(stream, "recurse%s", buf);
+		}
+	}
+
+	/* OK, not a recursion.  Remember this value for tracking.  */
+	if (VECT_PUSHBACK(&pointers, &value) < 0)
+		return -1;
+
 	struct value element;
 	int o;
 	if (value_init_deref(&element, value) < 0) {
@@ -260,6 +291,7 @@
 	value_destroy(&element);
 
 done:
+	vect_popback(&pointers);
 	return o;
 }
 
diff --git a/read_config_file.c b/read_config_file.c
index b93cec6..18fc156 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -43,14 +43,18 @@
 
 static int line_no;
 static char *filename;
+struct typedef_node_t;
 
 static struct arg_type_info *parse_nonpointer_type(char **str,
 						   struct param **extra_param,
-						   size_t param_num, int *ownp);
+						   size_t param_num, int *ownp,
+						   struct typedef_node_t *td);
 static struct arg_type_info *parse_type(char **str, struct param **extra_param,
-					size_t param_num, int *ownp);
+					size_t param_num, int *ownp,
+					struct typedef_node_t *in_typedef);
 static struct arg_type_info *parse_lens(char **str, struct param **extra_param,
-					size_t param_num, int *ownp);
+					size_t param_num, int *ownp,
+					struct typedef_node_t *in_typedef);
 static int parse_enum(char **str, struct arg_type_info **retp, int *ownp);
 
 Function *list_of_functions = NULL;
@@ -359,16 +363,17 @@
 	char *name;
 	struct arg_type_info *info;
 	int own_type;
+	int forward : 1;
 	struct typedef_node_t *next;
 } *typedefs = NULL;
 
-static struct arg_type_info *
+static struct typedef_node_t *
 lookup_typedef(const char *name)
 {
 	struct typedef_node_t *node;
 	for (node = typedefs; node != NULL; node = node->next)
 		if (strcmp(name, node->name) == 0)
-			return node->info;
+			return node;
 	return NULL;
 }
 
@@ -387,18 +392,30 @@
 	*str += len;
 	buf[len] = 0;
 
-	return lookup_typedef(buf);
+	struct typedef_node_t *td = lookup_typedef(buf);
+	if (td == NULL)
+		return NULL;
+	return td->info;
+}
+
+static void
+insert_typedef(struct typedef_node_t *td)
+{
+	if (td == NULL)
+		return;
+	td->next = typedefs;
+	typedefs = td;
 }
 
 static struct typedef_node_t *
-insert_typedef(char *name, struct arg_type_info *info, int own_type)
+new_typedef(char *name, struct arg_type_info *info, int own_type)
 {
 	struct typedef_node_t *binding = malloc(sizeof(*binding));
 	binding->name = name;
 	binding->info = info;
 	binding->own_type = own_type;
-	binding->next = typedefs;
-	typedefs = binding;
+	binding->forward = 0;
+	binding->next = NULL;
 	return binding;
 }
 
@@ -409,10 +426,15 @@
 	eat_spaces(str);
 	char *name = parse_ident(str);
 
-	struct arg_type_info *info = lookup_typedef(name);
-	if (info != NULL) {
+	/* Look through the typedef list whether we already have a
+	 * forward of this type.  If we do, it must be forward
+	 * structure.  */
+	struct typedef_node_t *forward = lookup_typedef(name);
+	if (forward != NULL
+	    && (forward->info->type != ARGTYPE_STRUCT
+		|| !forward->forward)) {
 		report_error(filename, line_no,
-			     "Redefinition of typedef '%s'\n", name);
+			     "Redefinition of typedef '%s'", name);
 		return;
 	}
 
@@ -422,11 +444,42 @@
 		return;
 	eat_spaces(str);
 
-	// Parse the type
-	int own;
-	info = parse_type(str, NULL, 0, &own);
+	struct typedef_node_t *this_td = new_typedef(name, NULL, 0);
+	this_td->info = parse_type(str, NULL, 0, &this_td->own_type, this_td);
 
-	insert_typedef(name, info, own);
+	if (this_td->info == NULL) {
+		free(this_td);
+		return;
+	}
+
+	if (forward == NULL) {
+		insert_typedef(this_td);
+		return;
+	}
+
+	/* If we are defining a forward, make sure the definition is a
+	 * structure as well.  */
+	if (this_td->info->type != ARGTYPE_STRUCT) {
+		report_error(filename, line_no,
+			     "Definition of forward '%s' must be a structure.",
+			     name);
+		if (this_td->own_type) {
+			type_destroy(this_td->info);
+			free(this_td->info);
+		}
+		return;
+	}
+
+	/* Now move guts of the actual type over to the
+	 * forward type.  We can't just move pointers around,
+	 * because references to forward must stay intact.  */
+	assert(this_td->own_type);
+	type_destroy(forward->info);
+	*forward->info = *this_td->info;
+	forward->forward = 0;
+	free(this_td->info);
+	free(name);
+	free(this_td);
 }
 
 static void
@@ -446,9 +499,26 @@
 
 /* Syntax: struct ( type,type,type,... ) */
 static int
-parse_struct(char **str, struct arg_type_info *info)
+parse_struct(char **str, struct arg_type_info *info,
+	     struct typedef_node_t *in_typedef)
 {
 	eat_spaces(str);
+
+	if (**str == ';') {
+		if (in_typedef == NULL) {
+			report_error(filename, line_no,
+				     "Forward struct can be declared only "
+				     "directly after a typedef.");
+			return -1;
+		}
+
+		/* Forward declaration is currently handled as an
+		 * empty struct.  */
+		type_init_struct(info);
+		in_typedef->forward = 1;
+		return 0;
+	}
+
 	if (parse_char(str, '(') < 0)
 		return -1;
 
@@ -469,7 +539,8 @@
 
 		eat_spaces(str);
 		int own;
-		struct arg_type_info *field = parse_lens(str, NULL, 0, &own);
+		struct arg_type_info *field = parse_lens(str, NULL, 0, &own,
+							 NULL);
 		if (field == NULL || type_struct_add(info, field, own)) {
 			type_destroy(info);
 			return -1;
@@ -536,7 +607,7 @@
 			free(info);
 
 			eat_spaces(str);
-			info = parse_type(str, NULL, 0, ownp);
+			info = parse_type(str, NULL, 0, ownp, NULL);
 			if (info == NULL)
 				goto fail;
 
@@ -681,7 +752,7 @@
 
 	eat_spaces(str);
 	int own;
-	struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own);
+	struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own, NULL);
 	if (elt_info == NULL)
 		return -1;
 
@@ -718,7 +789,7 @@
 	if (**str == '[') {
 		parse_char(str, '[');
 		eat_spaces(str);
-		*retp = parse_nonpointer_type(str, NULL, 0, ownp);
+		*retp = parse_nonpointer_type(str, NULL, 0, ownp, 0);
 		if (*retp == NULL)
 			return -1;
 
@@ -809,7 +880,7 @@
 
 static struct arg_type_info *
 parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
-		      int *ownp)
+		      int *ownp, struct typedef_node_t *in_typedef)
 {
 	enum arg_type type;
 	if (parse_arg_type(str, &type) < 0) {
@@ -828,8 +899,6 @@
 		return NULL;
 	}
 
-	int (*parser) (char **, struct arg_type_info *) = NULL;
-
 	/* For some types that's all we need.  */
 	switch (type) {
 	case ARGTYPE_VOID:
@@ -846,11 +915,7 @@
 		return type_get_simple(type);
 
 	case ARGTYPE_ARRAY:
-		parser = parse_array;
-		break;
-
 	case ARGTYPE_STRUCT:
-		parser = parse_struct;
 		break;
 
 	case ARGTYPE_POINTER:
@@ -868,9 +933,16 @@
 	}
 	*ownp = 1;
 
-	if (parser(str, info) < 0) {
-		free(info);
-		return NULL;
+	if (type == ARGTYPE_ARRAY) {
+		if (parse_array(str, info) < 0) {
+		fail:
+			free(info);
+			return NULL;
+		}
+	} else {
+		assert(type == ARGTYPE_STRUCT);
+		if (parse_struct(str, info, in_typedef) < 0)
+			goto fail;
 	}
 
 	return info;
@@ -901,10 +973,12 @@
 }
 
 static struct arg_type_info *
-parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp)
+parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp,
+	   struct typedef_node_t *in_typedef)
 {
 	struct arg_type_info *info
-		= parse_nonpointer_type(str, extra_param, param_num, ownp);
+		= parse_nonpointer_type(str, extra_param,
+					param_num, ownp, in_typedef);
 	if (info == NULL)
 		return NULL;
 
@@ -932,7 +1006,8 @@
 }
 
 static struct arg_type_info *
-parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp)
+parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp,
+	   struct typedef_node_t *in_typedef)
 {
 	int own_lens;
 	struct lens *lens = name2lens(str, &own_lens);
@@ -956,7 +1031,8 @@
 
 	if (has_args) {
 		eat_spaces(str);
-		info = parse_type(str, extra_param, param_num, ownp);
+		info = parse_type(str, extra_param, param_num, ownp,
+				  in_typedef);
 		if (info == NULL) {
 		fail:
 			if (own_lens && lens != NULL)
@@ -1014,7 +1090,7 @@
 	char *ptr = str;
 	assert(str != NULL);
 	int own;
-	struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own);
+	struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own, NULL);
 	assert(info != NULL);
 	free(str);
 	return info;
@@ -1045,7 +1121,8 @@
 		return NULL;
 	}
 
-	fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info);
+	fun->return_info = parse_lens(&str, NULL, 0,
+				      &fun->own_return_info, NULL);
 	if (fun->return_info == NULL) {
 	err:
 		debug(3, " Skipping line %d", line_no);
@@ -1096,7 +1173,7 @@
 		int own;
 		struct arg_type_info *type
 			= parse_lens(&str, &extra_param,
-				     fun->num_params - have_stop, &own);
+				     fun->num_params - have_stop, &own, NULL);
 		if (type == NULL) {
 			report_error(filename, line_no,
 				     "unknown argument type");
@@ -1181,8 +1258,8 @@
 	info[0].u.ptr_info.info = &info[1];
 	info[1].type = ARGTYPE_VOID;
 
-	insert_typedef(strdup("addr"), info, 0);
-	insert_typedef(strdup("file"), info, 1);
+	insert_typedef(new_typedef(strdup("addr"), info, 0));
+	insert_typedef(new_typedef(strdup("file"), info, 1));
 }
 
 void
diff --git a/testsuite/ltrace.main/parameters2.exp b/testsuite/ltrace.main/parameters2.exp
index a7c32e0..6982de3 100644
--- a/testsuite/ltrace.main/parameters2.exp
+++ b/testsuite/ltrace.main/parameters2.exp
@@ -27,4 +27,69 @@
     typedef aa = int;
 }] -- true] "error" != 0
 
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+    typedef aa = struct;
+    typedef aa = int;
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+    typedef aa = struct;
+    typedef aa = struct(int);
+    typedef aa = struct(int);
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+    typedef aa = struct;
+    typedef aa = struct();
+    typedef aa = struct();
+}] -- true] "error" != 0
+
+ltraceMatch1 [ltraceRun -L -F [ltraceSource conf {
+    typedef aa = struct(int, struct;);
+}] -- true] "error" != 0
+
+set libll [ltraceCompile libll.so [ltraceSource c {
+    struct xxx;
+    void ll(struct xxx *xxx) {}
+}]]
+
+set bin [ltraceCompile ll $libll [ltraceSource c {
+    struct xxx {
+	int i;
+	struct xxx *next;
+    };
+
+    void ll (struct xxx *xxx);
+    int main (int argc, char *argv[])
+    {
+	struct xxx a = { 1, 0 };
+	struct xxx b = { 2, &a };
+	struct xxx c = { 3, &b };
+	struct xxx d = { 4, &c };
+	ll (&d);
+
+	struct xxx e = { 1, 0 };
+	struct xxx f = { 2, &e };
+	e.next = &f;
+	ll (&f);
+
+	struct xxx g = { 1, &g };
+	ll (&g);
+
+	return 0;
+    }
+}]]
+
+set conf [ltraceSource conf {
+    typedef xxx = struct;
+    typedef xxx = struct(int, xxx*);
+    void ll(xxx*);
+}]
+
+ltraceMatch [ltraceRun -F $conf -e ll -- $bin] {
+    {{ll->ll\({ 4, { 3, { 2, { 1, nil } } } }\) *= <void>} == 1}
+    {{ll->ll\({ 2, { 1, recurse\^ } }\) *= <void>} == 1}
+    {{ll->ll\({ 1, recurse }\) *= <void>} == 1}
+}
+
 ltraceDone