Introduce print_array function for consistent decoding of arrays

* defs.h (print_array): New prototype.
* util.c (print_array): New function.
diff --git a/defs.h b/defs.h
index fe2e46b..97ac973 100644
--- a/defs.h
+++ b/defs.h
@@ -515,6 +515,22 @@
 extern int umovestr(struct tcb *, long, unsigned int, char *);
 extern int upeek(int pid, long, long *);
 
+extern bool
+print_array(struct tcb *tcp,
+	    const unsigned long start_addr,
+	    const size_t nmemb,
+	    void *const elem_buf,
+	    const size_t elem_size,
+	    int (*const umoven_func)(struct tcb *,
+				     long,
+				     unsigned int,
+				     void *),
+	    bool (*const print_func)(struct tcb *,
+				     void *elem_buf,
+				     size_t elem_size,
+				     void *opaque_data),
+	    void *const opaque_data);
+
 #if defined ALPHA || defined IA64 || defined MIPS \
  || defined SH || defined SPARC || defined SPARC64
 # define HAVE_GETRVAL2
diff --git a/util.c b/util.c
index a9bce99..454c7cf 100644
--- a/util.c
+++ b/util.c
@@ -1335,3 +1335,103 @@
 	}
 	return 0;
 }
+
+/*
+ * Iteratively fetch and print up to nmemb elements of elem_size size
+ * from the array that starts at tracee's address start_addr.
+ *
+ * Array elements are being fetched to the address specified by elem_buf.
+ *
+ * The fetcher callback function specified by umoven_func should follow
+ * the same semantics as umoven_or_printaddr function.
+ *
+ * The printer callback function specified by print_func is expected
+ * to print something; if it returns false, no more iterations will be made.
+ *
+ * The pointer specified by opaque_data is passed to each invocation
+ * of print_func callback function.
+ *
+ * This function prints:
+ * - "NULL", if start_addr is NULL;
+ * - "[]", if nmemb is 0;
+ * - start_addr, if nmemb * elem_size overflows or wraps around;
+ * - nothing, if the first element cannot be fetched
+ *   (if umoven_func returns non-zero), but it is assumed that
+ *   umoven_func has printed the address it failed to fetch data from;
+ * - elements of the array, delimited by ", ", with the array itself
+ *   enclosed with [] brackets.
+ *
+ * If abbrev(tcp) is true, then
+ * - the maximum number of elements printed equals to max_strlen;
+ * - "..." is printed instead of max_strlen+1 element
+ *   and no more iterations will be made.
+ *
+ * This function returns true only if
+ * - umoven_func has been called at least once AND
+ * - umoven_func has not returned false.
+ */
+bool
+print_array(struct tcb *tcp,
+	    const unsigned long start_addr,
+	    const size_t nmemb,
+	    void *const elem_buf,
+	    const size_t elem_size,
+	    int (*const umoven_func)(struct tcb *,
+				     long,
+				     unsigned int,
+				     void *),
+	    bool (*const print_func)(struct tcb *,
+				     void *elem_buf,
+				     size_t elem_size,
+				     void *opaque_data),
+	    void *const opaque_data)
+{
+	if (!start_addr) {
+		tprints("NULL");
+		return false;
+	}
+
+	if (!nmemb) {
+		tprints("[]");
+		return false;
+	}
+
+	const size_t size = nmemb * elem_size;
+	const unsigned long end_addr = start_addr + size;
+
+	if (end_addr <= start_addr || size / elem_size != nmemb) {
+		printaddr(start_addr);
+		return false;
+	}
+
+	const unsigned long abbrev_end =
+		(abbrev(tcp) && max_strlen < nmemb) ?
+			start_addr + elem_size * max_strlen : end_addr;
+	unsigned long cur;
+
+	for (cur = start_addr; cur < end_addr; cur += elem_size) {
+		if (cur != start_addr)
+			tprints(", ");
+
+		if (umoven_func(tcp, cur, elem_size, elem_buf))
+			break;
+
+		if (cur == start_addr)
+			tprints("[");
+
+		if (cur >= abbrev_end) {
+			tprints("...");
+			cur = end_addr;
+			break;
+		}
+
+		if (!print_func(tcp, elem_buf, elem_size, opaque_data)) {
+			cur = end_addr;
+			break;
+		}
+	}
+	if (cur != start_addr)
+		tprints("]");
+
+	return cur >= end_addr;
+}