Drop ARGTYPE_STRING, reimplement as lens over array of chars
diff --git a/ChangeLog b/ChangeLog
index 67564bd..06e70b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2012-01-09  Petr Machata  <pmachata@redhat.com>
 
+	* lens_default.c, lens_default.h (string_lens): New global variable
+	(format_naked_char): New function
+	* type.h, type.c (enum arg_type): Drop ARGTYPE_STRING
+	(type_init_string): Drop
+	* printf.c: Drop handling of ARGTYPE_STRING
+	* read_config_file.c: Likewise; support lens syntax of "string"
+	* value.c, value.h (value_string_to_charp): Drop
+
+2012-01-09  Petr Machata  <pmachata@redhat.com>
+
 	* read_config_file.c (try_parse_kwd): New function
 	(name2lens, parse_alias): Use it
 
diff --git a/lens_default.c b/lens_default.c
index e55aae6..f4e2bd8 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -141,15 +141,7 @@
 		return -1;
 	int c = (int)lc;
 
-	/* If this value is not wrapped in array, then this is not a
-	 * string, and we need to display quotes.  */
-	int quote = !(value->parent != NULL
-		      && (value->parent->type->type == ARGTYPE_ARRAY
-			  || value->parent->type->type == ARGTYPE_STRING_N));
 	int written = 0;
-	if (quote && acc_fprintf(&written, stream, "'") < 0)
-		return -1;
-
 	const char *fmt;
 	switch (c) {
 	case -1:
@@ -194,8 +186,19 @@
 
 	if (fmt != NULL && acc_fprintf(&written, stream, fmt, c) < 0)
 		return -1;
-	if (quote && acc_fprintf(&written, stream, "'") < 0)
+	return written;
+}
+
+static int
+format_naked_char(FILE *stream, struct value *value,
+		  struct value_dict *arguments)
+{
+	int written = 0;
+	if (acc_fprintf(&written, stream, "'") < 0
+	    || format_char(stream, value, arguments) < 0
+	    || acc_fprintf(&written, stream, "'") < 0)
 		return -1;
+
 	return written;
 }
 
@@ -313,9 +316,6 @@
 		     enum int_fmt_t int_fmt)
 {
 	switch (value->type->type) {
-		struct value *tmp;
-		int ret;
-
 	case ARGTYPE_VOID:
 		return fprintf(stream, "<void>");
 
@@ -332,7 +332,7 @@
 		return format_integer(stream, value, int_fmt, arguments);
 
 	case ARGTYPE_CHAR:
-		return format_char(stream, value, arguments);
+		return format_naked_char(stream, value, arguments);
 
 	case ARGTYPE_FLOAT:
 	case ARGTYPE_DOUBLE:
@@ -347,23 +347,9 @@
 		return format_integer(stream, value, INT_FMT_x, arguments);
 
 	case ARGTYPE_ARRAY:
-		if (value->type->u.array_info.elt_type->type != ARGTYPE_CHAR)
-			return format_array(stream, value, arguments,
-					    value->type->u.array_info.length,
-					    options.arraylen, 1,
-					    "[ ", " ]", ", ");
-
 		return format_array(stream, value, arguments,
 				    value->type->u.array_info.length,
-				    options.strlen, 0, "\"", "\"", "");
-
-	case ARGTYPE_STRING_N:
-		tmp = value_string_to_charp(value);
-		if (tmp == NULL)
-			return -1;
-		ret = format_argument(stream, tmp, arguments);
-		value_destroy(tmp);
-		return ret;
+				    options.arraylen, 1, "[ ", " ]", ", ");
 
 	case ARGTYPE_ENUM:
 		return format_enum(stream, value, arguments);
@@ -430,3 +416,65 @@
 struct lens guess_lens = {
 	.format_cb = guess_lens_format_cb,
 };
+
+
+static int
+string_lens_format_cb(struct lens *lens, FILE *stream,
+		      struct value *value, struct value_dict *arguments)
+{
+	switch (value->type->type) {
+	case ARGTYPE_POINTER:
+		/* This should really be written as either "string",
+		 * or, if lens, then string(array(char, zero)*).  But
+		 * I suspect people are so used to the char * C idiom,
+		 * that string(char *) might actually turn up.  So
+		 * let's just support it.  */
+		if (value->type->u.ptr_info.info->type == ARGTYPE_CHAR) {
+			struct arg_type_info info[2];
+			type_init_array(&info[1],
+					value->type->u.ptr_info.info, 0,
+					expr_node_zero(), 0);
+			type_init_pointer(&info[0], &info[1], 0);
+			info->lens = lens;
+			info->own_lens = 0;
+			struct value tmp;
+			if (value_clone(&tmp, value) < 0)
+				return -1;
+			value_set_type(&tmp, info, 0);
+			int ret = string_lens_format_cb(lens, stream, &tmp,
+							arguments);
+			type_destroy(&info[0]);
+			type_destroy(&info[1]);
+			value_destroy(&tmp);
+			return ret;
+		}
+
+		/* fall-through */
+	case ARGTYPE_VOID:
+	case ARGTYPE_FLOAT:
+	case ARGTYPE_DOUBLE:
+	case ARGTYPE_STRUCT:
+	case ARGTYPE_SHORT:
+	case ARGTYPE_INT:
+	case ARGTYPE_LONG:
+	case ARGTYPE_USHORT:
+	case ARGTYPE_UINT:
+	case ARGTYPE_ULONG:
+	case ARGTYPE_ENUM:
+		return toplevel_format_lens(lens, stream, value,
+					    arguments, INT_FMT_i);
+
+	case ARGTYPE_CHAR:
+		return format_char(stream, value, arguments);
+
+	case ARGTYPE_ARRAY:
+		return format_array(stream, value, arguments,
+				    value->type->u.array_info.length,
+				    options.strlen, 0, "\"", "\"", "");
+	}
+	abort();
+}
+
+struct lens string_lens = {
+	.format_cb = string_lens_format_cb,
+};
diff --git a/lens_default.h b/lens_default.h
index e84fd3e..7895a91 100644
--- a/lens_default.h
+++ b/lens_default.h
@@ -40,4 +40,7 @@
  * formatted in decimal).  */
 extern struct lens guess_lens;
 
+/* A lens for strings.  */
+extern struct lens string_lens;
+
 #endif /* LENS_DEFAULT_H */
diff --git a/printf.c b/printf.c
index 748c0d0..a3d7b30 100644
--- a/printf.c
+++ b/printf.c
@@ -30,6 +30,7 @@
 #include "expr.h"
 #include "zero.h"
 #include "param.h"
+#include "lens_default.h"
 
 struct param_enum {
 	struct value array;
@@ -47,11 +48,10 @@
 	assert(nargs == 1);
 
 	/* We expect a char array pointer.  */
-	if (cb_args->type->type != ARGTYPE_STRING_N
-	    && (cb_args->type->type != ARGTYPE_POINTER
-		|| cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY
-		|| (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type
-		    != ARGTYPE_CHAR)))
+	if (cb_args->type->type != ARGTYPE_POINTER
+	    || cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY
+	    || (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type
+		!= ARGTYPE_CHAR))
 		return NULL;
 
 	struct param_enum *self = malloc(sizeof(*self));
@@ -61,18 +61,7 @@
 		return NULL;
 	}
 
-	if (cb_args->type->type == ARGTYPE_STRING_N) {
-		struct value *tmp = value_string_to_charp(cb_args);
-		if (tmp == NULL)
-			goto fail;
-
-		if (value_init_deref(&self->array, tmp) < 0) {
-			value_destroy(tmp);
-			goto fail;
-		}
-		free(tmp);
-
-	} else if (value_init_deref(&self->array, cb_args) < 0)
+	if (value_init_deref(&self->array, cb_args) < 0)
 		goto fail;
 
 	assert(self->array.type->type == ARGTYPE_ARRAY);
@@ -294,6 +283,7 @@
 			/* XXX "ls" means wchar_t string.  */
 			elt_type = ARGTYPE_CHAR;
 			self->percent = 0;
+			lens = &string_lens;
 			break;
 
 		case 'p':
diff --git a/read_config_file.c b/read_config_file.c
index e59df92..bb133b3 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -449,9 +449,9 @@
 }
 
 static int
-parse_string(char **str, struct arg_type_info **retp)
+parse_string(char **str, struct arg_type_info **retp, int *ownp)
 {
-	struct arg_type_info *info = malloc(sizeof(*info));
+	struct arg_type_info *info = malloc(sizeof(*info) * 2);
 	if (info == NULL) {
 	fail:
 		free(info);
@@ -460,6 +460,7 @@
 
 	struct expr_node *length;
 	int own_length;
+	int with_arg = 0;
 
 	if (isdigit(**str)) {
 		/* string0 is string[retval], length is zero(retval)
@@ -500,6 +501,22 @@
 			eat_spaces(str);
 			parse_char(str, ']');
 
+		} else if (**str == '(') {
+			/* Usage of "string" as lens.  */
+			++*str;
+
+			free(info);
+
+			eat_spaces(str);
+			info = parse_type(str, NULL, 0, ownp);
+			if (info == NULL)
+				goto fail;
+
+			eat_spaces(str);
+			parse_char(str, ')');
+
+			with_arg = 1;
+
 		} else {
 			/* It was just a simple string after all.  */
 			length = expr_node_zero();
@@ -508,7 +525,16 @@
 	}
 
 	/* String is a pointer to array of chars.  */
-	type_init_string(info, length, own_length);
+	if (!with_arg) {
+		type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0,
+				length, own_length);
+
+		type_init_pointer(&info[0], &info[1], 0);
+		*ownp = 1;
+	}
+
+	info->lens = &string_lens;
+	info->own_lens = 0;
 
 	*retp = info;
 	return 0;
@@ -594,7 +620,7 @@
 	 * "string" is syntax.  */
 	if (strncmp(*str, "string", 6) == 0) {
 		(*str) += 6;
-		return parse_string(str, retp);
+		return parse_string(str, retp, ownp);
 
 	} else if (try_parse_kwd(str, "format") >= 0
 		   && extra_param != NULL) {
@@ -602,7 +628,7 @@
 		 * "string", but it smuggles to the parameter list of
 		 * a function a "printf" argument pack with this
 		 * parameter as argument.  */
-		if (parse_string(str, retp) < 0)
+		if (parse_string(str, retp, ownp) < 0)
 			return -1;
 
 		return build_printf_pack(extra_param, param_num);
@@ -748,12 +774,6 @@
 		parser = parse_struct;
 		break;
 
-	case ARGTYPE_STRING_N:
-		/* Strings are handled in aliases, to support
-		 * "stringN" syntax cleanly.  */
-		assert(type != ARGTYPE_STRING_N);
-		abort();
-
 	case ARGTYPE_POINTER:
 		/* Pointer syntax is not based on keyword, so we
 		 * should never get this type.  */
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index 54e6a91..d3ad69c 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -207,3 +207,19 @@
 func_lens(int a, long b, short c, long d)
 {
 }
+
+int
+func_bool(int a, int b)
+{
+	return !b;
+}
+
+void
+func_hide(int a, int b, int c, int d, int e, int f)
+{
+}
+
+void
+func_charp_string(char *p)
+{
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index 0377b73..5a3f6b2 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -200,5 +200,15 @@
   void func_lens(int, long, short, long);
   func_lens(22, 23, 24, 25);
 
+  int func_bool(int a, int b);
+  func_bool(1, 10);
+  func_bool(2, 0);
+
+  void func_hide(int a, int b, int c, int d, int e, int f);
+  func_hide(1, 2, 3, 4, 5, 6);
+
+  void func_charp_string(char *p);
+  func_charp_string("null-terminated string");
+
   return 0;
 }
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 3f8fea5..419ad1f 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -17,10 +17,13 @@
 void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*)
 void func_work(+string);
 void func_call(+string, string);
-struct(float,char,char) func_struct_2(int, struct(array(char, 6),float), double);
+struct(float,char,char) func_struct_2(int, struct(string(array(char, 6)),float), double);
 struct(long,long,long,long) func_struct_large(struct(long,long,long,long), struct(long,long,long,long));
 struct(char,char,long,long) func_struct_large2(struct(char,char,long,long), struct(char,char,long,long));
 struct(long,long,char,char) func_struct_large3(struct(long,long,char,char), struct(long,long,char,char));
 void func_many_args(int, int, long, double, char, int, float, char, int, double, int, double, char, int, short, int, char, float, float, double, long, float, float, float, float);
 int printf(format);
 void func_lens(octal, octal(long), hex(short), hex(long));
+bool(int) func_bool(int, bool(int));
+void func_hide(int, hide(int), hide(int), int, hide(int), int);
+void func_charp_string(string(char *));
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index 8975abe..9be7b58 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -117,3 +117,15 @@
 
 set pattern "func_lens(026, 027, 0x18, 0x19)"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_bool(1, true).*= false"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_bool(2, false).*= true"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_hide(1, 4, 6)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_charp_string(\\\"null-terminated string\\\")"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
diff --git a/type.c b/type.c
index 81c597f..00775aa 100644
--- a/type.c
+++ b/type.c
@@ -50,8 +50,6 @@
 
 #undef HANDLE
 
-	case ARGTYPE_STRING_N:
-
 	case ARGTYPE_ARRAY:
 	case ARGTYPE_ENUM:
 	case ARGTYPE_STRUCT:
@@ -243,15 +241,6 @@
 	info->u.array_info.own_length = own_length;
 }
 
-void
-type_init_string(struct arg_type_info *info,
-		 struct expr_node *length_expr, int own_length)
-{
-	type_init_common(info, ARGTYPE_STRING_N);
-	info->u.string_n_info.length = length_expr;
-	info->u.string_n_info.own_length = own_length;
-}
-
 static void
 type_array_destroy(struct arg_type_info *info)
 {
@@ -265,15 +254,6 @@
 	}
 }
 
-static void
-type_string_n_destroy(struct arg_type_info *info)
-{
-	if (info->u.array_info.own_length) {
-		expr_destroy(info->u.string_n_info.length);
-		free(info->u.string_n_info.length);
-	}
-}
-
 void
 type_init_pointer(struct arg_type_info *info,
 		  struct arg_type_info *pointee_info, int own_info)
@@ -315,9 +295,6 @@
 		type_pointer_destroy(info);
 		break;
 
-	case ARGTYPE_STRING_N:
-		type_string_n_destroy(info);
-
 	case ARGTYPE_VOID:
 	case ARGTYPE_INT:
 	case ARGTYPE_UINT:
@@ -408,8 +385,6 @@
 			return (size_t)-1;
 		return size;
 
-	case ARGTYPE_STRING_N:
-		/* String is a char* in disguise.  */
 	case ARGTYPE_POINTER:
 		return sizeof(void *);
 
@@ -502,9 +477,7 @@
 type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
 {
 	assert(type->type == ARGTYPE_STRUCT
-	       || type->type == ARGTYPE_ARRAY
-	       /* XXX Temporary, this will be removed.  */
-	       || type->type == ARGTYPE_STRING_N);
+	       || type->type == ARGTYPE_ARRAY);
 
 	switch (type->type) {
 		size_t alignment;
@@ -520,16 +493,13 @@
 
 		return emt * align(size, alignment);
 
-	case ARGTYPE_STRING_N:
-		return emt;
-
 	case ARGTYPE_STRUCT:
 		if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
 			return (size_t)-1;
 		return emt;
 
 	default:
-		abort ();
+		abort();
 	}
 }
 
@@ -537,9 +507,7 @@
 type_element(struct arg_type_info *info, size_t emt)
 {
 	assert(info->type == ARGTYPE_STRUCT
-	       || info->type == ARGTYPE_ARRAY
-	       /* XXX Temporary, this will be removed.  */
-	       || info->type == ARGTYPE_STRING_N);
+	       || info->type == ARGTYPE_ARRAY);
 
 	switch (info->type) {
 	case ARGTYPE_ARRAY:
@@ -549,10 +517,7 @@
 		assert(emt < type_struct_size(info));
 		return type_struct_get(info, emt);
 
-	case ARGTYPE_STRING_N:
-		return type_get_simple(ARGTYPE_CHAR);
-
 	default:
-		abort ();
+		abort();
 	}
 }
diff --git a/type.h b/type.h
index 5aff71f..d0b106f 100644
--- a/type.h
+++ b/type.h
@@ -37,7 +37,6 @@
 	ARGTYPE_USHORT,
 	ARGTYPE_FLOAT,
 	ARGTYPE_DOUBLE,
-	ARGTYPE_STRING_N,	/* String of known maxlen */
 	ARGTYPE_ARRAY,		/* Series of values in memory */
 	ARGTYPE_ENUM,		/* Enumeration */
 	ARGTYPE_STRUCT,		/* Structure of values */
@@ -57,12 +56,6 @@
 			int own_length:1;
 		} array_info;
 
-		/* ARGTYPE_STRING_N */
-		struct {
-			struct expr_node *length;
-			int own_length:1;
-		} string_n_info;
-
 		/* ARGTYPE_POINTER */
 		struct {
 			struct arg_type_info *info;
@@ -120,12 +113,6 @@
 		     struct arg_type_info *element_info, int own_info,
 		     struct expr_node *length_expr, int own_length);
 
-/* Initialize INFO so it becomes ARGTYPE_STRING_N.  Length is passed
- * in LENGTH_EXPR.  If OWN_LENGTH is true, the length is owned and
- * destroyed together with INFO.  */
-void type_init_string(struct arg_type_info *info,
-		      struct expr_node *length, int own_length);
-
 /* Initialize INFO so it becomes ARGTYPE_POINTER.  The pointee type is
  * passed in POINTEE_INFO.  If OWN_INFO, the pointee type is owned and
  * destroyed together with INFO.  */
diff --git a/value.c b/value.c
index 58e36c1..49e2f2c 100644
--- a/value.c
+++ b/value.c
@@ -199,28 +199,6 @@
 	return 0;
 }
 
-struct value *
-value_string_to_charp(struct value *value)
-{
-	struct arg_type_info *info = malloc(sizeof(*info) * 2);
-	if (info == NULL) {
-	fail:
-		free(info);
-		return NULL;
-	}
-	type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0,
-			value->type->u.string_n_info.length, 0);
-	type_init_pointer(&info[0], &info[1], 0);
-
-	struct value *tmp = malloc(sizeof(*tmp));
-	if (tmp == NULL)
-		goto fail;
-
-	value_clone(tmp, value);
-	value_set_type(tmp, info, 1);
-	return tmp;
-}
-
 size_t
 value_size(struct value *val, struct value_dict *arguments)
 {
diff --git a/value.h b/value.h
index f3238b6..e88b72a 100644
--- a/value.h
+++ b/value.h
@@ -149,10 +149,4 @@
  * if it isn't, <0 on error.  */
 int value_is_zero(struct value *val, struct value_dict *arguments);
 
-/* Take a VALUE of type ARGTYPE_STRING_N, and allocate a new value
- * that is ARGTYPE_POINTER to ARGTYPE_CHAR with the same length
- * expression.  Returns NULL on failure or a value, which you need to
- * free properly (i.e. call value_destroy and free).  */
-struct value *value_string_to_charp(struct value *value);
-
 #endif /* VALUE_H */