[Ltrace-devel] Series of patches for supporting more parameter types

Steve Fink sphink at gmail.com
Fri Jul 28 05:02:35 UTC 2006


On 7/26/06, Petr Machata <pmachata at redhat.com> wrote:
> I've looked at the patches today.  They are pretty generic code, so I've
> just gave it a shot on i686. Just nitpicking:
> * patches 1 and 4 lack ChangeLog entry.

Oh, right. I wasn't sure if the ChangeLog was only for user-visible
changes or not, and was inconsistent about updating it.

> * last hunk of patch 5 should be moved to patch 4, otherwise you are
> doing changes before you adjust what calls the changed code

Ouch. You're right.

> * patch 5 contains new files (parameters.c, parameters-lib.c) that fail
> to compile, and testsuite won't pass. The fix is trivial.

Odd. They compile for me, but it makes sense that your change would be needed.

> I'm attaching modified patches 4 and 5, should they come in handy.
> Didn't add ChangeLog entries though.

Thanks. I added ChangeLog entries and your changes and regenerated the
whole batch of patches. I also tacked on the rest of the changes that
I've had backed up, which add pointers to arrays and structs. The
struct patch is pretty much guaranteed to be somewhat nonportable
because of alignment issues, but I wanted to get it out here for
discussion.

Oh, I also switched the way I added ChangeLog entries midway through
this series, because it was looking rather verbose with a separate
change section per patch. The latter half are all aggregated under one
change heading. I'm not sure what the preferred style is.

Finally, I tacked on a patch to change the man page to say that bugs
should be sent to this mailing list. It previously said to send them
to the author, and gives Juan Cespedes' email address. That clearly
seems wrong, but I don't know if the mailing list is what it should be
changed to or not.

I'm afraid I'm going to be lazy and attach the raw, git-generated
patch messages instead of editing them nicely.
-------------- next part --------------
From e3dcba8aa67ef060b4de9de644ce04ac01d0a1b7 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 07:58:50 -0700
Subject: [PATCH 1/11] expand parameter information from arg_type to arg_type_info

Switch to using arg_type_info so that we can record additional
information about types other than their names. This has no effect
right now, but is the necessary support for things like enumerations,
typedefs, strings that take their length from any argument, etc.
---
 ChangeLog          |    7 +++
 ltrace.h           |   31 +++++++++++----
 output.c           |   24 +++++++-----
 read_config_file.c |  105 ++++++++++++++++++++++++++++++++++++++++------------
 4 files changed, 125 insertions(+), 42 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b89fbe0..f23e7cd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2006-07-27    Steve Fink <sphink at gmail.com>
+
+	* read_config_file.c: use arg_type_info in place of arg_type in
+	  order to eventually be able to record properties along with
+	  types.
+	* output.c: switch to using arg_type_info in output routines
+
 2006-07-18  Petr Machata <pmachata at redhat.com>
 
 	* elf.c: replace nonexistant elf_plt2addr with opd2addr, fix
diff --git a/ltrace.h b/ltrace.h
index 460167e..5561139 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -41,14 +41,26 @@ enum arg_type {
 	ARGTYPE_FILE,
 	ARGTYPE_FORMAT,		/* printf-like format */
 	ARGTYPE_STRING,
-	ARGTYPE_STRING0,	/* stringN: string up to (arg N) bytes */
-	ARGTYPE_STRING1,
-	ARGTYPE_STRING2,
-	ARGTYPE_STRING3,
-	ARGTYPE_STRING4,
-	ARGTYPE_STRING5
+	ARGTYPE_STRING_N,	/* stringN: string up to (arg N) bytes */
+        ARGTYPE_STRING0,
+        ARGTYPE_STRING1,
+        ARGTYPE_STRING2,
+        ARGTYPE_STRING3,
+        ARGTYPE_STRING4,
+        ARGTYPE_STRING5,
+        ARGTYPE_COUNT		/* number of ARGTYPE_* values */
 };
 
+typedef struct arg_type_info_t {
+    enum arg_type type;
+    union {
+	// ARGTYPE_STRING_N
+	struct {
+	    int size_spec;
+	} string_n_info;
+    } u;
+} arg_type_info;
+
 enum tof {
 	LT_TOF_NONE = 0,
 	LT_TOF_FUNCTION,	/* A real library function */
@@ -59,9 +71,9 @@ enum tof {
 
 struct function {
 	const char *name;
-	enum arg_type return_type;
+	arg_type_info *return_info;
 	int num_params;
-	enum arg_type arg_types[MAX_ARGS];
+	arg_type_info *arg_info[MAX_ARGS];
 	int params_right;
 	struct function *next;
 };
@@ -160,7 +172,7 @@ extern struct event *wait_for_something(
 extern void process_event(struct event *event);
 extern void execute_program(struct process *, char **);
 extern int display_arg(enum tof type, struct process *proc, int arg_num,
-		       enum arg_type at);
+		       arg_type_info *info);
 extern struct breakpoint *address2bpstruct(struct process *proc, void *addr);
 extern void breakpoints_init(struct process *proc);
 extern void insert_breakpoint(struct process *proc, void *addr,
@@ -173,6 +185,7 @@ extern void reinitialize_breakpoints(str
 extern struct process *open_program(char *filename, pid_t pid);
 extern void open_pid(pid_t pid, int verbose);
 extern void show_summary(void);
+extern arg_type_info *lookup_singleton(enum arg_type at);
 
 /* Arch-dependent stuff: */
 extern char *pid2name(pid_t pid);
diff --git a/output.c b/output.c
index 200126b..59c0807 100644
--- a/output.c
+++ b/output.c
@@ -164,6 +164,9 @@ static void tabto(int col)
 void output_left(enum tof type, struct process *proc, char *function_name)
 {
 	struct function *func;
+	static arg_type_info *arg_unknown = NULL;
+	if (arg_unknown == NULL)
+	    arg_unknown = lookup_singleton(ARGTYPE_UNKNOWN);
 
 	if (opt_c) {
 		return;
@@ -190,21 +193,21 @@ #endif
 		int i;
 		for (i = 0; i < 4; i++) {
 			current_column +=
-			    display_arg(type, proc, i, ARGTYPE_UNKNOWN);
+			    display_arg(type, proc, i, arg_unknown);
 			current_column += fprintf(output, ", ");
 		}
-		current_column += display_arg(type, proc, 4, ARGTYPE_UNKNOWN);
+		current_column += display_arg(type, proc, 4, arg_unknown);
 		return;
 	} else {
 		int i;
 		for (i = 0; i < func->num_params - func->params_right - 1; i++) {
 			current_column +=
-			    display_arg(type, proc, i, func->arg_types[i]);
+			    display_arg(type, proc, i, func->arg_info[i]);
 			current_column += fprintf(output, ", ");
 		}
 		if (func->num_params > func->params_right) {
 			current_column +=
-			    display_arg(type, proc, i, func->arg_types[i]);
+			    display_arg(type, proc, i, func->arg_info[i]);
 			if (func->params_right) {
 				current_column += fprintf(output, ", ");
 			}
@@ -218,6 +221,9 @@ #endif
 void output_right(enum tof type, struct process *proc, char *function_name)
 {
 	struct function *func = name2func(function_name);
+	static arg_type_info *arg_unknown = NULL;
+	if (arg_unknown == NULL)
+	    arg_unknown = lookup_singleton(ARGTYPE_UNKNOWN);
 
 	if (opt_c) {
 		struct opt_c_struct *st;
@@ -273,26 +279,26 @@ #endif
 		current_column += fprintf(output, ") ");
 		tabto(opt_a - 1);
 		fprintf(output, "= ");
-		display_arg(type, proc, -1, ARGTYPE_UNKNOWN);
+		display_arg(type, proc, -1, arg_unknown);
 	} else {
 		int i;
 		for (i = func->num_params - func->params_right;
 		     i < func->num_params - 1; i++) {
 			current_column +=
-			    display_arg(type, proc, i, func->arg_types[i]);
+			    display_arg(type, proc, i, func->arg_info[i]);
 			current_column += fprintf(output, ", ");
 		}
 		if (func->params_right) {
 			current_column +=
-			    display_arg(type, proc, i, func->arg_types[i]);
+			    display_arg(type, proc, i, func->arg_info[i]);
 		}
 		current_column += fprintf(output, ") ");
 		tabto(opt_a - 1);
 		fprintf(output, "= ");
-		if (func->return_type == ARGTYPE_VOID) {
+		if (func->return_info->type == ARGTYPE_VOID) {
 			fprintf(output, "<void>");
 		} else {
-			display_arg(type, proc, -1, func->return_type);
+			display_arg(type, proc, -1, func->return_info);
 		}
 	}
 	if (opt_T) {
diff --git a/read_config_file.c b/read_config_file.c
index 2c54f44..089d69a 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -11,19 +11,6 @@ #include "read_config_file.h"
 #include "output.h"
 #include "debug.h"
 
-/*
- *	"void"		ARGTYPE_VOID
- *	"int"		ARGTYPE_INT
- *	"uint"		ARGTYPE_UINT
- *	"long"		ARGTYPE_LONG
- *	"ulong"		ARGTYPE_ULONG
- *	"octal"		ARGTYPE_OCTAL
- *	"char"		ARGTYPE_CHAR
- *	"string"	ARGTYPE_STRING
- *	"format"	ARGTYPE_FORMAT
- *	"addr"		ARGTYPE_ADDR
- */
-
 struct function *list_of_functions = NULL;
 
 static struct list_of_pt_t {
@@ -51,7 +38,37 @@ static struct list_of_pt_t {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
 
-static enum arg_type str2type(char **str)
+static arg_type_info arg_type_singletons[] = {
+	{ ARGTYPE_VOID },
+	{ ARGTYPE_INT },
+	{ ARGTYPE_UINT },
+	{ ARGTYPE_LONG },
+	{ ARGTYPE_ULONG },
+	{ ARGTYPE_OCTAL },
+	{ ARGTYPE_CHAR },
+	{ ARGTYPE_ADDR },
+	{ ARGTYPE_FILE },
+	{ ARGTYPE_FORMAT },
+	{ ARGTYPE_STRING },
+	{ ARGTYPE_STRING_N },
+	{ ARGTYPE_STRING0 },
+	{ ARGTYPE_STRING1 },
+	{ ARGTYPE_STRING2 },
+	{ ARGTYPE_STRING3 },
+	{ ARGTYPE_STRING4 },
+	{ ARGTYPE_STRING5 },
+	{ ARGTYPE_UNKNOWN }
+};
+
+arg_type_info *lookup_singleton(enum arg_type at)
+{
+	if (at >= 0 && at <= ARGTYPE_COUNT)
+		return &arg_type_singletons[at];
+	else
+		return &arg_type_singletons[ARGTYPE_COUNT]; /* UNKNOWN */
+}
+
+static arg_type_info *str2type(char **str)
 {
 	struct list_of_pt_t *tmp = &list_of_pt[0];
 
@@ -59,11 +76,11 @@ static enum arg_type str2type(char **str
 		if (!strncmp(*str, tmp->name, strlen(tmp->name))
 		    && index(" ,)#", *(*str + strlen(tmp->name)))) {
 			*str += strlen(tmp->name);
-			return tmp->pt;
+			return lookup_singleton(tmp->pt);
 		}
 		tmp++;
 	}
-	return ARGTYPE_UNKNOWN;
+	return lookup_singleton(ARGTYPE_UNKNOWN);
 }
 
 static void eat_spaces(char **str)
@@ -105,9 +122,46 @@ static char *start_of_arg_sig(char *str)
 	return (stacked == 0) ? pos : NULL;
 }
 
+/*
+  Decide whether a type needs any additional parameters.
+  For now, we do not parse any nontrivial argument types.
+*/
+static int simple_type(enum arg_type at)
+{
+	return 1;
+}
+
 static int line_no;
 static char *filename;
 
+static arg_type_info *parse_type(char **str)
+{
+	arg_type_info *simple;
+	arg_type_info *info;
+
+	simple = str2type(str);
+	if (simple->type == ARGTYPE_UNKNOWN) {
+		return simple;		// UNKNOWN
+	}
+
+	if (simple_type(simple->type))
+		return simple;
+
+	info = malloc(sizeof(*info));
+	info->type = simple->type;
+
+	/* Code to parse parameterized types will go into the following
+	   switch statement. */
+
+	switch (info->type) {
+	default:
+		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
+			    filename, line_no);
+		free(info);
+		return NULL;
+	}
+}
+
 static struct function *process_line(char *buf)
 {
 	struct function fun;
@@ -119,12 +173,12 @@ static struct function *process_line(cha
 	line_no++;
 	debug(3, "Reading line %d of `%s'", line_no, filename);
 	eat_spaces(&str);
-	fun.return_type = str2type(&str);
-	if (fun.return_type == ARGTYPE_UNKNOWN) {
+	fun.return_info = parse_type(&str);
+	if (fun.return_info == NULL) {
 		debug(3, " Skipping line %d", line_no);
 		return NULL;
 	}
-	debug(4, " return_type = %d", fun.return_type);
+	debug(4, " return_type = %d", fun.return_info->type);
 	eat_spaces(&str);
 	tmp = start_of_arg_sig(str);
 	if (!tmp) {
@@ -148,9 +202,10 @@ static struct function *process_line(cha
 		} else if (fun.params_right) {
 			fun.params_right++;
 		}
-		fun.arg_types[i] = str2type(&str);
-		if (fun.return_type == ARGTYPE_UNKNOWN) {
-			output_line(0, "Syntax error in `%s', line %d",
+		fun.arg_info[i] = parse_type(&str);
+		if (fun.arg_info[i] == NULL) {
+			output_line(0, "Syntax error in `%s', line %d"
+                                    ": unknown argument type",
 				    filename, line_no);
 			return NULL;
 		}
@@ -161,8 +216,10 @@ static struct function *process_line(cha
 		} else if (*str == ')') {
 			continue;
 		} else {
-			output_line(0, "Syntax error in `%s', line %d",
-				    filename, line_no);
+			if (str[strlen(str) - 1] == '\n')
+				str[strlen(str) - 1] = '\0';
+			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
+				    filename, line_no, str);
 			return NULL;
 		}
 	}
-- 
1.4.1
-------------- next part --------------
From 1c01ce80c69ac100bc0bb714c31aa181ba45e7f5 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Sat, 22 Jul 2006 21:39:30 -0700
Subject: [PATCH 2/11] parameter specs string[argN], string[retval], and string[N]

Implement argument spec string[argN], equivalent to the preexisting
stringN, except that N is now unlimited. Also, string[retval] can
be used in place of string0 or string[arg0].

In addition, string[N] can be used to give a fixed max length.
---
 ChangeLog          |    4 ++
 display_args.c     |   68 ++++++++++++++++++----------------------
 etc/ltrace.conf    |   11 +++++--
 ltrace.h           |   10 +-----
 read_config_file.c |   88 +++++++++++++++++++++++++++++++++++++++++++---------
 5 files changed, 119 insertions(+), 62 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f23e7cd..2b2443c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2006-07-20    Steve Fink <sphink at gmail.com>
+
+	* Implement string[argN] and string[N] parameter descriptors.
+
 2006-07-27    Steve Fink <sphink at gmail.com>
 
 	* read_config_file.c: use arg_type_info in place of arg_type in
diff --git a/display_args.c b/display_args.c
index 083c9dc..1c15f22 100644
--- a/display_args.c
+++ b/display_args.c
@@ -11,19 +11,29 @@ #include "ltrace.h"
 #include "options.h"
 
 static int display_char(int what);
-static int display_string(enum tof type, struct process *proc, int arg_num);
-static int display_stringN(int arg2, enum tof type, struct process *proc,
-			   int arg_num);
+static int display_string(enum tof type, struct process *proc,
+			  int arg_num, arg_type_info *info,
+			  size_t maxlen);
 static int display_unknown(enum tof type, struct process *proc, int arg_num);
 static int display_format(enum tof type, struct process *proc, int arg_num);
 
+static int string_maxlength = INT_MAX;
+
+static long get_length(enum tof type, struct process *proc, int len_spec)
+{
+    if (len_spec > 0)
+	return len_spec;
+    return gimme_arg(type, proc, -len_spec - 1);
+}
+
 int
-display_arg(enum tof type, struct process *proc, int arg_num, enum arg_type at)
+display_arg(enum tof type, struct process *proc,
+	    int arg_num, arg_type_info *info)
 {
 	int tmp;
 	long arg;
 
-	switch (at) {
+	switch (info->type) {
 	case ARGTYPE_VOID:
 		return 0;
 	case ARGTYPE_INT:
@@ -62,19 +72,12 @@ display_arg(enum tof type, struct proces
 	case ARGTYPE_FORMAT:
 		return display_format(type, proc, arg_num);
 	case ARGTYPE_STRING:
-		return display_string(type, proc, arg_num);
-	case ARGTYPE_STRING0:
-		return display_stringN(0, type, proc, arg_num);
-	case ARGTYPE_STRING1:
-		return display_stringN(1, type, proc, arg_num);
-	case ARGTYPE_STRING2:
-		return display_stringN(2, type, proc, arg_num);
-	case ARGTYPE_STRING3:
-		return display_stringN(3, type, proc, arg_num);
-	case ARGTYPE_STRING4:
-		return display_stringN(4, type, proc, arg_num);
-	case ARGTYPE_STRING5:
-		return display_stringN(5, type, proc, arg_num);
+		return display_string(type, proc, arg_num, info,
+				      string_maxlength);
+	case ARGTYPE_STRING_N:
+		return display_string(type, proc, arg_num, info,
+				      get_length(type, proc,
+						 info->u.string_n_info.size_spec));
 	case ARGTYPE_UNKNOWN:
 	default:
 		return display_unknown(type, proc, arg_num);
@@ -106,11 +109,10 @@ static int display_char(int what)
 	}
 }
 
-static int string_maxlength = INT_MAX;
-
 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
 
-static int display_string(enum tof type, struct process *proc, int arg_num)
+static int display_string(enum tof type, struct process *proc,
+			  int arg_num, arg_type_info *info, size_t maxlength)
 {
 	void *addr;
 	unsigned char *str1;
@@ -122,13 +124,13 @@ static int display_string(enum tof type,
 		return fprintf(output, "NULL");
 	}
 
-	str1 = malloc(MIN(opt_s, string_maxlength) + 3);
+	str1 = malloc(MIN(opt_s, maxlength) + 3);
 	if (!str1) {
 		return fprintf(output, "???");
 	}
-	umovestr(proc, addr, MIN(opt_s, string_maxlength) + 1, str1);
+	umovestr(proc, addr, MIN(opt_s, maxlength) + 1, str1);
 	len = fprintf(output, "\"");
-	for (i = 0; i < MIN(opt_s, string_maxlength); i++) {
+	for (i = 0; i < MIN(opt_s, maxlength); i++) {
 		if (str1[i]) {
 			len += display_char(str1[i]);
 		} else {
@@ -136,24 +138,13 @@ static int display_string(enum tof type,
 		}
 	}
 	len += fprintf(output, "\"");
-	if (str1[i] && (opt_s <= string_maxlength)) {
+	if (str1[i] && (opt_s <= maxlength)) {
 		len += fprintf(output, "...");
 	}
 	free(str1);
 	return len;
 }
 
-static int
-display_stringN(int arg2, enum tof type, struct process *proc, int arg_num)
-{
-	int a;
-
-	string_maxlength = gimme_arg(type, proc, arg2 - 1);
-	a = display_string(type, proc, arg_num);
-	string_maxlength = INT_MAX;
-	return a;
-}
-
 static int display_unknown(enum tof type, struct process *proc, int arg_num)
 {
 	long tmp;
@@ -292,10 +283,13 @@ static int display_format(enum tof type,
 					len += fprintf(output, "'");
 					break;
 				} else if (c == 's') {
+					arg_type_info *info =
+					    lookup_singleton(ARGTYPE_STRING);
 					len += fprintf(output, ", ");
 					len +=
 					    display_string(type, proc,
-							   ++arg_num);
+							   ++arg_num, info,
+							   string_maxlength);
 					break;
 				} else if (c == 'p' || c == 'n') {
 					len +=
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 5c85e19..29c50ec 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -18,7 +18,14 @@
 ; file		== (FILE *)						[TODO]
 ; format	== ((const char *), ...)	[printf() like]		[TODO]
 ; string	== (char *)
-; stringN	== (char *)		[N>=0]	[show only up to (arg N) bytes]
+; string[argN]  == (char *)		[N>0]	[show only up to (arg N) bytes]
+; string[retval] == (char *)			[show only up to (return val) bytes]
+; string[arg0]	== (char *)			[same as string[retval]]
+; string[N]     == (char *)             [N>0]   [show only up to N bytes]
+
+; Backwards-compatibility:
+; string0	== (char *)			[same as string[retval]]
+; stringN	== (char *)		[N>0]	[same as string[argN]]
 
 ; arpa/inet.h
 int inet_aton(string,addr);
@@ -338,7 +345,7 @@ int getpgid(int);
 int isatty(int);
 int link(string,string);
 int mkdir(string,octal);
-long read(int, +string0, ulong);
+long read(int, +string[retval], ulong);
 int rmdir(string);
 int seteuid(uint);
 int setgid(int);
diff --git a/ltrace.h b/ltrace.h
index 5561139..dec2e0b 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -40,14 +40,8 @@ enum arg_type {
 	ARGTYPE_ADDR,
 	ARGTYPE_FILE,
 	ARGTYPE_FORMAT,		/* printf-like format */
-	ARGTYPE_STRING,
-	ARGTYPE_STRING_N,	/* stringN: string up to (arg N) bytes */
-        ARGTYPE_STRING0,
-        ARGTYPE_STRING1,
-        ARGTYPE_STRING2,
-        ARGTYPE_STRING3,
-        ARGTYPE_STRING4,
-        ARGTYPE_STRING5,
+	ARGTYPE_STRING,		/* NUL-terminated string */
+	ARGTYPE_STRING_N,	/* String of known maxlen */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
 };
 
diff --git a/read_config_file.c b/read_config_file.c
index 089d69a..6c9eb5d 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -29,12 +29,6 @@ static struct list_of_pt_t {
 	"file", ARGTYPE_FILE}, {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
-	"string0", ARGTYPE_STRING0}, {
-	"string1", ARGTYPE_STRING1}, {
-	"string2", ARGTYPE_STRING2}, {
-	"string3", ARGTYPE_STRING3}, {
-	"string4", ARGTYPE_STRING4}, {
-	"string5", ARGTYPE_STRING5}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
 
@@ -51,12 +45,6 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_FORMAT },
 	{ ARGTYPE_STRING },
 	{ ARGTYPE_STRING_N },
-	{ ARGTYPE_STRING0 },
-	{ ARGTYPE_STRING1 },
-	{ ARGTYPE_STRING2 },
-	{ ARGTYPE_STRING3 },
-	{ ARGTYPE_STRING4 },
-	{ ARGTYPE_STRING5 },
 	{ ARGTYPE_UNKNOWN }
 };
 
@@ -74,7 +62,7 @@ static arg_type_info *str2type(char **st
 
 	while (tmp->name) {
 		if (!strncmp(*str, tmp->name, strlen(tmp->name))
-		    && index(" ,)#", *(*str + strlen(tmp->name)))) {
+		    && index(" ,()#;012345[", *(*str + strlen(tmp->name)))) {
 			*str += strlen(tmp->name);
 			return lookup_singleton(tmp->pt);
 		}
@@ -134,6 +122,51 @@ static int simple_type(enum arg_type at)
 static int line_no;
 static char *filename;
 
+static int parse_int(char **str)
+{
+    char *end;
+    long n = strtol(*str, &end, 0);
+    if (end == *str) {
+	output_line(0, "Syntax error in `%s', line %d: Bad number",
+		    filename, line_no);
+	return 0;
+    }
+
+    *str = end;
+    return n;
+}
+
+/*
+ * Input:
+ *  argN   : The value of argument #N, counting from 1 (arg0 = retval)
+ *  eltN   : The value of element #N of the containing structure
+ *  retval : The return value
+ *  0      : Error
+ *  N      : The numeric value N, if N > 0
+ *
+ * Output:
+ * > 0   actual numeric value
+ * = 0   return value
+ * < 0   (arg -n), counting from one
+ */
+static int parse_argnum(char **str)
+{
+    int multiplier = 1;
+    int n = 0;
+
+    if (strncmp(*str, "arg", 3) == 0) {
+	(*str) += 3;
+	multiplier = -1;
+    } else if (strncmp(*str, "retval", 6) == 0) {
+	(*str) += 6;
+	return 0;
+    }
+
+    n = parse_int(str);
+
+    return n * multiplier;
+}
+
 static arg_type_info *parse_type(char **str)
 {
 	arg_type_info *simple;
@@ -144,7 +177,7 @@ static arg_type_info *parse_type(char **
 		return simple;		// UNKNOWN
 	}
 
-	if (simple_type(simple->type))
+	if (simple_type(simple->type) && simple->type != ARGTYPE_STRING)
 		return simple;
 
 	info = malloc(sizeof(*info));
@@ -154,6 +187,29 @@ static arg_type_info *parse_type(char **
 	   switch statement. */
 
 	switch (info->type) {
+
+	case ARGTYPE_STRING:
+	    if (!isdigit(**str) && **str != '[') {
+		/* Oops, was just a simple string after all */
+		free(info);
+		return simple;
+	    }
+
+	    info->type = ARGTYPE_STRING_N;
+
+	    /* Backwards compatibility for string0, string1, ... */
+	    if (isdigit(**str)) {
+		info->u.string_n_info.size_spec = -parse_int(str);
+		return info;
+	    }
+
+	    (*str)++;		// Skip past opening [
+	    eat_spaces(str);
+	    info->u.string_n_info.size_spec = parse_argnum(str);
+	    eat_spaces(str);
+	    (*str)++;		// Skip past closing ]
+	    return info;
+
 	default:
 		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
 			    filename, line_no);
@@ -174,7 +230,9 @@ static struct function *process_line(cha
 	debug(3, "Reading line %d of `%s'", line_no, filename);
 	eat_spaces(&str);
 	fun.return_info = parse_type(&str);
-	if (fun.return_info == NULL) {
+	if (fun.return_info == NULL)
+        	return NULL;
+	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
 		debug(3, " Skipping line %d", line_no);
 		return NULL;
 	}
-- 
1.4.1
-------------- next part --------------
From d50f01af0a4188d18814a01eebe37707950d602d Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 20 Jul 2006 21:59:43 -0700
Subject: [PATCH 3/11] Add 'ignore' parameter descriptor. It just skips the corresponding argument

(so there will be two commas in a row, or whatever.)
---
 ChangeLog          |    4 ++++
 display_args.c     |    2 ++
 etc/ltrace.conf    |    1 +
 ltrace.h           |    1 +
 read_config_file.c |    2 ++
 5 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2b2443c..48287ba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* various: add 'ignore' parameter descriptor
+
+2006-07-20    Steve Fink <sphink at gmail.com>
+
 	* Implement string[argN] and string[N] parameter descriptors.
 
 2006-07-27    Steve Fink <sphink at gmail.com>
diff --git a/display_args.c b/display_args.c
index 1c15f22..2669228 100644
--- a/display_args.c
+++ b/display_args.c
@@ -36,6 +36,8 @@ display_arg(enum tof type, struct proces
 	switch (info->type) {
 	case ARGTYPE_VOID:
 		return 0;
+        case ARGTYPE_IGNORE:
+        	return 0; /* Empty gap between commas */
 	case ARGTYPE_INT:
 		return fprintf(output, "%d",
 			       (int)gimme_arg(type, proc, arg_num));
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 29c50ec..50a41eb 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -22,6 +22,7 @@
 ; string[retval] == (char *)			[show only up to (return val) bytes]
 ; string[arg0]	== (char *)			[same as string[retval]]
 ; string[N]     == (char *)             [N>0]   [show only up to N bytes]
+; ignore	== (any)			[ignore arg, output blank]
 
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
diff --git a/ltrace.h b/ltrace.h
index dec2e0b..d030ce8 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -42,6 +42,7 @@ enum arg_type {
 	ARGTYPE_FORMAT,		/* printf-like format */
 	ARGTYPE_STRING,		/* NUL-terminated string */
 	ARGTYPE_STRING_N,	/* String of known maxlen */
+        ARGTYPE_IGNORE,		/* Leave parameter blank */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
 };
 
diff --git a/read_config_file.c b/read_config_file.c
index 6c9eb5d..3927af2 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -29,6 +29,7 @@ static struct list_of_pt_t {
 	"file", ARGTYPE_FILE}, {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
+	"ignore", ARGTYPE_IGNORE}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
 
@@ -45,6 +46,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_FORMAT },
 	{ ARGTYPE_STRING },
 	{ ARGTYPE_STRING_N },
+	{ ARGTYPE_IGNORE },
 	{ ARGTYPE_UNKNOWN }
 };
 
-- 
1.4.1
-------------- next part --------------
From c5536084b4fb3590b2a2915b10e47a7e44ba7f43 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 08:29:58 -0700
Subject: [PATCH 4/11] Report syntax errors when running grep.

Quote the pattern so spaces and similar things can be used.
---
 ChangeLog                               |    5 +++++
 testsuite/lib/ltrace.exp                |    8 +++++---
 testsuite/ltrace.minor/count-record.exp |   26 +++++++++++++-------------
 3 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 48287ba..4575942 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* testsuite/lib/ltrace.exp: better quoting and error detection for
+	  ltrace_verify_output's call to grep
+
+2006-07-20    Steve Fink <sphink at gmail.com>
+
 	* various: add 'ignore' parameter descriptor
 
 2006-07-20    Steve Fink <sphink at gmail.com>
diff --git a/testsuite/lib/ltrace.exp b/testsuite/lib/ltrace.exp
index a1d8dfe..b56d50d 100644
--- a/testsuite/lib/ltrace.exp
+++ b/testsuite/lib/ltrace.exp
@@ -260,10 +260,12 @@ #
 proc ltrace_verify_output { file_to_search pattern {instance_no 0}} {
 
 	# compute the number of PATTERN in FILE_TO_SEARCH by grep and wc.
-	catch "exec sh -c {grep $pattern $file_to_search | wc -l ;exit}" output
+	catch "exec sh -c {grep \"$pattern\" $file_to_search | wc -l ;exit}" output
 	verbose "output = $output"
-	
-	if { $instance_no == 0 } then {
+
+	if [ regexp "syntax error" $output ] then {
+		fail "Invalid regular expression $pattern"
+        } elseif { $instance_no == 0 } then {
 		if { $output == 0 } then {
 			fail "Fail to find $pattern in $file_to_search"
 		} else {
diff --git a/testsuite/ltrace.minor/count-record.exp b/testsuite/ltrace.minor/count-record.exp
index bd03d5b..7150568 100644
--- a/testsuite/ltrace.minor/count-record.exp
+++ b/testsuite/ltrace.minor/count-record.exp
@@ -48,30 +48,30 @@ #   3.81    0.000301         301        
 #   3.39    0.000268         268         1 fread
 #
 
-set pattern "\'1 rmdir\'"
+set pattern " 1 rmdir"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 fopen\'"
+set pattern " 1 fopen"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'2 remove\'"
+set pattern " 2 remove"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 mkdir\'"
+set pattern " 1 mkdir"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 fseek\'"
+set pattern " 1 fseek"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 fwrite\'"
+set pattern " 1 fwrite"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 fclose\'"
+set pattern " 1 fclose"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 rename\'"
+set pattern " 1 rename"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 symlink\'"
+set pattern " 1 symlink"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 chdir\'"
+set pattern " 1 chdir"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 access\'"
+set pattern " 1 access"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 getcwd\'"
+set pattern " 1 getcwd"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
-set pattern "\'1 fread\'"
+set pattern " 1 fread"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern
 
-- 
1.4.1
-------------- next part --------------
From 859887ca61fd94e94601c6f71e72e1867b06d34d Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 08:37:24 -0700
Subject: [PATCH 5/11] implement pointer parameters

switch to passing around values rather than argument numbers that need
to be fetched (needed for pointer params)

add tests for several recently-added parameter types
---
 ChangeLog                              |   12 ++-
 display_args.c                         |  132 +++++++++++++++++++-------------
 etc/ltrace.conf                        |    3 -
 ltrace.h                               |    7 ++
 read_config_file.c                     |   18 ++++
 sysdeps/README                         |    1 
 sysdeps/linux-gnu/trace.c              |   18 ++++
 testsuite/ltrace.main/Makefile         |    2 
 testsuite/ltrace.main/parameters-lib.c |   38 +++++++++
 testsuite/ltrace.main/parameters.c     |   44 +++++++++++
 testsuite/ltrace.main/parameters.conf  |    7 ++
 testsuite/ltrace.main/parameters.exp   |   52 +++++++++++++
 12 files changed, 273 insertions(+), 61 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4575942..f361d52 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,17 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
-	* testsuite/lib/ltrace.exp: better quoting and error detection for
-	  ltrace_verify_output's call to grep
+	* various: add pointers as valid parameter descriptor
+	* display_args.c: switch to passing around values rather than
+	  argument numbers that need to be fetched (needed for pointer params)
+	* testsuite/ltrace.main/parameters.*: add tests for several
+	  recently-added parameter types
+
+	* various: add 'ignore' parameter descriptor
 
 2006-07-20    Steve Fink <sphink at gmail.com>
 
-	* various: add 'ignore' parameter descriptor
+	* testsuite/lib/ltrace.exp: better quoting and error detection for
+	  ltrace_verify_output's call to grep
 
 2006-07-20    Steve Fink <sphink at gmail.com>
 
diff --git a/display_args.c b/display_args.c
index 2669228..66a9093 100644
--- a/display_args.c
+++ b/display_args.c
@@ -2,6 +2,7 @@ #if HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -12,9 +13,10 @@ #include "options.h"
 
 static int display_char(int what);
 static int display_string(enum tof type, struct process *proc,
-			  int arg_num, arg_type_info *info,
-			  size_t maxlen);
-static int display_unknown(enum tof type, struct process *proc, int arg_num);
+			  void* addr, size_t maxlen);
+static int display_value(enum tof type, struct process *proc,
+			 long value, arg_type_info *info);
+static int display_unknown(enum tof type, struct process *proc, long value);
 static int display_format(enum tof type, struct process *proc, int arg_num);
 
 static int string_maxlength = INT_MAX;
@@ -26,12 +28,30 @@ static long get_length(enum tof type, st
     return gimme_arg(type, proc, -len_spec - 1);
 }
 
-int
-display_arg(enum tof type, struct process *proc,
-	    int arg_num, arg_type_info *info)
+static int display_pointer(enum tof type, struct process *proc, long value,
+			   arg_type_info * info)
+{
+    long pointed_to;
+    arg_type_info *inner = info->u.ptr_info.info;
+
+    if (value == 0)
+      return fprintf(output, "NULL");
+    else if (umovelong(proc, (void *) value, &pointed_to) < 0)
+      return fprintf(output, "?");
+    else
+      return display_value(type, proc, pointed_to, inner);
+}
+
+/* Args:
+   type - syscall or shared library function or memory
+   proc - information about the traced process
+   value - the value to display
+   info - the description of the type to display
+*/
+int display_value(enum tof type, struct process *proc,
+                  long value, arg_type_info *info)
 {
 	int tmp;
-	long arg;
 
 	switch (info->type) {
 	case ARGTYPE_VOID:
@@ -39,52 +59,62 @@ display_arg(enum tof type, struct proces
         case ARGTYPE_IGNORE:
         	return 0; /* Empty gap between commas */
 	case ARGTYPE_INT:
-		return fprintf(output, "%d",
-			       (int)gimme_arg(type, proc, arg_num));
+          return fprintf(output, "%d", (int) value);
 	case ARGTYPE_UINT:
-		return fprintf(output, "%u",
-			       (unsigned)gimme_arg(type, proc, arg_num));
+          return fprintf(output, "%u", (unsigned) value);
 	case ARGTYPE_LONG:
 		if (proc->mask_32bit)
-			return fprintf(output, "%d",
-				       (int)gimme_arg(type, proc, arg_num));
-		return fprintf(output, "%ld", gimme_arg(type, proc, arg_num));
+			return fprintf(output, "%d", (int) value);
+		else
+			return fprintf(output, "%ld", value);
 	case ARGTYPE_ULONG:
 		if (proc->mask_32bit)
-			return fprintf(output, "%u",
-				       (unsigned)gimme_arg(type, proc,
-							   arg_num));
-		return fprintf(output, "%lu",
-			       (unsigned long)gimme_arg(type, proc, arg_num));
+			return fprintf(output, "%u", (unsigned) value);
+		else
+			return fprintf(output, "%lu", (unsigned long) value);
 	case ARGTYPE_OCTAL:
-		return fprintf(output, "0%o",
-			       (unsigned)gimme_arg(type, proc, arg_num));
+		return fprintf(output, "0%o", (unsigned) value);
 	case ARGTYPE_CHAR:
 		tmp = fprintf(output, "'");
-		tmp += display_char((int)gimme_arg(type, proc, arg_num));
+		tmp += display_char(value == -1 ? value : (char) value);
 		tmp += fprintf(output, "'");
 		return tmp;
 	case ARGTYPE_ADDR:
-		arg = gimme_arg(type, proc, arg_num);
-		if (!arg) {
+		if (!value)
 			return fprintf(output, "NULL");
-		} else {
-			return fprintf(output, "%p", (void *)arg);
-		}
+		else
+			return fprintf(output, "0x%08lx", value);
 	case ARGTYPE_FORMAT:
-		return display_format(type, proc, arg_num);
+		fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n");
+		exit(1);
 	case ARGTYPE_STRING:
-		return display_string(type, proc, arg_num, info,
+		return display_string(type, proc, (void*) value,
 				      string_maxlength);
 	case ARGTYPE_STRING_N:
-		return display_string(type, proc, arg_num, info,
+		return display_string(type, proc, (void*) value,
 				      get_length(type, proc,
 						 info->u.string_n_info.size_spec));
-	case ARGTYPE_UNKNOWN:
+	case ARGTYPE_POINTER:
+		return display_pointer(type, proc, value, info);
+ 	case ARGTYPE_UNKNOWN:
 	default:
-		return display_unknown(type, proc, arg_num);
+		return display_unknown(type, proc, value);
 	}
-	return fprintf(output, "?");
+}
+
+int display_arg(enum tof type, struct process *proc, int arg_num,
+		arg_type_info * info)
+{
+    long arg;
+
+    if (info->type == ARGTYPE_VOID) {
+	return 0;
+    } else if (info->type == ARGTYPE_FORMAT) {
+	return display_format(type, proc, arg_num);
+    } else {
+	arg = gimme_arg(type, proc, arg_num);
+	return display_value(type, proc, arg, info);
+    }
 }
 
 static int display_char(int what)
@@ -103,25 +133,23 @@ static int display_char(int what)
 	case '\\':
 		return fprintf(output, "\\\\");
 	default:
-		if ((what < 32) || (what > 126)) {
-			return fprintf(output, "\\%03o", (unsigned char)what);
-		} else {
+		if (isprint(what)) {
 			return fprintf(output, "%c", what);
+		} else {
+			return fprintf(output, "\\%03o", (unsigned char)what);
 		}
 	}
 }
 
 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
 
-static int display_string(enum tof type, struct process *proc,
-			  int arg_num, arg_type_info *info, size_t maxlength)
+static int display_string(enum tof type, struct process *proc, void *addr,
+			  size_t maxlength)
 {
-	void *addr;
 	unsigned char *str1;
 	int i;
 	int len = 0;
 
-	addr = (void *)gimme_arg(type, proc, arg_num);
 	if (!addr) {
 		return fprintf(output, "NULL");
 	}
@@ -147,21 +175,17 @@ static int display_string(enum tof type,
 	return len;
 }
 
-static int display_unknown(enum tof type, struct process *proc, int arg_num)
+static int display_unknown(enum tof type, struct process *proc, long value)
 {
-	long tmp;
-
-	tmp = gimme_arg(type, proc, arg_num);
-
 	if (proc->mask_32bit) {
-		if ((int)tmp < 1000000 && (int)tmp > -1000000)
-			return fprintf(output, "%d", (int)tmp);
+		if ((int)value < 1000000 && (int)value > -1000000)
+			return fprintf(output, "%d", (int)value);
 		else
-			return fprintf(output, "%p", (void *)tmp);
-	} else if (tmp < 1000000 && tmp > -1000000) {
-		return fprintf(output, "%ld", tmp);
+			return fprintf(output, "%p", (void *)value);
+	} else if (value < 1000000 && value > -1000000) {
+		return fprintf(output, "%ld", value);
 	} else {
-		return fprintf(output, "%p", (void *)tmp);
+		return fprintf(output, "%p", (void *)value);
 	}
 }
 
@@ -285,12 +309,12 @@ static int display_format(enum tof type,
 					len += fprintf(output, "'");
 					break;
 				} else if (c == 's') {
-					arg_type_info *info =
-					    lookup_singleton(ARGTYPE_STRING);
 					len += fprintf(output, ", ");
 					len +=
 					    display_string(type, proc,
-							   ++arg_num, info,
+							   (void *)gimme_arg(type,
+									     proc,
+									     ++arg_num),
 							   string_maxlength);
 					break;
 				} else if (c == 'p' || c == 'n') {
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 50a41eb..547a125 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -23,6 +23,7 @@
 ; string[arg0]	== (char *)			[same as string[retval]]
 ; string[N]     == (char *)             [N>0]   [show only up to N bytes]
 ; ignore	== (any)			[ignore arg, output blank]
+; type*		== (type *)			[pointer to any other type]
 
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
@@ -77,7 +78,7 @@ int open64(string,int,octal);		; WARNING
 int fnmatch(string, string, int);
 
 ; getopt.h
-int getopt_long(int,addr,string,addr,addr);
+int getopt_long(int,addr,string,addr,int*);
 int getopt_long_only(int,addr,string,addr,addr);
 
 ; grp.h
diff --git a/ltrace.h b/ltrace.h
index d030ce8..e649bb1 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -43,6 +43,7 @@ enum arg_type {
 	ARGTYPE_STRING,		/* NUL-terminated string */
 	ARGTYPE_STRING_N,	/* String of known maxlen */
         ARGTYPE_IGNORE,		/* Leave parameter blank */
+        ARGTYPE_POINTER,	/* Pointer to some other type */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
 };
 
@@ -53,6 +54,11 @@ typedef struct arg_type_info_t {
 	struct {
 	    int size_spec;
 	} string_n_info;
+
+	// ARGTYPE_POINTER
+	struct {
+	    struct arg_type_info_t *info;
+	} ptr_info;
     } u;
 } arg_type_info;
 
@@ -206,6 +212,7 @@ extern void continue_enabling_breakpoint
 extern long gimme_arg(enum tof type, struct process *proc, int arg_num);
 extern void save_register_args(enum tof type, struct process *proc);
 extern int umovestr(struct process *proc, void *addr, int len, void *laddr);
+extern int umovelong(struct process *proc, void *addr, long *result);
 extern int ffcheck(void *maddr);
 extern void *sym2addr(struct process *, struct library_symbol *);
 
diff --git a/read_config_file.c b/read_config_file.c
index 3927af2..15c3f39 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -47,6 +47,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_STRING },
 	{ ARGTYPE_STRING_N },
 	{ ARGTYPE_IGNORE },
+	{ ARGTYPE_POINTER },
 	{ ARGTYPE_UNKNOWN }
 };
 
@@ -64,7 +65,7 @@ static arg_type_info *str2type(char **st
 
 	while (tmp->name) {
 		if (!strncmp(*str, tmp->name, strlen(tmp->name))
-		    && index(" ,()#;012345[", *(*str + strlen(tmp->name)))) {
+		    && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
 			*str += strlen(tmp->name);
 			return lookup_singleton(tmp->pt);
 		}
@@ -169,7 +170,7 @@ static int parse_argnum(char **str)
     return n * multiplier;
 }
 
-static arg_type_info *parse_type(char **str)
+static arg_type_info *parse_nonpointer_type(char **str)
 {
 	arg_type_info *simple;
 	arg_type_info *info;
@@ -220,6 +221,19 @@ static arg_type_info *parse_type(char **
 	}
 }
 
+static arg_type_info *parse_type(char **str)
+{
+	arg_type_info *info = parse_nonpointer_type(str);
+	while (**str == '*') {
+		arg_type_info *outer = malloc(sizeof(*info));
+		outer->type = ARGTYPE_POINTER;
+		outer->u.ptr_info.info = info;
+		(*str)++;
+		info = outer;
+	}
+	return info;
+}
+
 static struct function *process_line(char *buf)
 {
 	struct function fun;
diff --git a/sysdeps/README b/sysdeps/README
index 58abd08..1ec4935 100644
--- a/sysdeps/README
+++ b/sysdeps/README
@@ -24,6 +24,7 @@ void * get_stack_pointer(pid_t pid);
 void * get_return_addr(pid_t pid, void * stack_pointer);
 long gimme_arg(enum tof type, struct process * proc, int arg_num);
 int umovestr(struct process * proc, void * addr, int len, void * laddr);
+int umovelong(struct process * proc, void * addr, long * result);
 char * pid2name(pid_t pid);
 void trace_me(void);
 int trace_pid(pid_t pid);
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 136d288..a1af202 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -151,6 +151,24 @@ #endif
 	}
 }
 
+/* Read a single long from the process's memory address 'addr' */
+int umovelong(struct process *proc, void *addr, long *result)
+{
+	long pointed_to;
+
+        errno = 0;
+        pointed_to = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
+        if (pointed_to == -1 && errno)
+          return -errno;
+
+        *result = pointed_to;
+        return 0;
+}
+
+/* Read a series of bytes starting at the process's memory address
+   'addr' and continuing until a NUL ('\0') is seen or 'len' bytes
+   have been read.
+*/
 int umovestr(struct process *proc, void *addr, int len, void *laddr)
 {
 	union {
diff --git a/testsuite/ltrace.main/Makefile b/testsuite/ltrace.main/Makefile
index 57699d7..db88c98 100644
--- a/testsuite/ltrace.main/Makefile
+++ b/testsuite/ltrace.main/Makefile
@@ -19,7 +19,7 @@ CLEANFILES = *.log *.sum site.bak setval
 
 .SUFFIXES:	
 clean:
-	-rm -f main main-internal system_calls signals 
+	-rm -f main main-internal system_calls signals parameters
 	-rm -f *.o *.so 
 	-rm -f *.ltrace
 	-rm -f $(CLEANFILES)
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
new file mode 100644
index 0000000..bc320cf
--- /dev/null
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -0,0 +1,38 @@
+#include <string.h>
+#include <stdio.h>
+
+void func_ignore(int a, int b, int c)
+{
+	printf("%d\n", a + b + c);
+}
+
+void func_intptr(int *i)
+{
+	printf("%d\n", *i);
+}
+
+void func_intptr_ret(int *i)
+{
+	*i = 42;
+}
+
+int func_strlen(char* p)
+{
+	strcpy(p, "Hello world");
+	return strlen(p);
+}
+
+void func_strfixed(char* p)
+{
+	strcpy(p, "Hello world");
+}
+
+void func_ppp(int*** ppp)
+{
+	printf("%d\n", ***ppp);
+}
+
+void func_stringp(char** sP)
+{
+	printf("%s\n", *sP);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
new file mode 100644
index 0000000..e26e0a6
--- /dev/null
+++ b/testsuite/ltrace.main/parameters.c
@@ -0,0 +1,44 @@
+/* Ltrace Test : parameters.c.
+   Objectives  : Verify that Ltrace can handle all the different
+   parameter types
+
+   This file was written by Steve Fink <sphink at gmail.com>. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+void func_ignore(int a, int b, int c);
+void func_intptr(int *i);
+void func_intptr_ret(int *i);
+int func_strlen(char*);
+void func_strfixed(char*);
+void func_ppp(int***);
+void func_stringp(char**);
+
+int 
+main ()
+{
+  int x = 17;
+  int *xP, **xPP;
+  char buf[200];
+  char *s;
+  func_ignore(1, 2, 3);
+  func_intptr(&x);
+  func_intptr_ret(&x);
+  func_strlen(buf);
+  printf("%s\n", buf);
+  func_strfixed(buf);
+  printf("%s\n", buf);
+  x = 80;
+  xP = &x;
+  xPP = &xP;
+  func_ppp(&xPP);
+  s = (char*) malloc(100);
+  strcpy(s, "Dude");
+  func_stringp(&s);
+}
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
new file mode 100644
index 0000000..7690f89
--- /dev/null
+++ b/testsuite/ltrace.main/parameters.conf
@@ -0,0 +1,7 @@
+void func_ignore(int,ignore,int)
+void func_intptr(int*)
+void func_intptr_ret(+int*)
+int func_strlen(+string[retval])
+void func_strfixed(string[4])
+void func_ppp(int***)
+void func_stringp(string*)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
new file mode 100644
index 0000000..21a02b4
--- /dev/null
+++ b/testsuite/ltrace.main/parameters.exp
@@ -0,0 +1,52 @@
+# This file was written by Steve Fink <sphink at gmail.com>.
+# Based on main.c by Yao Qi <qiyao at cn.ibm.com>.
+
+set testfile "parameters"
+set srcfile ${testfile}.c
+set binfile ${testfile}
+set libfile "parameters-lib"
+set libsrc $srcdir/$subdir/$libfile.c
+set lib_sl $srcdir/$subdir/lib$testfile.so
+
+
+if [get_compiler_info $binfile] {
+  return -1
+}
+
+verbose "compiling source file now....."
+if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" 
+  || [ltrace_compile $srcdir/$subdir/$srcfile $srcdir/$subdir/$binfile executable [list debug shlib=$lib_sl] ] != ""} {
+  send_user "Testcase compile failed, so all tests in this file will automatically fail.\n"
+}
+
+# set options for ltrace.
+ltrace_options "-l" "$srcdir/$subdir/libparameters.so" "-F" "$srcdir/$subdir/parameters.conf"
+
+# Run PUT for ltarce.
+set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile]
+
+# Check the output of this program.
+verbose "ltrace runtest output: $exec_output\n"
+if [regexp {ELF from incompatible architecture} $exec_output] {
+	fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!"
+	return 
+} elseif [ regexp {Couldn't get .hash data} $exec_output ] {
+	fail "Couldn't get .hash data!"
+	return
+}
+
+# Verify the output
+set pattern "func_ignore(1, *, *3)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_intptr(17)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_intptr_ret(42)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_strlen(\\\"Hello world\\\") *= *11"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_strfixed(\\\"Hell\\\")"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_ppp(80)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_stringp(\\\"Dude\\\")"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From 6f25f69301f3c7c187f12bac451647fe117e6e35 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 08:38:56 -0700
Subject: [PATCH 6/11] Implement enumerated parameters
---
 ChangeLog                              |    4 +
 display_args.c                         |   14 ++++
 etc/ltrace.conf                        |    9 ++
 ltrace.h                               |    8 ++
 read_config_file.c                     |  120 +++++++++++++++++++++++++++++++-
 testsuite/ltrace.main/parameters-lib.c |    5 +
 testsuite/ltrace.main/parameters.c     |   18 +++++
 testsuite/ltrace.main/parameters.conf  |    1 
 testsuite/ltrace.main/parameters.exp   |    2 +
 9 files changed, 177 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f361d52..83940b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* various: Implement enumerated parameters
+
+2006-07-21    <sphink at gmail.com>
+
 	* various: add pointers as valid parameter descriptor
 	* display_args.c: switch to passing around values rather than
 	  argument numbers that need to be fetched (needed for pointer params)
diff --git a/display_args.c b/display_args.c
index 66a9093..2a6b34f 100644
--- a/display_args.c
+++ b/display_args.c
@@ -42,6 +42,18 @@ static int display_pointer(enum tof type
       return display_value(type, proc, pointed_to, inner);
 }
 
+static int display_enum(enum tof type, struct process *proc,
+                        arg_type_info* info, long value)
+{
+    int ii;
+    for (ii = 0; ii < info->u.enum_info.entries; ++ii) {
+	if (info->u.enum_info.values[ii] == value)
+	    return fprintf(output, "%s", info->u.enum_info.keys[ii]);
+    }
+
+    return display_unknown(type, proc, value);
+}
+
 /* Args:
    type - syscall or shared library function or memory
    proc - information about the traced process
@@ -94,6 +106,8 @@ int display_value(enum tof type, struct 
 		return display_string(type, proc, (void*) value,
 				      get_length(type, proc,
 						 info->u.string_n_info.size_spec));
+        case ARGTYPE_ENUM:
+		return display_enum(type, proc, info, value);
 	case ARGTYPE_POINTER:
 		return display_pointer(type, proc, value, info);
  	case ARGTYPE_UNKNOWN:
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 547a125..bff45fa 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -24,11 +24,20 @@
 ; string[N]     == (char *)             [N>0]   [show only up to N bytes]
 ; ignore	== (any)			[ignore arg, output blank]
 ; type*		== (type *)			[pointer to any other type]
+; enum (key=value,key=value,...)		[enumeration, see below]
 
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
 ; stringN	== (char *)		[N>0]	[same as string[argN]]
 
+; Enumerations
+;
+; The syntax is a parenthesized list of key=value assignments, like so:
+;   enum (F_DUPFD=0,F_GETFD=1,F_SETFD=2)
+; an example usage might look like
+;   int fcntl(int,enum (F_DUPFD=0,F_GETFD=1,F_SETFD=2))
+;
+
 ; arpa/inet.h
 int inet_aton(string,addr);
 string inet_ntoa(addr);			; It isn't a ADDR but an hexa number...
diff --git a/ltrace.h b/ltrace.h
index e649bb1..3b40cb3 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -42,6 +42,7 @@ enum arg_type {
 	ARGTYPE_FORMAT,		/* printf-like format */
 	ARGTYPE_STRING,		/* NUL-terminated string */
 	ARGTYPE_STRING_N,	/* String of known maxlen */
+        ARGTYPE_ENUM,		/* Enumeration */
         ARGTYPE_IGNORE,		/* Leave parameter blank */
         ARGTYPE_POINTER,	/* Pointer to some other type */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
@@ -50,6 +51,13 @@ enum arg_type {
 typedef struct arg_type_info_t {
     enum arg_type type;
     union {
+	// ARGTYPE_ENUM
+	struct {
+	    size_t entries;
+	    char **keys;
+	    int *values;
+	} enum_info;
+
 	// ARGTYPE_STRING_N
 	struct {
 	    int size_spec;
diff --git a/read_config_file.c b/read_config_file.c
index 15c3f39..5b114de 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -11,6 +11,10 @@ #include "read_config_file.h"
 #include "output.h"
 #include "debug.h"
 
+static int line_no;
+static char *filename;
+static int error_count = 0;
+
 struct function *list_of_functions = NULL;
 
 static struct list_of_pt_t {
@@ -29,6 +33,7 @@ static struct list_of_pt_t {
 	"file", ARGTYPE_FILE}, {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
+	"enum", ARGTYPE_ENUM}, {
 	"ignore", ARGTYPE_IGNORE}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
@@ -46,6 +51,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_FORMAT },
 	{ ARGTYPE_STRING },
 	{ ARGTYPE_STRING_N },
+	{ ARGTYPE_ENUM },
 	{ ARGTYPE_IGNORE },
 	{ ARGTYPE_POINTER },
 	{ ARGTYPE_UNKNOWN }
@@ -81,6 +87,32 @@ static void eat_spaces(char **str)
 	}
 }
 
+static char *xstrndup(char *str, size_t len)
+{
+    char *ret = (char *) malloc(len + 1);
+    strncpy(ret, str, len);
+    ret[len] = 0;
+    return ret;
+}
+
+static char *parse_ident(char **str)
+{
+    char *ident = *str;
+
+    if (!isalnum(**str) && **str != '_') {
+	output_line(0, "Syntax error in `%s', line %d: Bad identifier",
+		    filename, line_no);
+	error_count++;
+	return NULL;
+    }
+
+    while (**str && (isalnum(**str) || **str == '_')) {
+	++(*str);
+    }
+
+    return xstrndup(ident, *str - ident);
+}
+
 /*
   Returns position in string at the left parenthesis which starts the
   function's argument signature. Returns NULL on error.
@@ -119,12 +151,17 @@ static char *start_of_arg_sig(char *str)
 */
 static int simple_type(enum arg_type at)
 {
+    switch (at) {
+    case ARGTYPE_STRING:
+    case ARGTYPE_STRING_N:
+    case ARGTYPE_ENUM:
+	return 0;
+
+    default:
 	return 1;
+    }
 }
 
-static int line_no;
-static char *filename;
-
 static int parse_int(char **str)
 {
     char *end;
@@ -132,6 +169,7 @@ static int parse_int(char **str)
     if (end == *str) {
 	output_line(0, "Syntax error in `%s', line %d: Bad number",
 		    filename, line_no);
+	error_count++;
 	return 0;
     }
 
@@ -191,6 +229,73 @@ static arg_type_info *parse_nonpointer_t
 
 	switch (info->type) {
 
+	// Syntax: enum ( keyname=value,keyname=value,... )
+	case ARGTYPE_ENUM:{
+	    struct enum_opt {
+		char *key;
+		int value;
+		struct enum_opt *next;
+	    };
+	    struct enum_opt *list = NULL;
+	    struct enum_opt *p;
+	    int entries = 0;
+	    int ii;
+
+	    eat_spaces(str);
+	    (*str)++;		// Get past open paren
+	    eat_spaces(str);
+
+	    while (**str && **str != ')') {
+		p = (struct enum_opt *) malloc(sizeof(*p));
+		eat_spaces(str);
+		p->key = parse_ident(str);
+		if (error_count) {
+		    free(p);
+		    return NULL;
+		}
+		eat_spaces(str);
+		if (**str != '=') {
+		    free(p->key);
+		    free(p);
+		    output_line(0,
+				"Syntax error in `%s', line %d: expected '=', got '%c'",
+				filename, line_no, **str);
+		    error_count++;
+		    return NULL;
+		}
+		++(*str);
+		eat_spaces(str);
+		p->value = parse_int(str);
+		p->next = list;
+		list = p;
+		++entries;
+
+		// Skip comma
+		eat_spaces(str);
+		if (**str == ',') {
+		    (*str)++;
+		    eat_spaces(str);
+		}
+	    }
+
+	    info->u.enum_info.entries = entries;
+	    info->u.enum_info.keys =
+		(char **) malloc(entries * sizeof(char *));
+	    info->u.enum_info.values =
+		(int *) malloc(entries * sizeof(int));
+	    for (ii = 0, p = NULL; list; ++ii, list = list->next) {
+		if (p)
+		    free(p);
+		info->u.enum_info.keys[ii] = list->key;
+		info->u.enum_info.values[ii] = list->value;
+		p = list;
+	    }
+	    if (p)
+		free(p);
+
+	    return info;
+	}
+
 	case ARGTYPE_STRING:
 	    if (!isdigit(**str) && **str != '[') {
 		/* Oops, was just a simple string after all */
@@ -217,6 +322,7 @@ static arg_type_info *parse_nonpointer_t
 		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
 			    filename, line_no);
 		free(info);
+		error_count++;
 		return NULL;
 	}
 }
@@ -258,6 +364,7 @@ static struct function *process_line(cha
 	if (!tmp) {
 		output_line(0, "Syntax error in `%s', line %d", filename,
 			    line_no);
+		error_count++;
 		return NULL;
 	}
 	*tmp = '\0';
@@ -281,6 +388,7 @@ static struct function *process_line(cha
 			output_line(0, "Syntax error in `%s', line %d"
                                     ": unknown argument type",
 				    filename, line_no);
+			error_count++;
 			return NULL;
 		}
 		eat_spaces(&str);
@@ -294,6 +402,7 @@ static struct function *process_line(cha
 				str[strlen(str) - 1] = '\0';
 			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
 				    filename, line_no, str);
+			error_count++;
 			return NULL;
 		}
 	}
@@ -318,7 +427,10 @@ void read_config_file(char *file)
 	}
 	line_no = 0;
 	while (fgets(buf, 1024, stream)) {
-		struct function *tmp = process_line(buf);
+		struct function *tmp;
+
+		error_count = 0;
+		tmp = process_line(buf);
 
 		if (tmp) {
 			debug(2, "New function: `%s'", tmp->name);
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index bc320cf..b43de08 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -36,3 +36,8 @@ void func_stringp(char** sP)
 {
 	printf("%s\n", *sP);
 }
+
+void func_enum(int x)
+{
+	printf("enum: %d\n", x);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index e26e0a6..d7c225f 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -20,6 +20,15 @@ void func_strfixed(char*);
 void func_ppp(int***);
 void func_stringp(char**);
 
+typedef enum {
+  RED,
+  GREEN,
+  BLUE,
+  CHARTREUSE,
+  PETUNIA
+} color_t;
+void func_enum(color_t);
+
 int 
 main ()
 {
@@ -27,18 +36,27 @@ main ()
   int *xP, **xPP;
   char buf[200];
   char *s;
+
   func_ignore(1, 2, 3);
+
   func_intptr(&x);
+
   func_intptr_ret(&x);
+
   func_strlen(buf);
   printf("%s\n", buf);
+
   func_strfixed(buf);
   printf("%s\n", buf);
+
   x = 80;
   xP = &x;
   xPP = &xP;
   func_ppp(&xPP);
+
   s = (char*) malloc(100);
   strcpy(s, "Dude");
   func_stringp(&s);
+
+  func_enum(BLUE);
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 7690f89..debb1dc 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -5,3 +5,4 @@ int func_strlen(+string[retval])
 void func_strfixed(string[4])
 void func_ppp(int***)
 void func_stringp(string*)
+void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4))
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index 21a02b4..024c3f3 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -50,3 +50,5 @@ set pattern "func_ppp(80)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "func_stringp(\\\"Dude\\\")"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_enum(BLUE)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From 3a6b72d7d33bbd6e0e99982e54c431f39947a2fd Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Sat, 22 Jul 2006 17:04:49 -0700
Subject: [PATCH 7/11] Implement short, ushort, float

The 'float' stuff works here, but it may not be portable.
---
 ChangeLog                              |    6 +++++-
 display_args.c                         |    9 +++++++++
 etc/ltrace.conf                        |    6 ++++--
 ltrace.h                               |    3 +++
 read_config_file.c                     |    6 ++++++
 testsuite/ltrace.main/parameters-lib.c |   15 +++++++++++++++
 testsuite/ltrace.main/parameters.c     |    9 +++++++++
 testsuite/ltrace.main/parameters.conf  |    3 +++
 testsuite/ltrace.main/parameters.exp   |    8 ++++++++
 9 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 83940b8..13239bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,12 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* various: Implement short, ushort, float types
+
+2006-07-21    Steve Fink <sphink at gmail.com>
+
 	* various: Implement enumerated parameters
 
-2006-07-21    <sphink at gmail.com>
+2006-07-21    Steve Fink <sphink at gmail.com>
 
 	* various: add pointers as valid parameter descriptor
 	* display_args.c: switch to passing around values rather than
diff --git a/display_args.c b/display_args.c
index 2a6b34f..bb0bca0 100644
--- a/display_args.c
+++ b/display_args.c
@@ -91,6 +91,15 @@ int display_value(enum tof type, struct 
 		tmp += display_char(value == -1 ? value : (char) value);
 		tmp += fprintf(output, "'");
 		return tmp;
+	case ARGTYPE_SHORT:
+		return fprintf(output, "%hd", (short) value);
+	case ARGTYPE_USHORT:
+		return fprintf(output, "%hu", (unsigned short) value);
+	case ARGTYPE_FLOAT: {
+		union { long l; float f; } cvt;
+		cvt.l = value;
+		return fprintf(output, "%f", cvt.f);
+	}
 	case ARGTYPE_ADDR:
 		if (!value)
 			return fprintf(output, "NULL");
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index bff45fa..66db092 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -14,6 +14,8 @@
 ; ulong		== (unsigned long)
 ; octal		== (unsigned)			[written in octal]
 ; char
+; short		== (short)
+; ushort	== (unsigned short)
 ; addr		== (void *)			[unsigned, written in hexa]
 ; file		== (FILE *)						[TODO]
 ; format	== ((const char *), ...)	[printf() like]		[TODO]
@@ -154,8 +156,8 @@ addr getservbyport(int, string);
 addr getservent(void);
 void herror(string);
 string hstrerror(int);
-;int rcmd(addr, ushort, string, string, string, addr); FIXME implement ushort
-;int rcmd_af(addr, ushort, string, string, string, addr, int); FIXME implement ushort
+int rcmd(addr, ushort, string, string, string, addr);
+int rcmd_af(addr, ushort, string, string, string, addr, int);
 int rexec(addr, int, string, string, string, addr);
 int rexec_af(addr, int, string, string, string, addr, int);
 int rresvport (addr);
diff --git a/ltrace.h b/ltrace.h
index 3b40cb3..2b34476 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -37,6 +37,9 @@ enum arg_type {
 	ARGTYPE_ULONG,
 	ARGTYPE_OCTAL,
 	ARGTYPE_CHAR,
+	ARGTYPE_SHORT,
+	ARGTYPE_USHORT,
+	ARGTYPE_FLOAT,
 	ARGTYPE_ADDR,
 	ARGTYPE_FILE,
 	ARGTYPE_FORMAT,		/* printf-like format */
diff --git a/read_config_file.c b/read_config_file.c
index 5b114de..dab66b3 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -29,6 +29,9 @@ static struct list_of_pt_t {
 	"ulong", ARGTYPE_ULONG}, {
 	"octal", ARGTYPE_OCTAL}, {
 	"char", ARGTYPE_CHAR}, {
+	"short", ARGTYPE_SHORT}, {
+	"ushort", ARGTYPE_USHORT}, {
+	"float", ARGTYPE_FLOAT}, {
 	"addr", ARGTYPE_ADDR}, {
 	"file", ARGTYPE_FILE}, {
 	"format", ARGTYPE_FORMAT}, {
@@ -46,6 +49,9 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_ULONG },
 	{ ARGTYPE_OCTAL },
 	{ ARGTYPE_CHAR },
+	{ ARGTYPE_SHORT },
+	{ ARGTYPE_USHORT },
+	{ ARGTYPE_FLOAT },
 	{ ARGTYPE_ADDR },
 	{ ARGTYPE_FILE },
 	{ ARGTYPE_FORMAT },
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index b43de08..870809d 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -41,3 +41,18 @@ void func_enum(int x)
 {
 	printf("enum: %d\n", x);
 }
+
+void func_short(short x1, short x2)
+{
+	printf("short: %hd %hd\n", x1, x2);
+}
+
+void func_ushort(unsigned short x1, unsigned short x2)
+{
+	printf("ushort: %hu %hu\n", x1, x2);
+}
+
+void func_float(float f1, float f2)
+{
+	printf("%f %f\n", f1, f2);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index d7c225f..93811c1 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -19,6 +19,9 @@ int func_strlen(char*);
 void func_strfixed(char*);
 void func_ppp(int***);
 void func_stringp(char**);
+void func_short(short, short);
+void func_ushort(unsigned short, unsigned short);
+void func_float(float, float);
 
 typedef enum {
   RED,
@@ -59,4 +62,10 @@ main ()
   func_stringp(&s);
 
   func_enum(BLUE);
+
+  func_short(-8, -9);
+  func_ushort(33, 34);
+  func_float(3.4, -3.4);
+
+  return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index debb1dc..394919b 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -6,3 +6,6 @@ void func_strfixed(string[4])
 void func_ppp(int***)
 void func_stringp(string*)
 void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4))
+void func_short(short,short)
+void func_ushort(ushort, ushort)
+void func_float(float,float)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index 024c3f3..e47dc0d 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -52,3 +52,11 @@ set pattern "func_stringp(\\\"Dude\\\")"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "func_enum(BLUE)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_short(-8, -9)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_ushort(33, 34)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_float(3.40*, -3.40*)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "exited (status 0)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From 8cb2b73ba6d2ec4e669f4505076e3b1b7e042254 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Sun, 23 Jul 2006 16:49:08 -0700
Subject: [PATCH 8/11] typedefs for parameter types

Allows creating aliases of complex parameters, so that you don't
have to keep repeating enum(RED=0,GREEN=1,BLUE=2) over and over.
---
 ChangeLog                              |    1 
 etc/ltrace.conf                        |    9 ++++
 read_config_file.c                     |   71 ++++++++++++++++++++++++++++++++
 testsuite/ltrace.main/parameters-lib.c |    5 ++
 testsuite/ltrace.main/parameters.c     |    3 +
 testsuite/ltrace.main/parameters.conf  |    2 +
 testsuite/ltrace.main/parameters.exp   |    2 +
 7 files changed, 93 insertions(+), 0 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 13239bc..5404ecf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* read-config-file.c: Implement typedef (aliased parameter types)
 	* various: Implement short, ushort, float types
 
 2006-07-21    Steve Fink <sphink at gmail.com>
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 66db092..2ef97e7 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -32,6 +32,15 @@
 ; string0	== (char *)			[same as string[retval]]
 ; stringN	== (char *)		[N>0]	[same as string[argN]]
 
+; Typedefs
+;
+; To make it easier to specify argument lists, you can use 'typedef'
+; directives to avoid repeating complex parameter descriptors:
+;
+;   typedef color = enum(RED=1,BLUE=2,GREEN=3)
+;   void draw_line(color,int,int,int,int)
+;   void draw_square(color,int,int,int,int)
+;
 ; Enumerations
 ;
 ; The syntax is a parenthesized list of key=value assignments, like so:
diff --git a/read_config_file.c b/read_config_file.c
index dab66b3..76104dc 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -15,6 +15,8 @@ static int line_no;
 static char *filename;
 static int error_count = 0;
 
+static arg_type_info *parse_type(char **str);
+
 struct function *list_of_functions = NULL;
 
 static struct list_of_pt_t {
@@ -214,13 +216,82 @@ static int parse_argnum(char **str)
     return n * multiplier;
 }
 
+struct typedef_node_t {
+    char *name;
+    arg_type_info *info;
+    struct typedef_node_t *next;
+} *typedefs = NULL;
+
+static arg_type_info *lookup_typedef(char **str)
+{
+    struct typedef_node_t *node;
+    char *end = *str;
+    while (*end && (isalnum(*end) || *end == '_'))
+	++end;
+    if (end == *str)
+	return NULL;
+
+    for (node = typedefs; node != NULL; node = node->next) {
+	if (strncmp(*str, node->name, end - *str) == 0) {
+	    (*str) += strlen(node->name);
+	    return node->info;
+	}
+    }
+
+    return NULL;
+}
+
+static void parse_typedef(char **str)
+{
+    char *name;
+    arg_type_info *info;
+    struct typedef_node_t *binding;
+
+    (*str) += strlen("typedef");
+    eat_spaces(str);
+
+    // Grab out the name of the type
+    name = parse_ident(str);
+
+    // Skip = sign
+    eat_spaces(str);
+    if (**str != '=') {
+	output_line(0,
+		    "Syntax error in `%s', line %d: expected '=', got '%c'",
+		    filename, line_no, **str);
+	error_count++;
+	return;
+    }
+    (*str)++;
+    eat_spaces(str);
+
+    // Parse the type
+    info = parse_type(str);
+
+    // Insert onto beginning of linked list
+    binding = malloc(sizeof(*binding));
+    binding->name = name;
+    binding->info = info;
+    binding->next = typedefs;
+    typedefs = binding;
+}
+
 static arg_type_info *parse_nonpointer_type(char **str)
 {
 	arg_type_info *simple;
 	arg_type_info *info;
 
+	if (strncmp(*str, "typedef", 7) == 0) {
+	    parse_typedef(str);
+	    return lookup_singleton(ARGTYPE_UNKNOWN);
+	}
+
 	simple = str2type(str);
 	if (simple->type == ARGTYPE_UNKNOWN) {
+	    info = lookup_typedef(str);
+	    if (info)
+		return info;
+	    else
 		return simple;		// UNKNOWN
 	}
 
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index 870809d..f5ed3f7 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -56,3 +56,8 @@ void func_float(float f1, float f2)
 {
 	printf("%f %f\n", f1, f2);
 }
+
+void func_typedef(int x)
+{
+	printf("typedef'd enum: %d\n", x);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index 93811c1..b573767 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -31,6 +31,7 @@ typedef enum {
   PETUNIA
 } color_t;
 void func_enum(color_t);
+void func_typedef(color_t);
 
 int 
 main ()
@@ -67,5 +68,7 @@ main ()
   func_ushort(33, 34);
   func_float(3.4, -3.4);
 
+  func_typedef(BLUE);
+
   return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 394919b..dd8294d 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -9,3 +9,5 @@ void func_enum(enum (RED=0,GREEN=1,BLUE=
 void func_short(short,short)
 void func_ushort(ushort, ushort)
 void func_float(float,float)
+typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)
+void func_typedef(color)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index e47dc0d..2026cbd 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -58,5 +58,7 @@ set pattern "func_ushort(33, 34)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "func_float(3.40*, -3.40*)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_typedef(BLUE)"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "exited (status 0)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From 7409a0a85b43cb9e98b95068e7273af043aa7ba4 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Sun, 23 Jul 2006 17:30:01 -0700
Subject: [PATCH 9/11] pointer to array parameters

Add yet another parameter type: pointers to arrays of (nearly)
anything else. Useful for things like OpenGL's glGenTextures().
---
 ChangeLog                              |    2 +
 defs.h                                 |    4 ++
 display_args.c                         |   65 +++++++++++++++++++++++++++++---
 etc/ltrace.conf                        |    9 ++++
 ltrace.1                               |    5 ++
 ltrace.h                               |    8 ++++
 options.c                              |    9 +++-
 options.h                              |    1 
 read_config_file.c                     |   39 +++++++++++++++++++
 testsuite/ltrace.main/parameters-lib.c |   18 +++++++++
 testsuite/ltrace.main/parameters.c     |   16 ++++++++
 testsuite/ltrace.main/parameters.conf  |    2 +
 testsuite/ltrace.main/parameters.exp   |    8 ++++
 13 files changed, 176 insertions(+), 10 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5404ecf..3236d86 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* display_args.c: Implement parameters that point to arrays
+	* options.c: Add -A option for giving max # array elts
 	* read-config-file.c: Implement typedef (aliased parameter types)
 	* various: Implement short, ushort, float types
 
diff --git a/defs.h b/defs.h
index 1dc9c98..e59ebca 100644
--- a/defs.h
+++ b/defs.h
@@ -10,3 +10,7 @@ #endif
 #ifndef DEFAULT_STRLEN
 #define DEFAULT_STRLEN  32	/* default maximum # of bytes printed in */
 #endif				/* strings (-s switch) */
+
+#ifndef DEFAULT_ARRAYLEN
+#define DEFAULT_ARRAYLEN  4	/* default maximum # array elements */
+#endif				/* (-A switch) */
diff --git a/display_args.c b/display_args.c
index bb0bca0..f91267c 100644
--- a/display_args.c
+++ b/display_args.c
@@ -20,6 +20,7 @@ static int display_unknown(enum tof type
 static int display_format(enum tof type, struct process *proc, int arg_num);
 
 static int string_maxlength = INT_MAX;
+static int array_maxlength = INT_MAX;
 
 static long get_length(enum tof type, struct process *proc, int len_spec)
 {
@@ -28,18 +29,68 @@ static long get_length(enum tof type, st
     return gimme_arg(type, proc, -len_spec - 1);
 }
 
+static int display_ptrto(enum tof type, struct process *proc, long item,
+			 arg_type_info * info)
+{
+    arg_type_info temp;
+    temp.type = ARGTYPE_POINTER;
+    temp.u.ptr_info.info = info;
+    return display_value(type, proc, item, &temp);
+}
+
+/*
+ * addr - A pointer to the first element of the array
+ *
+ * The function name is used to indicate that we're not actually
+ * looking at an 'array', which is a contiguous region of memory
+ * containing a sequence of elements of some type; instead, we have a
+ * pointer to that region of memory.
+ */
+static int display_arrayptr(enum tof type, struct process *proc,
+			    void *addr, arg_type_info * info)
+{
+    int len = 0;
+    int i;
+    int array_len;
+
+    if (addr == NULL)
+	return fprintf(output, "NULL");
+
+    array_len = get_length(type, proc, info->u.array_info.len_spec);
+    len += fprintf(output, "[ ");
+    for (i = 0; i < opt_A && i < array_maxlength && i < array_len; i++) {
+	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(output, ", ");
+	if (opt_d)
+	    len += fprintf(output, "%p=", addr);
+	len +=
+	    display_ptrto(type, proc, (long) addr, elt_type);
+	addr += elt_size;
+    }
+    if (i < array_len)
+	len += fprintf(output, "...");
+    len += fprintf(output, " ]");
+    return len;
+}
+ 
 static int display_pointer(enum tof type, struct process *proc, long value,
 			   arg_type_info * info)
 {
     long pointed_to;
     arg_type_info *inner = info->u.ptr_info.info;
 
-    if (value == 0)
-      return fprintf(output, "NULL");
-    else if (umovelong(proc, (void *) value, &pointed_to) < 0)
-      return fprintf(output, "?");
-    else
-      return display_value(type, proc, pointed_to, inner);
+    if (inner->type == ARGTYPE_ARRAY) {
+	return display_arrayptr(type, proc, (void*) value, inner);
+    } else {
+	if (value == 0)
+	    return fprintf(output, "NULL");
+	else if (umovelong(proc, (void *) value, &pointed_to) < 0)
+	    return fprintf(output, "?");
+	else
+	    return display_value(type, proc, pointed_to, inner);
+    }
 }
 
 static int display_enum(enum tof type, struct process *proc,
@@ -115,6 +166,8 @@ int display_value(enum tof type, struct 
 		return display_string(type, proc, (void*) value,
 				      get_length(type, proc,
 						 info->u.string_n_info.size_spec));
+	case ARGTYPE_ARRAY:
+		return fprintf(output, "<array without address>");
         case ARGTYPE_ENUM:
 		return display_enum(type, proc, info, value);
 	case ARGTYPE_POINTER:
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 2ef97e7..fa901fc 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -27,6 +27,9 @@
 ; ignore	== (any)			[ignore arg, output blank]
 ; type*		== (type *)			[pointer to any other type]
 ; enum (key=value,key=value,...)		[enumeration, see below]
+; array(type,argN)
+;		== (type[SIZE])			[array of (arg N) elements]
+; array(type,N)	== (type[N])			[array of N elements]
 
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
@@ -48,6 +51,12 @@
 ; an example usage might look like
 ;   int fcntl(int,enum (F_DUPFD=0,F_GETFD=1,F_SETFD=2))
 ;
+; Arrays
+;
+; NOTE: Uses of array(...) alone are very rare. You almost always
+; want array(...)*. The exceptions are when you have a fixed-size
+; array.
+
 
 ; arpa/inet.h
 int inet_aton(string,addr);
diff --git a/ltrace.1 b/ltrace.1
index 7c114cc..f7b7d95 100644
--- a/ltrace.1
+++ b/ltrace.1
@@ -6,7 +6,7 @@ ltrace \- A library call tracer
 
 .SH SYNOPSIS
 .B ltrace
-.I "[-CdfhiLrStttV] [-a column] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-X extern] [-x extern] ... [--align=column] [--debug] [--demangle] [--help] [--indent=nr] [--library=filename] [--output=filename] [--version] [command [arg ...]]"
+.I "[-CdfhiLrStttV] [-a column] [-A maxelts] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-X extern] [-x extern] ... [--align=column] [--debug] [--demangle] [--help] [--indent=nr] [--library=filename] [--output=filename] [--version] [command [arg ...]]"
 
 .SH DESCRIPTION
 .B ltrace
@@ -25,6 +25,9 @@ Its use is very similar to
 .I \-a, \-\-align column
 Align return values in a specific column (default column is 5/8 of screen width).
 .TP
+.I \-A maxelts
+Maximum number of array elements to print before suppressing the rest with an ellipsis ("...")
+.TP
 .I \-c
 Count time and calls for each library call and report a summary on program exit.
 .TP
diff --git a/ltrace.h b/ltrace.h
index 2b34476..fe5fd87 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -45,6 +45,7 @@ enum arg_type {
 	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_IGNORE,		/* Leave parameter blank */
         ARGTYPE_POINTER,	/* Pointer to some other type */
@@ -61,6 +62,13 @@ typedef struct arg_type_info_t {
 	    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;
diff --git a/options.c b/options.c
index bdf565b..272870b 100644
--- a/options.c
+++ b/options.c
@@ -30,6 +30,7 @@ int library_num = 0;
 static char *progname;		/* Program name (`ltrace') */
 FILE *output;
 int opt_a = DEFAULT_ACOLUMN;	/* default alignment column for results */
+int opt_A = DEFAULT_ARRAYLEN;	/* default maximum # array elements to print */
 int opt_c = 0;			/* Count time, calls, and report a summary on program exit */
 int opt_d = 0;			/* debug */
 int opt_i = 0;			/* instruction pointer */
@@ -78,6 +79,7 @@ # if HAVE_GETOPT_LONG
 # else
 		"  -a COLUMN           align return values in a secific column.\n"
 # endif
+                "  -A ARRAYLEN         maximum number of array elements to print.\n"
 		"  -c                  count time and calls, and report a summary on exit.\n"
 # ifdef USE_DEMANGLE
 #  if HAVE_GETOPT_LONG
@@ -202,14 +204,14 @@ #endif
 # ifdef USE_DEMANGLE
 				"C"
 # endif
-				"a:e:F:l:n:o:p:s:u:x:X:", long_options,
+				"a:A:e:F:l:n:o:p:s:u:x:X:", long_options,
 				&option_index);
 #else
 		c = getopt(argc, argv, "+cdfhiLrStTV"
 # ifdef USE_DEMANGLE
 			   "C"
 # endif
-			   "a:e:F:l:n:o:p:s:u:x:X:");
+			   "a:A:e:F:l:n:o:p:s:u:x:X:");
 #endif
 		if (c == -1) {
 			break;
@@ -218,6 +220,9 @@ #endif
 		case 'a':
 			opt_a = atoi(optarg);
 			break;
+                case 'A':
+			opt_A = atoi(optarg);
+			break;
 		case 'c':
 			opt_c++;
 			break;
diff --git a/options.h b/options.h
index 0b35693..9ead6f3 100644
--- a/options.h
+++ b/options.h
@@ -7,6 +7,7 @@ #include <sys/types.h>
 
 extern FILE *output;
 extern int opt_a;		/* default alignment column for results */
+extern int opt_A;		/* default maximum # of array elements printed */
 extern int opt_c;		/* count time, calls, and report a summary on program exit */
 extern int opt_d;		/* debug */
 extern int opt_i;		/* instruction pointer */
diff --git a/read_config_file.c b/read_config_file.c
index 76104dc..51d4d85 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -38,6 +38,7 @@ static struct list_of_pt_t {
 	"file", ARGTYPE_FILE}, {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
+	"array", ARGTYPE_ARRAY}, {
 	"enum", ARGTYPE_ENUM}, {
 	"ignore", ARGTYPE_IGNORE}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
@@ -59,6 +60,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_FORMAT },
 	{ ARGTYPE_STRING },
 	{ ARGTYPE_STRING_N },
+	{ ARGTYPE_ARRAY },
 	{ ARGTYPE_ENUM },
 	{ ARGTYPE_IGNORE },
 	{ ARGTYPE_POINTER },
@@ -162,6 +164,7 @@ static int simple_type(enum arg_type at)
     switch (at) {
     case ARGTYPE_STRING:
     case ARGTYPE_STRING_N:
+    case ARGTYPE_ARRAY:
     case ARGTYPE_ENUM:
 	return 0;
 
@@ -276,6 +279,26 @@ static void parse_typedef(char **str)
     typedefs = binding;
 }
 
+static size_t arg_sizeof(arg_type_info * arg)
+{
+    if (arg->type == ARGTYPE_CHAR) {
+	return sizeof(char);
+    } else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) {
+	return sizeof(short);
+    } else if (arg->type == ARGTYPE_FLOAT) {
+	return sizeof(float);
+    } else if (arg->type == ARGTYPE_ENUM) {
+	return sizeof(int);
+    } else if (arg->type == ARGTYPE_ARRAY) {
+	if (arg->u.array_info.len_spec > 0)
+	    return arg->u.array_info.len_spec * arg->u.array_info.elt_size;
+	else
+	    return sizeof(void *);
+    } else {
+	return sizeof(int);
+    }
+}
+
 static arg_type_info *parse_nonpointer_type(char **str)
 {
 	arg_type_info *simple;
@@ -306,7 +329,21 @@ static arg_type_info *parse_nonpointer_t
 
 	switch (info->type) {
 
-	// Syntax: enum ( keyname=value,keyname=value,... )
+	/* Syntax: array ( type, N|argN ) */
+	case ARGTYPE_ARRAY:
+	    (*str)++;		// Get past open paren
+	    eat_spaces(str);
+	    if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
+		return NULL;
+	    info->u.array_info.elt_size =
+		arg_sizeof(info->u.array_info.elt_type);
+	    (*str)++;		// Get past comma
+	    eat_spaces(str);
+	    info->u.array_info.len_spec = parse_argnum(str);
+	    (*str)++;		// Get past close paren
+	    return info;
+
+	/* Syntax: enum ( keyname=value,keyname=value,... ) */
 	case ARGTYPE_ENUM:{
 	    struct enum_opt {
 		char *key;
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index f5ed3f7..a45cd2e 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -61,3 +61,21 @@ void func_typedef(int x)
 {
 	printf("typedef'd enum: %d\n", x);
 }
+
+void func_arrayi(int* a, int N)
+{
+    int i;
+    printf("array[int]: ");
+    for (i = 0; i < N; i++)
+	printf("%d ", a[i]);
+    printf("\n");
+}
+
+void func_arrayf(float* a, int N)
+{
+    int i;
+    printf("array[float]: ");
+    for (i = 0; i < N; i++)
+	printf("%f ", a[i]);
+    printf("\n");
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index b573767..dbc8ace 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -22,6 +22,8 @@ void func_stringp(char**);
 void func_short(short, short);
 void func_ushort(unsigned short, unsigned short);
 void func_float(float, float);
+void func_arrayi(int*, int);
+void func_arrayf(float*, int);
 
 typedef enum {
   RED,
@@ -40,6 +42,8 @@ main ()
   int *xP, **xPP;
   char buf[200];
   char *s;
+  int *ai;
+  float *af;
 
   func_ignore(1, 2, 3);
 
@@ -70,5 +74,17 @@ main ()
 
   func_typedef(BLUE);
 
+  ai = (int*) calloc(sizeof(int), 8);
+  for (x = 0; x < 8; x++)
+    ai[x] = 10 + x;
+  func_arrayi(ai, 8);
+  func_arrayi(ai, 2);
+
+  af = (float*) calloc(sizeof(float), 8);
+  for (x = 0; x < 8; x++)
+    af[x] = 10.1 + x;
+  func_arrayf(af, 8);
+  func_arrayf(af, 2);
+
   return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index dd8294d..8216857 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -11,3 +11,5 @@ void func_ushort(ushort, ushort)
 void func_float(float,float)
 typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)
 void func_typedef(color)
+void func_arrayi(array(int,arg2)*,ignore)
+void func_arrayf(array(float,arg2)*,ignore)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index 2026cbd..ee269e4 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -60,5 +60,13 @@ set pattern "func_float(3.40*, -3.40*)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "func_typedef(BLUE)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., )"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_arrayi(. 10, 11 ., )"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., )"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_arrayf(. 10.10*, 11.10* ., )"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "exited (status 0)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From d956781b90b4b51760ad2df83bddab462957bfe7 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 21:38:50 -0700
Subject: [PATCH 10/11] struct parameters

Allow parameters to be pointers to structs, which themselves can contain
(nearly) any other type, including other structs or pointers to structs.
Lengths for strings and arrays can be read out of the struct itself.
---
 ChangeLog                              |    1 
 display_args.c                         |   83 +++++++++++++++++++----
 etc/ltrace.conf                        |   40 +++++++++++
 ltrace.h                               |   12 +++
 read_config_file.c                     |  114 +++++++++++++++++++++++++++++++-
 testsuite/ltrace.main/parameters-lib.c |   36 ++++++++++
 testsuite/ltrace.main/parameters.c     |   33 +++++++++
 testsuite/ltrace.main/parameters.conf  |    1 
 testsuite/ltrace.main/parameters.exp   |    2 +
 9 files changed, 304 insertions(+), 18 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3236d86..d4d30dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2006-07-20    Steve Fink <sphink at gmail.com>
 
+	* various: Implement struct parameters
 	* display_args.c: Implement parameters that point to arrays
 	* options.c: Add -A option for giving max # array elts
 	* read-config-file.c: Implement typedef (aliased parameter types)
diff --git a/display_args.c b/display_args.c
index f91267c..e8b20e1 100644
--- a/display_args.c
+++ b/display_args.c
@@ -15,27 +15,35 @@ static int display_char(int what);
 static int display_string(enum tof type, struct process *proc,
 			  void* addr, size_t maxlen);
 static int display_value(enum tof type, struct process *proc,
-			 long value, arg_type_info *info);
+			 long value, arg_type_info *info,
+			 void *st, arg_type_info* st_info);
 static int display_unknown(enum tof type, struct process *proc, long value);
 static int display_format(enum tof type, struct process *proc, int arg_num);
 
 static int string_maxlength = INT_MAX;
 static int array_maxlength = INT_MAX;
 
-static long get_length(enum tof type, struct process *proc, int len_spec)
+static long get_length(enum tof type, struct process *proc, int len_spec,
+		       void *st, arg_type_info* st_info)
 {
+    long len;
     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);
+	return len;
+    }
     return gimme_arg(type, proc, -len_spec - 1);
 }
 
 static int display_ptrto(enum tof type, struct process *proc, long item,
-			 arg_type_info * info)
+			 arg_type_info * info,
+			 void *st, arg_type_info* st_info)
 {
     arg_type_info temp;
     temp.type = ARGTYPE_POINTER;
     temp.u.ptr_info.info = info;
-    return display_value(type, proc, item, &temp);
+    return display_value(type, proc, item, &temp, st, st_info);
 }
 
 /*
@@ -47,7 +55,8 @@ static int display_ptrto(enum tof type, 
  * pointer to that region of memory.
  */
 static int display_arrayptr(enum tof type, struct process *proc,
-			    void *addr, arg_type_info * info)
+			    void *addr, arg_type_info * info,
+			    void *st, arg_type_info* st_info)
 {
     int len = 0;
     int i;
@@ -56,7 +65,8 @@ static int display_arrayptr(enum tof typ
     if (addr == NULL)
 	return fprintf(output, "NULL");
 
-    array_len = get_length(type, proc, info->u.array_info.len_spec);
+    array_len = get_length(type, proc, info->u.array_info.len_spec,
+			   st, st_info);
     len += fprintf(output, "[ ");
     for (i = 0; i < opt_A && i < array_maxlength && i < array_len; i++) {
 	arg_type_info *elt_type = info->u.array_info.elt_type;
@@ -66,7 +76,7 @@ static int display_arrayptr(enum tof typ
 	if (opt_d)
 	    len += fprintf(output, "%p=", addr);
 	len +=
-	    display_ptrto(type, proc, (long) addr, elt_type);
+	    display_ptrto(type, proc, (long) addr, elt_type, st, st_info);
 	addr += elt_size;
     }
     if (i < array_len)
@@ -75,21 +85,57 @@ static int display_arrayptr(enum tof typ
     return len;
 }
  
+/* addr - A pointer to the beginning of the memory region occupied by
+ *        the struct (aka a pointer to the struct)
+ */
+static int display_structptr(enum tof type, struct process *proc,
+			     void *addr, arg_type_info * info)
+{
+    int i;
+    arg_type_info *field;
+    int len = 0;
+
+    if (addr == NULL)
+	return fprintf(output, "NULL");
+
+    len += fprintf(output, "{ ");
+    for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) {
+	if (i != 0)
+	    len += fprintf(output, ", ");
+	if (opt_d)
+	    len +=
+		fprintf(output, "%p=",
+			addr + info->u.struct_info.offset[i]);
+	len +=
+	    display_ptrto(LT_TOF_STRUCT, proc,
+			  (long) addr + info->u.struct_info.offset[i],
+			  field, addr, info);
+    }
+    len += fprintf(output, " }");
+
+    return len;
+}
+
 static int display_pointer(enum tof type, struct process *proc, long value,
-			   arg_type_info * info)
+			   arg_type_info * info,
+			   void *st, arg_type_info* st_info)
 {
     long pointed_to;
     arg_type_info *inner = info->u.ptr_info.info;
 
     if (inner->type == ARGTYPE_ARRAY) {
-	return display_arrayptr(type, proc, (void*) value, inner);
+	return display_arrayptr(type, proc, (void*) value, inner,
+				st, st_info);
+    } else if (inner->type == ARGTYPE_STRUCT) {
+	return display_structptr(type, proc, (void *) value, inner);
     } else {
 	if (value == 0)
 	    return fprintf(output, "NULL");
 	else if (umovelong(proc, (void *) value, &pointed_to) < 0)
 	    return fprintf(output, "?");
 	else
-	    return display_value(type, proc, pointed_to, inner);
+	    return display_value(type, proc, pointed_to, inner,
+				 st, st_info);
     }
 }
 
@@ -110,9 +156,15 @@ static int display_enum(enum tof type, s
    proc - information about the traced process
    value - the value to display
    info - the description of the type to display
+   st - if the current value is a struct member, the address of the struct
+   st_info - type of the above struct
+
+   Those last two parameters are used for structs containing arrays or
+   strings whose length is given by another structure element.
 */
 int display_value(enum tof type, struct process *proc,
-                  long value, arg_type_info *info)
+                  long value, arg_type_info *info,
+		  void *st, arg_type_info* st_info)
 {
 	int tmp;
 
@@ -165,13 +217,16 @@ int display_value(enum tof type, struct 
 	case ARGTYPE_STRING_N:
 		return display_string(type, proc, (void*) value,
 				      get_length(type, proc,
-						 info->u.string_n_info.size_spec));
+						 info->u.string_n_info.size_spec, st, st_info));
 	case ARGTYPE_ARRAY:
 		return fprintf(output, "<array without address>");
         case ARGTYPE_ENUM:
 		return display_enum(type, proc, info, value);
+	case ARGTYPE_STRUCT:
+		return fprintf(output, "<struct without address>");
 	case ARGTYPE_POINTER:
-		return display_pointer(type, proc, value, info);
+		return display_pointer(type, proc, value, info,
+				       st, st_info);
  	case ARGTYPE_UNKNOWN:
 	default:
 		return display_unknown(type, proc, value);
@@ -189,7 +244,7 @@ int display_arg(enum tof type, struct pr
 	return display_format(type, proc, arg_num);
     } else {
 	arg = gimme_arg(type, proc, arg_num);
-	return display_value(type, proc, arg, info);
+	return display_value(type, proc, arg, info, NULL, NULL);
     }
 }
 
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index fa901fc..1fe808e 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -21,6 +21,7 @@
 ; format	== ((const char *), ...)	[printf() like]		[TODO]
 ; string	== (char *)
 ; string[argN]  == (char *)		[N>0]	[show only up to (arg N) bytes]
+; string[eltN]  == (char *)		[N>0]	[show only up to (elt N) bytes]
 ; string[retval] == (char *)			[show only up to (return val) bytes]
 ; string[arg0]	== (char *)			[same as string[retval]]
 ; string[N]     == (char *)             [N>0]   [show only up to N bytes]
@@ -29,12 +30,18 @@
 ; enum (key=value,key=value,...)		[enumeration, see below]
 ; array(type,argN)
 ;		== (type[SIZE])			[array of (arg N) elements]
+; array(type,eltN)
+;		== (type[SIZE])			[array of (struct element N) elements]
 ; array(type,N)	== (type[N])			[array of N elements]
-
+; struct(type,type,...)
+;		== (struct {...})		[struct of several types]
+;
 ; Backwards-compatibility:
 ; string0	== (char *)			[same as string[retval]]
 ; stringN	== (char *)		[N>0]	[same as string[argN]]
 
+
+
 ; Typedefs
 ;
 ; To make it easier to specify argument lists, you can use 'typedef'
@@ -56,6 +63,37 @@
 ; NOTE: Uses of array(...) alone are very rare. You almost always
 ; want array(...)*. The exceptions are when you have a fixed-size
 ; array.
+;
+; Structs
+;
+; NOTE: Uses of struct(...) alone are very rare. You almost always
+; want struct(...)* (a pointer to a struct) anyway. Most compilers
+; pass structs as pointers anyway, and those that don't are not yet
+; supported. The one time when you want to use a non-pointer
+; struct(...) type are when you have an array of structs, or a struct
+; containing another struct.
+;
+; For example, if you have
+;   struct s1 {
+;	int y_size;
+;	int * y;
+;	int z[3];
+;	struct { char c; } a;
+;	struct { char c; } * b;
+;   }
+; and a function
+;   void f(struct s1*)
+; then the corresponding ltrace spec is
+;   void f(struct(int,array(int,elt0),array(int,3),struct(char),struct(char)*)*)
+; which, formatted similarly to the C declaration, looks like
+;   void f(struct(
+;                 int,
+;                 array(int,elt0),
+;                 array(int,3),
+;                 struct(char),
+;                 struct(char)*
+;                )*
+;         )
 
 
 ; arpa/inet.h
diff --git a/ltrace.h b/ltrace.h
index fe5fd87..2e0d01e 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -47,6 +47,7 @@ enum arg_type {
 	ARGTYPE_STRING_N,	/* String of known maxlen */
         ARGTYPE_ARRAY,		/* Series of values in memory */
         ARGTYPE_ENUM,		/* Enumeration */
+        ARGTYPE_STRUCT,		/* Structure of values */
         ARGTYPE_IGNORE,		/* Leave parameter blank */
         ARGTYPE_POINTER,	/* Pointer to some other type */
         ARGTYPE_COUNT		/* number of ARGTYPE_* values */
@@ -74,6 +75,14 @@ typedef struct arg_type_info_t {
 	    int size_spec;
 	} string_n_info;
 
+	// ARGTYPE_STRUCT
+	struct {
+	    struct arg_type_info_t **fields;	// NULL-terminated
+	    size_t *gap;
+	    size_t *offset;
+	    size_t size;
+	} struct_info;
+
 	// ARGTYPE_POINTER
 	struct {
 	    struct arg_type_info_t *info;
@@ -86,7 +95,8 @@ enum tof {
 	LT_TOF_FUNCTION,	/* A real library function */
 	LT_TOF_FUNCTIONR,	/* Return from a real library function */
 	LT_TOF_SYSCALL,		/* A syscall */
-	LT_TOF_SYSCALLR		/* Return from a syscall */
+	LT_TOF_SYSCALLR,	/* Return from a syscall */
+        LT_TOF_STRUCT		/* Not a function; read args from struct */
 };
 
 struct function {
diff --git a/read_config_file.c b/read_config_file.c
index 51d4d85..ec1a4a8 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -19,6 +19,8 @@ static arg_type_info *parse_type(char **
 
 struct function *list_of_functions = NULL;
 
+/* Map of strings to type names. These do not need to be in any
+ * particular order */
 static struct list_of_pt_t {
 	char *name;
 	enum arg_type pt;
@@ -39,11 +41,15 @@ static struct list_of_pt_t {
 	"format", ARGTYPE_FORMAT}, {
 	"string", ARGTYPE_STRING}, {
 	"array", ARGTYPE_ARRAY}, {
+	"struct", ARGTYPE_STRUCT}, {
 	"enum", ARGTYPE_ENUM}, {
 	"ignore", ARGTYPE_IGNORE}, {
 	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
 };
 
+/* Array of singleton objects for each of the types. The order in this
+ * array must exactly match the list of enumerated values in
+ * ltrace.h */
 static arg_type_info arg_type_singletons[] = {
 	{ ARGTYPE_VOID },
 	{ ARGTYPE_INT },
@@ -62,6 +68,7 @@ static arg_type_info arg_type_singletons
 	{ ARGTYPE_STRING_N },
 	{ ARGTYPE_ARRAY },
 	{ ARGTYPE_ENUM },
+	{ ARGTYPE_STRUCT },
 	{ ARGTYPE_IGNORE },
 	{ ARGTYPE_POINTER },
 	{ ARGTYPE_UNKNOWN }
@@ -166,6 +173,7 @@ static int simple_type(enum arg_type at)
     case ARGTYPE_STRING_N:
     case ARGTYPE_ARRAY:
     case ARGTYPE_ENUM:
+    case ARGTYPE_STRUCT:
 	return 0;
 
     default:
@@ -178,8 +186,8 @@ static int parse_int(char **str)
     char *end;
     long n = strtol(*str, &end, 0);
     if (end == *str) {
-	output_line(0, "Syntax error in `%s', line %d: Bad number",
-		    filename, line_no);
+	output_line(0, "Syntax error in `%s', line %d: Bad number (%s)",
+		    filename, line_no, *str);
 	error_count++;
 	return 0;
     }
@@ -209,6 +217,9 @@ static int parse_argnum(char **str)
     if (strncmp(*str, "arg", 3) == 0) {
 	(*str) += 3;
 	multiplier = -1;
+    } else if (strncmp(*str, "elt", 3) == 0) {
+	(*str) += 3;
+	multiplier = -1;
     } else if (strncmp(*str, "retval", 6) == 0) {
 	(*str) += 6;
 	return 0;
@@ -289,6 +300,8 @@ static size_t arg_sizeof(arg_type_info *
 	return sizeof(float);
     } else if (arg->type == ARGTYPE_ENUM) {
 	return sizeof(int);
+    } else if (arg->type == ARGTYPE_STRUCT) {
+	return arg->u.struct_info.size;
     } else if (arg->type == ARGTYPE_ARRAY) {
 	if (arg->u.array_info.len_spec > 0)
 	    return arg->u.array_info.len_spec * arg->u.array_info.elt_size;
@@ -299,6 +312,63 @@ static size_t arg_sizeof(arg_type_info *
     }
 }
 
+/* I'm sure this isn't completely correct, but just try to get most of
+ * them right for now. */
+#undef alignof
+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
+static void align_struct(arg_type_info* info)
+{
+    struct {
+	char c;
+	int i;
+    } ci;
+    struct {
+	struct {
+	    char c;
+	} s;
+	int i;
+    } cis;
+
+    size_t int_alignment = alignof(i, ci);
+    size_t whole_struct_alignment = alignof(i, cis);
+
+    size_t offset;
+    size_t gap;
+    int i;
+
+    if (info->u.struct_info.size != 0)
+	return;			// Already done
+
+    // The gap array isn't actually needed anymore, because the
+    // offset can be used for everything.
+
+    // 1. Add internal padding
+    offset = 0;
+    for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
+	arg_type_info *field = info->u.struct_info.fields[i];
+	info->u.struct_info.offset[i] = offset;
+	offset += arg_sizeof(field);
+
+	if (offset % int_alignment != 0) {
+	    gap = int_alignment - offset % int_alignment;
+	    info->u.struct_info.gap[i] = gap;
+	    offset += gap;
+	}
+    }
+
+    // 2. Add padding at end of entire struct
+    for (i = 0; info->u.struct_info.fields[i] != NULL; i++);
+    if (i == 0)
+	return;
+    if (offset % whole_struct_alignment != 0) {
+	gap = whole_struct_alignment - offset % whole_struct_alignment;
+	info->u.struct_info.gap[i - 1] = gap;
+	offset += gap;
+    }
+
+    info->u.struct_info.size = offset;
+}
+
 static arg_type_info *parse_nonpointer_type(char **str)
 {
 	arg_type_info *simple;
@@ -432,6 +502,46 @@ static arg_type_info *parse_nonpointer_t
 	    (*str)++;		// Skip past closing ]
 	    return info;
 
+	// Syntax: struct ( type,type,type,... )
+    case ARGTYPE_STRUCT:{
+	    int field_num = 0;
+	    (*str)++;		// Get past open paren
+	    info->u.struct_info.fields =
+		malloc((MAX_ARGS + 1) * sizeof(void *));
+	    info->u.struct_info.gap =
+		malloc((MAX_ARGS + 1) * sizeof(size_t));
+	    info->u.struct_info.offset =
+		malloc((MAX_ARGS + 1) * sizeof(size_t));
+	    info->u.struct_info.size = 0;
+	    eat_spaces(str); // Empty arg list with whitespace inside
+	    while (**str && **str != ')') {
+		if (field_num == MAX_ARGS) {
+		    output_line(0,
+				"Error in `%s', line %d: Too many structure elements",
+				filename, line_no);
+		    error_count++;
+		    return NULL;
+		}
+		eat_spaces(str);
+		if (field_num != 0) {
+		    (*str)++;	// Get past comma
+		    eat_spaces(str);
+		}
+		info->u.struct_info.gap[field_num] = 0;
+		if ((info->u.struct_info.fields[field_num++] =
+		     parse_type(str)) == NULL)
+		    return NULL;
+
+		// Must trim trailing spaces so the check for
+		// the closing paren is simple
+		eat_spaces(str);
+	    }
+	    (*str)++;		// Get past closing paren
+	    info->u.struct_info.fields[field_num] = NULL;
+	    align_struct(info);
+	    return info;
+	}
+
 	default:
 		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
 			    filename, line_no);
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index a45cd2e..26e4c3b 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -79,3 +79,39 @@ void func_arrayf(float* a, int N)
 	printf("%f ", a[i]);
     printf("\n");
 }
+
+struct test_struct {
+    int simple;
+    int alen;
+    int slen;
+    struct { int a; int b; }* array;
+    struct { int a; int b; } seq[3];
+    char* str;
+    char* outer_str;
+};
+
+void func_struct(struct test_struct* x)
+{
+    char buf[100];
+    int i;
+
+    printf("struct: ");
+
+    printf("%d, [", x->simple);
+    for (i = 0; i < x->alen; i++) {
+	printf("%d/%d", x->array[i].a, x->array[i].b);
+	if (i < x->alen - 1)
+	    printf(" ");
+    }
+    printf("] [");
+    for (i = 0; i < 3; i++) {
+	printf("%d/%d", x->seq[i].a, x->seq[i].b);
+	if (i < 2)
+	    printf(" ");
+    }
+    printf("] ");
+
+    strncpy(buf, x->str, x->slen);
+    buf[x->slen] = '\0';
+    printf("%s\n", buf);
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index dbc8ace..d0fa483 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -24,6 +24,7 @@ void func_ushort(unsigned short, unsigne
 void func_float(float, float);
 void func_arrayi(int*, int);
 void func_arrayf(float*, int);
+void func_struct(void*);
 
 typedef enum {
   RED,
@@ -86,5 +87,37 @@ main ()
   func_arrayf(af, 8);
   func_arrayf(af, 2);
 
+  {
+    struct {
+      int simple;
+      int alen;
+      int slen;
+      struct { int a; int b; }* array;
+      struct { int a; int b; } seq[3];
+      char* str;
+    } x;
+
+    x.simple = 89;
+
+    x.alen = 2;
+    x.array = malloc(800);
+    x.array[0].a = 1;
+    x.array[0].b = 10;
+    x.array[1].a = 3;
+    x.array[1].b = 30;
+
+    x.seq[0].a = 4;
+    x.seq[0].b = 40;
+    x.seq[1].a = 5;
+    x.seq[1].b = 50;
+    x.seq[2].a = 6;
+    x.seq[2].b = 60;
+
+    x.slen = 3;
+    x.str = "123junk";
+
+    func_struct(&x);
+  }
+
   return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 8216857..e8bd3e2 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -13,3 +13,4 @@ typedef color = enum (RED=0,GREEN=1,BLUE
 void func_typedef(color)
 void func_arrayi(array(int,arg2)*,ignore)
 void func_arrayf(array(float,arg2)*,ignore)
+void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*)
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index ee269e4..f07c296 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -70,3 +70,5 @@ set pattern "func_arrayf(. 10.10*, 11.10
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
 set pattern "exited (status 0)"
 ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_struct({ 89, 2, 3, . { 1, 10 }, { 3, 30 } ., . { 4, 40 }, { 5, 50 }, { 6, 60 } ., \\\"123\\\" })"
+ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1
-- 
1.4.1
-------------- next part --------------
From ac41ae65c78c936ddbf853627533ab815af1a336 Mon Sep 17 00:00:00 2001
From: Steve Fink sphink at gmail.com <sphink at gmail.com>
Date: Thu, 27 Jul 2006 21:39:19 -0700
Subject: [PATCH 11/11] Change bug email address.
---
 ltrace.1 |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/ltrace.1 b/ltrace.1
index f7b7d95..30f51e8 100644
--- a/ltrace.1
+++ b/ltrace.1
@@ -166,7 +166,8 @@ Only ELF32 binaries are supported.
 .LP
 Calls to dlopen()ed libraries will not be traced.
 .PP
-If you like to report a bug, send a notice to the author, or use the
+If you would like to report a bug, send a message to the mailing list
+(ltrace-devel at lists.alioth.debian.org), or use the
 .BR reportbug(1)
 program if you are under the Debian GNU/Linux distribution.
 
-- 
1.4.1


More information about the Ltrace-devel mailing list