Mpersify fetchers of struct msghdr and struct mmsghdr

* fetch_struct_msghdr.c: New file.
* fetch_struct_mmsghdr.c: Likewise.
* Makefile.am (strace_SOURCES): Add them.
* net.c (struct msghdr32, struct mmsghdr32, copy_from_msghdr32,
fetch_msghdr, fetch_mmsghdr): Remove.
(decode_msghdr, dumpiov_in_msghdr): Use fetch_struct_msghdr instead
of fetch_msghdr.
(decode_mmsghdr): Change msg_len argument to use_msg_len.
Use fetch_struct_mmsghdr instead of fetch_mmsghdr.
Return fetch_struct_mmsghdr's return code.
(decode_mmsg): Rename to decode_mmsgvec.  Take addr and len arguments.
Do not print vlen and flags.  Check decode_mmsghdr's return code.
Print mmsghdr array using square brackets.
(dumpiov_in_mmsghdr): Use fetch_struct_mmsghdr instead of fetch_mmsghdr.
(SYS_FUNC(sendmmsg), SYS_FUNC(recvmmsg)): Use decode_mmsgvec instead
of decode_mmsg.  Print vlen and flags.
* tests/mmsg.c (main): Update expected output.
diff --git a/Makefile.am b/Makefile.am
index 1a57837..b310ec9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,8 @@
 	fcntl.c		\
 	fetch_seccomp_fprog.c \
 	fetch_struct_flock.c \
+	fetch_struct_mmsghdr.c \
+	fetch_struct_msghdr.c \
 	fetch_struct_statfs.c \
 	file.c		\
 	file_handle.c	\
diff --git a/fetch_struct_mmsghdr.c b/fetch_struct_mmsghdr.c
new file mode 100644
index 0000000..932de69
--- /dev/null
+++ b/fetch_struct_mmsghdr.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#include DEF_MPERS_TYPE(struct_mmsghdr)
+
+#include "msghdr.h"
+typedef struct mmsghdr struct_mmsghdr;
+
+#include MPERS_DEFS
+
+/*
+ * On success, return the number of fetched bytes.
+ * On error, return 0;
+ *
+ * This function cannot use umove_or_printaddr because
+ * it is called by dumpio and therefore cannot print anything.
+ */
+
+MPERS_PRINTER_DECL(int, fetch_struct_mmsghdr,
+		   struct tcb *tcp, const unsigned long addr, void *p)
+{
+	struct mmsghdr *p_native = p;
+	struct_mmsghdr v_compat;
+
+	if (sizeof(*p_native) == sizeof(v_compat))
+		return umove(tcp, addr, p_native) ? 0 : sizeof(*p_native);
+
+	if (umove(tcp, addr, &v_compat))
+		return 0;
+
+	p_native->msg_hdr.msg_name = (void *) (unsigned long)
+	 v_compat.msg_hdr.msg_name;
+
+	p_native->msg_hdr.msg_namelen =
+	 v_compat.msg_hdr.msg_namelen;
+
+	p_native->msg_hdr.msg_iov = (void *) (unsigned long)
+	 v_compat.msg_hdr.msg_iov;
+
+	p_native->msg_hdr.msg_iovlen =
+	 v_compat.msg_hdr.msg_iovlen;
+
+	p_native->msg_hdr.msg_control = (void *) (unsigned long)
+	 v_compat.msg_hdr.msg_control;
+
+	p_native->msg_hdr.msg_controllen =
+	 v_compat.msg_hdr.msg_controllen;
+
+	p_native->msg_hdr.msg_flags =
+	 v_compat.msg_hdr.msg_flags;
+
+	p_native->msg_len =
+	 v_compat.msg_len;
+
+	return sizeof(v_compat);
+}
diff --git a/fetch_struct_msghdr.c b/fetch_struct_msghdr.c
new file mode 100644
index 0000000..78b505b
--- /dev/null
+++ b/fetch_struct_msghdr.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#include DEF_MPERS_TYPE(struct_msghdr)
+
+#include "msghdr.h"
+typedef struct msghdr struct_msghdr;
+
+#include MPERS_DEFS
+
+/*
+ * On success, return the number of fetched bytes.
+ * On error, return 0;
+ *
+ * This function cannot use umove_or_printaddr because
+ * it is called by dumpio and therefore cannot print anything.
+ */
+
+MPERS_PRINTER_DECL(int, fetch_struct_msghdr,
+		   struct tcb *tcp, const unsigned long addr, void *p)
+{
+	struct msghdr *p_native = p;
+	struct_msghdr v_compat;
+
+	if (sizeof(*p_native) == sizeof(v_compat))
+		return umove(tcp, addr, p_native) ? 0 : sizeof(*p_native);
+
+	if (umove(tcp, addr, &v_compat))
+		return 0;
+
+	p_native->msg_name = (void *) (unsigned long)
+	 v_compat.msg_name;
+
+	p_native->msg_namelen =
+	 v_compat.msg_namelen;
+
+	p_native->msg_iov = (void *) (unsigned long)
+	 v_compat.msg_iov;
+
+	p_native->msg_iovlen =
+	 v_compat.msg_iovlen;
+
+	p_native->msg_control = (void *) (unsigned long)
+	 v_compat.msg_control;
+
+	p_native->msg_controllen =
+	 v_compat.msg_controllen;
+
+	p_native->msg_flags =
+	 v_compat.msg_flags;
+
+	return sizeof(v_compat);
+}
diff --git a/net.c b/net.c
index 3a7f940..7616a5b 100644
--- a/net.c
+++ b/net.c
@@ -427,80 +427,12 @@
 	tprints("}");
 }
 
-struct msghdr32 {
-	uint32_t /* void* */    msg_name;
-	uint32_t /* socklen_t */msg_namelen;
-	uint32_t /* iovec* */   msg_iov;
-	uint32_t /* size_t */   msg_iovlen;
-	uint32_t /* void* */    msg_control;
-	uint32_t /* size_t */   msg_controllen;
-	uint32_t /* int */      msg_flags;
-};
-struct mmsghdr32 {
-	struct msghdr32         msg_hdr;
-	uint32_t /* unsigned */ msg_len;
-};
-
-#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
-static void
-copy_from_msghdr32(struct msghdr *to_msg, struct msghdr32 *from_msg32)
-{
-	to_msg->msg_name       = (void*)(long)from_msg32->msg_name;
-	to_msg->msg_namelen    =              from_msg32->msg_namelen;
-	to_msg->msg_iov        = (void*)(long)from_msg32->msg_iov;
-	to_msg->msg_iovlen     =              from_msg32->msg_iovlen;
-	to_msg->msg_control    = (void*)(long)from_msg32->msg_control;
-	to_msg->msg_controllen =              from_msg32->msg_controllen;
-	to_msg->msg_flags      =              from_msg32->msg_flags;
-}
-#endif
-
-static bool
-fetch_msghdr(struct tcb *tcp, long addr, struct msghdr *msg)
-{
-#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
-	if (current_wordsize == 4) {
-		struct msghdr32 msg32;
-
-		if (umove(tcp, addr, &msg32) < 0)
-			return false;
-		copy_from_msghdr32(msg, &msg32);
-	} else
-#endif
-	if (umove(tcp, addr, msg) < 0)
-		return false;
-	return true;
-}
-
-static bool
-fetch_mmsghdr(struct tcb *tcp, long addr, unsigned int idx, struct mmsghdr *mmsg)
-{
-#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
-	if (current_wordsize == 4) {
-		struct mmsghdr32 mmsg32;
-
-		addr += sizeof(struct mmsghdr32) * idx;
-		if (umove(tcp, addr, &mmsg32) < 0)
-			return false;
-
-		copy_from_msghdr32(&mmsg->msg_hdr, &mmsg32.msg_hdr);
-		mmsg->msg_len = mmsg32.msg_len;
-	} else
-#endif
-	{
-		addr += sizeof(*mmsg) * idx;
-		if (umove(tcp, addr, mmsg) < 0)
-			return false;
-	}
-	return true;
-}
-
 static void
 decode_msghdr(struct tcb *tcp, long addr, unsigned long data_size)
 {
 	struct msghdr msg;
 
-	if (verbose(tcp) && fetch_msghdr(tcp, addr, &msg))
+	if (addr && verbose(tcp) && fetch_struct_msghdr(tcp, addr, &msg))
 		print_msghdr(tcp, &msg, data_size);
 	else
 		printaddr(addr);
@@ -511,62 +443,63 @@
 {
 	struct msghdr msg;
 
-	if (fetch_msghdr(tcp, addr, &msg))
+	if (fetch_struct_msghdr(tcp, addr, &msg))
 		dumpiov_upto(tcp, msg.msg_iovlen, (long)msg.msg_iov, data_size);
 }
 
-static void
-decode_mmsghdr(struct tcb *tcp, long addr, unsigned int idx, unsigned long msg_len)
+static int
+decode_mmsghdr(struct tcb *tcp, long addr, bool use_msg_len)
 {
 	struct mmsghdr mmsg;
+	int fetched = fetch_struct_mmsghdr(tcp, addr, &mmsg);
 
-	if (fetch_mmsghdr(tcp, addr, idx, &mmsg)) {
+	if (fetched) {
 		tprints("{");
-		print_msghdr(tcp, &mmsg.msg_hdr, msg_len ? msg_len : mmsg.msg_len);
+		print_msghdr(tcp, &mmsg.msg_hdr, use_msg_len ? mmsg.msg_len : -1UL);
 		tprintf(", %u}", mmsg.msg_len);
-	}
-	else
+	} else {
 		printaddr(addr);
+	}
+
+	return fetched;
 }
 
 static void
-decode_mmsg(struct tcb *tcp, unsigned long msg_len)
+decode_mmsgvec(struct tcb *tcp, unsigned long addr, unsigned int len,
+	       bool use_msg_len)
 {
-	/* mmsgvec */
 	if (syserror(tcp)) {
-		printaddr(tcp->u_arg[1]);
+		printaddr(addr);
 	} else {
-		unsigned int len = tcp->u_rval;
-		unsigned int i;
+		unsigned int i, fetched;
 
-		tprints("{");
-		for (i = 0; i < len; ++i) {
+		tprints("[");
+		for (i = 0; i < len; ++i, addr += fetched) {
 			if (i)
 				tprints(", ");
-			decode_mmsghdr(tcp, tcp->u_arg[1], i, msg_len);
+			fetched = decode_mmsghdr(tcp, addr, use_msg_len);
+			if (!fetched)
+				break;
 		}
-		tprints("}");
+		tprints("]");
 	}
-	/* vlen */
-	tprintf(", %u, ", (unsigned int) tcp->u_arg[2]);
-	/* flags */
-	printflags(msg_flags, tcp->u_arg[3], "MSG_???");
 }
 
 void
 dumpiov_in_mmsghdr(struct tcb *tcp, long addr)
 {
 	unsigned int len = tcp->u_rval;
-	unsigned int i;
+	unsigned int i, fetched;
 	struct mmsghdr mmsg;
 
-	for (i = 0; i < len; ++i) {
-		if (fetch_mmsghdr(tcp, addr, i, &mmsg)) {
-			tprintf(" = %lu buffers in vector %u\n",
-				(unsigned long)mmsg.msg_hdr.msg_iovlen, i);
-			dumpiov_upto(tcp, mmsg.msg_hdr.msg_iovlen,
-				(long)mmsg.msg_hdr.msg_iov, mmsg.msg_len);
-		}
+	for (i = 0; i < len; ++i, addr += fetched) {
+		fetched = fetch_struct_mmsghdr(tcp, addr, &mmsg);
+		if (!fetched)
+			break;
+		tprintf(" = %lu buffers in vector %u\n",
+			(unsigned long)mmsg.msg_hdr.msg_iovlen, i);
+		dumpiov_upto(tcp, mmsg.msg_hdr.msg_iovlen,
+			(long)mmsg.msg_hdr.msg_iov, mmsg.msg_len);
 	}
 }
 
@@ -727,12 +660,18 @@
 		tprints(", ");
 		if (!verbose(tcp)) {
 			printaddr(tcp->u_arg[1]);
+			/* vlen */
 			tprintf(", %u, ", (unsigned int) tcp->u_arg[2]);
+			/* flags */
 			printflags(msg_flags, tcp->u_arg[3], "MSG_???");
+			return RVAL_DECODED;
 		}
 	} else {
-		if (verbose(tcp))
-			decode_mmsg(tcp, (unsigned long) -1L);
+		decode_mmsgvec(tcp, tcp->u_arg[1], tcp->u_rval, false);
+		/* vlen */
+		tprintf(", %u, ", (unsigned int) tcp->u_arg[2]);
+		/* flags */
+		printflags(msg_flags, tcp->u_arg[3], "MSG_???");
 	}
 	return 0;
 }
@@ -820,7 +759,9 @@
 			tcp->auxstr = sprint_timespec(tcp, tcp->u_arg[4]);
 		} else {
 			printaddr(tcp->u_arg[1]);
+			/* vlen */
 			tprintf(", %u, ", (unsigned int) tcp->u_arg[2]);
+			/* flags */
 			printflags(msg_flags, tcp->u_arg[3], "MSG_???");
 			tprints(", ");
 			print_timespec(tcp, tcp->u_arg[4]);
@@ -828,7 +769,11 @@
 		return 0;
 	} else {
 		if (verbose(tcp)) {
-			decode_mmsg(tcp, 0);
+			decode_mmsgvec(tcp, tcp->u_arg[1], tcp->u_rval, true);
+			/* vlen */
+			tprintf(", %u, ", (unsigned int) tcp->u_arg[2]);
+			/* flags */
+			printflags(msg_flags, tcp->u_arg[3], "MSG_???");
 			tprints(", ");
 			/* timeout on entrance */
 			tprints(tcp->auxstr);
diff --git a/tests/mmsg.c b/tests/mmsg.c
index 00e1ef3..d82b7cd 100644
--- a/tests/mmsg.c
+++ b/tests/mmsg.c
@@ -139,12 +139,12 @@
 		perror_msg_and_skip("sendmmsg");
 	assert(r == (int) n_w_mmh);
 	assert(close(1) == 0);
-	tprintf("sendmmsg(1, {{{msg_name=NULL, msg_namelen=0"
+	tprintf("sendmmsg(1, [{{msg_name=NULL, msg_namelen=0"
 		", msg_iov=[{\"%s\", %u}, {\"%s\", %u}], msg_iovlen=%u"
 		", msg_controllen=0, msg_flags=0}, %u}"
 		", {{msg_name=NULL, msg_namelen=0"
 		", msg_iov=[{\"%s\", %u}], msg_iovlen=%u"
-		", msg_controllen=0, msg_flags=0}, %u}}, %u"
+		", msg_controllen=0, msg_flags=0}, %u}], %u"
 		", MSG_DONTROUTE|MSG_NOSIGNAL) = %d\n"
 		" = %u buffers in vector 0\n"
 		" * %u bytes in buffer 0\n"
@@ -213,12 +213,12 @@
 
 	assert(recv_mmsg(0, r_mmh, n_r_mmh, MSG_DONTWAIT, NULL) == (int) n_r_mmh);
 	assert(close(0) == 0);
-	tprintf("recvmmsg(0, {{{msg_name=NULL, msg_namelen=0"
+	tprintf("recvmmsg(0, [{{msg_name=NULL, msg_namelen=0"
 		", msg_iov=[{\"%s\", %u}], msg_iovlen=%u"
 		", msg_controllen=0, msg_flags=0}, %u}"
 		", {{msg_name=NULL, msg_namelen=0"
 		", msg_iov=[{\"%s\", %u}, {\"\", %u}], msg_iovlen=%u"
-		", msg_controllen=0, msg_flags=0}, %u}}, %u"
+		", msg_controllen=0, msg_flags=0}, %u}], %u"
 		", MSG_DONTWAIT, NULL) = %d (left NULL)\n"
 		" = %u buffers in vector 0\n"
 		" * %u bytes in buffer 0\n"