Add lens "bitvec" for displaying objects as bit vectors
diff --git a/NEWS b/NEWS
index 8e33596..fc07f31 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,10 @@
     Please read ltrace.conf(5) man page, chapter "recursive
     structures", to learn about this new feature.
 
+*** New lens "bitvec" is available
+    This allows displaying various data types as bit vectors.  Please
+    read ltrace.conf(5) to learn more.
+
 * Version 0.7.0
 ** Tracing
 *** Full support for tracing multi-threaded processes
diff --git a/lens_default.c b/lens_default.c
index 7ad2ee4..093b803 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -468,6 +468,18 @@
 
 
 static int
+dec_lens_format_cb(struct lens *lens, FILE *stream,
+		   struct value *value, struct value_dict *arguments)
+{
+	return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_u);
+}
+
+struct lens dec_lens = {
+	.format_cb = dec_lens_format_cb,
+};
+
+
+static int
 guess_lens_format_cb(struct lens *lens, FILE *stream,
 		     struct value *value, struct value_dict *arguments)
 {
@@ -576,3 +588,117 @@
 struct lens string_lens = {
 	.format_cb = string_lens_format_cb,
 };
+
+static int
+out_bits(FILE *stream, size_t low, size_t high)
+{
+	if (low == high)
+		return fprintf(stream, "%zd", low);
+	else
+		return fprintf(stream, "%zd-%zd", low, high);
+}
+
+static unsigned
+bitcount(unsigned u)
+{
+	int c = 0;
+	for (; u > 0; u &= u - 1)
+		c++;
+	return c;
+}
+
+static int
+bitvect_lens_format_cb(struct lens *lens, FILE *stream,
+		       struct value *value, struct value_dict *arguments)
+{
+	unsigned char *data = value_get_data(value, arguments);
+	if (data == NULL)
+		return -1;
+	size_t sz = type_sizeof(value->inferior, value->type);
+	if (sz == (size_t)-1)
+		return -1;
+
+	size_t i;
+	unsigned char buf[sz];
+	switch ((int)value->type->type) {
+		union bitvect_integral_64
+		{
+			uint8_t u8;
+			uint16_t u16;
+			uint32_t u32;
+			uint64_t u64;
+			unsigned char buf[0];
+		} bv;
+
+	case ARGTYPE_POINTER:
+		return format_pointer(stream, value, arguments);
+
+	case ARGTYPE_STRUCT:
+	case ARGTYPE_ARRAY:
+		break;
+
+	default:
+		assert(sz <= sizeof(bv));
+		memmove(bv.buf, data, sz);
+
+		if (sz == 1)
+			bv.u64 = bv.u8;
+		else if (sz == 2)
+			bv.u64 = bv.u16;
+		else if (sz == 4)
+			bv.u64 = bv.u32;
+
+		for (i = 0; i < sz; ++i) {
+			buf[i] = bv.u64 & 0xff;
+			bv.u64 >>= 8;
+		}
+		data = buf;
+	}
+
+	size_t bits = 0;
+	for (i = 0; i < sz; ++i)
+		bits += bitcount(data[i]);
+
+	/* If there's more 1's than 0's, show inverse.  */
+	unsigned neg = bits > sz * 4 ? 0xff : 0x00;
+
+	int o = 0;
+	if (acc_fprintf(&o, stream, "%s<", "~" + (neg == 0x00)) < 0)
+		return -1;
+
+	size_t bitno = 0;
+	ssize_t low = -1;
+	for (i = 0; i < sz; ++i) {
+		unsigned char m;
+		unsigned char d = data[i] ^ neg;
+		for (m = 0x01; m != 0; m <<= 1) {
+			int bit = !!(m & d);
+			if (low < 0) {
+				if (bit) {
+					if (low == -2
+					    && acc_fprintf(&o, stream, ",") < 0)
+						return -1;
+					low = bitno;
+				}
+			} else if (!bit) {
+				if (account_output(&o, out_bits(stream, low,
+								bitno-1)) < 0)
+					return -1;
+				low = -2;
+			}
+			bitno++;
+		}
+	}
+	if (low >= 0 && account_output(&o, out_bits(stream, low, bitno-1)) < 0)
+		return -1;
+
+	if (fputc('>', stream) < 0)
+		return -1;
+	o += 1;
+
+	return o;
+}
+
+struct lens bitvect_lens = {
+	.format_cb = bitvect_lens_format_cb,
+};
diff --git a/lens_default.h b/lens_default.h
index 9a9d0c6..1206ffc 100644
--- a/lens_default.h
+++ b/lens_default.h
@@ -1,6 +1,6 @@
 /*
  * This file is part of ltrace.
- * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011, 2012 Petr Machata, Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -46,4 +46,7 @@
 /* A lens for strings.  */
 extern struct lens string_lens;
 
+/* A lens for bit vector.  */
+extern struct lens bitvect_lens;
+
 #endif /* LENS_DEFAULT_H */
diff --git a/ltrace.conf.5 b/ltrace.conf.5
index 2b166fb..5d3b472 100644
--- a/ltrace.conf.5
+++ b/ltrace.conf.5
@@ -139,6 +139,28 @@
 Arguments with zero value are shown as "false", other are shown as
 "true".
 
+.TP
+.B bitvec(\fITYPE\fB)
+Underlying argument is interpreted as a bit vector and a summary of
+bits set in the vector is displayed.  For example if bits 3,4,5 and 7
+of the bit vector are set, ltrace shows <3-5,7>.  Empty bit vector is
+displayed as <>.  If there are more bits set than unset, inverse is
+shown instead: e.g. ~<0> when a number 0xfffffffe is displayed.  Full
+set is thus displayed ~<>.
+
+If the underlying type is integral, then bits are shown in their
+natural little-endian order, with LSB being bit 0.
+E.g. \fBbitvec(ushort)\fR with value 0x0102 would be displayed as
+<1,8>, irrespective of underlying byte order.
+
+For other data types (notably structures and arrays), the underlying
+data is interpreted byte after byte.  Bit 0 of first byte has number
+0, bit 0 of second byte number 8, and so on.  Thus
+\fBbitvec(struct(int))\fR is endian sensitive, and will show bytes
+comprising the integer in their memory order.  Pointers are first
+dereferenced, thus \fBbitvec(array(char, \fR32\fB)*)\fR is actually a
+pointer to 256-bit bit vector.
+
 .PP
 .B string(\fITYPE\fB)
 .br
diff --git a/read_config_file.c b/read_config_file.c
index 58a4a14..724d99e 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -954,6 +954,7 @@
 } lenses[] = {
 	{ "hide", &blind_lens },
 	{ "octal", &octal_lens },
+	{ "bitvec", &bitvect_lens },
 	{ "hex", &hex_lens },
 	{ "bool", &bool_lens },
 	{ "guess", &guess_lens },
diff --git a/testsuite/ltrace.main/parameters2.exp b/testsuite/ltrace.main/parameters2.exp
index e13f9b3..ff61d21 100644
--- a/testsuite/ltrace.main/parameters2.exp
+++ b/testsuite/ltrace.main/parameters2.exp
@@ -126,7 +126,7 @@
 }
 
 ltraceParamTest {
-    typedef hexptr = hex(uint);
+    typedef hexptr = hex(uint*);
     void fun(hexptr);
 } {
     void fun(unsigned *arg);
@@ -139,4 +139,31 @@
     {{fun\(0x123\) *= <void>} == 1}
 }
 
+ltraceParamTest {
+    void fun(bitvec(uint));
+    void fun2(bitvec(array(char, 32)*));
+} {
+    void fun(unsigned i);
+    void fun2(unsigned char *arr);
+} {
+    void fun(unsigned i) {}
+    void fun2(unsigned char *arr) {}
+} {
+    fun(0);
+    fun(0x123);
+    fun(0xfffffffe);
+    fun(0xffffffff);
+
+    unsigned char bytes[32] = {0x00};
+    bytes[1] = 0xff;
+    bytes[31] = 0x80;
+    fun2(bytes);
+} {
+    {{fun\(<>\) *= <void>} == 1}
+    {{fun\(<0-1,5,8>\) *= <void>} == 1}
+    {{fun\(~<0>\) *= <void>} == 1}
+    {{fun\(~<>\) *= <void>} == 1}
+    {{fun2\(<8-15,255>\) *= <void>} == 1}
+}
+
 ltraceDone