diff --git a/SOURCES/0001-dmidecode-Print-type-33-name-unconditionally.patch b/SOURCES/0001-dmidecode-Print-type-33-name-unconditionally.patch
new file mode 100644
index 0000000..1467d9b
--- /dev/null
+++ b/SOURCES/0001-dmidecode-Print-type-33-name-unconditionally.patch
@@ -0,0 +1,30 @@
+From 65438a7ec0f4cddccf810136da6f280bd148af71 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:20 +0100
+Subject: [PATCH 01/23] dmidecode: Print type 33 name unconditionally
+
+Even if a type 33 structure is too short, we can still display its
+type name as we do for all other structure types.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 8ebd8626d6ea..71c166f0595d 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -4807,8 +4807,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 33: /* 7.34 64-bit Memory Error Information */
+-			if (h->length < 0x1F) break;
+ 			printf("64-bit Memory Error Information\n");
++			if (h->length < 0x1F) break;
+ 			printf("\tType: %s\n",
+ 				dmi_memory_error_type(data[0x04]));
+ 			printf("\tGranularity: %s\n",
+-- 
+2.17.1
+
diff --git a/SOURCES/0002-dmidecode-Don-t-choke-on-invalid-processor-voltage.patch b/SOURCES/0002-dmidecode-Don-t-choke-on-invalid-processor-voltage.patch
new file mode 100644
index 0000000..d886ab2
--- /dev/null
+++ b/SOURCES/0002-dmidecode-Don-t-choke-on-invalid-processor-voltage.patch
@@ -0,0 +1,37 @@
+From 5bb7eb173b72256f70c6b3f3916d7a444be93340 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:23 +0100
+Subject: [PATCH 02/23] dmidecode: Don't choke on invalid processor voltage
+
+If the processor voltage encoding has some of the reserved bits set
+and none of the proper bits set, print it as "Unknown" instead of an
+empty field.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 71c166f0595d..ef9bbd54b7f8 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -1190,13 +1190,13 @@ static void dmi_processor_voltage(u8 code)
+ 
+ 	if (code & 0x80)
+ 		printf(" %.1f V", (float)(code & 0x7f) / 10);
++	else if ((code & 0x07) == 0x00)
++		printf(" Unknown");
+ 	else
+ 	{
+ 		for (i = 0; i <= 2; i++)
+ 			if (code & (1 << i))
+ 				printf(" %s", voltage[i]);
+-		if (code == 0x00)
+-			printf(" Unknown");
+ 	}
+ }
+ 
+-- 
+2.17.1
+
diff --git a/SOURCES/0003-dmidecode-Simplify-the-formatting-of-memory-error-st.patch b/SOURCES/0003-dmidecode-Simplify-the-formatting-of-memory-error-st.patch
new file mode 100644
index 0000000..5c3c0c3
--- /dev/null
+++ b/SOURCES/0003-dmidecode-Simplify-the-formatting-of-memory-error-st.patch
@@ -0,0 +1,58 @@
+From 1347ccca96db6e157af39fcc565466fa98b9220b Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:27 +0100
+Subject: [PATCH 03/23] dmidecode: Simplify the formatting of memory error
+ status
+
+Make the logic more simple so that we always report the status on a
+single line.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index ef9bbd54b7f8..5a0631e926c7 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -1515,18 +1515,19 @@ static void dmi_memory_module_size(u8 code)
+ 		printf(" (Single-bank Connection)");
+ }
+ 
+-static void dmi_memory_module_error(u8 code, const char *prefix)
++static void dmi_memory_module_error(u8 code)
+ {
++	static const char *status[] = {
++		"OK", /* 0x00 */
++		"Uncorrectable Errors",
++		"Correctable Errors",
++		"Correctable and Uncorrectable Errors" /* 0x03 */
++	};
++
+ 	if (code & (1 << 2))
+ 		printf(" See Event Log\n");
+ 	else
+-	{	if ((code & 0x03) == 0)
+-			printf(" OK\n");
+-		if (code & (1 << 0))
+-			printf("%sUncorrectable Errors\n", prefix);
+-		if (code & (1 << 1))
+-			printf("%sCorrectable Errors\n", prefix);
+-	}
++		printf(" %s\n", status[code & 0x03]);
+ }
+ 
+ /*
+@@ -4142,7 +4143,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			dmi_memory_module_size(data[0x0A]);
+ 			printf("\n");
+ 			printf("\tError Status:");
+-			dmi_memory_module_error(data[0x0B], "\t\t");
++			dmi_memory_module_error(data[0x0B]);
+ 			break;
+ 
+ 		case 7: /* 7.8 Cache Information */
+-- 
+2.17.1
+
diff --git a/SOURCES/0004-dmidecode-Fix-the-alignment-of-type-25-name.patch b/SOURCES/0004-dmidecode-Fix-the-alignment-of-type-25-name.patch
new file mode 100644
index 0000000..78d1caf
--- /dev/null
+++ b/SOURCES/0004-dmidecode-Fix-the-alignment-of-type-25-name.patch
@@ -0,0 +1,28 @@
+From 557c3c373a9992d45d4358a6a2ccf53b03276f39 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:30 +0100
+Subject: [PATCH 04/23] dmidecode: Fix the alignment of type 25 name
+
+No tabulation needed before DMI structure names.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 5a0631e926c7..b459ed0ff84e 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -4651,7 +4651,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 25: /* 7.26 System Power Controls */
+-			printf("\tSystem Power Controls\n");
++			printf("System Power Controls\n");
+ 			if (h->length < 0x09) break;
+ 			printf("\tNext Scheduled Power-on:");
+ 			dmi_power_controls_power_on(data + 0x04);
+-- 
+2.17.1
+
diff --git a/SOURCES/0005-dmidecode-Code-indentation-fixes.patch b/SOURCES/0005-dmidecode-Code-indentation-fixes.patch
new file mode 100644
index 0000000..c3a481f
--- /dev/null
+++ b/SOURCES/0005-dmidecode-Code-indentation-fixes.patch
@@ -0,0 +1,35 @@
+From 37a856266e4fb2f85bd8f2f269db8bcfb1962eb0 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:33 +0100
+Subject: [PATCH 05/23] dmidecode: Code indentation fixes
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index b459ed0ff84e..0d95cf9d8f7b 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -3588,7 +3588,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	printf("%s\t\tHost IP Assignment Type: %s\n", prefix,
+ 		dmi_protocol_assignment_type(assign_val));
+ 
+-	 /* DSP0270: 8.6: Redfish Over IP Host Address format */
++	/* DSP0270: 8.6: Redfish Over IP Host Address format */
+ 	addrtype = rdata[17];
+ 	addrstr = dmi_address_type(addrtype);
+ 	printf("%s\t\tHost IP Address Format: %s\n", prefix,
+@@ -4856,7 +4856,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 					WORD(data + 0x07));
+ 				if (WORD(data + 0x09) != 0xFFFF)
+ 					printf("\tThreshold Handle: 0x%04X\n",
+-					WORD(data + 0x09));
++						WORD(data + 0x09));
+ 			}
+ 			break;
+ 
+-- 
+2.17.1
+
diff --git a/SOURCES/0006-dmidecode-Reduce-the-indentation-of-type-42-structur.patch b/SOURCES/0006-dmidecode-Reduce-the-indentation-of-type-42-structur.patch
new file mode 100644
index 0000000..67bb30e
--- /dev/null
+++ b/SOURCES/0006-dmidecode-Reduce-the-indentation-of-type-42-structur.patch
@@ -0,0 +1,161 @@
+From 61f3f163b98b271c6613b9744128b99f33e6e19e Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:37 +0100
+Subject: [PATCH 06/23] dmidecode: Reduce the indentation of type 42 structures
+
+There is no reason to indent the device information that much in type
+42 structures. Remove the extra level of indentation for consistency
+and readability.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 40 ++++++++++++++++++++--------------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 0d95cf9d8f7b..fc140e7e36a9 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -3545,7 +3545,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	/* DSP0270: 8.5: Protocol Record Data */
+ 	rdata = &rec[0x2];
+ 
+-	printf("%s\tProtocol ID: %02x (%s)\n", prefix, rid,
++	printf("%sProtocol ID: %02x (%s)\n", prefix, rid,
+ 		dmi_protocol_record_type(rid));
+ 
+ 	/*
+@@ -3575,7 +3575,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	 * endianess of the field is always little after version 2.6.0
+ 	 * we can just pick a sufficiently recent version here.
+ 	 */
+-	printf("%s\t\tService UUID: ", prefix);
++	printf("%s\tService UUID: ", prefix);
+ 	dmi_system_uuid(&rdata[0], 0x311);
+ 	printf("\n");
+ 
+@@ -3585,13 +3585,13 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	 * uses decimal, so as to make it more comparable
+ 	 */
+ 	assign_val = rdata[16];
+-	printf("%s\t\tHost IP Assignment Type: %s\n", prefix,
++	printf("%s\tHost IP Assignment Type: %s\n", prefix,
+ 		dmi_protocol_assignment_type(assign_val));
+ 
+ 	/* DSP0270: 8.6: Redfish Over IP Host Address format */
+ 	addrtype = rdata[17];
+ 	addrstr = dmi_address_type(addrtype);
+-	printf("%s\t\tHost IP Address Format: %s\n", prefix,
++	printf("%s\tHost IP Address Format: %s\n", prefix,
+ 		addrstr);
+ 
+ 	/* DSP0270: 8.6 IP Assignment types */
+@@ -3599,24 +3599,24 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	if (assign_val == 0x1 || assign_val == 0x3)
+ 	{
+ 		/* DSP0270: 8.6: the Host IPv[4|6] Address */
+-		printf("%s\t\t%s Address: %s\n", prefix, addrstr,
++		printf("%s\t%s Address: %s\n", prefix, addrstr,
+ 			dmi_address_decode(&rdata[18], buf, addrtype));
+ 
+ 		/* DSP0270: 8.6: Prints the Host IPv[4|6] Mask */
+-		printf("%s\t\t%s Mask: %s\n", prefix, addrstr,
++		printf("%s\t%s Mask: %s\n", prefix, addrstr,
+ 			dmi_address_decode(&rdata[34], buf, addrtype));
+ 	}
+ 
+ 	/* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */
+ 	assign_val = rdata[50];
+ 	/* Redfish Service IP Discovery type mirrors Host IP Assignment type */
+-	printf("%s\t\tRedfish Service IP Discovery Type: %s\n", prefix,
++	printf("%s\tRedfish Service IP Discovery Type: %s\n", prefix,
+ 		dmi_protocol_assignment_type(assign_val));
+ 
+ 	/* DSP0270: 8.6: Get the Redfish Service IP Address Format */
+ 	addrtype = rdata[51];
+ 	addrstr = dmi_address_type(addrtype);
+-	printf("%s\t\tRedfish Service IP Address Format: %s\n", prefix,
++	printf("%s\tRedfish Service IP Address Format: %s\n", prefix,
+ 		addrstr);
+ 
+ 	if (assign_val == 0x1 || assign_val == 0x3)
+@@ -3625,20 +3625,20 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 		u32 vlan;
+ 
+ 		/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Address */
+-		printf("%s\t\t%s Redfish Service Address: %s\n", prefix,
++		printf("%s\t%s Redfish Service Address: %s\n", prefix,
+ 			addrstr, dmi_address_decode(&rdata[52], buf,
+ 			addrtype));
+ 
+ 		/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Mask */
+-		printf("%s\t\t%s Redfish Service Mask: %s\n", prefix,
++		printf("%s\t%s Redfish Service Mask: %s\n", prefix,
+ 			addrstr, dmi_address_decode(&rdata[68], buf,
+ 			addrtype));
+ 
+ 		/* DSP0270: 8.6: Redfish vlan and port info */
+ 		port = WORD(&rdata[84]);
+ 		vlan = DWORD(&rdata[86]);
+-		printf("%s\t\tRedfish Service Port: %hu\n", prefix, port);
+-		printf("%s\t\tRedfish Service Vlan: %u\n", prefix, vlan);
++		printf("%s\tRedfish Service Port: %hu\n", prefix, port);
++		printf("%s\tRedfish Service Vlan: %u\n", prefix, vlan);
+ 	}
+ 
+ 	/* DSP0270: 8.6: Redfish host length and name */
+@@ -3655,7 +3655,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 		hname = out_of_spec;
+ 		hlen = strlen(out_of_spec);
+ 	}
+-	printf("%s\t\tRedfish Service Hostname: %.*s\n", prefix, hlen, hname);
++	printf("%s\tRedfish Service Hostname: %.*s\n", prefix, hlen, hname);
+ }
+ 
+ /*
+@@ -3728,10 +3728,10 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			/* USB Device Type - need at least 6 bytes */
+ 			u8 *usbdata = &data[0x7];
+ 			/* USB Device Descriptor: idVendor */
+-			printf("%s\tidVendor: 0x%04x\n", prefix,
++			printf("%sidVendor: 0x%04x\n", prefix,
+ 				WORD(&usbdata[0x0]));
+ 			/* USB Device Descriptor: idProduct */
+-			printf("%s\tidProduct: 0x%04x\n", prefix,
++			printf("%sidProduct: 0x%04x\n", prefix,
+ 				WORD(&usbdata[0x2]));
+ 			/*
+ 			 * USB Serial number is here, but its useless, don't
+@@ -3743,16 +3743,16 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			/* PCI Device Type - Need at least 8 bytes */
+ 			u8 *pcidata = &data[0x7];
+ 			/* PCI Device Descriptor: VendorID */
+-			printf("%s\tVendorID: 0x%04x\n", prefix,
++			printf("%sVendorID: 0x%04x\n", prefix,
+ 				WORD(&pcidata[0x0]));
+ 			/* PCI Device Descriptor: DeviceID */
+-			printf("%s\tDeviceID: 0x%04x\n", prefix,
++			printf("%sDeviceID: 0x%04x\n", prefix,
+ 				WORD(&pcidata[0x2]));
+ 			/* PCI Device Descriptor: PCI SubvendorID */
+-			printf("%s\tSubVendorID: 0x%04x\n", prefix,
++			printf("%sSubVendorID: 0x%04x\n", prefix,
+ 				WORD(&pcidata[0x4]));
+ 			/* PCI Device Descriptor: PCI SubdeviceID */
+-			printf("%s\tSubDeviceID: 0x%04x\n", prefix,
++			printf("%sSubDeviceID: 0x%04x\n", prefix,
+ 				WORD(&pcidata[0x6]));
+ 		}
+ 		else if (type == 0x4 && len >= 5)
+@@ -3760,7 +3760,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			/* OEM Device Type - Need at least 4 bytes */
+ 			u8 *oemdata = &data[0x7];
+ 			/* OEM Device Descriptor: IANA */
+-			printf("%s\tVendor ID: 0x%02x:0x%02x:0x%02x:0x%02x\n",
++			printf("%sVendor ID: 0x%02x:0x%02x:0x%02x:0x%02x\n",
+ 				prefix, oemdata[0x0], oemdata[0x1],
+ 				oemdata[0x2], oemdata[0x3]);
+ 		}
+-- 
+2.17.1
+
diff --git a/SOURCES/0007-dmidecode-Move-type-42-warning-messages-to-stderr.patch b/SOURCES/0007-dmidecode-Move-type-42-warning-messages-to-stderr.patch
new file mode 100644
index 0000000..bcae831
--- /dev/null
+++ b/SOURCES/0007-dmidecode-Move-type-42-warning-messages-to-stderr.patch
@@ -0,0 +1,47 @@
+From 30121a064378b2c0174659cd52449c70aa2c271f Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:40 +0100
+Subject: [PATCH 07/23] dmidecode: Move type 42 warning messages to stderr
+
+Write warning messages about invalid type 42 structures to stderr as
+we do for all other warning messages.
+
+Also include the handle and record numbers in these warning messages
+to make the problem easier to analyze.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index fc140e7e36a9..50fd0bffa26d 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -3780,8 +3780,9 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 	total_read++;
+ 	if (total_read > h->length)
+ 	{
+-		printf("%s\tWARN: Total read length %d exceeds total structure length %d\n",
+-			prefix, total_read, h->length);
++		fprintf(stderr,
++			"Total read length %d exceeds total structure length %d (handle 0x%04hx)\n",
++			total_read, h->length, h->handle);
+ 		return;
+ 	}
+ 
+@@ -3801,8 +3802,9 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			total_read += rec[1] + 2;
+ 			if (total_read > h->length)
+ 			{
+-				printf("%s\tWARN: Total read length %d exceeds total structure length %d\n",
+-					prefix, total_read, h->length);
++				fprintf(stderr,
++					"Total read length %d exceeds total structure length %d (handle 0x%04hx, record %d)\n",
++					total_read, h->length, h->handle, i + 1);
+ 				return;
+ 			}
+ 
+-- 
+2.17.1
+
diff --git a/SOURCES/0008-dmidecode-Refactor-ASCII-filtering-of-DMI-strings.patch b/SOURCES/0008-dmidecode-Refactor-ASCII-filtering-of-DMI-strings.patch
new file mode 100644
index 0000000..db25961
--- /dev/null
+++ b/SOURCES/0008-dmidecode-Refactor-ASCII-filtering-of-DMI-strings.patch
@@ -0,0 +1,122 @@
+From 26a66d708c6caef09912245c93371ec96c1e0cbd Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Mon, 23 Mar 2020 16:47:44 +0100
+Subject: [PATCH 08/23] dmidecode: Refactor ASCII filtering of DMI strings
+
+Split dmi_string into 3 functions to make it more modular. ASCII
+filtering is an explicit option now to give the caller more control.
+
+Use the new functions in dmi_dump to avoid reimplementing the ASCII
+filtering and printing characters one by one.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 60 ++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 32 insertions(+), 28 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 50fd0bffa26d..8ec1d6b9597a 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -109,13 +109,19 @@ int is_printable(const u8 *data, int len)
+ 	return 1;
+ }
+ 
+-const char *dmi_string(const struct dmi_header *dm, u8 s)
++/* Replace non-ASCII characters with dots */
++static void ascii_filter(char *bp, size_t len)
+ {
+-	char *bp = (char *)dm->data;
+-	size_t i, len;
++	size_t i;
+ 
+-	if (s == 0)
+-		return "Not Specified";
++	for (i = 0; i < len; i++)
++		if (bp[i] < 32 || bp[i] == 127)
++			bp[i] = '.';
++}
++
++static char *_dmi_string(const struct dmi_header *dm, u8 s, int filter)
++{
++	char *bp = (char *)dm->data;
+ 
+ 	bp += dm->length;
+ 	while (s > 1 && *bp)
+@@ -126,16 +132,24 @@ const char *dmi_string(const struct dmi_header *dm, u8 s)
+ 	}
+ 
+ 	if (!*bp)
+-		return bad_index;
++		return NULL;
+ 
+-	if (!(opt.flags & FLAG_DUMP))
+-	{
+-		/* ASCII filtering */
+-		len = strlen(bp);
+-		for (i = 0; i < len; i++)
+-			if (bp[i] < 32 || bp[i] == 127)
+-				bp[i] = '.';
+-	}
++	if (filter)
++		ascii_filter(bp, strlen(bp));
++
++	return bp;
++}
++
++const char *dmi_string(const struct dmi_header *dm, u8 s)
++{
++	char *bp;
++
++	if (s == 0)
++		return "Not Specified";
++
++	bp = _dmi_string(dm, s, 1);
++	if (bp == NULL)
++		return bad_index;
+ 
+ 	return bp;
+ }
+@@ -208,7 +222,7 @@ static int dmi_bcd_range(u8 value, u8 low, u8 high)
+ static void dmi_dump(const struct dmi_header *h, const char *prefix)
+ {
+ 	int row, i;
+-	const char *s;
++	char *s;
+ 
+ 	printf("%sHeader and Data:\n", prefix);
+ 	for (row = 0; row < ((h->length - 1) >> 4) + 1; row++)
+@@ -224,7 +238,7 @@ static void dmi_dump(const struct dmi_header *h, const char *prefix)
+ 	{
+ 		printf("%sStrings:\n", prefix);
+ 		i = 1;
+-		while ((s = dmi_string(h, i++)) != bad_index)
++		while ((s = _dmi_string(h, i++, !(opt.flags & FLAG_DUMP))))
+ 		{
+ 			if (opt.flags & FLAG_DUMP)
+ 			{
+@@ -238,19 +252,9 @@ static void dmi_dump(const struct dmi_header *h, const char *prefix)
+ 					printf("\n");
+ 				}
+ 				/* String isn't filtered yet so do it now */
+-				printf("%s\t\"", prefix);
+-				while (*s)
+-				{
+-					if (*s < 32 || *s == 127)
+-						fputc('.', stdout);
+-					else
+-						fputc(*s, stdout);
+-					s++;
+-				}
+-				printf("\"\n");
++				ascii_filter(s, l - 1);
+ 			}
+-			else
+-				printf("%s\t%s\n", prefix, s);
++			printf("%s\t%s\n", prefix, s);
+ 		}
+ 	}
+ }
+-- 
+2.17.1
+
diff --git a/SOURCES/0009-dmidecode-Add-helper-function-pr_comment.patch b/SOURCES/0009-dmidecode-Add-helper-function-pr_comment.patch
new file mode 100644
index 0000000..1eb9e8b
--- /dev/null
+++ b/SOURCES/0009-dmidecode-Add-helper-function-pr_comment.patch
@@ -0,0 +1,214 @@
+From 2241f1dd232fe8e1d57fdb2482ad417ebe23279e Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:51:46 +0200
+Subject: [PATCH 09/23] dmidecode: Add helper function pr_comment
+
+Print all comments through a helper function pr_comment.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ Makefile    |  7 +++++--
+ dmidecode.c | 33 +++++++++++++++++----------------
+ dmioutput.c | 35 +++++++++++++++++++++++++++++++++++
+ dmioutput.h | 22 ++++++++++++++++++++++
+ 4 files changed, 79 insertions(+), 18 deletions(-)
+ create mode 100644 dmioutput.c
+ create mode 100644 dmioutput.h
+
+diff --git a/Makefile b/Makefile
+index 77c931091b0f..5d58266ef2de 100644
+--- a/Makefile
++++ b/Makefile
+@@ -61,8 +61,8 @@ all : $(PROGRAMS)
+ # Programs
+ #
+ 
+-dmidecode : dmidecode.o dmiopt.o dmioem.o util.o
+-	$(CC) $(LDFLAGS) dmidecode.o dmiopt.o dmioem.o util.o -o $@
++dmidecode : dmidecode.o dmiopt.o dmioem.o dmioutput.o util.o
++	$(CC) $(LDFLAGS) dmidecode.o dmiopt.o dmioem.o dmioutput.o util.o -o $@
+ 
+ biosdecode : biosdecode.o util.o
+ 	$(CC) $(LDFLAGS) biosdecode.o util.o -o $@
+@@ -87,6 +87,9 @@ dmiopt.o : dmiopt.c config.h types.h util.h dmidecode.h dmiopt.h
+ dmioem.o : dmioem.c types.h dmidecode.h dmioem.h
+ 	$(CC) $(CFLAGS) -c $< -o $@
+ 
++dmioutput.o : dmioutput.c types.h dmioutput.h
++	$(CC) $(CFLAGS) -c $< -o $@
++
+ biosdecode.o : biosdecode.c version.h types.h util.h config.h 
+ 	$(CC) $(CFLAGS) -c $< -o $@
+ 
+diff --git a/dmidecode.c b/dmidecode.c
+index 8ec1d6b9597a..2939b2d307fc 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -80,6 +80,7 @@
+ #include "dmidecode.h"
+ #include "dmiopt.h"
+ #include "dmioem.h"
++#include "dmioutput.h"
+ 
+ #define out_of_spec "<OUT OF SPEC>"
+ static const char *bad_index = "<BAD INDEX>";
+@@ -5162,7 +5163,7 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
+ static void dmi_table_dump(const u8 *buf, u32 len)
+ {
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("# Writing %d bytes to %s.\n", len, opt.dumpfile);
++		pr_comment("Writing %d bytes to %s.", len, opt.dumpfile);
+ 	write_dump(32, len, buf, opt.dumpfile, 0);
+ }
+ 
+@@ -5283,11 +5284,11 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
+ 
+ 	if (ver > SUPPORTED_SMBIOS_VER && !(opt.flags & FLAG_QUIET))
+ 	{
+-		printf("# SMBIOS implementations newer than version %u.%u.%u are not\n"
+-		       "# fully supported by this version of dmidecode.\n",
+-		       SUPPORTED_SMBIOS_VER >> 16,
+-		       (SUPPORTED_SMBIOS_VER >> 8) & 0xFF,
+-		       SUPPORTED_SMBIOS_VER & 0xFF);
++		pr_comment("SMBIOS implementations newer than version %u.%u.%u are not",
++			   SUPPORTED_SMBIOS_VER >> 16,
++			   (SUPPORTED_SMBIOS_VER >> 8) & 0xFF,
++			   SUPPORTED_SMBIOS_VER & 0xFF);
++		pr_comment("fully supported by this version of dmidecode.");
+ 	}
+ 
+ 	if (!(opt.flags & FLAG_QUIET))
+@@ -5417,8 +5418,8 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
+ 		overwrite_smbios3_address(crafted);
+ 
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("# Writing %d bytes to %s.\n", crafted[0x06],
+-			       opt.dumpfile);
++			pr_comment("Writing %d bytes to %s.", crafted[0x06],
++				   opt.dumpfile);
+ 		write_dump(0, crafted[0x06], crafted, opt.dumpfile, 1);
+ 	}
+ 
+@@ -5478,8 +5479,8 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
+ 		overwrite_dmi_address(crafted + 0x10);
+ 
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("# Writing %d bytes to %s.\n", crafted[0x05],
+-				opt.dumpfile);
++			pr_comment("Writing %d bytes to %s.", crafted[0x05],
++				   opt.dumpfile);
+ 		write_dump(0, crafted[0x05], crafted, opt.dumpfile, 1);
+ 	}
+ 
+@@ -5507,8 +5508,8 @@ static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
+ 		overwrite_dmi_address(crafted);
+ 
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("# Writing %d bytes to %s.\n", 0x0F,
+-				opt.dumpfile);
++			pr_comment("Writing %d bytes to %s.", 0x0F,
++				   opt.dumpfile);
+ 		write_dump(0, 0x0F, crafted, opt.dumpfile, 1);
+ 	}
+ 
+@@ -5586,8 +5587,8 @@ static int address_from_efi(off_t *address)
+ #endif
+ 
+ 	if (ret == 0 && !(opt.flags & FLAG_QUIET))
+-		printf("# %s entry point at 0x%08llx\n",
+-		       eptype, (unsigned long long)*address);
++		pr_comment("%s entry point at 0x%08llx",
++			   eptype, (unsigned long long)*address);
+ 
+ 	return ret;
+ }
+@@ -5638,7 +5639,7 @@ int main(int argc, char * const argv[])
+ 	}
+ 
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("# dmidecode %s\n", VERSION);
++		pr_comment("dmidecode %s", VERSION);
+ 
+ 	/* Read from dump if so instructed */
+ 	if (opt.flags & FLAG_FROM_DUMP)
+@@ -5783,7 +5784,7 @@ int main(int argc, char * const argv[])
+ 
+ done:
+ 	if (!found && !(opt.flags & FLAG_QUIET))
+-		printf("# No SMBIOS nor DMI entry point found, sorry.\n");
++		pr_comment("No SMBIOS nor DMI entry point found, sorry.");
+ 
+ 	free(buf);
+ exit_free:
+diff --git a/dmioutput.c b/dmioutput.c
+new file mode 100644
+index 000000000000..e762a035f39d
+--- /dev/null
++++ b/dmioutput.c
+@@ -0,0 +1,35 @@
++/*
++ * Generic output functions
++ * This file is part of the dmidecode project.
++ *
++ *   Copyright (C) 2020 Jean Delvare <jdelvare@suse.de>
++ *
++ *   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; either version 2 of the License, or
++ *   (at your option) any later version.
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with this program; if not, write to the Free Software
++ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <stdarg.h>
++#include <stdio.h>
++#include "dmioutput.h"
++
++void pr_comment(const char *format, ...)
++{
++	va_list args;
++
++	printf("# ");
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+new file mode 100644
+index 000000000000..b6cf5ee8b60e
+--- /dev/null
++++ b/dmioutput.h
+@@ -0,0 +1,22 @@
++/*
++ * Generic output functions
++ * This file is part of the dmidecode project.
++ *
++ *   Copyright (C) 2020 Jean Delvare <jdelvare@suse.de>
++ *
++ *   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; either version 2 of the License, or
++ *   (at your option) any later version.
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with this program; if not, write to the Free Software
++ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++void pr_comment(const char *format, ...);
+-- 
+2.17.1
+
diff --git a/SOURCES/0010-dmidecode-Add-helper-function-pr_info.patch b/SOURCES/0010-dmidecode-Add-helper-function-pr_info.patch
new file mode 100644
index 0000000..547e0cc
--- /dev/null
+++ b/SOURCES/0010-dmidecode-Add-helper-function-pr_info.patch
@@ -0,0 +1,142 @@
+From dd593d2f0c7272658070208d0a6957d77fc7e6f2 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:51:51 +0200
+Subject: [PATCH 10/23] dmidecode: Add helper function pr_info
+
+Print all info messages through a helper function pr_info.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 30 +++++++++++++++---------------
+ dmioutput.c | 10 ++++++++++
+ dmioutput.h |  1 +
+ 3 files changed, 26 insertions(+), 15 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 2939b2d307fc..e3f6e300efc2 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -5296,11 +5296,11 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
+ 		if (opt.type == NULL)
+ 		{
+ 			if (num)
+-				printf("%u structures occupying %u bytes.\n",
+-				       num, len);
++				pr_info("%u structures occupying %u bytes.",
++					num, len);
+ 			if (!(opt.flags & FLAG_FROM_DUMP))
+-				printf("Table at 0x%08llX.\n",
+-				       (unsigned long long)base);
++				pr_info("Table at 0x%08llX.",
++					(unsigned long long)base);
+ 		}
+ 		printf("\n");
+ 	}
+@@ -5397,8 +5397,8 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
+ 
+ 	ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09];
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("SMBIOS %u.%u.%u present.\n",
+-		       buf[0x07], buf[0x08], buf[0x09]);
++		pr_info("SMBIOS %u.%u.%u present.",
++			buf[0x07], buf[0x08], buf[0x09]);
+ 
+ 	offset = QWORD(buf + 0x10);
+ 	if (!(flags & FLAG_NO_FILE_OFFSET) && offset.h && sizeof(off_t) < 8)
+@@ -5465,7 +5465,7 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
+ 			break;
+ 	}
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("SMBIOS %u.%u present.\n",
++		pr_info("SMBIOS %u.%u present.",
+ 			ver >> 8, ver & 0xFF);
+ 
+ 	dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C),
+@@ -5493,7 +5493,7 @@ static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
+ 		return 0;
+ 
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("Legacy DMI %u.%u present.\n",
++		pr_info("Legacy DMI %u.%u present.",
+ 			buf[0x0E] >> 4, buf[0x0E] & 0x0F);
+ 
+ 	dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C),
+@@ -5645,8 +5645,8 @@ int main(int argc, char * const argv[])
+ 	if (opt.flags & FLAG_FROM_DUMP)
+ 	{
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("Reading SMBIOS/DMI data from file %s.\n",
+-			       opt.dumpfile);
++			pr_info("Reading SMBIOS/DMI data from file %s.",
++				opt.dumpfile);
+ 		if ((buf = mem_chunk(0, 0x20, opt.dumpfile)) == NULL)
+ 		{
+ 			ret = 1;
+@@ -5681,7 +5681,7 @@ int main(int argc, char * const argv[])
+ 	 && (buf = read_file(0, &size, SYS_ENTRY_FILE)) != NULL)
+ 	{
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("Getting SMBIOS data from sysfs.\n");
++			pr_info("Getting SMBIOS data from sysfs.");
+ 		if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0)
+ 		{
+ 			if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
+@@ -5701,7 +5701,7 @@ int main(int argc, char * const argv[])
+ 		if (found)
+ 			goto done;
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("Failed to get SMBIOS data from sysfs.\n");
++			pr_info("Failed to get SMBIOS data from sysfs.");
+ 	}
+ 
+ 	/* Next try EFI (ia64, Intel-based Mac, arm64) */
+@@ -5716,8 +5716,8 @@ int main(int argc, char * const argv[])
+ 	}
+ 
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("Found SMBIOS entry point in EFI, reading table from %s.\n",
+-		       opt.devmem);
++		pr_info("Found SMBIOS entry point in EFI, reading table from %s.",
++			opt.devmem);
+ 	if ((buf = mem_chunk(fp, 0x20, opt.devmem)) == NULL)
+ 	{
+ 		ret = 1;
+@@ -5739,7 +5739,7 @@ int main(int argc, char * const argv[])
+ memory_scan:
+ #if defined __i386__ || defined __x86_64__
+ 	if (!(opt.flags & FLAG_QUIET))
+-		printf("Scanning %s for entry point.\n", opt.devmem);
++		pr_info("Scanning %s for entry point.", opt.devmem);
+ 	/* Fallback to memory scan (x86, x86_64) */
+ 	if ((buf = mem_chunk(0xF0000, 0x10000, opt.devmem)) == NULL)
+ 	{
+diff --git a/dmioutput.c b/dmioutput.c
+index e762a035f39d..e702f114bb4a 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -33,3 +33,13 @@ void pr_comment(const char *format, ...)
+ 	va_end(args);
+ 	printf("\n");
+ }
++
++void pr_info(const char *format, ...)
++{
++	va_list args;
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index b6cf5ee8b60e..0dd8f0811934 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -20,3 +20,4 @@
+  */
+ 
+ void pr_comment(const char *format, ...);
++void pr_info(const char *format, ...);
+-- 
+2.17.1
+
diff --git a/SOURCES/0011-dmidecode-Protect-dmidecode.h-against-double-inclusi.patch b/SOURCES/0011-dmidecode-Protect-dmidecode.h-against-double-inclusi.patch
new file mode 100644
index 0000000..ac660e2
--- /dev/null
+++ b/SOURCES/0011-dmidecode-Protect-dmidecode.h-against-double-inclusi.patch
@@ -0,0 +1,36 @@
+From 0c6f1819b5421166f55369bb33f538bf64805ff6 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:51:54 +0200
+Subject: [PATCH 11/23] dmidecode: Protect dmidecode.h against double inclusion
+
+We'll soon need to include dmidecode.h from another header file, so
+protect it against double inclusion.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/dmidecode.h b/dmidecode.h
+index 20e7e96efc0f..9ecc1791702d 100644
+--- a/dmidecode.h
++++ b/dmidecode.h
+@@ -18,6 +18,9 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
++#ifndef DMIDECODE_H
++#define DMIDECODE_H
++
+ #include "types.h"
+ 
+ struct dmi_header
+@@ -30,3 +33,5 @@ struct dmi_header
+ 
+ int is_printable(const u8 *data, int len);
+ const char *dmi_string(const struct dmi_header *dm, u8 s);
++
++#endif
+-- 
+2.17.1
+
diff --git a/SOURCES/0012-dmidecode-Add-helper-function-pr_handle.patch b/SOURCES/0012-dmidecode-Add-helper-function-pr_handle.patch
new file mode 100644
index 0000000..d140c0b
--- /dev/null
+++ b/SOURCES/0012-dmidecode-Add-helper-function-pr_handle.patch
@@ -0,0 +1,58 @@
+From d37eed24b07da16719ce969f119b4c636e0e2d96 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:51:57 +0200
+Subject: [PATCH 12/23] dmidecode: Add helper function pr_handle
+
+Print the handle information through a helper function pr_handle.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 3 +--
+ dmioutput.c | 6 ++++++
+ dmioutput.h | 3 +++
+ 3 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index e3f6e300efc2..8ba8d078614e 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -5212,8 +5212,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+ 
+ 		if (display
+ 		 && (!(opt.flags & FLAG_QUIET) || (opt.flags & FLAG_DUMP)))
+-			printf("Handle 0x%04X, DMI type %d, %d bytes\n",
+-				h.handle, h.type, h.length);
++			pr_handle(&h);
+ 
+ 		/* Look for the next handle */
+ 		next = data + h.length;
+diff --git a/dmioutput.c b/dmioutput.c
+index e702f114bb4a..ad3b0398c446 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -43,3 +43,9 @@ void pr_info(const char *format, ...)
+ 	va_end(args);
+ 	printf("\n");
+ }
++
++void pr_handle(const struct dmi_header *h)
++{
++	printf("Handle 0x%04X, DMI type %d, %d bytes\n",
++	       h->handle, h->type, h->length);
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 0dd8f0811934..6ef60f0ee3cd 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -19,5 +19,8 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
++#include "dmidecode.h"
++
+ void pr_comment(const char *format, ...);
+ void pr_info(const char *format, ...);
++void pr_handle(const struct dmi_header *h);
+-- 
+2.17.1
+
diff --git a/SOURCES/0013-dmidecode-Add-helper-function-pr_handle_name.patch b/SOURCES/0013-dmidecode-Add-helper-function-pr_handle_name.patch
new file mode 100644
index 0000000..f9f3aed
--- /dev/null
+++ b/SOURCES/0013-dmidecode-Add-helper-function-pr_handle_name.patch
@@ -0,0 +1,569 @@
+From ebbe1dbe9a4413232949d97a506db6a7c00865fa Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:52:00 +0200
+Subject: [PATCH 13/23] dmidecode: Add helper function pr_handle_name
+
+Print the name of each handle type through a helper function
+pr_handle_name.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 99 ++++++++++++++++++++++++++---------------------------
+ dmioem.c    | 25 +++++++-------
+ dmioutput.c | 10 ++++++
+ dmioutput.h |  1 +
+ 4 files changed, 73 insertions(+), 62 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 8ba8d078614e..c226bad06638 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -2083,11 +2083,10 @@ static void dmi_on_board_devices(const struct dmi_header *h, const char *prefix)
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		if (count == 1)
+-			printf("%sOn Board Device Information\n",
+-				prefix);
++			pr_handle_name("On Board Device Information");
+ 		else
+-			printf("%sOn Board Device %d Information\n",
+-				prefix, i + 1);
++			pr_handle_name("On Board Device %d Information",
++				       i + 1);
+ 		printf("%s\tType: %s\n",
+ 			prefix, dmi_on_board_devices_type(p[2 * i] & 0x7F));
+ 		printf("%s\tStatus: %s\n",
+@@ -3397,7 +3396,7 @@ static void dmi_additional_info(const struct dmi_header *h, const char *prefix)
+ 
+ 	for (i = 0; i < count; i++)
+ 	{
+-		printf("%sAdditional Information %d\n", prefix, i + 1);
++		pr_handle_name("Additional Information %d", i + 1);
+ 
+ 		/* Check for short entries */
+ 		if (h->length < offset + 1) break;
+@@ -3893,7 +3892,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 	switch (h->type)
+ 	{
+ 		case 0: /* 7.1 BIOS Information */
+-			printf("BIOS Information\n");
++			pr_handle_name("BIOS Information");
+ 			if (h->length < 0x12) break;
+ 			printf("\tVendor: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -3933,7 +3932,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 1: /* 7.2 System Information */
+-			printf("System Information\n");
++			pr_handle_name("System Information");
+ 			if (h->length < 0x08) break;
+ 			printf("\tManufacturer: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -3957,7 +3956,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 2: /* 7.3 Base Board Information */
+-			printf("Base Board Information\n");
++			pr_handle_name("Base Board Information");
+ 			if (h->length < 0x08) break;
+ 			printf("\tManufacturer: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -3988,7 +3987,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 3: /* 7.4 Chassis Information */
+-			printf("Chassis Information\n");
++			pr_handle_name("Chassis Information");
+ 			if (h->length < 0x09) break;
+ 			printf("\tManufacturer: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4030,7 +4029,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 4: /* 7.5 Processor Information */
+-			printf("Processor Information\n");
++			pr_handle_name("Processor Information");
+ 			if (h->length < 0x1A) break;
+ 			printf("\tSocket Designation: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4100,7 +4099,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 5: /* 7.6 Memory Controller Information */
+-			printf("Memory Controller Information\n");
++			pr_handle_name("Memory Controller Information");
+ 			if (h->length < 0x0F) break;
+ 			printf("\tError Detecting Method: %s\n",
+ 				dmi_memory_controller_ed_method(data[0x04]));
+@@ -4130,7 +4129,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 6: /* 7.7 Memory Module Information */
+-			printf("Memory Module Information\n");
++			pr_handle_name("Memory Module Information");
+ 			if (h->length < 0x0C) break;
+ 			printf("\tSocket Designation: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4154,7 +4153,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 7: /* 7.8 Cache Information */
+-			printf("Cache Information\n");
++			pr_handle_name("Cache Information");
+ 			if (h->length < 0x0F) break;
+ 			printf("\tSocket Designation: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4197,7 +4196,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 8: /* 7.9 Port Connector Information */
+-			printf("Port Connector Information\n");
++			pr_handle_name("Port Connector Information");
+ 			if (h->length < 0x09) break;
+ 			printf("\tInternal Reference Designator: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4212,7 +4211,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 9: /* 7.10 System Slots */
+-			printf("System Slot Information\n");
++			pr_handle_name("System Slot Information");
+ 			if (h->length < 0x0C) break;
+ 			printf("\tDesignation: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4243,19 +4242,19 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 11: /* 7.12 OEM Strings */
+-			printf("OEM Strings\n");
++			pr_handle_name("OEM Strings");
+ 			if (h->length < 0x05) break;
+ 			dmi_oem_strings(h, "\t");
+ 			break;
+ 
+ 		case 12: /* 7.13 System Configuration Options */
+-			printf("System Configuration Options\n");
++			pr_handle_name("System Configuration Options");
+ 			if (h->length < 0x05) break;
+ 			dmi_system_configuration_options(h, "\t");
+ 			break;
+ 
+ 		case 13: /* 7.14 BIOS Language Information */
+-			printf("BIOS Language Information\n");
++			pr_handle_name("BIOS Language Information");
+ 			if (h->length < 0x16) break;
+ 			if (ver >= 0x0201)
+ 			{
+@@ -4269,7 +4268,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 14: /* 7.15 Group Associations */
+-			printf("Group Associations\n");
++			pr_handle_name("Group Associations");
+ 			if (h->length < 0x05) break;
+ 			printf("\tName: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4279,7 +4278,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 15: /* 7.16 System Event Log */
+-			printf("System Event Log\n");
++			pr_handle_name("System Event Log");
+ 			if (h->length < 0x14) break;
+ 			printf("\tArea Length: %u bytes\n",
+ 				WORD(data + 0x04));
+@@ -4311,7 +4310,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 16: /* 7.17 Physical Memory Array */
+-			printf("Physical Memory Array\n");
++			pr_handle_name("Physical Memory Array");
+ 			if (h->length < 0x0F) break;
+ 			printf("\tLocation: %s\n",
+ 				dmi_memory_array_location(data[0x04]));
+@@ -4347,7 +4346,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 17: /* 7.18 Memory Device */
+-			printf("Memory Device\n");
++			pr_handle_name("Memory Device");
+ 			if (h->length < 0x15) break;
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+@@ -4457,7 +4456,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 18: /* 7.19 32-bit Memory Error Information */
+-			printf("32-bit Memory Error Information\n");
++			pr_handle_name("32-bit Memory Error Information");
+ 			if (h->length < 0x17) break;
+ 			printf("\tType: %s\n",
+ 				dmi_memory_error_type(data[0x04]));
+@@ -4480,7 +4479,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 19: /* 7.20 Memory Array Mapped Address */
+-			printf("Memory Array Mapped Address\n");
++			pr_handle_name("Memory Array Mapped Address");
+ 			if (h->length < 0x0F) break;
+ 			if (h->length >= 0x1F && DWORD(data + 0x04) == 0xFFFFFFFF)
+ 			{
+@@ -4516,7 +4515,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 20: /* 7.21 Memory Device Mapped Address */
+-			printf("Memory Device Mapped Address\n");
++			pr_handle_name("Memory Device Mapped Address");
+ 			if (h->length < 0x13) break;
+ 			if (h->length >= 0x23 && DWORD(data + 0x04) == 0xFFFFFFFF)
+ 			{
+@@ -4559,7 +4558,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 21: /* 7.22 Built-in Pointing Device */
+-			printf("Built-in Pointing Device\n");
++			pr_handle_name("Built-in Pointing Device");
+ 			if (h->length < 0x07) break;
+ 			printf("\tType: %s\n",
+ 				dmi_pointing_device_type(data[0x04]));
+@@ -4570,7 +4569,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 22: /* 7.23 Portable Battery */
+-			printf("Portable Battery\n");
++			pr_handle_name("Portable Battery");
+ 			if (h->length < 0x10) break;
+ 			printf("\tLocation: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4618,7 +4617,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 23: /* 7.24 System Reset */
+-			printf("System Reset\n");
++			pr_handle_name("System Reset");
+ 			if (h->length < 0x0D) break;
+ 			printf("\tStatus: %s\n",
+ 				data[0x04] & (1 << 0) ? "Enabled" : "Disabled");
+@@ -4645,7 +4644,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 24: /* 7.25 Hardware Security */
+-			printf("Hardware Security\n");
++			pr_handle_name("Hardware Security");
+ 			if (h->length < 0x05) break;
+ 			printf("\tPower-On Password Status: %s\n",
+ 				dmi_hardware_security_status(data[0x04] >> 6));
+@@ -4658,7 +4657,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 25: /* 7.26 System Power Controls */
+-			printf("System Power Controls\n");
++			pr_handle_name("System Power Controls");
+ 			if (h->length < 0x09) break;
+ 			printf("\tNext Scheduled Power-on:");
+ 			dmi_power_controls_power_on(data + 0x04);
+@@ -4666,7 +4665,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 26: /* 7.27 Voltage Probe */
+-			printf("Voltage Probe\n");
++			pr_handle_name("Voltage Probe");
+ 			if (h->length < 0x14) break;
+ 			printf("\tDescription: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4698,7 +4697,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 27: /* 7.28 Cooling Device */
+-			printf("Cooling Device\n");
++			pr_handle_name("Cooling Device");
+ 			if (h->length < 0x0C) break;
+ 			if (!(opt.flags & FLAG_QUIET) && WORD(data + 0x04) != 0xFFFF)
+ 				printf("\tTemperature Probe Handle: 0x%04X\n",
+@@ -4721,7 +4720,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 28: /* 7.29 Temperature Probe */
+-			printf("Temperature Probe\n");
++			pr_handle_name("Temperature Probe");
+ 			if (h->length < 0x14) break;
+ 			printf("\tDescription: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4753,7 +4752,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 29: /* 7.30 Electrical Current Probe */
+-			printf("Electrical Current Probe\n");
++			pr_handle_name("Electrical Current Probe");
+ 			if (h->length < 0x14) break;
+ 			printf("\tDescription: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4785,7 +4784,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 30: /* 7.31 Out-of-band Remote Access */
+-			printf("Out-of-band Remote Access\n");
++			pr_handle_name("Out-of-band Remote Access");
+ 			if (h->length < 0x06) break;
+ 			printf("\tManufacturer Name: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4796,7 +4795,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 31: /* 7.32 Boot Integrity Services Entry Point */
+-			printf("Boot Integrity Services Entry Point\n");
++			pr_handle_name("Boot Integrity Services Entry Point");
+ 			if (h->length < 0x1C) break;
+ 			printf("\tChecksum: %s\n",
+ 				checksum(data, h->length) ? "OK" : "Invalid");
+@@ -4808,14 +4807,14 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 32: /* 7.33 System Boot Information */
+-			printf("System Boot Information\n");
++			pr_handle_name("System Boot Information");
+ 			if (h->length < 0x0B) break;
+ 			printf("\tStatus: %s\n",
+ 				dmi_system_boot_status(data[0x0A]));
+ 			break;
+ 
+ 		case 33: /* 7.34 64-bit Memory Error Information */
+-			printf("64-bit Memory Error Information\n");
++			pr_handle_name("64-bit Memory Error Information");
+ 			if (h->length < 0x1F) break;
+ 			printf("\tType: %s\n",
+ 				dmi_memory_error_type(data[0x04]));
+@@ -4838,7 +4837,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 34: /* 7.35 Management Device */
+-			printf("Management Device\n");
++			pr_handle_name("Management Device");
+ 			if (h->length < 0x0B) break;
+ 			printf("\tDescription: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4851,7 +4850,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 35: /* 7.36 Management Device Component */
+-			printf("Management Device Component\n");
++			pr_handle_name("Management Device Component");
+ 			if (h->length < 0x0B) break;
+ 			printf("\tDescription: %s\n",
+ 				dmi_string(h, data[0x04]));
+@@ -4868,7 +4867,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 36: /* 7.37 Management Device Threshold Data */
+-			printf("Management Device Threshold Data\n");
++			pr_handle_name("Management Device Threshold Data");
+ 			if (h->length < 0x10) break;
+ 			if (WORD(data + 0x04) != 0x8000)
+ 				printf("\tLower Non-critical Threshold: %d\n",
+@@ -4891,7 +4890,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 37: /* 7.38 Memory Channel */
+-			printf("Memory Channel\n");
++			pr_handle_name("Memory Channel");
+ 			if (h->length < 0x07) break;
+ 			printf("\tType: %s\n",
+ 				dmi_memory_channel_type(data[0x04]));
+@@ -4908,7 +4907,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			 * We use the word "Version" instead of "Revision", conforming to
+ 			 * the IPMI specification.
+ 			 */
+-			printf("IPMI Device Information\n");
++			pr_handle_name("IPMI Device Information");
+ 			if (h->length < 0x10) break;
+ 			printf("\tInterface Type: %s\n",
+ 				dmi_ipmi_interface_type(data[0x04]));
+@@ -4946,7 +4945,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 39: /* 7.40 System Power Supply */
+-			printf("System Power Supply\n");
++			pr_handle_name("System Power Supply");
+ 			if (h->length < 0x10) break;
+ 			if (data[0x04] != 0x00)
+ 				printf("\tPower Unit Group: %u\n",
+@@ -5006,7 +5005,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 41: /* 7.42 Onboard Device Extended Information */
+-			printf("Onboard Device\n");
++			pr_handle_name("Onboard Device");
+ 			if (h->length < 0x0B) break;
+ 			printf("\tReference Designation: %s\n", dmi_string(h, data[0x04]));
+ 			printf("\tType: %s\n",
+@@ -5018,7 +5017,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 42: /* 7.43 Management Controller Host Interface */
+-			printf("Management Controller Host Interface\n");
++			pr_handle_name("Management Controller Host Interface");
+ 			if (ver < 0x0302)
+ 			{
+ 				if (h->length < 0x05) break;
+@@ -5043,7 +5042,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 43: /* 7.44 TPM Device */
+-			printf("TPM Device\n");
++			pr_handle_name("TPM Device");
+ 			if (h->length < 0x1B) break;
+ 			printf("\tVendor ID:");
+ 			dmi_tpm_vendor_id(data + 0x04);
+@@ -5080,11 +5079,11 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			break;
+ 
+ 		case 126: /* 7.44 Inactive */
+-			printf("Inactive\n");
++			pr_handle_name("Inactive");
+ 			break;
+ 
+ 		case 127: /* 7.45 End Of Table */
+-			printf("End Of Table\n");
++			pr_handle_name("End Of Table");
+ 			break;
+ 
+ 		default:
+@@ -5092,7 +5091,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				break;
+ 			if (opt.flags & FLAG_QUIET)
+ 				return;
+-			printf("%s Type\n",
++			pr_handle_name("%s Type",
+ 				h->type >= 128 ? "OEM-specific" : "Unknown");
+ 			dmi_dump(h, "\t");
+ 	}
+diff --git a/dmioem.c b/dmioem.c
+index 1a9bd8264fcb..c999c08c4475 100644
+--- a/dmioem.c
++++ b/dmioem.c
+@@ -25,6 +25,7 @@
+ #include "types.h"
+ #include "dmidecode.h"
+ #include "dmioem.h"
++#include "dmioutput.h"
+ 
+ /*
+  * Globals for vendor-specific decodes
+@@ -92,7 +93,7 @@ static int dmi_decode_acer(const struct dmi_header *h)
+ 			 * brands, including Fujitsu-Siemens, Medion, Lenovo,
+ 			 * and eMachines.
+ 			 */
+-			printf("Acer Hotkey Function\n");
++			pr_handle_name("Acer Hotkey Function");
+ 			if (h->length < 0x0F) break;
+ 			cap = WORD(data + 0x04);
+ 			printf("\tFunction bitmap for Communication Button: 0x%04hx\n", cap);
+@@ -157,7 +158,7 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			/*
+ 			 * Vendor Specific: HPE ProLiant System/Rack Locator
+ 			 */
+-			printf("%s ProLiant System/Rack Locator\n", company);
++			pr_handle_name("%s ProLiant System/Rack Locator", company);
+ 			if (h->length < 0x0B) break;
+ 			printf("\tRack Name: %s\n", dmi_string(h, data[0x04]));
+ 			printf("\tEnclosure Name: %s\n", dmi_string(h, data[0x05]));
+@@ -189,10 +190,9 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 *
+ 			 * Type 221: is deprecated in the latest docs
+ 			 */
+-			printf("%s %s\n", company,
+-				h->type == 221 ?
+-					"BIOS iSCSI NIC PCI and MAC Information" :
+-					"BIOS PXE NIC PCI and MAC Information");
++			pr_handle_name("%s %s", company, h->type == 221 ?
++				       "BIOS iSCSI NIC PCI and MAC Information" :
++				       "BIOS PXE NIC PCI and MAC Information");
+ 			nic = 1;
+ 			ptr = 4;
+ 			while (h->length >= ptr + 8)
+@@ -224,7 +224,8 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 *  0x08  |   MAC  | 32B   | MAC addr padded w/ 0s
+ 			 *  0x28  | Port No| BYTE  | Each NIC maps to a Port
+ 			 */
+-			printf("%s BIOS PXE NIC PCI and MAC Information\n", company);
++			pr_handle_name("%s BIOS PXE NIC PCI and MAC Information",
++				       company);
+ 			if (h->length < 0x0E) break;
+ 			/* If the record isn't long enough, we don't have an ID
+ 			 * use 0xFF to use the internal counter.
+@@ -240,7 +241,7 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 *
+ 			 * Source: hpwdt kernel driver
+ 			 */
+-			printf("%s 64-bit CRU Information\n", company);
++			pr_handle_name("%s 64-bit CRU Information", company);
+ 			if (h->length < 0x18) break;
+ 			printf("\tSignature: 0x%08x", DWORD(data + 0x04));
+ 			if (is_printable(data + 0x04, 4))
+@@ -265,7 +266,7 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 *
+ 			 * Source: hpwdt kernel driver
+ 			 */
+-			printf("%s ProLiant Information\n", company);
++			pr_handle_name("%s ProLiant Information", company);
+ 			if (h->length < 0x08) break;
+ 			printf("\tPower Features: 0x%08x\n", DWORD(data + 0x04));
+ 			if (h->length < 0x0C) break;
+@@ -318,7 +319,7 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 			 || strcmp(dmi_string(h, 1), "TVT-Enablement") != 0)
+ 				return 0;
+ 
+-			printf("ThinkVantage Technologies\n");
++			pr_handle_name("ThinkVantage Technologies");
+ 			printf("\tVersion: %u\n", data[0x04]);
+ 			printf("\tDiagnostics: %s\n",
+ 				data[0x14] & 0x80 ? "Available" : "No");
+@@ -357,7 +358,7 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 			if (data[0x06] != 0x07 || data[0x07] != 0x03 || data[0x08] != 0x01)
+ 				return 0;
+ 
+-			printf("ThinkPad Device Presence Detection\n");
++			pr_handle_name("ThinkPad Device Presence Detection");
+ 			printf("\tFingerprint Reader: %s\n",
+ 				data[0x09] & 0x01 ? "Present" : "No");
+ 			break;
+@@ -390,7 +391,7 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 			if (data[0x0A] != 0x0B || data[0x0B] != 0x07 || data[0x0C] != 0x01)
+ 				return 0;
+ 
+-			printf("ThinkPad Embedded Controller Program\n");
++			pr_handle_name("ThinkPad Embedded Controller Program");
+ 			printf("\tVersion ID: %s\n", dmi_string(h, 1));
+ 			printf("\tRelease Date: %s\n", dmi_string(h, 2));
+ 			break;
+diff --git a/dmioutput.c b/dmioutput.c
+index ad3b0398c446..ca7edab5cc69 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -49,3 +49,13 @@ void pr_handle(const struct dmi_header *h)
+ 	printf("Handle 0x%04X, DMI type %d, %d bytes\n",
+ 	       h->handle, h->type, h->length);
+ }
++
++void pr_handle_name(const char *format, ...)
++{
++	va_list args;
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 6ef60f0ee3cd..0acdce7658c9 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -24,3 +24,4 @@
+ void pr_comment(const char *format, ...);
+ void pr_info(const char *format, ...);
+ void pr_handle(const struct dmi_header *h);
++void pr_handle_name(const char *format, ...);
+-- 
+2.17.1
+
diff --git a/SOURCES/0014-dmidecode-Add-helper-function-pr_attr.patch b/SOURCES/0014-dmidecode-Add-helper-function-pr_attr.patch
new file mode 100644
index 0000000..cf7a2fb
--- /dev/null
+++ b/SOURCES/0014-dmidecode-Add-helper-function-pr_attr.patch
@@ -0,0 +1,2987 @@
+From 07968a8a578d34137eb6db0c26b860c567f60903 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:52:04 +0200
+Subject: [PATCH 14/23] dmidecode: Add helper function pr_attr
+
+Print all first-level attributes through a helper function pr_attr.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 1357 ++++++++++++++++++++++++---------------------------
+ dmioem.c    |   66 +--
+ dmioutput.c |   12 +
+ dmioutput.h |    1 +
+ 4 files changed, 691 insertions(+), 745 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index c226bad06638..7ab058b61ef0 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -261,7 +261,7 @@ static void dmi_dump(const struct dmi_header *h, const char *prefix)
+ }
+ 
+ /* shift is 0 if the value is in bytes, 1 if it is in kilobytes */
+-static void dmi_print_memory_size(u64 code, int shift)
++static void dmi_print_memory_size(const char *attr, u64 code, int shift)
+ {
+ 	unsigned long capacity;
+ 	u16 split[7];
+@@ -301,7 +301,7 @@ static void dmi_print_memory_size(u64 code, int shift)
+ 	else
+ 		capacity = split[i];
+ 
+-	printf(" %lu %s", capacity, unit[i + shift]);
++	pr_attr(attr, "%lu %s", capacity, unit[i + shift]);
+ }
+ 
+ /*
+@@ -310,10 +310,19 @@ static void dmi_print_memory_size(u64 code, int shift)
+ 
+ static void dmi_bios_runtime_size(u32 code)
+ {
++	const char *format;
++
+ 	if (code & 0x000003FF)
+-		printf(" %u bytes", code);
++	{
++		format = "%u bytes";
++	}
+ 	else
+-		printf(" %u kB", code >> 10);
++	{
++		format = "%u kB";
++		code >>= 10;
++	}
++
++	pr_attr("Runtime Size", format, code);
+ }
+ 
+ static void dmi_bios_rom_size(u8 code1, u16 code2)
+@@ -325,10 +334,10 @@ static void dmi_bios_rom_size(u8 code1, u16 code2)
+ 	if (code1 != 0xFF)
+ 	{
+ 		u64 s = { .l = (code1 + 1) << 6 };
+-		dmi_print_memory_size(s, 1);
++		dmi_print_memory_size("ROM Size", s, 1);
+ 	}
+ 	else
+-		printf(" %u %s", code2 & 0x3FFF, unit[code2 >> 14]);
++		pr_attr("ROM Size", "%u %s", code2 & 0x3FFF, unit[code2 >> 14]);
+ }
+ 
+ static void dmi_bios_characteristics(u64 code, const char *prefix)
+@@ -426,7 +435,7 @@ static void dmi_bios_characteristics_x2(u8 code, const char *prefix)
+  * 7.2 System Information (Type 1)
+  */
+ 
+-static void dmi_system_uuid(const u8 *p, u16 ver)
++static void dmi_system_uuid(const char *attr, const u8 *p, u16 ver)
+ {
+ 	int only0xFF = 1, only0x00 = 1;
+ 	int i;
+@@ -439,12 +448,18 @@ static void dmi_system_uuid(const u8 *p, u16 ver)
+ 
+ 	if (only0xFF)
+ 	{
+-		printf("Not Present");
++		if (attr)
++			pr_attr(attr, "Not Present");
++		else
++			printf("Not Present\n");
+ 		return;
+ 	}
+ 	if (only0x00)
+ 	{
+-		printf("Not Settable");
++		if (attr)
++			pr_attr(attr, "Not Settable");
++		else
++			printf("Not Settable\n");
+ 		return;
+ 	}
+ 
+@@ -457,13 +472,29 @@ static void dmi_system_uuid(const u8 *p, u16 ver)
+ 	 * for older versions.
+ 	 */
+ 	if (ver >= 0x0206)
+-		printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+-			p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
+-			p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++	{
++		if (attr)
++			pr_attr(attr,
++				"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
++				p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
++				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++		else
++			printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
++				p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
++				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++	}
+ 	else
+-		printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+-			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+-			p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++	{
++		if (attr)
++			pr_attr(attr,
++				"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
++				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
++				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++		else
++			printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
++				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
++				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++	}
+ }
+ 
+ static const char *dmi_system_wake_up_type(u8 code)
+@@ -649,17 +680,17 @@ static const char *dmi_chassis_security_status(u8 code)
+ static void dmi_chassis_height(u8 code)
+ {
+ 	if (code == 0x00)
+-		printf(" Unspecified");
++		pr_attr("Height", "Unspecified");
+ 	else
+-		printf(" %u U", code);
++		pr_attr("Height", "%u U", code);
+ }
+ 
+ static void dmi_chassis_power_cords(u8 code)
+ {
+ 	if (code == 0x00)
+-		printf(" Unspecified");
++		pr_attr("Number Of Power Cords", "Unspecified");
+ 	else
+-		printf(" %u", code);
++		pr_attr("Number Of Power Cords", "%u", code);
+ }
+ 
+ static void dmi_chassis_elements(u8 count, u8 len, const u8 *p, const char *prefix)
+@@ -1052,8 +1083,8 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 	 * This might help learn about new processors supporting the
+ 	 * CPUID instruction or another form of identification.
+ 	 */
+-	printf("%sID: %02X %02X %02X %02X %02X %02X %02X %02X\n",
+-		prefix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
++	pr_attr("ID", "%02X %02X %02X %02X %02X %02X %02X %02X",
++		p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ 
+ 	if (type == 0x05) /* 80386 */
+ 	{
+@@ -1061,8 +1092,9 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 		/*
+ 		 * 80386 have a different signature.
+ 		 */
+-		printf("%sSignature: Type %u, Family %u, Major Stepping %u, Minor Stepping %u\n",
+-			prefix, dx >> 12, (dx >> 8) & 0xF,
++		pr_attr("Signature",
++			"Type %u, Family %u, Major Stepping %u, Minor Stepping %u",
++			dx >> 12, (dx >> 8) & 0xF,
+ 			(dx >> 4) & 0xF, dx & 0xF);
+ 		return;
+ 	}
+@@ -1080,8 +1112,9 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 			sig = 1;
+ 		else
+ 		{
+-			printf("%sSignature: Type %u, Family %u, Model %u, Stepping %u\n",
+-				prefix, (dx >> 12) & 0x3, (dx >> 8) & 0xF,
++			pr_attr("Signature",
++				"Type %u, Family %u, Model %u, Stepping %u",
++				(dx >> 12) & 0x3, (dx >> 8) & 0xF,
+ 				(dx >> 4) & 0xF, dx & 0xF);
+ 			return;
+ 		}
+@@ -1097,8 +1130,9 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 		 */
+ 		if (midr == 0)
+ 			return;
+-		printf("%sSignature: Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u\n",
+-			prefix, midr >> 24, (midr >> 20) & 0xF,
++		pr_attr("Signature",
++			"Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u",
++			midr >> 24, (midr >> 20) & 0xF,
+ 			(midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF);
+ 		return;
+ 	}
+@@ -1153,15 +1187,15 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 	switch (sig)
+ 	{
+ 		case 1: /* Intel */
+-			printf("%sSignature: Type %u, Family %u, Model %u, Stepping %u\n",
+-				prefix, (eax >> 12) & 0x3,
++			pr_attr("Signature",
++				"Type %u, Family %u, Model %u, Stepping %u",
++				(eax >> 12) & 0x3,
+ 				((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F),
+ 				((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F),
+ 				eax & 0xF);
+ 			break;
+ 		case 2: /* AMD, publication #25481 revision 2.28 */
+-			printf("%sSignature: Family %u, Model %u, Stepping %u\n",
+-				prefix,
++			pr_attr("Signature", "Family %u, Model %u, Stepping %u",
+ 				((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0),
+ 				((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0),
+ 				eax & 0xF);
+@@ -1183,7 +1217,7 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 	}
+ }
+ 
+-static void dmi_processor_voltage(u8 code)
++static void dmi_processor_voltage(const char *attr, u8 code)
+ {
+ 	/* 7.5.4 */
+ 	static const char *voltage[] = {
+@@ -1194,25 +1228,47 @@ static void dmi_processor_voltage(u8 code)
+ 	int i;
+ 
+ 	if (code & 0x80)
+-		printf(" %.1f V", (float)(code & 0x7f) / 10);
++		pr_attr(attr, "%.1f V", (float)(code & 0x7f) / 10);
+ 	else if ((code & 0x07) == 0x00)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+ 	{
++		char voltage_str[18];
++		int off = 0;
++
+ 		for (i = 0; i <= 2; i++)
++		{
+ 			if (code & (1 << i))
+-				printf(" %s", voltage[i]);
++			{
++				/* Insert space if not the first value */
++				off += sprintf(voltage_str + off,
++					       off ? " %s" :"%s",
++					       voltage[i]);
++			}
++		}
++		if (off)
++			pr_attr(attr, voltage_str);
+ 	}
+ }
+ 
+-static void dmi_processor_frequency(const u8 *p)
++static void dmi_processor_frequency(const char *attr, const u8 *p)
+ {
+ 	u16 code = WORD(p);
+ 
+ 	if (code)
+-		printf("%u MHz", code);
++	{
++		if (attr)
++			pr_attr(attr, "%u MHz", code);
++		else
++			printf("%u MHz\n", code);
++	}
+ 	else
+-		printf("Unknown");
++	{
++		if (attr)
++			pr_attr(attr, "Unknown");
++		else
++			printf("Unknown\n");
++	}
+ }
+ 
+ /* code is assumed to be a 3-bit value */
+@@ -1303,17 +1359,18 @@ static const char *dmi_processor_upgrade(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_processor_cache(u16 code, const char *level, u16 ver)
++static void dmi_processor_cache(const char *attr, u16 code, const char *level,
++				u16 ver)
+ {
+ 	if (code == 0xFFFF)
+ 	{
+ 		if (ver >= 0x0203)
+-			printf(" Not Provided");
++			pr_attr(attr, "Not Provided");
+ 		else
+-			printf(" No %s Cache", level);
++			pr_attr(attr, "No %s Cache", level);
+ 	}
+ 	else
+-		printf(" 0x%04X", code);
++		pr_attr(attr, "0x%04X", code);
+ }
+ 
+ static void dmi_processor_characteristics(u16 code, const char *prefix)
+@@ -1478,46 +1535,48 @@ static void dmi_memory_module_types(u16 code, const char *sep)
+ static void dmi_memory_module_connections(u8 code)
+ {
+ 	if (code == 0xFF)
+-		printf(" None");
++		pr_attr("Bank Connections", "None");
++	else if ((code & 0xF0) == 0xF0)
++		pr_attr("Bank Connections", "%u", code & 0x0F);
++	else if ((code & 0x0F) == 0x0F)
++		pr_attr("Bank Connections", "%u", code >> 4);
+ 	else
+-	{
+-		if ((code & 0xF0) != 0xF0)
+-			printf(" %u", code >> 4);
+-		if ((code & 0x0F) != 0x0F)
+-			printf(" %u", code & 0x0F);
+-	}
++		pr_attr("Bank Connections", "%u %u", code >> 4, code & 0x0F);
+ }
+ 
+-static void dmi_memory_module_speed(u8 code)
++static void dmi_memory_module_speed(const char *attr, u8 code)
+ {
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %u ns", code);
++		pr_attr(attr, "%u ns", code);
+ }
+ 
+-static void dmi_memory_module_size(u8 code)
++static void dmi_memory_module_size(const char *attr, u8 code)
+ {
++	const char *connection;
++
+ 	/* 7.7.2 */
++	if (code & 0x80)
++		connection = " (Double-bank Connection)";
++	else
++		connection = " (Single-bank Connection)";
++
+ 	switch (code & 0x7F)
+ 	{
+ 		case 0x7D:
+-			printf(" Not Determinable");
++			pr_attr(attr, "Not Determinable%s", connection);
+ 			break;
+ 		case 0x7E:
+-			printf(" Disabled");
++			pr_attr(attr, "Disabled%s", connection);
+ 			break;
+ 		case 0x7F:
+-			printf(" Not Installed");
++			pr_attr(attr, "Not Installed");
+ 			return;
+ 		default:
+-			printf(" %u MB", 1 << (code & 0x7F));
++			pr_attr(attr, "%u MB%s", 1 << (code & 0x7F),
++				connection);
+ 	}
+-
+-	if (code & 0x80)
+-		printf(" (Double-bank Connection)");
+-	else
+-		printf(" (Single-bank Connection)");
+ }
+ 
+ static void dmi_memory_module_error(u8 code)
+@@ -1530,9 +1589,9 @@ static void dmi_memory_module_error(u8 code)
+ 	};
+ 
+ 	if (code & (1 << 2))
+-		printf(" See Event Log\n");
++		pr_attr("Error Status", "See Event Log");
+ 	else
+-		printf(" %s\n", status[code & 0x03]);
++		pr_attr("Error Status", "%s", status[code & 0x03]);
+ }
+ 
+ /*
+@@ -1564,7 +1623,7 @@ static const char *dmi_cache_location(u8 code)
+ 	return location[code];
+ }
+ 
+-static void dmi_cache_size_2(u32 code)
++static void dmi_cache_size_2(const char *attr, u32 code)
+ {
+ 	u64 size;
+ 
+@@ -1581,12 +1640,13 @@ static void dmi_cache_size_2(u32 code)
+ 	}
+ 
+ 	/* Use a more convenient unit for large cache size */
+-	dmi_print_memory_size(size, 1);
++	dmi_print_memory_size(attr, size, 1);
+ }
+ 
+-static void dmi_cache_size(u16 code)
++static void dmi_cache_size(const char *attr, u16 code)
+ {
+-	dmi_cache_size_2((((u32)code & 0x8000LU) << 16) | (code & 0x7FFFLU));
++	dmi_cache_size_2(attr,
++			 (((u32)code & 0x8000LU) << 16) | (code & 0x7FFFLU));
+ }
+ 
+ static void dmi_cache_types(u16 code, const char *sep)
+@@ -1938,16 +1998,16 @@ static const char *dmi_slot_length(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_slot_id(u8 code1, u8 code2, u8 type, const char *prefix)
++static void dmi_slot_id(u8 code1, u8 code2, u8 type)
+ {
+ 	/* 7.10.5 */
+ 	switch (type)
+ 	{
+ 		case 0x04: /* MCA */
+-			printf("%sID: %u\n", prefix, code1);
++			pr_attr("ID", "%u", code1);
+ 			break;
+ 		case 0x05: /* EISA */
+-			printf("%sID: %u\n", prefix, code1);
++			pr_attr("ID", "%u", code1);
+ 			break;
+ 		case 0x06: /* PCI */
+ 		case 0x0E: /* PCI */
+@@ -1985,10 +2045,10 @@ static void dmi_slot_id(u8 code1, u8 code2, u8 type, const char *prefix)
+ 		case 0xBB: /* PCI Express 4 */
+ 		case 0xBC: /* PCI Express 4 */
+ 		case 0xBD: /* PCI Express 4 */
+-			printf("%sID: %u\n", prefix, code1);
++			pr_attr("ID", "%u", code1);
+ 			break;
+ 		case 0x07: /* PCMCIA */
+-			printf("%sID: Adapter %u, Socket %u\n", prefix, code1, code2);
++			pr_attr("ID", "Adapter %u, Socket %u", code1, code2);
+ 			break;
+ 	}
+ }
+@@ -2031,22 +2091,26 @@ static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
+ 	}
+ }
+ 
+-static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3, const char *prefix)
++static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3)
+ {
+ 	/* 7.10.8 */
+ 	if (!(code1 == 0xFFFF && code2 == 0xFF && code3 == 0xFF))
+-		printf("%sBus Address: %04x:%02x:%02x.%x\n",
+-		       prefix, code1, code2, code3 >> 3, code3 & 0x7);
++		pr_attr("Bus Address", "%04x:%02x:%02x.%x",
++			code1, code2, code3 >> 3, code3 & 0x7);
+ }
+ 
+-static void dmi_slot_peers(u8 n, const u8 *data, const char *prefix)
++static void dmi_slot_peers(u8 n, const u8 *data)
+ {
++	char attr[16];
+ 	int i;
+ 
+ 	for (i = 1; i <= n; i++, data += 5)
+-		printf("%sPeer Device %d: %04x:%02x:%02x.%x (Width %u)\n",
+-		       prefix, i, WORD(data), data[2], data[3] >> 3,
+-		       data[3] & 0x07, data[4]);
++	{
++		sprintf(attr, "Peer Device %hu", i);
++		pr_attr(attr, "%04x:%02x:%02x.%x (Width %u)",
++			WORD(data), data[2], data[3] >> 3, data[3] & 0x07,
++			data[4]);
++	}
+ }
+ 
+ /*
+@@ -2074,7 +2138,7 @@ static const char *dmi_on_board_devices_type(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_on_board_devices(const struct dmi_header *h, const char *prefix)
++static void dmi_on_board_devices(const struct dmi_header *h)
+ {
+ 	u8 *p = h->data + 4;
+ 	u8 count = (h->length - 0x04) / 2;
+@@ -2087,12 +2151,11 @@ static void dmi_on_board_devices(const struct dmi_header *h, const char *prefix)
+ 		else
+ 			pr_handle_name("On Board Device %d Information",
+ 				       i + 1);
+-		printf("%s\tType: %s\n",
+-			prefix, dmi_on_board_devices_type(p[2 * i] & 0x7F));
+-		printf("%s\tStatus: %s\n",
+-			prefix, p[2 * i] & 0x80 ? "Enabled" : "Disabled");
+-		printf("%s\tDescription: %s\n",
+-			prefix, dmi_string(h, p[2 * i + 1]));
++		pr_attr("Type", "%s",
++			dmi_on_board_devices_type(p[2 * i] & 0x7F));
++		pr_attr("Status", "%s",
++			p[2 * i] & 0x80 ? "Enabled" : "Disabled");
++		pr_attr("Description", "%s", dmi_string(h, p[2 * i + 1]));
+ 	}
+ }
+ 
+@@ -2100,30 +2163,36 @@ static void dmi_on_board_devices(const struct dmi_header *h, const char *prefix)
+  * 7.12 OEM Strings (Type 11)
+  */
+ 
+-static void dmi_oem_strings(const struct dmi_header *h, const char *prefix)
++static void dmi_oem_strings(const struct dmi_header *h)
+ {
++	char attr[11];
+ 	u8 *p = h->data + 4;
+ 	u8 count = p[0x00];
+ 	int i;
+ 
+ 	for (i = 1; i <= count; i++)
+-		printf("%sString %d: %s\n",
+-			prefix, i, dmi_string(h, i));
++	{
++		sprintf(attr, "String %hu", i);
++		pr_attr(attr, "%s",dmi_string(h, i));
++	}
+ }
+ 
+ /*
+  * 7.13 System Configuration Options (Type 12)
+  */
+ 
+-static void dmi_system_configuration_options(const struct dmi_header *h, const char *prefix)
++static void dmi_system_configuration_options(const struct dmi_header *h)
+ {
++	char attr[11];
+ 	u8 *p = h->data + 4;
+ 	u8 count = p[0x00];
+ 	int i;
+ 
+ 	for (i = 1; i <= count; i++)
+-		printf("%sOption %d: %s\n",
+-			prefix, i, dmi_string(h, i));
++	{
++		sprintf(attr, "Option %hu", i);
++		pr_attr(attr, "%s",dmi_string(h, i));
++	}
+ }
+ 
+ /*
+@@ -2197,7 +2266,7 @@ static void dmi_event_log_status(u8 code)
+ 		"Full" /* 1 */
+ 	};
+ 
+-	printf(" %s, %s",
++	pr_attr("Status", "%s, %s",
+ 		valid[(code >> 0) & 1], full[(code >> 1) & 1]);
+ }
+ 
+@@ -2209,16 +2278,17 @@ static void dmi_event_log_address(u8 method, const u8 *p)
+ 		case 0x00:
+ 		case 0x01:
+ 		case 0x02:
+-			printf(" Index 0x%04X, Data 0x%04X", WORD(p), WORD(p + 2));
++			pr_attr("Access Address", "Index 0x%04X, Data 0x%04X",
++				WORD(p), WORD(p + 2));
+ 			break;
+ 		case 0x03:
+-			printf(" 0x%08X", DWORD(p));
++			pr_attr("Access Address", "0x%08X", DWORD(p));
+ 			break;
+ 		case 0x04:
+-			printf(" 0x%04X", WORD(p));
++			pr_attr("Access Address", "0x%04X", WORD(p));
+ 			break;
+ 		default:
+-			printf(" Unknown");
++			pr_attr("Access Address", "Unknown");
+ 	}
+ }
+ 
+@@ -2295,19 +2365,22 @@ static const char *dmi_event_log_descriptor_format(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_event_log_descriptors(u8 count, u8 len, const u8 *p, const char *prefix)
++static void dmi_event_log_descriptors(u8 count, u8 len, const u8 *p)
+ {
+ 	/* 7.16.1 */
++	char attr[16];
+ 	int i;
+ 
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		if (len >= 0x02)
+ 		{
+-			printf("%sDescriptor %u: %s\n",
+-				prefix, i + 1, dmi_event_log_descriptor_type(p[i * len]));
+-			printf("%sData Format %u: %s\n",
+-				prefix, i + 1, dmi_event_log_descriptor_format(p[i * len + 1]));
++			sprintf(attr, "Descriptor %hu", i + 1);
++			pr_attr(attr, "%s",
++				dmi_event_log_descriptor_type(p[i * len]));
++			sprintf(attr, "Data Format %hu", i + 1);
++			pr_attr(attr, "%s",
++				dmi_event_log_descriptor_format(p[i * len + 1]));
+ 		}
+ 	}
+ }
+@@ -2385,40 +2458,40 @@ static const char *dmi_memory_array_ec_type(u8 code)
+ static void dmi_memory_array_error_handle(u16 code)
+ {
+ 	if (code == 0xFFFE)
+-		printf(" Not Provided");
++		pr_attr("Error Information Handle", "Not Provided");
+ 	else if (code == 0xFFFF)
+-		printf(" No Error");
++		pr_attr("Error Information Handle", "No Error");
+ 	else
+-		printf(" 0x%04X", code);
++		pr_attr("Error Information Handle", "0x%04X", code);
+ }
+ 
+ /*
+  * 7.18 Memory Device (Type 17)
+  */
+ 
+-static void dmi_memory_device_width(u16 code)
++static void dmi_memory_device_width(const char *attr, u16 code)
+ {
+ 	/*
+ 	 * If no memory module is present, width may be 0
+ 	 */
+ 	if (code == 0xFFFF || code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %u bits", code);
++		pr_attr(attr, "%u bits", code);
+ }
+ 
+ static void dmi_memory_device_size(u16 code)
+ {
+ 	if (code == 0)
+-		printf(" No Module Installed");
++		pr_attr("Size", "No Module Installed");
+ 	else if (code == 0xFFFF)
+-		printf(" Unknown");
++		pr_attr("Size", "Unknown");
+ 	else
+ 	{
+ 		u64 s = { .l = code & 0x7FFF };
+ 		if (!(code & 0x8000))
+ 			s.l <<= 10;
+-		dmi_print_memory_size(s, 1);
++		dmi_print_memory_size("Size", s, 1);
+ 	}
+ }
+ 
+@@ -2431,19 +2504,20 @@ static void dmi_memory_device_extended_size(u32 code)
+ 	 * as an integer without rounding
+ 	 */
+ 	if (code & 0x3FFUL)
+-		printf(" %lu MB", (unsigned long)code);
++		pr_attr("Size", "%lu MB", (unsigned long)code);
+ 	else if (code & 0xFFC00UL)
+-		printf(" %lu GB", (unsigned long)code >> 10);
++		pr_attr("Size", "%lu GB", (unsigned long)code >> 10);
+ 	else
+-		printf(" %lu TB", (unsigned long)code >> 20);
++		pr_attr("Size", "%lu TB", (unsigned long)code >> 20);
+ }
+ 
+-static void dmi_memory_voltage_value(u16 code)
++static void dmi_memory_voltage_value(const char *attr, u16 code)
+ {
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(code % 100 ? " %g V" : " %.1f V", (float)code / 1000);
++		pr_attr(attr, code % 100 ? "%g V" : "%.1f V",
++			(float)code / 1000);
+ }
+ 
+ static const char *dmi_memory_device_form_factor(u8 code)
+@@ -2476,11 +2550,11 @@ static const char *dmi_memory_device_form_factor(u8 code)
+ static void dmi_memory_device_set(u8 code)
+ {
+ 	if (code == 0)
+-		printf(" None");
++		pr_attr("Set", "None");
+ 	else if (code == 0xFF)
+-		printf(" Unknown");
++		pr_attr("Set", "Unknown");
+ 	else
+-		printf(" %u", code);
++		pr_attr("Set", "%u", code);
+ }
+ 
+ static const char *dmi_memory_device_type(u8 code)
+@@ -2547,25 +2621,29 @@ static void dmi_memory_device_type_detail(u16 code)
+ 		"Unbuffered (Unregistered)",
+ 		"LRDIMM"  /* 15 */
+ 	};
++	char list[172];		/* Update length if you touch the array above */
+ 
+ 	if ((code & 0xFFFE) == 0)
+-		printf(" None");
++		pr_attr("Type Detail", "None");
+ 	else
+ 	{
+-		int i;
++		int i, off = 0;
+ 
++		list[0] = '\0';
+ 		for (i = 1; i <= 15; i++)
+ 			if (code & (1 << i))
+-				printf(" %s", detail[i - 1]);
++				off += sprintf(list + off, off ? " %s" : "%s",
++					       detail[i - 1]);
++		pr_attr("Type Detail", list);
+ 	}
+ }
+ 
+-static void dmi_memory_device_speed(u16 code)
++static void dmi_memory_device_speed(const char *attr, u16 code)
+ {
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %u MT/s", code);
++		pr_attr(attr, "%u MT/s", code);
+ }
+ 
+ static void dmi_memory_technology(u8 code)
+@@ -2581,9 +2659,9 @@ static void dmi_memory_technology(u8 code)
+ 		"Intel Optane DC persistent memory" /* 0x07 */
+ 	};
+ 	if (code >= 0x01 && code <= 0x07)
+-		printf(" %s", technology[code - 0x01]);
++		pr_attr("Memory Technology", "%s", technology[code - 0x01]);
+ 	else
+-		printf(" %s", out_of_spec);
++		pr_attr("Memory Technology", "%s", out_of_spec);
+ }
+ 
+ static void dmi_memory_operating_mode_capability(u16 code)
+@@ -2596,49 +2674,54 @@ static void dmi_memory_operating_mode_capability(u16 code)
+ 		"Byte-accessible persistent memory",
+ 		"Block-accessible persistent memory" /* 5 */
+ 	};
++	char list[99];		/* Update length if you touch the array above */
+ 
+ 	if ((code & 0xFFFE) == 0)
+-		printf(" None");
++		pr_attr("Memory Operating Mode Capability", "None");
+ 	else {
+-		int i;
++		int i, off = 0;
+ 
++		list[0] = '\0';
+ 		for (i = 1; i <= 5; i++)
+ 			if (code & (1 << i))
+-				printf(" %s", mode[i - 1]);
++				off += sprintf(list + off, off ? " %s" : "%s",
++					       mode[i - 1]);
++		pr_attr("Memory Operating Mode Capability", list);
+ 	}
+ }
+ 
+-static void dmi_memory_manufacturer_id(u16 code)
++static void dmi_memory_manufacturer_id(const char *attr, u16 code)
+ {
+ 	/* 7.18.8 */
+ 	/* 7.18.10 */
+ 	/* LSB is 7-bit Odd Parity number of continuation codes */
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" Bank %d, Hex 0x%02X", (code & 0x7F) + 1, code >> 8);
++		pr_attr(attr, "Bank %d, Hex 0x%02X",
++			(code & 0x7F) + 1, code >> 8);
+ }
+ 
+-static void dmi_memory_product_id(u16 code)
++static void dmi_memory_product_id(const char *attr, u16 code)
+ {
+ 	/* 7.18.9 */
+ 	/* 7.18.11 */
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" 0x%04X", code);
++		pr_attr(attr, "0x%04X", code);
+ }
+ 
+-static void dmi_memory_size(u64 code)
++static void dmi_memory_size(const char *attr, u64 code)
+ {
+ 	/* 7.18.12 */
+ 	/* 7.18.13 */
+ 	if (code.h == 0xFFFFFFFF && code.l == 0xFFFFFFFF)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else if (code.h == 0x0 && code.l == 0x0)
+-		printf(" None");
++		pr_attr(attr, "None");
+ 	else
+-		dmi_print_memory_size(code, 0);
++		dmi_print_memory_size(attr, code, 0);
+ }
+ 
+ /*
+@@ -2704,17 +2787,17 @@ static const char *dmi_memory_error_operation(u8 code)
+ static void dmi_memory_error_syndrome(u32 code)
+ {
+ 	if (code == 0x00000000)
+-		printf(" Unknown");
++		pr_attr("Vendor Syndrome", "Unknown");
+ 	else
+-		printf(" 0x%08X", code);
++		pr_attr("Vendor Syndrome", "0x%08X", code);
+ }
+ 
+-static void dmi_32bit_memory_error_address(u32 code)
++static void dmi_32bit_memory_error_address(const char *attr, u32 code)
+ {
+ 	if (code == 0x80000000)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" 0x%08X", code);
++		pr_attr(attr, "0x%08X", code);
+ }
+ 
+ /*
+@@ -2724,23 +2807,23 @@ static void dmi_32bit_memory_error_address(u32 code)
+ static void dmi_mapped_address_size(u32 code)
+ {
+ 	if (code == 0)
+-		printf(" Invalid");
++		pr_attr("Range Size", "Invalid");
+ 	else
+ 	{
+ 		u64 size;
+ 
+ 		size.h = 0;
+ 		size.l = code;
+-		dmi_print_memory_size(size, 1);
++		dmi_print_memory_size("Range Size", size, 1);
+ 	}
+ }
+ 
+ static void dmi_mapped_address_extended_size(u64 start, u64 end)
+ {
+ 	if (start.h == end.h && start.l == end.l)
+-		printf(" Invalid");
++		pr_attr("Range Size", "Invalid");
+ 	else
+-		dmi_print_memory_size(u64_range(start, end), 0);
++		dmi_print_memory_size("Range Size", u64_range(start, end), 0);
+ }
+ 
+ /*
+@@ -2750,36 +2833,32 @@ static void dmi_mapped_address_extended_size(u64 start, u64 end)
+ static void dmi_mapped_address_row_position(u8 code)
+ {
+ 	if (code == 0)
+-		printf(" %s", out_of_spec);
++		pr_attr("Partition Row Position", "%s", out_of_spec);
+ 	else if (code == 0xFF)
+-		printf(" Unknown");
++		pr_attr("Partition Row Position", "Unknown");
+ 	else
+-		printf(" %u", code);
++		pr_attr("Partition Row Position", "%u", code);
+ }
+ 
+-static void dmi_mapped_address_interleave_position(u8 code, const char *prefix)
++static void dmi_mapped_address_interleave_position(u8 code)
+ {
+ 	if (code != 0)
+ 	{
+-		printf("%sInterleave Position:", prefix);
+ 		if (code == 0xFF)
+-			printf(" Unknown");
++			pr_attr("Interleave Position", "Unknown");
+ 		else
+-			printf(" %u", code);
+-		printf("\n");
++			pr_attr("Interleave Position", "%u", code);
+ 	}
+ }
+ 
+-static void dmi_mapped_address_interleaved_data_depth(u8 code, const char *prefix)
++static void dmi_mapped_address_interleaved_data_depth(u8 code)
+ {
+ 	if (code != 0)
+ 	{
+-		printf("%sInterleaved Data Depth:", prefix);
+ 		if (code == 0xFF)
+-			printf(" Unknown");
++			pr_attr("Interleaved Data Depth", "Unknown");
+ 		else
+-			printf(" %u", code);
+-		printf("\n");
++			pr_attr("Interleaved Data Depth", "%u", code);
+ 	}
+ }
+ 
+@@ -2859,25 +2938,25 @@ static const char *dmi_battery_chemistry(u8 code)
+ static void dmi_battery_capacity(u16 code, u8 multiplier)
+ {
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr("Design Capacity", "Unknown");
+ 	else
+-		printf(" %u mWh", code * multiplier);
++		pr_attr("Design Capacity", "%u mWh", code * multiplier);
+ }
+ 
+ static void dmi_battery_voltage(u16 code)
+ {
+ 	if (code == 0)
+-		printf(" Unknown");
++		pr_attr("Design Voltage", "Unknown");
+ 	else
+-		printf(" %u mV", code);
++		pr_attr("Design Voltage", "%u mV", code);
+ }
+ 
+ static void dmi_battery_maximum_error(u8 code)
+ {
+ 	if (code == 0xFF)
+-		printf(" Unknown");
++		pr_attr("Maximum Error", "Unknown");
+ 	else
+-		printf(" %u%%", code);
++		pr_attr("Maximum Error", "%u%%", code);
+ }
+ 
+ /*
+@@ -2897,20 +2976,20 @@ static const char *dmi_system_reset_boot_option(u8 code)
+ 	return option[code];
+ }
+ 
+-static void dmi_system_reset_count(u16 code)
++static void dmi_system_reset_count(const char *attr, u16 code)
+ {
+ 	if (code == 0xFFFF)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %u", code);
++		pr_attr(attr, "%u", code);
+ }
+ 
+-static void dmi_system_reset_timer(u16 code)
++static void dmi_system_reset_timer(const char *attr, u16 code)
+ {
+ 	if (code == 0xFFFF)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %u min", code);
++		pr_attr(attr, "%u min", code);
+ }
+ 
+ /*
+@@ -2935,27 +3014,32 @@ static const char *dmi_hardware_security_status(u8 code)
+ 
+ static void dmi_power_controls_power_on(const u8 *p)
+ {
++	char time[15];
++	int off = 0;
++
+ 	/* 7.26.1 */
+ 	if (dmi_bcd_range(p[0], 0x01, 0x12))
+-		printf(" %02X", p[0]);
++		off += sprintf(time + off, "%02X", p[0]);
+ 	else
+-		printf(" *");
++		off += sprintf(time + off, "*");
+ 	if (dmi_bcd_range(p[1], 0x01, 0x31))
+-		printf("-%02X", p[1]);
++		off += sprintf(time + off, "-%02X", p[1]);
+ 	else
+-		printf("-*");
++		off += sprintf(time + off, "-*");
+ 	if (dmi_bcd_range(p[2], 0x00, 0x23))
+-		printf(" %02X", p[2]);
++		off += sprintf(time + off, " %02X", p[2]);
+ 	else
+-		printf(" *");
++		off += sprintf(time + off, " *");
+ 	if (dmi_bcd_range(p[3], 0x00, 0x59))
+-		printf(":%02X", p[3]);
++		off += sprintf(time + off, ":%02X", p[3]);
+ 	else
+-		printf(":*");
++		off += sprintf(time + off, ":*");
+ 	if (dmi_bcd_range(p[4], 0x00, 0x59))
+-		printf(":%02X", p[4]);
++		off += sprintf(time + off, ":%02X", p[4]);
+ 	else
+-		printf(":*");
++		off += sprintf(time + off, ":*");
++
++	pr_attr("Next Scheduled Power-on", time);
+ }
+ 
+ /*
+@@ -3001,28 +3085,28 @@ static const char *dmi_probe_status(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_voltage_probe_value(u16 code)
++static void dmi_voltage_probe_value(const char *attr, u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %.3f V", (float)(i16)code / 1000);
++		pr_attr(attr, "%.3f V", (float)(i16)code / 1000);
+ }
+ 
+ static void dmi_voltage_probe_resolution(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr("Resolution", "Unknown");
+ 	else
+-		printf(" %.1f mV", (float)code / 10);
++		pr_attr("Resolution", "%.1f mV", (float)code / 10);
+ }
+ 
+ static void dmi_probe_accuracy(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr("Accuracy", "Unknown");
+ 	else
+-		printf(" %.2f%%", (float)code / 100);
++		pr_attr("Accuracy", "%.2f%%", (float)code / 100);
+ }
+ 
+ /*
+@@ -3058,9 +3142,9 @@ static const char *dmi_cooling_device_type(u8 code)
+ static void dmi_cooling_device_speed(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown Or Non-rotating");
++		pr_attr("Nominal Speed", "Unknown Or Non-rotating");
+ 	else
+-		printf(" %u rpm", code);
++		pr_attr("Nominal Speed", "%u rpm", code);
+ }
+ 
+ /*
+@@ -3093,40 +3177,40 @@ static const char *dmi_temperature_probe_location(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_temperature_probe_value(u16 code)
++static void dmi_temperature_probe_value(const char *attr, u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %.1f deg C", (float)(i16)code / 10);
++		pr_attr(attr, "%.1f deg C", (float)(i16)code / 10);
+ }
+ 
+ static void dmi_temperature_probe_resolution(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr("Resolution", "Unknown");
+ 	else
+-		printf(" %.3f deg C", (float)code / 1000);
++		pr_attr("Resolution", "%.3f deg C", (float)code / 1000);
+ }
+ 
+ /*
+  * 7.30 Electrical Current Probe (Type 29)
+  */
+ 
+-static void dmi_current_probe_value(u16 code)
++static void dmi_current_probe_value(const char *attr, u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" %.3f A", (float)(i16)code / 1000);
++		pr_attr(attr, "%.3f A", (float)(i16)code / 1000);
+ }
+ 
+ static void dmi_current_probe_resolution(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr("Resolution", "Unknown");
+ 	else
+-		printf(" %.1f mA", (float)code / 10);
++		pr_attr("Resolution", "%.1f mA", (float)code / 10);
+ }
+ 
+ /*
+@@ -3160,12 +3244,12 @@ static const char *dmi_system_boot_status(u8 code)
+  * 7.34 64-bit Memory Error Information (Type 33)
+  */
+ 
+-static void dmi_64bit_memory_error_address(u64 code)
++static void dmi_64bit_memory_error_address(const char *attr, u64 code)
+ {
+ 	if (code.h == 0x80000000 && code.l == 0x00000000)
+-		printf(" Unknown");
++		pr_attr(attr, "Unknown");
+ 	else
+-		printf(" 0x%08X%08X", code.h, code.l);
++		pr_attr(attr, "0x%08X%08X", code.h, code.l);
+ }
+ 
+ /*
+@@ -3253,17 +3337,20 @@ static const char *dmi_memory_channel_type(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_memory_channel_devices(u8 count, const u8 *p, const char *prefix)
++static void dmi_memory_channel_devices(u8 count, const u8 *p)
+ {
++	char attr[18];
+ 	int i;
+ 
+ 	for (i = 1; i <= count; i++)
+ 	{
+-		printf("%sDevice %u Load: %u\n",
+-			prefix, i, p[3 * i]);
++		sprintf(attr, "Device %hu Load", i);
++		pr_attr(attr, "%u", p[3 * i]);
+ 		if (!(opt.flags & FLAG_QUIET))
+-			printf("%sDevice %u Handle: 0x%04X\n",
+-				prefix, i, WORD(p + 3 * i + 1));
++		{
++			sprintf(attr, "Device %hu Handle", i);
++			pr_attr(attr, "0x%04X", WORD(p + 3 * i + 1));
++		}
+ 	}
+ }
+ 
+@@ -3291,12 +3378,13 @@ static void dmi_ipmi_base_address(u8 type, const u8 *p, u8 lsb)
+ {
+ 	if (type == 0x04) /* SSIF */
+ 	{
+-		printf("0x%02X (SMBus)", (*p) >> 1);
++		pr_attr("Base Address", "0x%02X (SMBus)", (*p) >> 1);
+ 	}
+ 	else
+ 	{
+ 		u64 address = QWORD(p);
+-		printf("0x%08X%08X (%s)", address.h, (address.l & ~1) | lsb,
++		pr_attr("Base Address", "0x%08X%08X (%s)",
++			address.h, (address.l & ~1) | lsb,
+ 			address.l & 1 ? "I/O" : "Memory-mapped");
+ 	}
+ }
+@@ -3322,9 +3410,9 @@ static const char *dmi_ipmi_register_spacing(u8 code)
+ static void dmi_power_supply_power(u16 code)
+ {
+ 	if (code == 0x8000)
+-		printf(" Unknown");
++		pr_attr("Max Power Capacity", "Unknown");
+ 	else
+-		printf(" %u W", (unsigned int)code);
++		pr_attr("Max Power Capacity", "%u W", (unsigned int)code);
+ }
+ 
+ static const char *dmi_power_supply_type(u8 code)
+@@ -3387,7 +3475,7 @@ static const char *dmi_power_supply_range_switching(u8 code)
+  * whether it's worth the effort.
+  */
+ 
+-static void dmi_additional_info(const struct dmi_header *h, const char *prefix)
++static void dmi_additional_info(const struct dmi_header *h)
+ {
+ 	u8 *p = h->data + 4;
+ 	u8 count = *p++;
+@@ -3403,30 +3491,28 @@ static void dmi_additional_info(const struct dmi_header *h, const char *prefix)
+ 		length = p[0x00];
+ 		if (length < 0x05 || h->length < offset + length) break;
+ 
+-		printf("%s\tReferenced Handle: 0x%04x\n",
+-			prefix, WORD(p + 0x01));
+-		printf("%s\tReferenced Offset: 0x%02x\n",
+-			prefix, p[0x03]);
+-		printf("%s\tString: %s\n",
+-			prefix, dmi_string(h, p[0x04]));
++		pr_attr("Referenced Handle", "0x%04x",
++			WORD(p + 0x01));
++		pr_attr("Referenced Offset", "0x%02x",
++			p[0x03]);
++		pr_attr("String", "%s",
++			dmi_string(h, p[0x04]));
+ 
+-		printf("%s\tValue: ", prefix);
+ 		switch (length - 0x05)
+ 		{
+ 			case 1:
+-				printf("0x%02x", p[0x05]);
++				pr_attr("Value", "0x%02x", p[0x05]);
+ 				break;
+ 			case 2:
+-				printf("0x%04x", WORD(p + 0x05));
++				pr_attr("Value", "0x%04x", WORD(p + 0x05));
+ 				break;
+ 			case 4:
+-				printf("0x%08x", DWORD(p + 0x05));
++				pr_attr("Value", "0x%08x", DWORD(p + 0x05));
+ 				break;
+ 			default:
+-				printf("Unexpected size");
++				pr_attr("Value", "Unexpected size");
+ 				break;
+ 		}
+-		printf("\n");
+ 
+ 		p += length;
+ 		offset += length;
+@@ -3580,8 +3666,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	 * we can just pick a sufficiently recent version here.
+ 	 */
+ 	printf("%s\tService UUID: ", prefix);
+-	dmi_system_uuid(&rdata[0], 0x311);
+-	printf("\n");
++	dmi_system_uuid(NULL, &rdata[0], 0x311);	/* FIXME */
+ 
+ 	/*
+ 	 * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type
+@@ -3710,7 +3795,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 		return;
+ 
+ 	type = data[0x4];
+-	printf("%sHost Interface Type: %s\n", prefix,
++	pr_attr("Host Interface Type", "%s",
+ 		dmi_management_controller_host_type(type));
+ 
+ 	/*
+@@ -3725,17 +3810,17 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 		/* DSP0270: 8.3 Table 2: Device Type */
+ 		type = data[0x6];
+ 
+-		printf("%sDevice Type: %s\n", prefix,
++		pr_attr("Device Type", "%s",
+ 			dmi_parse_device_type(type));
+ 		if (type == 0x2 && len >= 5)
+ 		{
+ 			/* USB Device Type - need at least 6 bytes */
+ 			u8 *usbdata = &data[0x7];
+ 			/* USB Device Descriptor: idVendor */
+-			printf("%sidVendor: 0x%04x\n", prefix,
++			pr_attr("idVendor", "0x%04x",
+ 				WORD(&usbdata[0x0]));
+ 			/* USB Device Descriptor: idProduct */
+-			printf("%sidProduct: 0x%04x\n", prefix,
++			pr_attr("idProduct", "0x%04x",
+ 				WORD(&usbdata[0x2]));
+ 			/*
+ 			 * USB Serial number is here, but its useless, don't
+@@ -3747,16 +3832,16 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			/* PCI Device Type - Need at least 8 bytes */
+ 			u8 *pcidata = &data[0x7];
+ 			/* PCI Device Descriptor: VendorID */
+-			printf("%sVendorID: 0x%04x\n", prefix,
++			pr_attr("VendorID", "0x%04x",
+ 				WORD(&pcidata[0x0]));
+ 			/* PCI Device Descriptor: DeviceID */
+-			printf("%sDeviceID: 0x%04x\n", prefix,
++			pr_attr("DeviceID", "0x%04x",
+ 				WORD(&pcidata[0x2]));
+ 			/* PCI Device Descriptor: PCI SubvendorID */
+-			printf("%sSubVendorID: 0x%04x\n", prefix,
++			pr_attr("SubVendorID", "0x%04x",
+ 				WORD(&pcidata[0x4]));
+ 			/* PCI Device Descriptor: PCI SubdeviceID */
+-			printf("%sSubDeviceID: 0x%04x\n", prefix,
++			pr_attr("SubDeviceID", "0x%04x",
+ 				WORD(&pcidata[0x6]));
+ 		}
+ 		else if (type == 0x4 && len >= 5)
+@@ -3764,8 +3849,8 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 			/* OEM Device Type - Need at least 4 bytes */
+ 			u8 *oemdata = &data[0x7];
+ 			/* OEM Device Descriptor: IANA */
+-			printf("%sVendor ID: 0x%02x:0x%02x:0x%02x:0x%02x\n",
+-				prefix, oemdata[0x0], oemdata[0x1],
++			pr_attr("Vendor ID", "0x%02x:0x%02x:0x%02x:0x%02x",
++				oemdata[0x0], oemdata[0x1],
+ 				oemdata[0x2], oemdata[0x3]);
+ 		}
+ 		/* Don't mess with unknown types for now */
+@@ -3848,7 +3933,7 @@ static void dmi_tpm_vendor_id(const u8 *p)
+ 	/* Terminate the string */
+ 	vendor_id[i] = '\0';
+ 
+-	printf(" %s", vendor_id);
++	pr_attr("Vendor ID", "%s", vendor_id);
+ }
+ 
+ static void dmi_tpm_characteristics(u64 code, const char *prefix)
+@@ -3894,11 +3979,11 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 0: /* 7.1 BIOS Information */
+ 			pr_handle_name("BIOS Information");
+ 			if (h->length < 0x12) break;
+-			printf("\tVendor: %s\n",
++			pr_attr("Vendor", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tVersion: %s\n",
++			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x05]));
+-			printf("\tRelease Date: %s\n",
++			pr_attr("Release Date", "%s",
+ 				dmi_string(h, data[0x08]));
+ 			/*
+ 			 * On IA-64, the BIOS base address will read 0 because
+@@ -3907,15 +3992,11 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			 */
+ 			if (WORD(data + 0x06) != 0)
+ 			{
+-				printf("\tAddress: 0x%04X0\n",
++				pr_attr("Address", "0x%04X0",
+ 					WORD(data + 0x06));
+-				printf("\tRuntime Size:");
+ 				dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4);
+-				printf("\n");
+ 			}
+-			printf("\tROM Size:");
+ 			dmi_bios_rom_size(data[0x09], h->length < 0x1A ? 16 : WORD(data + 0x18));
+-			printf("\n");
+ 			printf("\tCharacteristics:\n");
+ 			dmi_bios_characteristics(QWORD(data + 0x0A), "\t\t");
+ 			if (h->length < 0x13) break;
+@@ -3924,61 +4005,59 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			dmi_bios_characteristics_x2(data[0x13], "\t\t");
+ 			if (h->length < 0x18) break;
+ 			if (data[0x14] != 0xFF && data[0x15] != 0xFF)
+-				printf("\tBIOS Revision: %u.%u\n",
++				pr_attr("BIOS Revision", "%u.%u",
+ 					data[0x14], data[0x15]);
+ 			if (data[0x16] != 0xFF && data[0x17] != 0xFF)
+-				printf("\tFirmware Revision: %u.%u\n",
++				pr_attr("Firmware Revision", "%u.%u",
+ 					data[0x16], data[0x17]);
+ 			break;
+ 
+ 		case 1: /* 7.2 System Information */
+ 			pr_handle_name("System Information");
+ 			if (h->length < 0x08) break;
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tProduct Name: %s\n",
++			pr_attr("Product Name", "%s",
+ 				dmi_string(h, data[0x05]));
+-			printf("\tVersion: %s\n",
++			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x06]));
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x07]));
+ 			if (h->length < 0x19) break;
+-			printf("\tUUID: ");
+-			dmi_system_uuid(data + 0x08, ver);
+-			printf("\n");
+-			printf("\tWake-up Type: %s\n",
++			dmi_system_uuid("UUID", data + 0x08, ver);
++			pr_attr("Wake-up Type", "%s",
+ 				dmi_system_wake_up_type(data[0x18]));
+ 			if (h->length < 0x1B) break;
+-			printf("\tSKU Number: %s\n",
++			pr_attr("SKU Number", "%s",
+ 				dmi_string(h, data[0x19]));
+-			printf("\tFamily: %s\n",
++			pr_attr("Family", "%s",
+ 				dmi_string(h, data[0x1A]));
+ 			break;
+ 
+ 		case 2: /* 7.3 Base Board Information */
+ 			pr_handle_name("Base Board Information");
+ 			if (h->length < 0x08) break;
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tProduct Name: %s\n",
++			pr_attr("Product Name", "%s",
+ 				dmi_string(h, data[0x05]));
+-			printf("\tVersion: %s\n",
++			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x06]));
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x07]));
+ 			if (h->length < 0x09) break;
+-			printf("\tAsset Tag: %s\n",
++			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x08]));
+ 			if (h->length < 0x0A) break;
+ 			printf("\tFeatures:");
+ 			dmi_base_board_features(data[0x09], "\t\t");
+ 			if (h->length < 0x0E) break;
+-			printf("\tLocation In Chassis: %s\n",
++			pr_attr("Location In Chassis", "%s",
+ 				dmi_string(h, data[0x0A]));
+ 			if (!(opt.flags & FLAG_QUIET))
+-				printf("\tChassis Handle: 0x%04X\n",
++				pr_attr("Chassis Handle", "0x%04X",
+ 					WORD(data + 0x0B));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_base_board_type(data[0x0D]));
+ 			if (h->length < 0x0F) break;
+ 			if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+@@ -3989,109 +4068,94 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 3: /* 7.4 Chassis Information */
+ 			pr_handle_name("Chassis Information");
+ 			if (h->length < 0x09) break;
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_chassis_type(data[0x05]));
+-			printf("\tLock: %s\n",
++			pr_attr("Lock", "%s",
+ 				dmi_chassis_lock(data[0x05] >> 7));
+-			printf("\tVersion: %s\n",
++			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x06]));
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x07]));
+-			printf("\tAsset Tag: %s\n",
++			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x08]));
+ 			if (h->length < 0x0D) break;
+-			printf("\tBoot-up State: %s\n",
++			pr_attr("Boot-up State", "%s",
+ 				dmi_chassis_state(data[0x09]));
+-			printf("\tPower Supply State: %s\n",
++			pr_attr("Power Supply State", "%s",
+ 				dmi_chassis_state(data[0x0A]));
+-			printf("\tThermal State: %s\n",
++			pr_attr("Thermal State", "%s",
+ 				dmi_chassis_state(data[0x0B]));
+-			printf("\tSecurity Status: %s\n",
++			pr_attr("Security Status", "%s",
+ 				dmi_chassis_security_status(data[0x0C]));
+ 			if (h->length < 0x11) break;
+-			printf("\tOEM Information: 0x%08X\n",
++			pr_attr("OEM Information", "0x%08X",
+ 				DWORD(data + 0x0D));
+ 			if (h->length < 0x13) break;
+-			printf("\tHeight:");
+ 			dmi_chassis_height(data[0x11]);
+-			printf("\n");
+-			printf("\tNumber Of Power Cords:");
+ 			dmi_chassis_power_cords(data[0x12]);
+-			printf("\n");
+ 			if (h->length < 0x15) break;
+ 			if (h->length < 0x15 + data[0x13] * data[0x14]) break;
+ 			dmi_chassis_elements(data[0x13], data[0x14], data + 0x15, "\t");
+ 			if (h->length < 0x16 + data[0x13] * data[0x14]) break;
+-			printf("\tSKU Number: %s\n",
++			pr_attr("SKU Number", "%s",
+ 				dmi_string(h, data[0x15 + data[0x13] * data[0x14]]));
+ 			break;
+ 
+ 		case 4: /* 7.5 Processor Information */
+ 			pr_handle_name("Processor Information");
+ 			if (h->length < 0x1A) break;
+-			printf("\tSocket Designation: %s\n",
++			pr_attr("Socket Designation", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_processor_type(data[0x05]));
+-			printf("\tFamily: %s\n",
++			pr_attr("Family", "%s",
+ 				dmi_processor_family(h, ver));
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x07]));
+ 			dmi_processor_id(h, "\t");
+-			printf("\tVersion: %s\n",
++			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x10]));
+-			printf("\tVoltage:");
+-			dmi_processor_voltage(data[0x11]);
+-			printf("\n");
+-			printf("\tExternal Clock: ");
+-			dmi_processor_frequency(data + 0x12);
+-			printf("\n");
+-			printf("\tMax Speed: ");
+-			dmi_processor_frequency(data + 0x14);
+-			printf("\n");
+-			printf("\tCurrent Speed: ");
+-			dmi_processor_frequency(data + 0x16);
+-			printf("\n");
++			dmi_processor_voltage("Voltage", data[0x11]);
++			dmi_processor_frequency("External Clock", data + 0x12);
++			dmi_processor_frequency("Max Speed", data + 0x14);
++			dmi_processor_frequency("Current Speed", data + 0x16);
+ 			if (data[0x18] & (1 << 6))
+-				printf("\tStatus: Populated, %s\n",
++				pr_attr("Status", "Populated, %s",
+ 					dmi_processor_status(data[0x18] & 0x07));
+ 			else
+-				printf("\tStatus: Unpopulated\n");
+-			printf("\tUpgrade: %s\n",
++				pr_attr("Status", "Unpopulated");
++			pr_attr("Upgrade", "%s",
+ 				dmi_processor_upgrade(data[0x19]));
+ 			if (h->length < 0x20) break;
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+-				printf("\tL1 Cache Handle:");
+-				dmi_processor_cache(WORD(data + 0x1A), "L1", ver);
+-				printf("\n");
+-				printf("\tL2 Cache Handle:");
+-				dmi_processor_cache(WORD(data + 0x1C), "L2", ver);
+-				printf("\n");
+-				printf("\tL3 Cache Handle:");
+-				dmi_processor_cache(WORD(data + 0x1E), "L3", ver);
+-				printf("\n");
++				dmi_processor_cache("L1 Cache Handle",
++						    WORD(data + 0x1A), "L1", ver);
++				dmi_processor_cache("L2 Cache Handle",
++						    WORD(data + 0x1C), "L2", ver);
++				dmi_processor_cache("L3 Cache Handle",
++						    WORD(data + 0x1E), "L3", ver);
+ 			}
+ 			if (h->length < 0x23) break;
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x20]));
+-			printf("\tAsset Tag: %s\n",
++			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x21]));
+-			printf("\tPart Number: %s\n",
++			pr_attr("Part Number", "%s",
+ 				dmi_string(h, data[0x22]));
+ 			if (h->length < 0x28) break;
+ 			if (data[0x23] != 0)
+-				printf("\tCore Count: %u\n",
++				pr_attr("Core Count", "%u",
+ 					h->length >= 0x2C && data[0x23] == 0xFF ?
+ 					WORD(data + 0x2A) : data[0x23]);
+ 			if (data[0x24] != 0)
+-				printf("\tCore Enabled: %u\n",
++				pr_attr("Core Enabled", "%u",
+ 					h->length >= 0x2E && data[0x24] == 0xFF ?
+ 					WORD(data + 0x2C) : data[0x24]);
+ 			if (data[0x25] != 0)
+-				printf("\tThread Count: %u\n",
++				pr_attr("Thread Count", "%u",
+ 					h->length >= 0x30 && data[0x25] == 0xFF ?
+ 					WORD(data + 0x2E) : data[0x25]);
+ 			printf("\tCharacteristics:");
+@@ -4101,26 +4165,24 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 5: /* 7.6 Memory Controller Information */
+ 			pr_handle_name("Memory Controller Information");
+ 			if (h->length < 0x0F) break;
+-			printf("\tError Detecting Method: %s\n",
++			pr_attr("Error Detecting Method", "%s",
+ 				dmi_memory_controller_ed_method(data[0x04]));
+ 			printf("\tError Correcting Capabilities:");
+ 			dmi_memory_controller_ec_capabilities(data[0x05], "\t\t");
+-			printf("\tSupported Interleave: %s\n",
++			pr_attr("Supported Interleave", "%s",
+ 				dmi_memory_controller_interleave(data[0x06]));
+-			printf("\tCurrent Interleave: %s\n",
++			pr_attr("Current Interleave", "%s",
+ 				dmi_memory_controller_interleave(data[0x07]));
+-			printf("\tMaximum Memory Module Size: %u MB\n",
++			pr_attr("Maximum Memory Module Size", "%u MB",
+ 				1 << data[0x08]);
+-			printf("\tMaximum Total Memory Size: %u MB\n",
++			pr_attr("Maximum Total Memory Size", "%u MB",
+ 				data[0x0E] * (1 << data[0x08]));
+ 			printf("\tSupported Speeds:");
+ 			dmi_memory_controller_speeds(WORD(data + 0x09), "\t\t");
+ 			printf("\tSupported Memory Types:");
+ 			dmi_memory_module_types(WORD(data + 0x0B), "\n\t\t");
+ 			printf("\n");
+-			printf("\tMemory Module Voltage:");
+-			dmi_processor_voltage(data[0x0D]);
+-			printf("\n");
++			dmi_processor_voltage("Memory Module Voltage", data[0x0D]);
+ 			if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+ 			dmi_memory_controller_slots(data[0x0E], data + 0x0F, "\t");
+ 			if (h->length < 0x10 + data[0x0E] * sizeof(u16)) break;
+@@ -4131,52 +4193,39 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 6: /* 7.7 Memory Module Information */
+ 			pr_handle_name("Memory Module Information");
+ 			if (h->length < 0x0C) break;
+-			printf("\tSocket Designation: %s\n",
++			pr_attr("Socket Designation", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tBank Connections:");
+ 			dmi_memory_module_connections(data[0x05]);
+-			printf("\n");
+-			printf("\tCurrent Speed:");
+-			dmi_memory_module_speed(data[0x06]);
+-			printf("\n");
++			dmi_memory_module_speed("Current Speed", data[0x06]);
+ 			printf("\tType:");
+ 			dmi_memory_module_types(WORD(data + 0x07), " ");
+ 			printf("\n");
+-			printf("\tInstalled Size:");
+-			dmi_memory_module_size(data[0x09]);
+-			printf("\n");
+-			printf("\tEnabled Size:");
+-			dmi_memory_module_size(data[0x0A]);
+-			printf("\n");
+-			printf("\tError Status:");
++			dmi_memory_module_size("Installed Size", data[0x09]);
++			dmi_memory_module_size("Enabled Size", data[0x0A]);
+ 			dmi_memory_module_error(data[0x0B]);
+ 			break;
+ 
+ 		case 7: /* 7.8 Cache Information */
+ 			pr_handle_name("Cache Information");
+ 			if (h->length < 0x0F) break;
+-			printf("\tSocket Designation: %s\n",
++			pr_attr("Socket Designation", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tConfiguration: %s, %s, Level %u\n",
++			pr_attr("Configuration", "%s, %s, Level %u",
+ 				WORD(data + 0x05) & 0x0080 ? "Enabled" : "Disabled",
+ 				WORD(data + 0x05) & 0x0008 ? "Socketed" : "Not Socketed",
+ 				(WORD(data + 0x05) & 0x0007) + 1);
+-			printf("\tOperational Mode: %s\n",
++			pr_attr("Operational Mode", "%s",
+ 				dmi_cache_mode((WORD(data + 0x05) >> 8) & 0x0003));
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_cache_location((WORD(data + 0x05) >> 5) & 0x0003));
+-			printf("\tInstalled Size:");
+ 			if (h->length >= 0x1B)
+-				dmi_cache_size_2(DWORD(data + 0x17));
++				dmi_cache_size_2("Installed Size", DWORD(data + 0x17));
+ 			else
+-				dmi_cache_size(WORD(data + 0x09));
+-			printf("\n");
+-			printf("\tMaximum Size:");
++				dmi_cache_size("Installed Size", WORD(data + 0x09));
+ 			if (h->length >= 0x17)
+-				dmi_cache_size_2(DWORD(data + 0x13));
++				dmi_cache_size_2("Maximum Size", DWORD(data + 0x13));
+ 			else
+-				dmi_cache_size(WORD(data + 0x07));
+-			printf("\n");
++				dmi_cache_size("Maximum Size", WORD(data + 0x07));
+ 			printf("\tSupported SRAM Types:");
+ 			dmi_cache_types(WORD(data + 0x0B), "\n\t\t");
+ 			printf("\n");
+@@ -4184,73 +4233,71 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			dmi_cache_types(WORD(data + 0x0D), " ");
+ 			printf("\n");
+ 			if (h->length < 0x13) break;
+-			printf("\tSpeed:");
+-			dmi_memory_module_speed(data[0x0F]);
+-			printf("\n");
+-			printf("\tError Correction Type: %s\n",
++			dmi_memory_module_speed("Speed", data[0x0F]);
++			pr_attr("Error Correction Type", "%s",
+ 				dmi_cache_ec_type(data[0x10]));
+-			printf("\tSystem Type: %s\n",
++			pr_attr("System Type", "%s",
+ 				dmi_cache_type(data[0x11]));
+-			printf("\tAssociativity: %s\n",
++			pr_attr("Associativity", "%s",
+ 				dmi_cache_associativity(data[0x12]));
+ 			break;
+ 
+ 		case 8: /* 7.9 Port Connector Information */
+ 			pr_handle_name("Port Connector Information");
+ 			if (h->length < 0x09) break;
+-			printf("\tInternal Reference Designator: %s\n",
++			pr_attr("Internal Reference Designator", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tInternal Connector Type: %s\n",
++			pr_attr("Internal Connector Type", "%s",
+ 				dmi_port_connector_type(data[0x05]));
+-			printf("\tExternal Reference Designator: %s\n",
++			pr_attr("External Reference Designator", "%s",
+ 				dmi_string(h, data[0x06]));
+-			printf("\tExternal Connector Type: %s\n",
++			pr_attr("External Connector Type", "%s",
+ 				dmi_port_connector_type(data[0x07]));
+-			printf("\tPort Type: %s\n",
++			pr_attr("Port Type", "%s",
+ 				dmi_port_type(data[0x08]));
+ 			break;
+ 
+ 		case 9: /* 7.10 System Slots */
+ 			pr_handle_name("System Slot Information");
+ 			if (h->length < 0x0C) break;
+-			printf("\tDesignation: %s\n",
++			pr_attr("Designation", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tType: %s%s\n",
++			pr_attr("Type", "%s%s",
+ 				dmi_slot_bus_width(data[0x06]),
+ 				dmi_slot_type(data[0x05]));
+-			printf("\tCurrent Usage: %s\n",
++			pr_attr("Current Usage", "%s",
+ 				dmi_slot_current_usage(data[0x07]));
+-			printf("\tLength: %s\n",
++			pr_attr("Length", "%s",
+ 				dmi_slot_length(data[0x08]));
+-			dmi_slot_id(data[0x09], data[0x0A], data[0x05], "\t");
++			dmi_slot_id(data[0x09], data[0x0A], data[0x05]);
+ 			printf("\tCharacteristics:");
+ 			if (h->length < 0x0D)
+ 				dmi_slot_characteristics(data[0x0B], 0x00, "\t\t");
+ 			else
+ 				dmi_slot_characteristics(data[0x0B], data[0x0C], "\t\t");
+ 			if (h->length < 0x11) break;
+-			dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10], "\t");
++			dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10]);
+ 			if (h->length < 0x13) break;
+-			printf("\tData Bus Width: %u\n", data[0x11]);
+-			printf("\tPeer Devices: %u\n", data[0x12]);
++			pr_attr("Data Bus Width", "%u", data[0x11]);
++			pr_attr("Peer Devices", "%u", data[0x12]);
+ 			if (h->length - 0x13 >= data[0x12] * 5)
+-				dmi_slot_peers(data[0x12], data + 0x13, "\t");
++				dmi_slot_peers(data[0x12], data + 0x13);
+ 			break;
+ 
+ 		case 10: /* 7.11 On Board Devices Information */
+-			dmi_on_board_devices(h, "");
++			dmi_on_board_devices(h);
+ 			break;
+ 
+ 		case 11: /* 7.12 OEM Strings */
+ 			pr_handle_name("OEM Strings");
+ 			if (h->length < 0x05) break;
+-			dmi_oem_strings(h, "\t");
++			dmi_oem_strings(h);
+ 			break;
+ 
+ 		case 12: /* 7.13 System Configuration Options */
+ 			pr_handle_name("System Configuration Options");
+ 			if (h->length < 0x05) break;
+-			dmi_system_configuration_options(h, "\t");
++			dmi_system_configuration_options(h);
+ 			break;
+ 
+ 		case 13: /* 7.14 BIOS Language Information */
+@@ -4258,19 +4305,19 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x16) break;
+ 			if (ver >= 0x0201)
+ 			{
+-				printf("\tLanguage Description Format: %s\n",
++				pr_attr("Language Description Format", "%s",
+ 					dmi_bios_language_format(data[0x05]));
+ 			}
+ 			printf("\tInstallable Languages: %u\n", data[0x04]);
+ 			dmi_bios_languages(h, "\t\t");
+-			printf("\tCurrently Installed Language: %s\n",
++			pr_attr("Currently Installed Language", "%s",
+ 				dmi_string(h, data[0x15]));
+ 			break;
+ 
+ 		case 14: /* 7.15 Group Associations */
+ 			pr_handle_name("Group Associations");
+ 			if (h->length < 0x05) break;
+-			printf("\tName: %s\n",
++			pr_attr("Name", "%s",
+ 				dmi_string(h, data[0x04]));
+ 			printf("\tItems: %u\n",
+ 				(h->length - 0x05) / 3);
+@@ -4280,51 +4327,47 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 15: /* 7.16 System Event Log */
+ 			pr_handle_name("System Event Log");
+ 			if (h->length < 0x14) break;
+-			printf("\tArea Length: %u bytes\n",
++			pr_attr("Area Length", "%u bytes",
+ 				WORD(data + 0x04));
+-			printf("\tHeader Start Offset: 0x%04X\n",
++			pr_attr("Header Start Offset", "0x%04X",
+ 				WORD(data + 0x06));
+ 			if (WORD(data + 0x08) - WORD(data + 0x06))
+-				printf("\tHeader Length: %u byte%s\n",
++				pr_attr("Header Length", "%u byte%s",
+ 					WORD(data + 0x08) - WORD(data + 0x06),
+ 					WORD(data + 0x08) - WORD(data + 0x06) > 1 ? "s" : "");
+-			printf("\tData Start Offset: 0x%04X\n",
++			pr_attr("Data Start Offset", "0x%04X",
+ 				WORD(data + 0x08));
+-			printf("\tAccess Method: %s\n",
++			pr_attr("Access Method", "%s",
+ 				dmi_event_log_method(data[0x0A]));
+-			printf("\tAccess Address:");
+ 			dmi_event_log_address(data[0x0A], data + 0x10);
+-			printf("\n");
+-			printf("\tStatus:");
+ 			dmi_event_log_status(data[0x0B]);
+-			printf("\n");
+-			printf("\tChange Token: 0x%08X\n",
++			pr_attr("Change Token", "0x%08X",
+ 				DWORD(data + 0x0C));
+ 			if (h->length < 0x17) break;
+-			printf("\tHeader Format: %s\n",
++			pr_attr("Header Format", "%s",
+ 				dmi_event_log_header_type(data[0x14]));
+-			printf("\tSupported Log Type Descriptors: %u\n",
++			pr_attr("Supported Log Type Descriptors", "%u",
+ 				data[0x15]);
+ 			if (h->length < 0x17 + data[0x15] * data[0x16]) break;
+-			dmi_event_log_descriptors(data[0x15], data[0x16], data + 0x17, "\t");
++			dmi_event_log_descriptors(data[0x15], data[0x16], data + 0x17);
+ 			break;
+ 
+ 		case 16: /* 7.17 Physical Memory Array */
+ 			pr_handle_name("Physical Memory Array");
+ 			if (h->length < 0x0F) break;
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_memory_array_location(data[0x04]));
+-			printf("\tUse: %s\n",
++			pr_attr("Use", "%s",
+ 				dmi_memory_array_use(data[0x05]));
+-			printf("\tError Correction Type: %s\n",
++			pr_attr("Error Correction Type", "%s",
+ 				dmi_memory_array_ec_type(data[0x06]));
+-			printf("\tMaximum Capacity:");
+ 			if (DWORD(data + 0x07) == 0x80000000)
+ 			{
+ 				if (h->length < 0x17)
+-					printf(" Unknown");
++					pr_attr("Maximum Capacity", "Unknown");
+ 				else
+-					dmi_print_memory_size(QWORD(data + 0x0F), 0);
++					dmi_print_memory_size("Maximum Capacity",
++							      QWORD(data + 0x0F), 0);
+ 			}
+ 			else
+ 			{
+@@ -4332,16 +4375,12 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 
+ 				capacity.h = 0;
+ 				capacity.l = DWORD(data + 0x07);
+-				dmi_print_memory_size(capacity, 1);
++				dmi_print_memory_size("Maximum Capacity",
++						      capacity, 1);
+ 			}
+-			printf("\n");
+ 			if (!(opt.flags & FLAG_QUIET))
+-			{
+-				printf("\tError Information Handle:");
+ 				dmi_memory_array_error_handle(WORD(data + 0x0B));
+-				printf("\n");
+-			}
+-			printf("\tNumber Of Devices: %u\n",
++			pr_attr("Number Of Devices", "%u",
+ 				WORD(data + 0x0D));
+ 			break;
+ 
+@@ -4350,132 +4389,91 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x15) break;
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+-				printf("\tArray Handle: 0x%04X\n",
++				pr_attr("Array Handle", "0x%04X",
+ 					WORD(data + 0x04));
+-				printf("\tError Information Handle:");
+ 				dmi_memory_array_error_handle(WORD(data + 0x06));
+-				printf("\n");
+ 			}
+-			printf("\tTotal Width:");
+-			dmi_memory_device_width(WORD(data + 0x08));
+-			printf("\n");
+-			printf("\tData Width:");
+-			dmi_memory_device_width(WORD(data + 0x0A));
+-			printf("\n");
+-			printf("\tSize:");
++			dmi_memory_device_width("Total Width", WORD(data + 0x08));
++			dmi_memory_device_width("Data Width", WORD(data + 0x0A));
+ 			if (h->length >= 0x20 && WORD(data + 0x0C) == 0x7FFF)
+ 				dmi_memory_device_extended_size(DWORD(data + 0x1C));
+ 			else
+ 				dmi_memory_device_size(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tForm Factor: %s\n",
++			pr_attr("Form Factor", "%s",
+ 				dmi_memory_device_form_factor(data[0x0E]));
+-			printf("\tSet:");
+ 			dmi_memory_device_set(data[0x0F]);
+-			printf("\n");
+-			printf("\tLocator: %s\n",
++			pr_attr("Locator", "%s",
+ 				dmi_string(h, data[0x10]));
+-			printf("\tBank Locator: %s\n",
++			pr_attr("Bank Locator", "%s",
+ 				dmi_string(h, data[0x11]));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_memory_device_type(data[0x12]));
+-			printf("\tType Detail:");
+ 			dmi_memory_device_type_detail(WORD(data + 0x13));
+-			printf("\n");
+ 			if (h->length < 0x17) break;
+-			printf("\tSpeed:");
+-			dmi_memory_device_speed(WORD(data + 0x15));
+-			printf("\n");
++			dmi_memory_device_speed("Speed", WORD(data + 0x15));
+ 			if (h->length < 0x1B) break;
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x17]));
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x18]));
+-			printf("\tAsset Tag: %s\n",
++			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x19]));
+-			printf("\tPart Number: %s\n",
++			pr_attr("Part Number", "%s",
+ 				dmi_string(h, data[0x1A]));
+ 			if (h->length < 0x1C) break;
+-			printf("\tRank: ");
+ 			if ((data[0x1B] & 0x0F) == 0)
+-				printf("Unknown");
++				pr_attr("Rank", "Unknown");
+ 			else
+-				printf("%u", data[0x1B] & 0x0F);
+-			printf("\n");
++				pr_attr("Rank", "%u", data[0x1B] & 0x0F);
+ 			if (h->length < 0x22) break;
+-			printf("\tConfigured Memory Speed:");
+-			dmi_memory_device_speed(WORD(data + 0x20));
+-			printf("\n");
++			dmi_memory_device_speed("Configured Memory Speed",
++						WORD(data + 0x20));
+ 			if (h->length < 0x28) break;
+-			printf("\tMinimum Voltage:");
+-			dmi_memory_voltage_value(WORD(data + 0x22));
+-			printf("\n");
+-			printf("\tMaximum Voltage:");
+-			dmi_memory_voltage_value(WORD(data + 0x24));
+-			printf("\n");
+-			printf("\tConfigured Voltage:");
+-			dmi_memory_voltage_value(WORD(data + 0x26));
+-			printf("\n");
++			dmi_memory_voltage_value("Minimum Voltage",
++						 WORD(data + 0x22));
++			dmi_memory_voltage_value("Maximum Voltage",
++						 WORD(data + 0x24));
++			dmi_memory_voltage_value("Configured Voltage",
++						 WORD(data + 0x26));
+ 			if (h->length < 0x34) break;
+-			printf("\tMemory Technology:");
+ 			dmi_memory_technology(data[0x28]);
+-			printf("\n");
+-			printf("\tMemory Operating Mode Capability:");
+ 			dmi_memory_operating_mode_capability(WORD(data + 0x29));
+-			printf("\n");
+-			printf("\tFirmware Version: %s\n",
++			pr_attr("Firmware Version", "%s",
+ 				dmi_string(h, data[0x2B]));
+-			printf("\tModule Manufacturer ID:");
+-			dmi_memory_manufacturer_id(WORD(data + 0x2C));
+-			printf("\n");
+-			printf("\tModule Product ID:");
+-			dmi_memory_product_id(WORD(data + 0x2E));
+-			printf("\n");
+-			printf("\tMemory Subsystem Controller Manufacturer ID:");
+-			dmi_memory_manufacturer_id(WORD(data + 0x30));
+-			printf("\n");
+-			printf("\tMemory Subsystem Controller Product ID:");
+-			dmi_memory_product_id(WORD(data + 0x32));
+-			printf("\n");
++			dmi_memory_manufacturer_id("Module Manufacturer ID",
++						   WORD(data + 0x2C));
++			dmi_memory_product_id("Module Product ID",
++					      WORD(data + 0x2E));
++			dmi_memory_manufacturer_id("Memory Subsystem Controller Manufacturer ID",
++						   WORD(data + 0x30));
++			dmi_memory_product_id("Memory Subsystem Controller Product ID",
++					      WORD(data + 0x32));
+ 			if (h->length < 0x3C) break;
+-			printf("\tNon-Volatile Size:");
+-			dmi_memory_size(QWORD(data + 0x34));
+-			printf("\n");
++			dmi_memory_size("Non-Volatile Size", QWORD(data + 0x34));
+ 			if (h->length < 0x44) break;
+-			printf("\tVolatile Size:");
+-			dmi_memory_size(QWORD(data + 0x3C));
+-			printf("\n");
++			dmi_memory_size("Volatile Size", QWORD(data + 0x3C));
+ 			if (h->length < 0x4C) break;
+-			printf("\tCache Size:");
+-			dmi_memory_size(QWORD(data + 0x44));
+-			printf("\n");
++			dmi_memory_size("Cache Size", QWORD(data + 0x44));
+ 			if (h->length < 0x54) break;
+-			printf("\tLogical Size:");
+-			dmi_memory_size(QWORD(data + 0x4C));
+-			printf("\n");
++			dmi_memory_size("Logical Size", QWORD(data + 0x4C));
+ 			break;
+ 
+ 		case 18: /* 7.19 32-bit Memory Error Information */
+ 			pr_handle_name("32-bit Memory Error Information");
+ 			if (h->length < 0x17) break;
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_memory_error_type(data[0x04]));
+-			printf("\tGranularity: %s\n",
++			pr_attr("Granularity", "%s",
+ 				dmi_memory_error_granularity(data[0x05]));
+-			printf("\tOperation: %s\n",
++			pr_attr("Operation", "%s",
+ 				dmi_memory_error_operation(data[0x06]));
+-			printf("\tVendor Syndrome:");
+ 			dmi_memory_error_syndrome(DWORD(data + 0x07));
+-			printf("\n");
+-			printf("\tMemory Array Address:");
+-			dmi_32bit_memory_error_address(DWORD(data + 0x0B));
+-			printf("\n");
+-			printf("\tDevice Address:");
+-			dmi_32bit_memory_error_address(DWORD(data + 0x0F));
+-			printf("\n");
+-			printf("\tResolution:");
+-			dmi_32bit_memory_error_address(DWORD(data + 0x13));
+-			printf("\n");
++			dmi_32bit_memory_error_address("Memory Array Address",
++						       DWORD(data + 0x0B));
++			dmi_32bit_memory_error_address("Device Address",
++						       DWORD(data + 0x0F));
++			dmi_32bit_memory_error_address("Resolution",
++						       DWORD(data + 0x13));
+ 			break;
+ 
+ 		case 19: /* 7.20 Memory Array Mapped Address */
+@@ -4488,29 +4486,26 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				start = QWORD(data + 0x0F);
+ 				end = QWORD(data + 0x17);
+ 
+-				printf("\tStarting Address: 0x%08X%08Xk\n",
++				pr_attr("Starting Address", "0x%08X%08Xk",
+ 					start.h, start.l);
+-				printf("\tEnding Address: 0x%08X%08Xk\n",
++				pr_attr("Ending Address", "0x%08X%08Xk",
+ 					end.h, end.l);
+-				printf("\tRange Size:");
+ 				dmi_mapped_address_extended_size(start, end);
+ 			}
+ 			else
+ 			{
+-				printf("\tStarting Address: 0x%08X%03X\n",
++				pr_attr("Starting Address", "0x%08X%03X",
+ 					DWORD(data + 0x04) >> 2,
+ 					(DWORD(data + 0x04) & 0x3) << 10);
+-				printf("\tEnding Address: 0x%08X%03X\n",
++				pr_attr("Ending Address", "0x%08X%03X",
+ 					DWORD(data + 0x08) >> 2,
+ 					((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
+-				printf("\tRange Size:");
+ 				dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
+ 			}
+-			printf("\n");
+ 			if (!(opt.flags & FLAG_QUIET))
+-				printf("\tPhysical Array Handle: 0x%04X\n",
++				pr_attr("Physical Array Handle", "0x%04X",
+ 					WORD(data + 0x0C));
+-			printf("\tPartition Width: %u\n",
++			pr_attr("Partition Width", "%u",
+ 				data[0x0E]);
+ 			break;
+ 
+@@ -4524,344 +4519,288 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				start = QWORD(data + 0x13);
+ 				end = QWORD(data + 0x1B);
+ 
+-				printf("\tStarting Address: 0x%08X%08Xk\n",
++				pr_attr("Starting Address", "0x%08X%08Xk",
+ 					start.h, start.l);
+-				printf("\tEnding Address: 0x%08X%08Xk\n",
++				pr_attr("Ending Address", "0x%08X%08Xk",
+ 					end.h, end.l);
+-				printf("\tRange Size:");
+ 				dmi_mapped_address_extended_size(start, end);
+ 			}
+ 			else
+ 			{
+-				printf("\tStarting Address: 0x%08X%03X\n",
++				pr_attr("Starting Address", "0x%08X%03X",
+ 					DWORD(data + 0x04) >> 2,
+ 					(DWORD(data + 0x04) & 0x3) << 10);
+-				printf("\tEnding Address: 0x%08X%03X\n",
++				pr_attr("Ending Address", "0x%08X%03X",
+ 					DWORD(data + 0x08) >> 2,
+ 					((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
+-				printf("\tRange Size:");
+ 				dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
+ 			}
+-			printf("\n");
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+-				printf("\tPhysical Device Handle: 0x%04X\n",
++				pr_attr("Physical Device Handle", "0x%04X",
+ 					WORD(data + 0x0C));
+-				printf("\tMemory Array Mapped Address Handle: 0x%04X\n",
++				pr_attr("Memory Array Mapped Address Handle", "0x%04X",
+ 					WORD(data + 0x0E));
+ 			}
+-			printf("\tPartition Row Position:");
+ 			dmi_mapped_address_row_position(data[0x10]);
+-			printf("\n");
+-			dmi_mapped_address_interleave_position(data[0x11], "\t");
+-			dmi_mapped_address_interleaved_data_depth(data[0x12], "\t");
++			dmi_mapped_address_interleave_position(data[0x11]);
++			dmi_mapped_address_interleaved_data_depth(data[0x12]);
+ 			break;
+ 
+ 		case 21: /* 7.22 Built-in Pointing Device */
+ 			pr_handle_name("Built-in Pointing Device");
+ 			if (h->length < 0x07) break;
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_pointing_device_type(data[0x04]));
+-			printf("\tInterface: %s\n",
++			pr_attr("Interface", "%s",
+ 				dmi_pointing_device_interface(data[0x05]));
+-			printf("\tButtons: %u\n",
++			pr_attr("Buttons", "%u",
+ 				data[0x06]);
+ 			break;
+ 
+ 		case 22: /* 7.23 Portable Battery */
+ 			pr_handle_name("Portable Battery");
+ 			if (h->length < 0x10) break;
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x05]));
+ 			if (data[0x06] || h->length < 0x1A)
+-				printf("\tManufacture Date: %s\n",
++				pr_attr("Manufacture Date", "%s",
+ 					dmi_string(h, data[0x06]));
+ 			if (data[0x07] || h->length < 0x1A)
+-				printf("\tSerial Number: %s\n",
++				pr_attr("Serial Number", "%s",
+ 					dmi_string(h, data[0x07]));
+-			printf("\tName: %s\n",
++			pr_attr("Name", "%s",
+ 				dmi_string(h, data[0x08]));
+ 			if (data[0x09] != 0x02 || h->length < 0x1A)
+-				printf("\tChemistry: %s\n",
++				pr_attr("Chemistry", "%s",
+ 					dmi_battery_chemistry(data[0x09]));
+-			printf("\tDesign Capacity:");
+ 			if (h->length < 0x16)
+ 				dmi_battery_capacity(WORD(data + 0x0A), 1);
+ 			else
+ 				dmi_battery_capacity(WORD(data + 0x0A), data[0x15]);
+-			printf("\n");
+-			printf("\tDesign Voltage:");
+ 			dmi_battery_voltage(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tSBDS Version: %s\n",
++			pr_attr("SBDS Version", "%s",
+ 				dmi_string(h, data[0x0E]));
+-			printf("\tMaximum Error:");
+ 			dmi_battery_maximum_error(data[0x0F]);
+-			printf("\n");
+ 			if (h->length < 0x1A) break;
+ 			if (data[0x07] == 0)
+-				printf("\tSBDS Serial Number: %04X\n",
++				pr_attr("SBDS Serial Number", "%04X",
+ 					WORD(data + 0x10));
+ 			if (data[0x06] == 0)
+-				printf("\tSBDS Manufacture Date: %u-%02u-%02u\n",
++				pr_attr("SBDS Manufacture Date", "%u-%02u-%02u",
+ 					1980 + (WORD(data + 0x12) >> 9),
+ 					(WORD(data + 0x12) >> 5) & 0x0F,
+ 					WORD(data + 0x12) & 0x1F);
+ 			if (data[0x09] == 0x02)
+-				printf("\tSBDS Chemistry: %s\n",
++				pr_attr("SBDS Chemistry", "%s",
+ 					dmi_string(h, data[0x14]));
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x16));
+ 			break;
+ 
+ 		case 23: /* 7.24 System Reset */
+ 			pr_handle_name("System Reset");
+ 			if (h->length < 0x0D) break;
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				data[0x04] & (1 << 0) ? "Enabled" : "Disabled");
+-			printf("\tWatchdog Timer: %s\n",
++			pr_attr("Watchdog Timer", "%s",
+ 				data[0x04] & (1 << 5) ? "Present" : "Not Present");
+ 			if (!(data[0x04] & (1 << 5)))
+ 				break;
+-			printf("\tBoot Option: %s\n",
++			pr_attr("Boot Option", "%s",
+ 				dmi_system_reset_boot_option((data[0x04] >> 1) & 0x3));
+-			printf("\tBoot Option On Limit: %s\n",
++			pr_attr("Boot Option On Limit", "%s",
+ 				dmi_system_reset_boot_option((data[0x04] >> 3) & 0x3));
+-			printf("\tReset Count:");
+-			dmi_system_reset_count(WORD(data + 0x05));
+-			printf("\n");
+-			printf("\tReset Limit:");
+-			dmi_system_reset_count(WORD(data + 0x07));
+-			printf("\n");
+-			printf("\tTimer Interval:");
+-			dmi_system_reset_timer(WORD(data + 0x09));
+-			printf("\n");
+-			printf("\tTimeout:");
+-			dmi_system_reset_timer(WORD(data + 0x0B));
+-			printf("\n");
++			dmi_system_reset_count("Reset Count", WORD(data + 0x05));
++			dmi_system_reset_count("Reset Limit", WORD(data + 0x07));
++			dmi_system_reset_timer("Timer Interval", WORD(data + 0x09));
++			dmi_system_reset_timer("Timeout", WORD(data + 0x0B));
+ 			break;
+ 
+ 		case 24: /* 7.25 Hardware Security */
+ 			pr_handle_name("Hardware Security");
+ 			if (h->length < 0x05) break;
+-			printf("\tPower-On Password Status: %s\n",
++			pr_attr("Power-On Password Status", "%s",
+ 				dmi_hardware_security_status(data[0x04] >> 6));
+-			printf("\tKeyboard Password Status: %s\n",
++			pr_attr("Keyboard Password Status", "%s",
+ 				dmi_hardware_security_status((data[0x04] >> 4) & 0x3));
+-			printf("\tAdministrator Password Status: %s\n",
++			pr_attr("Administrator Password Status", "%s",
+ 				dmi_hardware_security_status((data[0x04] >> 2) & 0x3));
+-			printf("\tFront Panel Reset Status: %s\n",
++			pr_attr("Front Panel Reset Status", "%s",
+ 				dmi_hardware_security_status(data[0x04] & 0x3));
+ 			break;
+ 
+ 		case 25: /* 7.26 System Power Controls */
+ 			pr_handle_name("System Power Controls");
+ 			if (h->length < 0x09) break;
+-			printf("\tNext Scheduled Power-on:");
+ 			dmi_power_controls_power_on(data + 0x04);
+-			printf("\n");
+ 			break;
+ 
+ 		case 26: /* 7.27 Voltage Probe */
+ 			pr_handle_name("Voltage Probe");
+ 			if (h->length < 0x14) break;
+-			printf("\tDescription: %s\n",
++			pr_attr("Description", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_voltage_probe_location(data[0x05] & 0x1f));
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				dmi_probe_status(data[0x05] >> 5));
+-			printf("\tMaximum Value:");
+-			dmi_voltage_probe_value(WORD(data + 0x06));
+-			printf("\n");
+-			printf("\tMinimum Value:");
+-			dmi_voltage_probe_value(WORD(data + 0x08));
+-			printf("\n");
+-			printf("\tResolution:");
++			dmi_voltage_probe_value("Maximum Value", WORD(data + 0x06));
++			dmi_voltage_probe_value("Minimum Value", WORD(data + 0x08));
+ 			dmi_voltage_probe_resolution(WORD(data + 0x0A));
+-			printf("\n");
+-			printf("\tTolerance:");
+-			dmi_voltage_probe_value(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tAccuracy:");
++			dmi_voltage_probe_value("Tolerance", WORD(data + 0x0C));
+ 			dmi_probe_accuracy(WORD(data + 0x0E));
+-			printf("\n");
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x10));
+ 			if (h->length < 0x16) break;
+-			printf("\tNominal Value:");
+-			dmi_voltage_probe_value(WORD(data + 0x14));
+-			printf("\n");
++			dmi_voltage_probe_value("Nominal Value", WORD(data + 0x14));
+ 			break;
+ 
+ 		case 27: /* 7.28 Cooling Device */
+ 			pr_handle_name("Cooling Device");
+ 			if (h->length < 0x0C) break;
+ 			if (!(opt.flags & FLAG_QUIET) && WORD(data + 0x04) != 0xFFFF)
+-				printf("\tTemperature Probe Handle: 0x%04X\n",
++				pr_attr("Temperature Probe Handle", "0x%04X",
+ 					WORD(data + 0x04));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_cooling_device_type(data[0x06] & 0x1f));
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				dmi_probe_status(data[0x06] >> 5));
+ 			if (data[0x07] != 0x00)
+-				printf("\tCooling Unit Group: %u\n",
++				pr_attr("Cooling Unit Group", "%u",
+ 					data[0x07]);
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x08));
+ 			if (h->length < 0x0E) break;
+-			printf("\tNominal Speed:");
+ 			dmi_cooling_device_speed(WORD(data + 0x0C));
+-			printf("\n");
+ 			if (h->length < 0x0F) break;
+-			printf("\tDescription: %s\n", dmi_string(h, data[0x0E]));
++			pr_attr("Description", "%s", dmi_string(h, data[0x0E]));
+ 			break;
+ 
+ 		case 28: /* 7.29 Temperature Probe */
+ 			pr_handle_name("Temperature Probe");
+ 			if (h->length < 0x14) break;
+-			printf("\tDescription: %s\n",
++			pr_attr("Description", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_temperature_probe_location(data[0x05] & 0x1F));
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				dmi_probe_status(data[0x05] >> 5));
+-			printf("\tMaximum Value:");
+-			dmi_temperature_probe_value(WORD(data + 0x06));
+-			printf("\n");
+-			printf("\tMinimum Value:");
+-			dmi_temperature_probe_value(WORD(data + 0x08));
+-			printf("\n");
+-			printf("\tResolution:");
++			dmi_temperature_probe_value("Maximum Value",
++						    WORD(data + 0x06));
++			dmi_temperature_probe_value("Minimum Value",
++						    WORD(data + 0x08));
+ 			dmi_temperature_probe_resolution(WORD(data + 0x0A));
+-			printf("\n");
+-			printf("\tTolerance:");
+-			dmi_temperature_probe_value(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tAccuracy:");
++			dmi_temperature_probe_value("Tolerance",
++						    WORD(data + 0x0C));
+ 			dmi_probe_accuracy(WORD(data + 0x0E));
+-			printf("\n");
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x10));
+ 			if (h->length < 0x16) break;
+-			printf("\tNominal Value:");
+-			dmi_temperature_probe_value(WORD(data + 0x14));
+-			printf("\n");
++			dmi_temperature_probe_value("Nominal Value",
++						    WORD(data + 0x14));
+ 			break;
+ 
+ 		case 29: /* 7.30 Electrical Current Probe */
+ 			pr_handle_name("Electrical Current Probe");
+ 			if (h->length < 0x14) break;
+-			printf("\tDescription: %s\n",
++			pr_attr("Description", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_voltage_probe_location(data[5] & 0x1F));
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				dmi_probe_status(data[0x05] >> 5));
+-			printf("\tMaximum Value:");
+-			dmi_current_probe_value(WORD(data + 0x06));
+-			printf("\n");
+-			printf("\tMinimum Value:");
+-			dmi_current_probe_value(WORD(data + 0x08));
+-			printf("\n");
+-			printf("\tResolution:");
++			dmi_current_probe_value("Maximum Value",
++						WORD(data + 0x06));
++			dmi_current_probe_value("Minimum Value",
++						WORD(data + 0x08));
+ 			dmi_current_probe_resolution(WORD(data + 0x0A));
+-			printf("\n");
+-			printf("\tTolerance:");
+-			dmi_current_probe_value(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tAccuracy:");
++			dmi_current_probe_value("Tolerance",
++						WORD(data + 0x0C));
+ 			dmi_probe_accuracy(WORD(data + 0x0E));
+-			printf("\n");
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x10));
+ 			if (h->length < 0x16) break;
+-			printf("\tNominal Value:");
+-			dmi_current_probe_value(WORD(data + 0x14));
+-			printf("\n");
++			dmi_current_probe_value("Nominal Value",
++						WORD(data + 0x14));
+ 			break;
+ 
+ 		case 30: /* 7.31 Out-of-band Remote Access */
+ 			pr_handle_name("Out-of-band Remote Access");
+ 			if (h->length < 0x06) break;
+-			printf("\tManufacturer Name: %s\n",
++			pr_attr("Manufacturer Name", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tInbound Connection: %s\n",
++			pr_attr("Inbound Connection", "%s",
+ 				data[0x05] & (1 << 0) ? "Enabled" : "Disabled");
+-			printf("\tOutbound Connection: %s\n",
++			pr_attr("Outbound Connection", "%s",
+ 				data[0x05] & (1 << 1) ? "Enabled" : "Disabled");
+ 			break;
+ 
+ 		case 31: /* 7.32 Boot Integrity Services Entry Point */
+ 			pr_handle_name("Boot Integrity Services Entry Point");
+ 			if (h->length < 0x1C) break;
+-			printf("\tChecksum: %s\n",
++			pr_attr("Checksum", "%s",
+ 				checksum(data, h->length) ? "OK" : "Invalid");
+-			printf("\t16-bit Entry Point Address: %04X:%04X\n",
++			pr_attr("16-bit Entry Point Address", "%04X:%04X",
+ 				DWORD(data + 0x08) >> 16,
+ 				DWORD(data + 0x08) & 0xFFFF);
+-			printf("\t32-bit Entry Point Address: 0x%08X\n",
++			pr_attr("32-bit Entry Point Address", "0x%08X",
+ 				DWORD(data + 0x0C));
+ 			break;
+ 
+ 		case 32: /* 7.33 System Boot Information */
+ 			pr_handle_name("System Boot Information");
+ 			if (h->length < 0x0B) break;
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				dmi_system_boot_status(data[0x0A]));
+ 			break;
+ 
+ 		case 33: /* 7.34 64-bit Memory Error Information */
+ 			pr_handle_name("64-bit Memory Error Information");
+ 			if (h->length < 0x1F) break;
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_memory_error_type(data[0x04]));
+-			printf("\tGranularity: %s\n",
++			pr_attr("Granularity", "%s",
+ 				dmi_memory_error_granularity(data[0x05]));
+-			printf("\tOperation: %s\n",
++			pr_attr("Operation", "%s",
+ 				dmi_memory_error_operation(data[0x06]));
+-			printf("\tVendor Syndrome:");
+ 			dmi_memory_error_syndrome(DWORD(data + 0x07));
+-			printf("\n");
+-			printf("\tMemory Array Address:");
+-			dmi_64bit_memory_error_address(QWORD(data + 0x0B));
+-			printf("\n");
+-			printf("\tDevice Address:");
+-			dmi_64bit_memory_error_address(QWORD(data + 0x13));
+-			printf("\n");
+-			printf("\tResolution:");
+-			dmi_32bit_memory_error_address(DWORD(data + 0x1B));
+-			printf("\n");
++			dmi_64bit_memory_error_address("Memory Array Address",
++						       QWORD(data + 0x0B));
++			dmi_64bit_memory_error_address("Device Address",
++						       QWORD(data + 0x13));
++			dmi_32bit_memory_error_address("Resolution",
++						       DWORD(data + 0x1B));
+ 			break;
+ 
+ 		case 34: /* 7.35 Management Device */
+ 			pr_handle_name("Management Device");
+ 			if (h->length < 0x0B) break;
+-			printf("\tDescription: %s\n",
++			pr_attr("Description", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_management_device_type(data[0x05]));
+-			printf("\tAddress: 0x%08X\n",
++			pr_attr("Address", "0x%08X",
+ 				DWORD(data + 0x06));
+-			printf("\tAddress Type: %s\n",
++			pr_attr("Address Type", "%s",
+ 				dmi_management_device_address_type(data[0x0A]));
+ 			break;
+ 
+ 		case 35: /* 7.36 Management Device Component */
+ 			pr_handle_name("Management Device Component");
+ 			if (h->length < 0x0B) break;
+-			printf("\tDescription: %s\n",
++			pr_attr("Description", "%s",
+ 				dmi_string(h, data[0x04]));
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+-				printf("\tManagement Device Handle: 0x%04X\n",
++				pr_attr("Management Device Handle", "0x%04X",
+ 					WORD(data + 0x05));
+-				printf("\tComponent Handle: 0x%04X\n",
++				pr_attr("Component Handle", "0x%04X",
+ 					WORD(data + 0x07));
+ 				if (WORD(data + 0x09) != 0xFFFF)
+-					printf("\tThreshold Handle: 0x%04X\n",
++					pr_attr("Threshold Handle", "0x%04X",
+ 						WORD(data + 0x09));
+ 			}
+ 			break;
+@@ -4870,36 +4809,36 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			pr_handle_name("Management Device Threshold Data");
+ 			if (h->length < 0x10) break;
+ 			if (WORD(data + 0x04) != 0x8000)
+-				printf("\tLower Non-critical Threshold: %d\n",
++				pr_attr("Lower Non-critical Threshold", "%d",
+ 					(i16)WORD(data + 0x04));
+ 			if (WORD(data + 0x06) != 0x8000)
+-				printf("\tUpper Non-critical Threshold: %d\n",
++				pr_attr("Upper Non-critical Threshold", "%d",
+ 					(i16)WORD(data + 0x06));
+ 			if (WORD(data + 0x08) != 0x8000)
+-				printf("\tLower Critical Threshold: %d\n",
++				pr_attr("Lower Critical Threshold", "%d",
+ 					(i16)WORD(data + 0x08));
+ 			if (WORD(data + 0x0A) != 0x8000)
+-				printf("\tUpper Critical Threshold: %d\n",
++				pr_attr("Upper Critical Threshold", "%d",
+ 					(i16)WORD(data + 0x0A));
+ 			if (WORD(data + 0x0C) != 0x8000)
+-				printf("\tLower Non-recoverable Threshold: %d\n",
++				pr_attr("Lower Non-recoverable Threshold", "%d",
+ 					(i16)WORD(data + 0x0C));
+ 			if (WORD(data + 0x0E) != 0x8000)
+-				printf("\tUpper Non-recoverable Threshold: %d\n",
++				pr_attr("Upper Non-recoverable Threshold", "%d",
+ 					(i16)WORD(data + 0x0E));
+ 			break;
+ 
+ 		case 37: /* 7.38 Memory Channel */
+ 			pr_handle_name("Memory Channel");
+ 			if (h->length < 0x07) break;
+-			printf("\tType: %s\n",
++			pr_attr("Type", "%s",
+ 				dmi_memory_channel_type(data[0x04]));
+-			printf("\tMaximal Load: %u\n",
++			pr_attr("Maximal Load", "%u",
+ 				data[0x05]);
+-			printf("\tDevices: %u\n",
++			pr_attr("Devices", "%u",
+ 				data[0x06]);
+ 			if (h->length < 0x07 + 3 * data[0x06]) break;
+-			dmi_memory_channel_devices(data[0x06], data + 0x07, "\t");
++			dmi_memory_channel_devices(data[0x06], data + 0x07);
+ 			break;
+ 
+ 		case 38: /* 7.39 IPMI Device Information */
+@@ -4909,37 +4848,35 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			 */
+ 			pr_handle_name("IPMI Device Information");
+ 			if (h->length < 0x10) break;
+-			printf("\tInterface Type: %s\n",
++			pr_attr("Interface Type", "%s",
+ 				dmi_ipmi_interface_type(data[0x04]));
+-			printf("\tSpecification Version: %u.%u\n",
++			pr_attr("Specification Version", "%u.%u",
+ 				data[0x05] >> 4, data[0x05] & 0x0F);
+-			printf("\tI2C Slave Address: 0x%02x\n",
++			pr_attr("I2C Slave Address", "0x%02x",
+ 				data[0x06] >> 1);
+ 			if (data[0x07] != 0xFF)
+-				printf("\tNV Storage Device Address: %u\n",
++				pr_attr("NV Storage Device Address", "%u",
+ 					data[0x07]);
+ 			else
+-				printf("\tNV Storage Device: Not Present\n");
+-			printf("\tBase Address: ");
++				pr_attr("NV Storage Device", "Not Present");
+ 			dmi_ipmi_base_address(data[0x04], data + 0x08,
+ 				h->length < 0x11 ? 0 : (data[0x10] >> 4) & 1);
+-			printf("\n");
+ 			if (h->length < 0x12) break;
+ 			if (data[0x04] != 0x04)
+ 			{
+-				printf("\tRegister Spacing: %s\n",
++				pr_attr("Register Spacing", "%s",
+ 					dmi_ipmi_register_spacing(data[0x10] >> 6));
+ 				if (data[0x10] & (1 << 3))
+ 				{
+-					printf("\tInterrupt Polarity: %s\n",
++					pr_attr("Interrupt Polarity", "%s",
+ 						data[0x10] & (1 << 1) ? "Active High" : "Active Low");
+-					printf("\tInterrupt Trigger Mode: %s\n",
++					pr_attr("Interrupt Trigger Mode", "%s",
+ 						data[0x10] & (1 << 0) ? "Level" : "Edge");
+ 				}
+ 			}
+ 			if (data[0x11] != 0x00)
+ 			{
+-				printf("\tInterrupt Number: %u\n",
++				pr_attr("Interrupt Number", "%u",
+ 					data[0x11]);
+ 			}
+ 			break;
+@@ -4948,51 +4885,47 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			pr_handle_name("System Power Supply");
+ 			if (h->length < 0x10) break;
+ 			if (data[0x04] != 0x00)
+-				printf("\tPower Unit Group: %u\n",
++				pr_attr("Power Unit Group", "%u",
+ 					data[0x04]);
+-			printf("\tLocation: %s\n",
++			pr_attr("Location", "%s",
+ 				dmi_string(h, data[0x05]));
+-			printf("\tName: %s\n",
++			pr_attr("Name", "%s",
+ 				dmi_string(h, data[0x06]));
+-			printf("\tManufacturer: %s\n",
++			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x07]));
+-			printf("\tSerial Number: %s\n",
++			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x08]));
+-			printf("\tAsset Tag: %s\n",
++			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x09]));
+-			printf("\tModel Part Number: %s\n",
++			pr_attr("Model Part Number", "%s",
+ 				dmi_string(h, data[0x0A]));
+-			printf("\tRevision: %s\n",
++			pr_attr("Revision", "%s",
+ 				dmi_string(h, data[0x0B]));
+-			printf("\tMax Power Capacity:");
+ 			dmi_power_supply_power(WORD(data + 0x0C));
+-			printf("\n");
+-			printf("\tStatus:");
+ 			if (WORD(data + 0x0E) & (1 << 1))
+-				printf(" Present, %s",
++				pr_attr("Status", "Present, %s",
+ 					dmi_power_supply_status((WORD(data + 0x0E) >> 7) & 0x07));
+ 			else
+-				printf(" Not Present");
+-			printf("\n");
+-			printf("\tType: %s\n",
++				pr_attr("Status", "Not Present");
++			pr_attr("Type", "%s",
+ 				dmi_power_supply_type((WORD(data + 0x0E) >> 10) & 0x0F));
+-			printf("\tInput Voltage Range Switching: %s\n",
++			pr_attr("Input Voltage Range Switching", "%s",
+ 				dmi_power_supply_range_switching((WORD(data + 0x0E) >> 3) & 0x0F));
+-			printf("\tPlugged: %s\n",
++			pr_attr("Plugged", "%s",
+ 				WORD(data + 0x0E) & (1 << 2) ? "No" : "Yes");
+-			printf("\tHot Replaceable: %s\n",
++			pr_attr("Hot Replaceable", "%s",
+ 				WORD(data + 0x0E) & (1 << 0) ? "Yes" : "No");
+ 			if (h->length < 0x16) break;
+ 			if (!(opt.flags & FLAG_QUIET))
+ 			{
+ 				if (WORD(data + 0x10) != 0xFFFF)
+-					printf("\tInput Voltage Probe Handle: 0x%04X\n",
++					pr_attr("Input Voltage Probe Handle", "0x%04X",
+ 						WORD(data + 0x10));
+ 				if (WORD(data + 0x12) != 0xFFFF)
+-					printf("\tCooling Device Handle: 0x%04X\n",
++					pr_attr("Cooling Device Handle", "0x%04X",
+ 						WORD(data + 0x12));
+ 				if (WORD(data + 0x14) != 0xFFFF)
+-					printf("\tInput Current Probe Handle: 0x%04X\n",
++					pr_attr("Input Current Probe Handle", "0x%04X",
+ 						WORD(data + 0x14));
+ 			}
+ 			break;
+@@ -5001,19 +4934,19 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x0B) break;
+ 			if (opt.flags & FLAG_QUIET)
+ 				return;
+-			dmi_additional_info(h, "");
++			dmi_additional_info(h);
+ 			break;
+ 
+ 		case 41: /* 7.42 Onboard Device Extended Information */
+ 			pr_handle_name("Onboard Device");
+ 			if (h->length < 0x0B) break;
+-			printf("\tReference Designation: %s\n", dmi_string(h, data[0x04]));
+-			printf("\tType: %s\n",
++			pr_attr("Reference Designation", "%s", dmi_string(h, data[0x04]));
++			pr_attr("Type", "%s",
+ 				dmi_on_board_devices_type(data[0x05] & 0x7F));
+-			printf("\tStatus: %s\n",
++			pr_attr("Status", "%s",
+ 				data[0x05] & 0x80 ? "Enabled" : "Disabled");
+-			printf("\tType Instance: %u\n", data[0x06]);
+-			dmi_slot_segment_bus_func(WORD(data + 0x07), data[0x09], data[0x0A], "\t");
++			pr_attr("Type Instance", "%u", data[0x06]);
++			dmi_slot_segment_bus_func(WORD(data + 0x07), data[0x09], data[0x0A]);
+ 			break;
+ 
+ 		case 42: /* 7.43 Management Controller Host Interface */
+@@ -5021,7 +4954,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (ver < 0x0302)
+ 			{
+ 				if (h->length < 0x05) break;
+-				printf("\tInterface Type: %s\n",
++				pr_attr("Interface Type", "%s",
+ 					dmi_management_controller_host_type(data[0x04]));
+ 				/*
+ 				 * There you have a type-dependent, variable-length
+@@ -5032,7 +4965,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				if (h->length < 0x09) break;
+ 				if (data[0x04] == 0xF0)		/* OEM */
+ 				{
+-					printf("\tVendor ID: 0x%02X%02X%02X%02X\n",
++					pr_attr("Vendor ID", "0x%02X%02X%02X%02X",
+ 						data[0x05], data[0x06], data[0x07],
+ 						data[0x08]);
+ 				}
+@@ -5044,10 +4977,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 		case 43: /* 7.44 TPM Device */
+ 			pr_handle_name("TPM Device");
+ 			if (h->length < 0x1B) break;
+-			printf("\tVendor ID:");
+ 			dmi_tpm_vendor_id(data + 0x04);
+-			printf("\n");
+-			printf("\tSpecification Version: %d.%d\n", data[0x08], data[0x09]);
++			pr_attr("Specification Version", "%d.%d", data[0x08], data[0x09]);
+ 			switch (data[0x08])
+ 			{
+ 				case 0x01:
+@@ -5056,11 +4987,11 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 					 * redundant with the above, and uncoded
+ 					 * in a silly way.
+ 					 */
+-					printf("\tFirmware Revision: %u.%u\n",
++					pr_attr("Firmware Revision", "%u.%u",
+ 						data[0x0C], data[0x0D]);
+ 					break;
+ 				case 0x02:
+-					printf("\tFirmware Revision: %u.%u\n",
++					pr_attr("Firmware Revision", "%u.%u",
+ 						DWORD(data + 0x0A) >> 16,
+ 						DWORD(data + 0x0A) & 0xFFFF);
+ 					/*
+@@ -5070,11 +5001,11 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 					 */
+ 					break;
+ 			}
+-			printf("\tDescription: %s\n", dmi_string(h, data[0x12]));
++			pr_attr("Description", "%s", dmi_string(h, data[0x12]));
+ 			printf("\tCharacteristics:\n");
+ 			dmi_tpm_characteristics(QWORD(data + 0x13), "\t\t");
+ 			if (h->length < 0x1F) break;
+-			printf("\tOEM-specific Information: 0x%08X\n",
++			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x1B));
+ 			break;
+ 
+@@ -5141,8 +5072,7 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
+ 				printf("%u.%u\n", data[key - 1], data[key]);
+ 			break;
+ 		case 0x108:
+-			dmi_system_uuid(data + offset, ver);
+-			printf("\n");
++			dmi_system_uuid(NULL, data + offset, ver);
+ 			break;
+ 		case 0x305:
+ 			printf("%s\n", dmi_chassis_type(data[offset]));
+@@ -5151,8 +5081,7 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
+ 			printf("%s\n", dmi_processor_family(h, ver));
+ 			break;
+ 		case 0x416:
+-			dmi_processor_frequency(data + offset);
+-			printf("\n");
++			dmi_processor_frequency(NULL, data + offset);
+ 			break;
+ 		default:
+ 			printf("%s\n", dmi_string(h, data[offset]));
+diff --git a/dmioem.c b/dmioem.c
+index c999c08c4475..873ec1068c16 100644
+--- a/dmioem.c
++++ b/dmioem.c
+@@ -96,16 +96,16 @@ static int dmi_decode_acer(const struct dmi_header *h)
+ 			pr_handle_name("Acer Hotkey Function");
+ 			if (h->length < 0x0F) break;
+ 			cap = WORD(data + 0x04);
+-			printf("\tFunction bitmap for Communication Button: 0x%04hx\n", cap);
++			pr_attr("Function bitmap for Communication Button", "0x%04hx", cap);
+ 			printf("\t\tWiFi: %s\n", cap & 0x0001 ? "Yes" : "No");
+ 			printf("\t\t3G: %s\n", cap & 0x0040 ? "Yes" : "No");
+ 			printf("\t\tWiMAX: %s\n", cap & 0x0080 ? "Yes" : "No");
+ 			printf("\t\tBluetooth: %s\n", cap & 0x0800 ? "Yes" : "No");
+-			printf("\tFunction bitmap for Application Button: 0x%04hx\n", WORD(data + 0x06));
+-			printf("\tFunction bitmap for Media Button: 0x%04hx\n", WORD(data + 0x08));
+-			printf("\tFunction bitmap for Display Button: 0x%04hx\n", WORD(data + 0x0A));
+-			printf("\tFunction bitmap for Others Button: 0x%04hx\n", WORD(data + 0x0C));
+-			printf("\tCommunication Function Key Number: %d\n", data[0x0E]);
++			pr_attr("Function bitmap for Application Button", "0x%04hx", WORD(data + 0x06));
++			pr_attr("Function bitmap for Media Button", "0x%04hx", WORD(data + 0x08));
++			pr_attr("Function bitmap for Display Button", "0x%04hx", WORD(data + 0x0A));
++			pr_attr("Function bitmap for Others Button", "0x%04hx", WORD(data + 0x0C));
++			pr_attr("Communication Function Key Number", "%d", data[0x0E]);
+ 			break;
+ 
+ 		default:
+@@ -128,19 +128,21 @@ static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac)
+ 	 * 640K ought to be enough for anybody(said no one, ever).
+ 	 * */
+ 	static u8 nic_ctr;
++	char attr[8];
+ 
+ 	if (id == 0xFF)
+ 		id = ++nic_ctr;
+ 
++	sprintf(attr, "NIC %hu", id);
+ 	if (dev == 0x00 && bus == 0x00)
+-		printf("\tNIC %d: Disabled\n", id);
++		pr_attr(attr, "Disabled");
+ 	else if (dev == 0xFF && bus == 0xFF)
+-		printf("\tNIC %d: Not Installed\n", id);
++		pr_attr(attr, "Not Installed");
+ 	else
+ 	{
+-		printf("\tNIC %d: PCI device %02x:%02x.%x, "
+-			"MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
+-			id, bus, dev >> 3, dev & 7,
++		pr_attr(attr, "PCI device %02x:%02x.%x, "
++			"MAC address %02X:%02X:%02X:%02X:%02X:%02X",
++			bus, dev >> 3, dev & 7,
+ 			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ 	}
+ }
+@@ -160,13 +162,13 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 */
+ 			pr_handle_name("%s ProLiant System/Rack Locator", company);
+ 			if (h->length < 0x0B) break;
+-			printf("\tRack Name: %s\n", dmi_string(h, data[0x04]));
+-			printf("\tEnclosure Name: %s\n", dmi_string(h, data[0x05]));
+-			printf("\tEnclosure Model: %s\n", dmi_string(h, data[0x06]));
+-			printf("\tEnclosure Serial: %s\n", dmi_string(h, data[0x0A]));
+-			printf("\tEnclosure Bays: %d\n", data[0x08]);
+-			printf("\tServer Bay: %s\n", dmi_string(h, data[0x07]));
+-			printf("\tBays Filled: %d\n", data[0x09]);
++			pr_attr("Rack Name", "%s", dmi_string(h, data[0x04]));
++			pr_attr("Enclosure Name", "%s", dmi_string(h, data[0x05]));
++			pr_attr("Enclosure Model", "%s", dmi_string(h, data[0x06]));
++			pr_attr("Enclosure Serial", "%s", dmi_string(h, data[0x0A]));
++			pr_attr("Enclosure Bays", "%d", data[0x08]);
++			pr_attr("Server Bay", "%s", dmi_string(h, data[0x07]));
++			pr_attr("Bays Filled", "%d", data[0x09]);
+ 			break;
+ 
+ 		case 209:
+@@ -243,20 +245,22 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 */
+ 			pr_handle_name("%s 64-bit CRU Information", company);
+ 			if (h->length < 0x18) break;
+-			printf("\tSignature: 0x%08x", DWORD(data + 0x04));
+ 			if (is_printable(data + 0x04, 4))
+-				printf(" (%c%c%c%c)", data[0x04], data[0x05],
++				pr_attr("Signature", "0x%08x (%c%c%c%c)",
++					DWORD(data + 0x04),
++					data[0x04], data[0x05],
+ 					data[0x06], data[0x07]);
+-			printf("\n");
++			else
++				pr_attr("Signature", "0x%08x", DWORD(data + 0x04));
+ 			if (DWORD(data + 0x04) == 0x55524324)
+ 			{
+ 				u64 paddr = QWORD(data + 0x08);
+ 				paddr.l += DWORD(data + 0x14);
+ 				if (paddr.l < DWORD(data + 0x14))
+ 					paddr.h++;
+-				printf("\tPhysical Address: 0x%08x%08x\n",
++				pr_attr("Physical Address", "0x%08x%08x",
+ 					paddr.h, paddr.l);
+-				printf("\tLength: 0x%08x\n", DWORD(data + 0x10));
++				pr_attr("Length", "0x%08x", DWORD(data + 0x10));
+ 			}
+ 			break;
+ 
+@@ -268,12 +272,12 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			 */
+ 			pr_handle_name("%s ProLiant Information", company);
+ 			if (h->length < 0x08) break;
+-			printf("\tPower Features: 0x%08x\n", DWORD(data + 0x04));
++			pr_attr("Power Features", "0x%08x", DWORD(data + 0x04));
+ 			if (h->length < 0x0C) break;
+-			printf("\tOmega Features: 0x%08x\n", DWORD(data + 0x08));
++			pr_attr("Omega Features", "0x%08x", DWORD(data + 0x08));
+ 			if (h->length < 0x14) break;
+ 			feat = DWORD(data + 0x10);
+-			printf("\tMisc. Features: 0x%08x\n", feat);
++			pr_attr("Misc. Features", "0x%08x", feat);
+ 			printf("\t\tiCRU: %s\n", feat & 0x0001 ? "Yes" : "No");
+ 			printf("\t\tUEFI: %s\n", feat & 0x1400 ? "Yes" : "No");
+ 			break;
+@@ -320,8 +324,8 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 				return 0;
+ 
+ 			pr_handle_name("ThinkVantage Technologies");
+-			printf("\tVersion: %u\n", data[0x04]);
+-			printf("\tDiagnostics: %s\n",
++			pr_attr("Version", "%u", data[0x04]);
++			pr_attr("Diagnostics", "%s",
+ 				data[0x14] & 0x80 ? "Available" : "No");
+ 			break;
+ 
+@@ -359,7 +363,7 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 				return 0;
+ 
+ 			pr_handle_name("ThinkPad Device Presence Detection");
+-			printf("\tFingerprint Reader: %s\n",
++			pr_attr("Fingerprint Reader", "%s",
+ 				data[0x09] & 0x01 ? "Present" : "No");
+ 			break;
+ 
+@@ -392,8 +396,8 @@ static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+ 				return 0;
+ 
+ 			pr_handle_name("ThinkPad Embedded Controller Program");
+-			printf("\tVersion ID: %s\n", dmi_string(h, 1));
+-			printf("\tRelease Date: %s\n", dmi_string(h, 2));
++			pr_attr("Version ID", "%s", dmi_string(h, 1));
++			pr_attr("Release Date", "%s", dmi_string(h, 2));
+ 			break;
+ 
+ 		default:
+diff --git a/dmioutput.c b/dmioutput.c
+index ca7edab5cc69..2330b65755c9 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -59,3 +59,15 @@ void pr_handle_name(const char *format, ...)
+ 	va_end(args);
+ 	printf("\n");
+ }
++
++void pr_attr(const char *name, const char *format, ...)
++{
++	va_list args;
++
++	printf("\t%s: ", name);
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 0acdce7658c9..981dcb45f26a 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -25,3 +25,4 @@ void pr_comment(const char *format, ...);
+ void pr_info(const char *format, ...);
+ void pr_handle(const struct dmi_header *h);
+ void pr_handle_name(const char *format, ...);
++void pr_attr(const char *name, const char *format, ...);
+-- 
+2.17.1
+
diff --git a/SOURCES/0015-dmidecode-Add-helper-functions-pr_list_start-item-en.patch b/SOURCES/0015-dmidecode-Add-helper-functions-pr_list_start-item-en.patch
new file mode 100644
index 0000000..319877a
--- /dev/null
+++ b/SOURCES/0015-dmidecode-Add-helper-functions-pr_list_start-item-en.patch
@@ -0,0 +1,727 @@
+From 5893721859b73354a935135b4aee297bd41a5b1a Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 09:57:56 +0200
+Subject: [PATCH 15/23] dmidecode: Add helper functions pr_list_start/item/end
+
+Print lists through helper functions. pr_list_start() starts the
+list, with an optional value. pr_list_item() prints a single item.
+pr_list_end() is a no-op for plain text output, but is in place in
+anticipation of supporting other output formats such as HTML.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 260 +++++++++++++++++++++++++++++-----------------------
+ dmioutput.c |  35 +++++++
+ dmioutput.h |   3 +
+ 3 files changed, 184 insertions(+), 114 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 7ab058b61ef0..5a5299ed13ba 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -340,7 +340,7 @@ static void dmi_bios_rom_size(u8 code1, u16 code2)
+ 		pr_attr("ROM Size", "%u %s", code2 & 0x3FFF, unit[code2 >> 14]);
+ }
+ 
+-static void dmi_bios_characteristics(u64 code, const char *prefix)
++static void dmi_bios_characteristics(u64 code)
+ {
+ 	/* 7.1.1 */
+ 	static const char *characteristics[] = {
+@@ -381,18 +381,16 @@ static void dmi_bios_characteristics(u64 code, const char *prefix)
+ 	 */
+ 	if (code.l & (1 << 3))
+ 	{
+-		printf("%s%s\n",
+-			prefix, characteristics[0]);
++		pr_list_item("%s", characteristics[0]);
+ 		return;
+ 	}
+ 
+ 	for (i = 4; i <= 31; i++)
+ 		if (code.l & (1 << i))
+-			printf("%s%s\n",
+-				prefix, characteristics[i - 3]);
++			pr_list_item("%s", characteristics[i - 3]);
+ }
+ 
+-static void dmi_bios_characteristics_x1(u8 code, const char *prefix)
++static void dmi_bios_characteristics_x1(u8 code)
+ {
+ 	/* 7.1.2.1 */
+ 	static const char *characteristics[] = {
+@@ -409,11 +407,10 @@ static void dmi_bios_characteristics_x1(u8 code, const char *prefix)
+ 
+ 	for (i = 0; i <= 7; i++)
+ 		if (code & (1 << i))
+-			printf("%s%s\n",
+-				prefix, characteristics[i]);
++			pr_list_item("%s", characteristics[i]);
+ }
+ 
+-static void dmi_bios_characteristics_x2(u8 code, const char *prefix)
++static void dmi_bios_characteristics_x2(u8 code)
+ {
+ 	/* 37.1.2.2 */
+ 	static const char *characteristics[] = {
+@@ -427,8 +424,7 @@ static void dmi_bios_characteristics_x2(u8 code, const char *prefix)
+ 
+ 	for (i = 0; i <= 4; i++)
+ 		if (code & (1 << i))
+-			printf("%s%s\n",
+-				prefix, characteristics[i]);
++			pr_list_item("%s", characteristics[i]);
+ }
+ 
+ /*
+@@ -521,7 +517,7 @@ static const char *dmi_system_wake_up_type(u8 code)
+  * 7.3 Base Board Information (Type 2)
+  */
+ 
+-static void dmi_base_board_features(u8 code, const char *prefix)
++static void dmi_base_board_features(u8 code)
+ {
+ 	/* 7.3.1 */
+ 	static const char *features[] = {
+@@ -533,17 +529,17 @@ static void dmi_base_board_features(u8 code, const char *prefix)
+ 	};
+ 
+ 	if ((code & 0x1F) == 0)
+-		printf(" None\n");
++		pr_list_start("Features", "%s", "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start("Features", NULL);
+ 		for (i = 0; i <= 4; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s\n",
+-					prefix, features[i]);
++				pr_list_item("%s", features[i]);
+ 	}
++	pr_list_end();
+ }
+ 
+ static const char *dmi_base_board_type(u8 code)
+@@ -570,15 +566,14 @@ static const char *dmi_base_board_type(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_base_board_handles(u8 count, const u8 *p, const char *prefix)
++static void dmi_base_board_handles(u8 count, const u8 *p)
+ {
+ 	int i;
+ 
+-	printf("%sContained Object Handles: %u\n",
+-		prefix, count);
++	pr_list_start("Contained Object Handles", "%u", count);
+ 	for (i = 0; i < count; i++)
+-		printf("%s\t0x%04X\n",
+-			prefix, WORD(p + sizeof(u16) * i));
++		pr_list_item("0x%04X", WORD(p + sizeof(u16) * i));
++	pr_list_end();
+ }
+ 
+ /*
+@@ -693,27 +688,29 @@ static void dmi_chassis_power_cords(u8 code)
+ 		pr_attr("Number Of Power Cords", "%u", code);
+ }
+ 
+-static void dmi_chassis_elements(u8 count, u8 len, const u8 *p, const char *prefix)
++static void dmi_chassis_elements(u8 count, u8 len, const u8 *p)
+ {
+ 	int i;
+ 
+-	printf("%sContained Elements: %u\n",
+-		prefix, count);
++	pr_list_start("Contained Elements", "%u", count);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		if (len >= 0x03)
+ 		{
+-			printf("%s\t%s (",
+-				prefix, p[i * len] & 0x80 ?
++			const char *type;
++
++			type = (p[i * len] & 0x80) ?
+ 				dmi_smbios_structure_type(p[i * len] & 0x7F) :
+-				dmi_base_board_type(p[i * len] & 0x7F));
++				dmi_base_board_type(p[i * len] & 0x7F);
++
+ 			if (p[1 + i * len] == p[2 + i * len])
+-				printf("%u", p[1 + i * len]);
++				pr_list_item("%s (%u)", type, p[1 + i * len]);
+ 			else
+-				printf("%u-%u", p[1 + i * len], p[2 + i * len]);
+-			printf(")\n");
++				pr_list_item("%s (%u-%u)", type, p[1 + i * len],
++					     p[2 + i * len]);
+ 		}
+ 	}
++	pr_list_end();
+ }
+ 
+ /*
+@@ -1033,7 +1030,7 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver)
+ 	}
+ }
+ 
+-static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
++static void dmi_processor_id(const struct dmi_header *h)
+ {
+ 	/* Intel AP-485 revision 36, table 2-4 */
+ 	static const char *flags[32] = {
+@@ -1203,18 +1200,18 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
+ 	}
+ 
+ 	edx = DWORD(p + 4);
+-	printf("%sFlags:", prefix);
+ 	if ((edx & 0xBFEFFBFF) == 0)
+-		printf(" None\n");
++		pr_list_start("Flags", "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start("Flags", NULL);
+ 		for (i = 0; i <= 31; i++)
+ 			if (flags[i] != NULL && edx & (1 << i))
+-				printf("%s\t%s\n", prefix, flags[i]);
++				pr_list_item("%s", flags[i]);
+ 	}
++	pr_list_end();
+ }
+ 
+ static void dmi_processor_voltage(const char *attr, u8 code)
+@@ -1373,7 +1370,7 @@ static void dmi_processor_cache(const char *attr, u16 code, const char *level,
+ 		pr_attr(attr, "0x%04X", code);
+ }
+ 
+-static void dmi_processor_characteristics(u16 code, const char *prefix)
++static void dmi_processor_characteristics(const char *attr, u16 code)
+ {
+ 	/* 7.5.9 */
+ 	static const char *characteristics[] = {
+@@ -1386,15 +1383,16 @@ static void dmi_processor_characteristics(u16 code, const char *prefix)
+ 	};
+ 
+ 	if ((code & 0x00FC) == 0)
+-		printf(" None\n");
++		pr_attr(attr, "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start(attr, NULL);
+ 		for (i = 2; i <= 7; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s\n", prefix, characteristics[i - 2]);
++				pr_list_item("%s", characteristics[i - 2]);
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -1421,7 +1419,7 @@ static const char *dmi_memory_controller_ed_method(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_memory_controller_ec_capabilities(u8 code, const char *prefix)
++static void dmi_memory_controller_ec_capabilities(const char *attr, u8 code)
+ {
+ 	/* 7.6.2 */
+ 	static const char *capabilities[] = {
+@@ -1434,15 +1432,16 @@ static void dmi_memory_controller_ec_capabilities(u8 code, const char *prefix)
+ 	};
+ 
+ 	if ((code & 0x3F) == 0)
+-		printf(" None\n");
++		pr_attr(attr, "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start(attr, NULL);
+ 		for (i = 0; i <= 5; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s\n", prefix, capabilities[i]);
++				pr_list_item("%s", capabilities[i]);
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -1464,7 +1463,7 @@ static const char *dmi_memory_controller_interleave(u8 code)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_memory_controller_speeds(u16 code, const char *prefix)
++static void dmi_memory_controller_speeds(const char *attr, u16 code)
+ {
+ 	/* 7.6.4 */
+ 	const char *speeds[] = {
+@@ -1476,34 +1475,34 @@ static void dmi_memory_controller_speeds(u16 code, const char *prefix)
+ 	};
+ 
+ 	if ((code & 0x001F) == 0)
+-		printf(" None\n");
++		pr_attr(attr, "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start(attr, NULL);
+ 		for (i = 0; i <= 4; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s\n", prefix, speeds[i]);
++				pr_list_item("%s", speeds[i]);
++		pr_list_end();
+ 	}
+ }
+ 
+-static void dmi_memory_controller_slots(u8 count, const u8 *p, const char *prefix)
++static void dmi_memory_controller_slots(u8 count, const u8 *p)
+ {
+ 	int i;
+ 
+-	printf("%sAssociated Memory Slots: %u\n",
+-		prefix, count);
++	pr_list_start("Associated Memory Slots", "%u", count);
+ 	for (i = 0; i < count; i++)
+-		printf("%s\t0x%04X\n",
+-			prefix, WORD(p + sizeof(u16) * i));
++		pr_list_item("0x%04X", WORD(p + sizeof(u16) * i));
++	pr_list_end();
+ }
+ 
+ /*
+  * 7.7 Memory Module Information (Type 6)
+  */
+ 
+-static void dmi_memory_module_types(u16 code, const char *sep)
++static void dmi_memory_module_types(const char *attr, u16 code, int flat)
+ {
+ 	/* 7.7.1 */
+ 	static const char *types[] = {
+@@ -1521,14 +1520,34 @@ static void dmi_memory_module_types(u16 code, const char *sep)
+ 	};
+ 
+ 	if ((code & 0x07FF) == 0)
+-		printf(" None");
++		pr_attr(attr, "None");
++	else if (flat)
++	{
++		char type_str[68];
++		int i, off = 0;
++
++		for (i = 0; i <= 10; i++)
++		{
++			if (code & (1 << i))
++			{
++				/* Insert space if not the first value */
++				off += sprintf(type_str + off,
++					       off ? " %s" :"%s",
++					       types[i]);
++			}
++		}
++		if (off)
++			pr_attr(attr, type_str);
++	}
+ 	else
+ 	{
+ 		int i;
+ 
++		pr_list_start(attr, NULL);
+ 		for (i = 0; i <= 10; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s", sep, types[i]);
++				pr_list_item("%s", types[i]);
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -1649,7 +1668,7 @@ static void dmi_cache_size(const char *attr, u16 code)
+ 			 (((u32)code & 0x8000LU) << 16) | (code & 0x7FFFLU));
+ }
+ 
+-static void dmi_cache_types(u16 code, const char *sep)
++static void dmi_cache_types(const char *attr, u16 code, int flat)
+ {
+ 	/* 7.8.2 */
+ 	static const char *types[] = {
+@@ -1663,14 +1682,34 @@ static void dmi_cache_types(u16 code, const char *sep)
+ 	};
+ 
+ 	if ((code & 0x007F) == 0)
+-		printf(" None");
++		pr_attr(attr, "None");
++	else if (flat)
++	{
++		char type_str[70];
++		int i, off = 0;
++
++		for (i = 0; i <= 6; i++)
++		{
++			if (code & (1 << i))
++			{
++				/* Insert space if not the first value */
++				off += sprintf(type_str + off,
++					       off ? " %s" :"%s",
++					       types[i]);
++			}
++		}
++		if (off)
++			pr_attr(attr, type_str);
++	}
+ 	else
+ 	{
+ 		int i;
+ 
++		pr_list_start(attr, NULL);
+ 		for (i = 0; i <= 6; i++)
+ 			if (code & (1 << i))
+-				printf("%s%s", sep, types[i]);
++				pr_list_item("%s", types[i]);
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -2053,7 +2092,7 @@ static void dmi_slot_id(u8 code1, u8 code2, u8 type)
+ 	}
+ }
+ 
+-static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
++static void dmi_slot_characteristics(const char *attr, u8 code1, u8 code2)
+ {
+ 	/* 7.10.6 */
+ 	static const char *characteristics1[] = {
+@@ -2074,20 +2113,21 @@ static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
+ 	};
+ 
+ 	if (code1 & (1 << 0))
+-		printf(" Unknown\n");
++		pr_attr(attr, "Unknown");
+ 	else if ((code1 & 0xFE) == 0 && (code2 & 0x07) == 0)
+-		printf(" None\n");
++		pr_attr(attr, "None");
+ 	else
+ 	{
+ 		int i;
+ 
+-		printf("\n");
++		pr_list_start(attr, NULL);
+ 		for (i = 1; i <= 7; i++)
+ 			if (code1 & (1 << i))
+-				printf("%s%s\n", prefix, characteristics1[i - 1]);
++				pr_list_item("%s", characteristics1[i - 1]);
+ 		for (i = 0; i <= 3; i++)
+ 			if (code2 & (1 << i))
+-				printf("%s%s\n", prefix, characteristics2[i]);
++				pr_list_item("%s", characteristics2[i]);
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -2199,15 +2239,14 @@ static void dmi_system_configuration_options(const struct dmi_header *h)
+  * 7.14 BIOS Language Information (Type 13)
+  */
+ 
+-static void dmi_bios_languages(const struct dmi_header *h, const char *prefix)
++static void dmi_bios_languages(const struct dmi_header *h)
+ {
+ 	u8 *p = h->data + 4;
+ 	u8 count = p[0x00];
+ 	int i;
+ 
+ 	for (i = 1; i <= count; i++)
+-		printf("%s%s\n",
+-			prefix, dmi_string(h, i));
++		pr_list_item("%s", dmi_string(h, i));
+ }
+ 
+ static const char *dmi_bios_language_format(u8 code)
+@@ -2222,14 +2261,14 @@ static const char *dmi_bios_language_format(u8 code)
+  * 7.15 Group Associations (Type 14)
+  */
+ 
+-static void dmi_group_associations_items(u8 count, const u8 *p, const char *prefix)
++static void dmi_group_associations_items(u8 count, const u8 *p)
+ {
+ 	int i;
+ 
+ 	for (i = 0; i < count; i++)
+ 	{
+-		printf("%s0x%04X (%s)\n",
+-			prefix, WORD(p + 3 * i + 1),
++		pr_list_item("0x%04X (%s)",
++			WORD(p + 3 * i + 1),
+ 			dmi_smbios_structure_type(p[3 * i]));
+ 	}
+ }
+@@ -3936,7 +3975,7 @@ static void dmi_tpm_vendor_id(const u8 *p)
+ 	pr_attr("Vendor ID", "%s", vendor_id);
+ }
+ 
+-static void dmi_tpm_characteristics(u64 code, const char *prefix)
++static void dmi_tpm_characteristics(u64 code)
+ {
+ 	/* 7.1.1 */
+ 	static const char *characteristics[] = {
+@@ -3952,15 +3991,13 @@ static void dmi_tpm_characteristics(u64 code, const char *prefix)
+ 	 */
+ 	if (code.l & (1 << 2))
+ 	{
+-		printf("%s%s\n",
+-			prefix, characteristics[0]);
++		pr_list_item("%s", characteristics[0]);
+ 		return;
+ 	}
+ 
+ 	for (i = 3; i <= 5; i++)
+ 		if (code.l & (1 << i))
+-			printf("%s%s\n",
+-				prefix, characteristics[i - 2]);
++			pr_list_item("%s", characteristics[i - 2]);
+ }
+ 
+ /*
+@@ -3997,12 +4034,13 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4);
+ 			}
+ 			dmi_bios_rom_size(data[0x09], h->length < 0x1A ? 16 : WORD(data + 0x18));
+-			printf("\tCharacteristics:\n");
+-			dmi_bios_characteristics(QWORD(data + 0x0A), "\t\t");
++			pr_list_start("Characteristics", NULL);
++			dmi_bios_characteristics(QWORD(data + 0x0A));
++			pr_list_end();
+ 			if (h->length < 0x13) break;
+-			dmi_bios_characteristics_x1(data[0x12], "\t\t");
++			dmi_bios_characteristics_x1(data[0x12]);
+ 			if (h->length < 0x14) break;
+-			dmi_bios_characteristics_x2(data[0x13], "\t\t");
++			dmi_bios_characteristics_x2(data[0x13]);
+ 			if (h->length < 0x18) break;
+ 			if (data[0x14] != 0xFF && data[0x15] != 0xFF)
+ 				pr_attr("BIOS Revision", "%u.%u",
+@@ -4049,8 +4087,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			pr_attr("Asset Tag", "%s",
+ 				dmi_string(h, data[0x08]));
+ 			if (h->length < 0x0A) break;
+-			printf("\tFeatures:");
+-			dmi_base_board_features(data[0x09], "\t\t");
++			dmi_base_board_features(data[0x09]);
+ 			if (h->length < 0x0E) break;
+ 			pr_attr("Location In Chassis", "%s",
+ 				dmi_string(h, data[0x0A]));
+@@ -4062,7 +4099,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x0F) break;
+ 			if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+ 			if (!(opt.flags & FLAG_QUIET))
+-				dmi_base_board_handles(data[0x0E], data + 0x0F, "\t");
++				dmi_base_board_handles(data[0x0E], data + 0x0F);
+ 			break;
+ 
+ 		case 3: /* 7.4 Chassis Information */
+@@ -4097,7 +4134,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			dmi_chassis_power_cords(data[0x12]);
+ 			if (h->length < 0x15) break;
+ 			if (h->length < 0x15 + data[0x13] * data[0x14]) break;
+-			dmi_chassis_elements(data[0x13], data[0x14], data + 0x15, "\t");
++			dmi_chassis_elements(data[0x13], data[0x14], data + 0x15);
+ 			if (h->length < 0x16 + data[0x13] * data[0x14]) break;
+ 			pr_attr("SKU Number", "%s",
+ 				dmi_string(h, data[0x15 + data[0x13] * data[0x14]]));
+@@ -4114,7 +4151,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				dmi_processor_family(h, ver));
+ 			pr_attr("Manufacturer", "%s",
+ 				dmi_string(h, data[0x07]));
+-			dmi_processor_id(h, "\t");
++			dmi_processor_id(h);
+ 			pr_attr("Version", "%s",
+ 				dmi_string(h, data[0x10]));
+ 			dmi_processor_voltage("Voltage", data[0x11]);
+@@ -4158,8 +4195,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				pr_attr("Thread Count", "%u",
+ 					h->length >= 0x30 && data[0x25] == 0xFF ?
+ 					WORD(data + 0x2E) : data[0x25]);
+-			printf("\tCharacteristics:");
+-			dmi_processor_characteristics(WORD(data + 0x26), "\t\t");
++			dmi_processor_characteristics("Characteristics",
++						      WORD(data + 0x26));
+ 			break;
+ 
+ 		case 5: /* 7.6 Memory Controller Information */
+@@ -4167,8 +4204,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x0F) break;
+ 			pr_attr("Error Detecting Method", "%s",
+ 				dmi_memory_controller_ed_method(data[0x04]));
+-			printf("\tError Correcting Capabilities:");
+-			dmi_memory_controller_ec_capabilities(data[0x05], "\t\t");
++			dmi_memory_controller_ec_capabilities("Error Correcting Capabilities",
++							      data[0x05]);
+ 			pr_attr("Supported Interleave", "%s",
+ 				dmi_memory_controller_interleave(data[0x06]));
+ 			pr_attr("Current Interleave", "%s",
+@@ -4177,17 +4214,16 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				1 << data[0x08]);
+ 			pr_attr("Maximum Total Memory Size", "%u MB",
+ 				data[0x0E] * (1 << data[0x08]));
+-			printf("\tSupported Speeds:");
+-			dmi_memory_controller_speeds(WORD(data + 0x09), "\t\t");
+-			printf("\tSupported Memory Types:");
+-			dmi_memory_module_types(WORD(data + 0x0B), "\n\t\t");
+-			printf("\n");
++			dmi_memory_controller_speeds("Supported Speeds",
++						     WORD(data + 0x09));
++			dmi_memory_module_types("Supported Memory Types",
++						WORD(data + 0x0B), 0);
+ 			dmi_processor_voltage("Memory Module Voltage", data[0x0D]);
+ 			if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+-			dmi_memory_controller_slots(data[0x0E], data + 0x0F, "\t");
++			dmi_memory_controller_slots(data[0x0E], data + 0x0F);
+ 			if (h->length < 0x10 + data[0x0E] * sizeof(u16)) break;
+-			printf("\tEnabled Error Correcting Capabilities:");
+-			dmi_memory_controller_ec_capabilities(data[0x0F + data[0x0E] * sizeof(u16)], "\t\t");
++			dmi_memory_controller_ec_capabilities("Enabled Error Correcting Capabilities",
++							      data[0x0F + data[0x0E] * sizeof(u16)]);
+ 			break;
+ 
+ 		case 6: /* 7.7 Memory Module Information */
+@@ -4197,9 +4233,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				dmi_string(h, data[0x04]));
+ 			dmi_memory_module_connections(data[0x05]);
+ 			dmi_memory_module_speed("Current Speed", data[0x06]);
+-			printf("\tType:");
+-			dmi_memory_module_types(WORD(data + 0x07), " ");
+-			printf("\n");
++			dmi_memory_module_types("Type", WORD(data + 0x07), 1);
+ 			dmi_memory_module_size("Installed Size", data[0x09]);
+ 			dmi_memory_module_size("Enabled Size", data[0x0A]);
+ 			dmi_memory_module_error(data[0x0B]);
+@@ -4226,12 +4260,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				dmi_cache_size_2("Maximum Size", DWORD(data + 0x13));
+ 			else
+ 				dmi_cache_size("Maximum Size", WORD(data + 0x07));
+-			printf("\tSupported SRAM Types:");
+-			dmi_cache_types(WORD(data + 0x0B), "\n\t\t");
+-			printf("\n");
+-			printf("\tInstalled SRAM Type:");
+-			dmi_cache_types(WORD(data + 0x0D), " ");
+-			printf("\n");
++			dmi_cache_types("Supported SRAM Types", WORD(data + 0x0B), 0);
++			dmi_cache_types("Installed SRAM Type", WORD(data + 0x0D), 1);
+ 			if (h->length < 0x13) break;
+ 			dmi_memory_module_speed("Speed", data[0x0F]);
+ 			pr_attr("Error Correction Type", "%s",
+@@ -4270,11 +4300,10 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			pr_attr("Length", "%s",
+ 				dmi_slot_length(data[0x08]));
+ 			dmi_slot_id(data[0x09], data[0x0A], data[0x05]);
+-			printf("\tCharacteristics:");
+ 			if (h->length < 0x0D)
+-				dmi_slot_characteristics(data[0x0B], 0x00, "\t\t");
++				dmi_slot_characteristics("Characteristics", data[0x0B], 0x00);
+ 			else
+-				dmi_slot_characteristics(data[0x0B], data[0x0C], "\t\t");
++				dmi_slot_characteristics("Characteristics", data[0x0B], data[0x0C]);
+ 			if (h->length < 0x11) break;
+ 			dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10]);
+ 			if (h->length < 0x13) break;
+@@ -4308,8 +4337,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				pr_attr("Language Description Format", "%s",
+ 					dmi_bios_language_format(data[0x05]));
+ 			}
+-			printf("\tInstallable Languages: %u\n", data[0x04]);
+-			dmi_bios_languages(h, "\t\t");
++			pr_list_start("Installable Languages", "%u", data[0x04]);
++			dmi_bios_languages(h);
++			pr_list_end();
+ 			pr_attr("Currently Installed Language", "%s",
+ 				dmi_string(h, data[0x15]));
+ 			break;
+@@ -4319,9 +4349,10 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			if (h->length < 0x05) break;
+ 			pr_attr("Name", "%s",
+ 				dmi_string(h, data[0x04]));
+-			printf("\tItems: %u\n",
++			pr_list_start("Items", "%u",
+ 				(h->length - 0x05) / 3);
+-			dmi_group_associations_items((h->length - 0x05) / 3, data + 0x05, "\t\t");
++			dmi_group_associations_items((h->length - 0x05) / 3, data + 0x05);
++			pr_list_end();
+ 			break;
+ 
+ 		case 15: /* 7.16 System Event Log */
+@@ -5002,8 +5033,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 					break;
+ 			}
+ 			pr_attr("Description", "%s", dmi_string(h, data[0x12]));
+-			printf("\tCharacteristics:\n");
+-			dmi_tpm_characteristics(QWORD(data + 0x13), "\t\t");
++			pr_list_start("Characteristics", NULL);
++			dmi_tpm_characteristics(QWORD(data + 0x13));
++			pr_list_end();
+ 			if (h->length < 0x1F) break;
+ 			pr_attr("OEM-specific Information", "0x%08X",
+ 				DWORD(data + 0x1B));
+diff --git a/dmioutput.c b/dmioutput.c
+index 2330b65755c9..4c8a32a3569a 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -71,3 +71,38 @@ void pr_attr(const char *name, const char *format, ...)
+ 	va_end(args);
+ 	printf("\n");
+ }
++
++void pr_list_start(const char *name, const char *format, ...)
++{
++	va_list args;
++
++	printf("\t%s:", name);
++
++	/* format is optional, skip value if not provided */
++	if (format)
++	{
++		printf(" ");
++		va_start(args, format);
++		vprintf(format, args);
++		va_end(args);
++	}
++	printf("\n");
++
++}
++
++void pr_list_item(const char *format, ...)
++{
++	va_list args;
++
++	printf("\t\t");
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
++
++void pr_list_end(void)
++{
++	/* a no-op for text output */
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 981dcb45f26a..33f09c4452bb 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -26,3 +26,6 @@ void pr_info(const char *format, ...);
+ void pr_handle(const struct dmi_header *h);
+ void pr_handle_name(const char *format, ...);
+ void pr_attr(const char *name, const char *format, ...);
++void pr_list_start(const char *name, const char *format, ...);
++void pr_list_item(const char *format, ...);
++void pr_list_end(void);
+-- 
+2.17.1
+
diff --git a/SOURCES/0016-dmidecode-Add-helper-function-pr_subattr.patch b/SOURCES/0016-dmidecode-Add-helper-function-pr_subattr.patch
new file mode 100644
index 0000000..6e92b55
--- /dev/null
+++ b/SOURCES/0016-dmidecode-Add-helper-function-pr_subattr.patch
@@ -0,0 +1,307 @@
+From 20d50c70b98d2a7013b604d575656a098d294f7f Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:00:27 +0200
+Subject: [PATCH 16/23] dmidecode: Add helper function pr_subattr
+
+Print all second-level attributes through a helper function pr_subattr.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 68 ++++++++++++++++++++++++++++-------------------------
+ dmioem.c    | 12 +++++-----
+ dmioutput.c | 12 ++++++++++
+ dmioutput.h |  1 +
+ 4 files changed, 55 insertions(+), 38 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 5a5299ed13ba..3d1da955bab8 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -431,7 +431,8 @@ static void dmi_bios_characteristics_x2(u8 code)
+  * 7.2 System Information (Type 1)
+  */
+ 
+-static void dmi_system_uuid(const char *attr, const u8 *p, u16 ver)
++static void dmi_system_uuid(void (*print_cb)(const char *name, const char *format, ...),
++			    const char *attr, const u8 *p, u16 ver)
+ {
+ 	int only0xFF = 1, only0x00 = 1;
+ 	int i;
+@@ -444,16 +445,16 @@ static void dmi_system_uuid(const char *attr, const u8 *p, u16 ver)
+ 
+ 	if (only0xFF)
+ 	{
+-		if (attr)
+-			pr_attr(attr, "Not Present");
++		if (print_cb)
++			print_cb(attr, "Not Present");
+ 		else
+ 			printf("Not Present\n");
+ 		return;
+ 	}
+ 	if (only0x00)
+ 	{
+-		if (attr)
+-			pr_attr(attr, "Not Settable");
++		if (print_cb)
++			print_cb(attr, "Not Settable");
+ 		else
+ 			printf("Not Settable\n");
+ 		return;
+@@ -469,8 +470,8 @@ static void dmi_system_uuid(const char *attr, const u8 *p, u16 ver)
+ 	 */
+ 	if (ver >= 0x0206)
+ 	{
+-		if (attr)
+-			pr_attr(attr,
++		if (print_cb)
++			print_cb(attr,
+ 				"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ 				p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
+ 				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+@@ -481,8 +482,8 @@ static void dmi_system_uuid(const char *attr, const u8 *p, u16 ver)
+ 	}
+ 	else
+ 	{
+-		if (attr)
+-			pr_attr(attr,
++		if (print_cb)
++			print_cb(attr,
+ 				"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ 				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+ 				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+@@ -3655,7 +3656,7 @@ static const char *dmi_address_decode(u8 *data, char *storage, u8 addrtype)
+ /*
+  * DSP0270: 8.5: Parse the protocol record format
+  */
+-static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
++static void dmi_parse_protocol_record(u8 *rec)
+ {
+ 	u8 rid;
+ 	u8 rlen;
+@@ -3666,6 +3667,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	u8 hlen;
+ 	const char *addrstr;
+ 	const char *hname;
++	char attr[38];
+ 
+ 	/* DSP0270: 8.5: Protocol Identifier */
+ 	rid = rec[0x0];
+@@ -3674,7 +3676,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	/* DSP0270: 8.5: Protocol Record Data */
+ 	rdata = &rec[0x2];
+ 
+-	printf("%sProtocol ID: %02x (%s)\n", prefix, rid,
++	pr_attr("Protocol ID", "%02x (%s)", rid,
+ 		dmi_protocol_record_type(rid));
+ 
+ 	/*
+@@ -3704,8 +3706,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	 * endianess of the field is always little after version 2.6.0
+ 	 * we can just pick a sufficiently recent version here.
+ 	 */
+-	printf("%s\tService UUID: ", prefix);
+-	dmi_system_uuid(NULL, &rdata[0], 0x311);	/* FIXME */
++	dmi_system_uuid(pr_subattr, "Service UUID", &rdata[0], 0x311);
+ 
+ 	/*
+ 	 * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type
+@@ -3713,13 +3714,13 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	 * uses decimal, so as to make it more comparable
+ 	 */
+ 	assign_val = rdata[16];
+-	printf("%s\tHost IP Assignment Type: %s\n", prefix,
++	pr_subattr("Host IP Assignment Type", "%s",
+ 		dmi_protocol_assignment_type(assign_val));
+ 
+ 	/* DSP0270: 8.6: Redfish Over IP Host Address format */
+ 	addrtype = rdata[17];
+ 	addrstr = dmi_address_type(addrtype);
+-	printf("%s\tHost IP Address Format: %s\n", prefix,
++	pr_subattr("Host IP Address Format", "%s",
+ 		addrstr);
+ 
+ 	/* DSP0270: 8.6 IP Assignment types */
+@@ -3727,24 +3728,26 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 	if (assign_val == 0x1 || assign_val == 0x3)
+ 	{
+ 		/* DSP0270: 8.6: the Host IPv[4|6] Address */
+-		printf("%s\t%s Address: %s\n", prefix, addrstr,
++		sprintf(attr, "%s Address", addrstr);
++		pr_subattr(attr, "%s",
+ 			dmi_address_decode(&rdata[18], buf, addrtype));
+ 
+ 		/* DSP0270: 8.6: Prints the Host IPv[4|6] Mask */
+-		printf("%s\t%s Mask: %s\n", prefix, addrstr,
++		sprintf(attr, "%s Mask", addrstr);
++		pr_subattr(attr, "%s",
+ 			dmi_address_decode(&rdata[34], buf, addrtype));
+ 	}
+ 
+ 	/* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */
+ 	assign_val = rdata[50];
+ 	/* Redfish Service IP Discovery type mirrors Host IP Assignment type */
+-	printf("%s\tRedfish Service IP Discovery Type: %s\n", prefix,
++	pr_subattr("Redfish Service IP Discovery Type", "%s",
+ 		dmi_protocol_assignment_type(assign_val));
+ 
+ 	/* DSP0270: 8.6: Get the Redfish Service IP Address Format */
+ 	addrtype = rdata[51];
+ 	addrstr = dmi_address_type(addrtype);
+-	printf("%s\tRedfish Service IP Address Format: %s\n", prefix,
++	pr_subattr("Redfish Service IP Address Format", "%s",
+ 		addrstr);
+ 
+ 	if (assign_val == 0x1 || assign_val == 0x3)
+@@ -3753,20 +3756,22 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 		u32 vlan;
+ 
+ 		/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Address */
+-		printf("%s\t%s Redfish Service Address: %s\n", prefix,
+-			addrstr, dmi_address_decode(&rdata[52], buf,
++		sprintf(attr, "%s Redfish Service Address", addrstr);
++		pr_subattr(attr, "%s",
++			dmi_address_decode(&rdata[52], buf,
+ 			addrtype));
+ 
+ 		/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Mask */
+-		printf("%s\t%s Redfish Service Mask: %s\n", prefix,
+-			addrstr, dmi_address_decode(&rdata[68], buf,
++		sprintf(attr, "%s Redfish Service Mask", addrstr);
++		pr_subattr(attr, "%s",
++			dmi_address_decode(&rdata[68], buf,
+ 			addrtype));
+ 
+ 		/* DSP0270: 8.6: Redfish vlan and port info */
+ 		port = WORD(&rdata[84]);
+ 		vlan = DWORD(&rdata[86]);
+-		printf("%s\tRedfish Service Port: %hu\n", prefix, port);
+-		printf("%s\tRedfish Service Vlan: %u\n", prefix, vlan);
++		pr_subattr("Redfish Service Port", "%hu", port);
++		pr_subattr("Redfish Service Vlan", "%u", vlan);
+ 	}
+ 
+ 	/* DSP0270: 8.6: Redfish host length and name */
+@@ -3783,7 +3788,7 @@ static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+ 		hname = out_of_spec;
+ 		hlen = strlen(out_of_spec);
+ 	}
+-	printf("%s\tRedfish Service Hostname: %.*s\n", prefix, hlen, hname);
++	pr_subattr("Redfish Service Hostname", "%.*s", hlen, hname);
+ }
+ 
+ /*
+@@ -3803,8 +3808,7 @@ static const char *dmi_parse_device_type(u8 type)
+ 	return out_of_spec;
+ }
+ 
+-static void dmi_parse_controller_structure(const struct dmi_header *h,
+-					   const char *prefix)
++static void dmi_parse_controller_structure(const struct dmi_header *h)
+ {
+ 	int i;
+ 	u8 *data = h->data;
+@@ -3936,7 +3940,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h,
+ 				return;
+ 			}
+ 
+-			dmi_parse_protocol_record(prefix, rec);
++			dmi_parse_protocol_record(rec);
+ 
+ 			/*
+ 			 * DSP0270: 8.6
+@@ -4062,7 +4066,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 			pr_attr("Serial Number", "%s",
+ 				dmi_string(h, data[0x07]));
+ 			if (h->length < 0x19) break;
+-			dmi_system_uuid("UUID", data + 0x08, ver);
++			dmi_system_uuid(pr_attr, "UUID", data + 0x08, ver);
+ 			pr_attr("Wake-up Type", "%s",
+ 				dmi_system_wake_up_type(data[0x18]));
+ 			if (h->length < 0x1B) break;
+@@ -5002,7 +5006,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				}
+ 			}
+ 			else
+-				dmi_parse_controller_structure(h, "\t");
++				dmi_parse_controller_structure(h);
+ 			break;
+ 
+ 		case 43: /* 7.44 TPM Device */
+@@ -5104,7 +5108,7 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
+ 				printf("%u.%u\n", data[key - 1], data[key]);
+ 			break;
+ 		case 0x108:
+-			dmi_system_uuid(NULL, data + offset, ver);
++			dmi_system_uuid(NULL, NULL, data + offset, ver);
+ 			break;
+ 		case 0x305:
+ 			printf("%s\n", dmi_chassis_type(data[offset]));
+diff --git a/dmioem.c b/dmioem.c
+index 873ec1068c16..9d6ec7520287 100644
+--- a/dmioem.c
++++ b/dmioem.c
+@@ -97,10 +97,10 @@ static int dmi_decode_acer(const struct dmi_header *h)
+ 			if (h->length < 0x0F) break;
+ 			cap = WORD(data + 0x04);
+ 			pr_attr("Function bitmap for Communication Button", "0x%04hx", cap);
+-			printf("\t\tWiFi: %s\n", cap & 0x0001 ? "Yes" : "No");
+-			printf("\t\t3G: %s\n", cap & 0x0040 ? "Yes" : "No");
+-			printf("\t\tWiMAX: %s\n", cap & 0x0080 ? "Yes" : "No");
+-			printf("\t\tBluetooth: %s\n", cap & 0x0800 ? "Yes" : "No");
++			pr_subattr("WiFi", "%s", cap & 0x0001 ? "Yes" : "No");
++			pr_subattr("3G", "%s", cap & 0x0040 ? "Yes" : "No");
++			pr_subattr("WiMAX", "%s", cap & 0x0080 ? "Yes" : "No");
++			pr_subattr("Bluetooth", "%s", cap & 0x0800 ? "Yes" : "No");
+ 			pr_attr("Function bitmap for Application Button", "0x%04hx", WORD(data + 0x06));
+ 			pr_attr("Function bitmap for Media Button", "0x%04hx", WORD(data + 0x08));
+ 			pr_attr("Function bitmap for Display Button", "0x%04hx", WORD(data + 0x0A));
+@@ -278,8 +278,8 @@ static int dmi_decode_hp(const struct dmi_header *h)
+ 			if (h->length < 0x14) break;
+ 			feat = DWORD(data + 0x10);
+ 			pr_attr("Misc. Features", "0x%08x", feat);
+-			printf("\t\tiCRU: %s\n", feat & 0x0001 ? "Yes" : "No");
+-			printf("\t\tUEFI: %s\n", feat & 0x1400 ? "Yes" : "No");
++			pr_subattr("iCRU", "%s", feat & 0x0001 ? "Yes" : "No");
++			pr_subattr("UEFI", "%s", feat & 0x1400 ? "Yes" : "No");
+ 			break;
+ 
+ 		default:
+diff --git a/dmioutput.c b/dmioutput.c
+index 4c8a32a3569a..da04450494f7 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -72,6 +72,18 @@ void pr_attr(const char *name, const char *format, ...)
+ 	printf("\n");
+ }
+ 
++void pr_subattr(const char *name, const char *format, ...)
++{
++	va_list args;
++
++	printf("\t\t%s: ", name);
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
++
+ void pr_list_start(const char *name, const char *format, ...)
+ {
+ 	va_list args;
+diff --git a/dmioutput.h b/dmioutput.h
+index 33f09c4452bb..58ca5a854a79 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -26,6 +26,7 @@ void pr_info(const char *format, ...);
+ void pr_handle(const struct dmi_header *h);
+ void pr_handle_name(const char *format, ...);
+ void pr_attr(const char *name, const char *format, ...);
++void pr_subattr(const char *name, const char *format, ...);
+ void pr_list_start(const char *name, const char *format, ...);
+ void pr_list_item(const char *format, ...);
+ void pr_list_end(void);
+-- 
+2.17.1
+
diff --git a/SOURCES/0017-dmidecode-Use-the-print-helpers-in-dump-mode-too.patch b/SOURCES/0017-dmidecode-Use-the-print-helpers-in-dump-mode-too.patch
new file mode 100644
index 0000000..7627758
--- /dev/null
+++ b/SOURCES/0017-dmidecode-Use-the-print-helpers-in-dump-mode-too.patch
@@ -0,0 +1,95 @@
+From da06888d08b9f1108fe89560c06d39675c10cd95 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:00:30 +0200
+Subject: [PATCH 17/23] dmidecode: Use the print helpers in dump mode too
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 30 ++++++++++++++++++------------
+ 1 file changed, 18 insertions(+), 12 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 3d1da955bab8..5e9b9899ec1f 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -220,43 +220,49 @@ static int dmi_bcd_range(u8 value, u8 low, u8 high)
+ 	return 1;
+ }
+ 
+-static void dmi_dump(const struct dmi_header *h, const char *prefix)
++static void dmi_dump(const struct dmi_header *h)
+ {
++	static char raw_data[48];
+ 	int row, i;
++	unsigned int off;
+ 	char *s;
+ 
+-	printf("%sHeader and Data:\n", prefix);
++	pr_list_start("Header and Data", NULL);
+ 	for (row = 0; row < ((h->length - 1) >> 4) + 1; row++)
+ 	{
+-		printf("%s\t", prefix);
++		off = 0;
+ 		for (i = 0; i < 16 && i < h->length - (row << 4); i++)
+-			printf("%s%02X", i ? " " : "",
++			off += sprintf(raw_data + off, i ? " %02X" : "%02X",
+ 			       (h->data)[(row << 4) + i]);
+-		printf("\n");
++		pr_list_item(raw_data);
+ 	}
++	pr_list_end();
+ 
+ 	if ((h->data)[h->length] || (h->data)[h->length + 1])
+ 	{
+-		printf("%sStrings:\n", prefix);
++		pr_list_start("Strings", NULL);
+ 		i = 1;
+ 		while ((s = _dmi_string(h, i++, !(opt.flags & FLAG_DUMP))))
+ 		{
+ 			if (opt.flags & FLAG_DUMP)
+ 			{
+ 				int j, l = strlen(s) + 1;
++
++				off = 0;
+ 				for (row = 0; row < ((l - 1) >> 4) + 1; row++)
+ 				{
+-					printf("%s\t", prefix);
+ 					for (j = 0; j < 16 && j < l - (row << 4); j++)
+-						printf("%s%02X", j ? " " : "",
++						off += sprintf(raw_data + off,
++						       j ? " %02X" : "%02X",
+ 						       (unsigned char)s[(row << 4) + j]);
+-					printf("\n");
++					pr_list_item(raw_data);
+ 				}
+ 				/* String isn't filtered yet so do it now */
+ 				ascii_filter(s, l - 1);
+ 			}
+-			printf("%s\t%s\n", prefix, s);
++			pr_list_item("%s", s);
+ 		}
++		pr_list_end();
+ 	}
+ }
+ 
+@@ -5060,7 +5066,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				return;
+ 			pr_handle_name("%s Type",
+ 				h->type >= 128 ? "OEM-specific" : "Unknown");
+-			dmi_dump(h, "\t");
++			dmi_dump(h);
+ 	}
+ 	printf("\n");
+ }
+@@ -5206,7 +5212,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+ 		{
+ 			if (opt.flags & FLAG_DUMP)
+ 			{
+-				dmi_dump(&h, "\t");
++				dmi_dump(&h);
+ 				printf("\n");
+ 			}
+ 			else
+-- 
+2.17.1
+
diff --git a/SOURCES/0018-dmidecode-Add-helper-function-for-separators.patch b/SOURCES/0018-dmidecode-Add-helper-function-for-separators.patch
new file mode 100644
index 0000000..4d64681
--- /dev/null
+++ b/SOURCES/0018-dmidecode-Add-helper-function-for-separators.patch
@@ -0,0 +1,80 @@
+From 9c7db76f373a1d25e52177b81e97bb9e0f9c32b5 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:00:33 +0200
+Subject: [PATCH 18/23] dmidecode: Add helper function for separators
+
+A simple helper function to print a blank line between records.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c | 9 +++++----
+ dmioutput.c | 5 +++++
+ dmioutput.h | 1 +
+ 3 files changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 5e9b9899ec1f..3551637e7b16 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -5068,7 +5068,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
+ 				h->type >= 128 ? "OEM-specific" : "Unknown");
+ 			dmi_dump(h);
+ 	}
+-	printf("\n");
++	pr_sep();
+ }
+ 
+ static void to_dmi_header(struct dmi_header *h, u8 *data)
+@@ -5195,7 +5195,8 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+ 		if ((unsigned long)(next - buf) > len)
+ 		{
+ 			if (display && !(opt.flags & FLAG_QUIET))
+-				printf("\t<TRUNCATED>\n\n");
++				printf("\t<TRUNCATED>\n");
++			pr_sep();
+ 			data = next;
+ 			break;
+ 		}
+@@ -5213,7 +5214,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+ 			if (opt.flags & FLAG_DUMP)
+ 			{
+ 				dmi_dump(&h);
+-				printf("\n");
++				pr_sep();
+ 			}
+ 			else
+ 				dmi_decode(&h, ver);
+@@ -5271,7 +5272,7 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
+ 				pr_info("Table at 0x%08llX.",
+ 					(unsigned long long)base);
+ 		}
+-		printf("\n");
++		pr_sep();
+ 	}
+ 
+ 	if ((flags & FLAG_NO_FILE_OFFSET) || (opt.flags & FLAG_FROM_DUMP))
+diff --git a/dmioutput.c b/dmioutput.c
+index da04450494f7..ef1c41b2f94e 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -118,3 +118,8 @@ void pr_list_end(void)
+ {
+ 	/* a no-op for text output */
+ }
++
++void pr_sep(void)
++{
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 58ca5a854a79..6b5f0e0e92c5 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -30,3 +30,4 @@ void pr_subattr(const char *name, const char *format, ...);
+ void pr_list_start(const char *name, const char *format, ...);
+ void pr_list_item(const char *format, ...);
+ void pr_list_end(void);
++void pr_sep(void);
+-- 
+2.17.1
+
diff --git a/SOURCES/0019-dmidecode-Add-helper-function-for-structure-errors.patch b/SOURCES/0019-dmidecode-Add-helper-function-for-structure-errors.patch
new file mode 100644
index 0000000..98a87c9
--- /dev/null
+++ b/SOURCES/0019-dmidecode-Add-helper-function-for-structure-errors.patch
@@ -0,0 +1,60 @@
+From 25e63d7757f77a15704175c00193753be4176797 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:00:36 +0200
+Subject: [PATCH 19/23] dmidecode: Add helper function for structure errors
+
+Add a helper function to print structure errors, specifically for
+structures which do not fit in the table.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ dmidecode.c |  2 +-
+ dmioutput.c | 12 ++++++++++++
+ dmioutput.h |  1 +
+ 3 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 3551637e7b16..59f4fe09e642 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -5195,7 +5195,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+ 		if ((unsigned long)(next - buf) > len)
+ 		{
+ 			if (display && !(opt.flags & FLAG_QUIET))
+-				printf("\t<TRUNCATED>\n");
++				pr_struct_err("<TRUNCATED>");
+ 			pr_sep();
+ 			data = next;
+ 			break;
+diff --git a/dmioutput.c b/dmioutput.c
+index ef1c41b2f94e..42f8d3218c43 100644
+--- a/dmioutput.c
++++ b/dmioutput.c
+@@ -123,3 +123,15 @@ void pr_sep(void)
+ {
+ 	printf("\n");
+ }
++
++void pr_struct_err(const char *format, ...)
++{
++	va_list args;
++
++	printf("\t");
++
++	va_start(args, format);
++	vprintf(format, args);
++	va_end(args);
++	printf("\n");
++}
+diff --git a/dmioutput.h b/dmioutput.h
+index 6b5f0e0e92c5..a492ec0eb4e7 100644
+--- a/dmioutput.h
++++ b/dmioutput.h
+@@ -31,3 +31,4 @@ void pr_list_start(const char *name, const char *format, ...);
+ void pr_list_item(const char *format, ...);
+ void pr_list_end(void);
+ void pr_sep(void);
++void pr_struct_err(const char *format, ...);
+-- 
+2.17.1
+
diff --git a/SOURCES/0020-dmidecode-White-space-fixes.patch b/SOURCES/0020-dmidecode-White-space-fixes.patch
new file mode 100644
index 0000000..4dbd844
--- /dev/null
+++ b/SOURCES/0020-dmidecode-White-space-fixes.patch
@@ -0,0 +1,34 @@
+From e0d05fdeb0cd9b6be47fb7e9c11ae6b5794f3df3 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:02:04 +0200
+Subject: [PATCH 20/23] dmidecode: White space fixes
+
+---
+ dmidecode.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/dmidecode.c b/dmidecode.c
+index 59f4fe09e642..d0dc98467bdc 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -3312,7 +3312,7 @@ static void dmi_fixup_type_34(struct dmi_header *h, int display)
+ {
+ 	u8 *p = h->data;
+ 
+- 	/* Make sure the hidden data is ASCII only */
++	/* Make sure the hidden data is ASCII only */
+ 	if (h->length == 0x10
+ 	 && is_printable(p + 0x0B, 0x10 - 0x0B))
+ 	{
+@@ -3603,7 +3603,7 @@ static const char *dmi_protocol_record_type(u8 type)
+ 		"Reserved",
+ 		"IPMI",
+ 		"MCTP",
+-		"Redfish over IP", 	/* 0x4 */
++		"Redfish over IP",	/* 0x4 */
+ 	};
+ 
+ 	if (type <= 0x4)
+-- 
+2.17.1
+
diff --git a/SOURCES/0021-dmidecode-Update-copyright-year.patch b/SOURCES/0021-dmidecode-Update-copyright-year.patch
new file mode 100644
index 0000000..4546f3a
--- /dev/null
+++ b/SOURCES/0021-dmidecode-Update-copyright-year.patch
@@ -0,0 +1,67 @@
+From fd4a87bffcf55db6c9c9d460b1b0b5e6fe9a0f03 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 1 Apr 2020 10:04:27 +0200
+Subject: [PATCH 21/23] dmidecode: Update copyright year
+
+---
+ Makefile    | 2 +-
+ dmidecode.c | 2 +-
+ dmidecode.h | 2 +-
+ dmioem.c    | 2 +-
+ 4 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 5d58266ef2de..97a1782fde3f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,7 +4,7 @@
+ #   VPD Decode
+ #
+ #   Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+-#   Copyright (C) 2002-2015 Jean Delvare <jdelvare@suse.de>
++#   Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
+ #
+ #   This program is free software; you can redistribute it and/or modify
+ #   it under the terms of the GNU General Public License as published by
+diff --git a/dmidecode.c b/dmidecode.c
+index d0dc98467bdc..981fe9697458 100644
+--- a/dmidecode.c
++++ b/dmidecode.c
+@@ -2,7 +2,7 @@
+  * DMI Decode
+  *
+  *   Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+- *   Copyright (C) 2002-2019 Jean Delvare <jdelvare@suse.de>
++ *   Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+diff --git a/dmidecode.h b/dmidecode.h
+index 9ecc1791702d..1dc59a7e5536 100644
+--- a/dmidecode.h
++++ b/dmidecode.h
+@@ -1,7 +1,7 @@
+ /*
+  * This file is part of the dmidecode project.
+  *
+- *   Copyright (C) 2005-2008 Jean Delvare <jdelvare@suse.de>
++ *   Copyright (C) 2005-2020 Jean Delvare <jdelvare@suse.de>
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+diff --git a/dmioem.c b/dmioem.c
+index 9d6ec7520287..60b667416563 100644
+--- a/dmioem.c
++++ b/dmioem.c
+@@ -2,7 +2,7 @@
+  * Decoding of OEM-specific entries
+  * This file is part of the dmidecode project.
+  *
+- *   Copyright (C) 2007-2008 Jean Delvare <jdelvare@suse.de>
++ *   Copyright (C) 2007-2020 Jean Delvare <jdelvare@suse.de>
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+-- 
+2.17.1
+
diff --git a/SOURCES/0022-dmidecode-Add-missing-build-dependencies-on-dmioutpu.patch b/SOURCES/0022-dmidecode-Add-missing-build-dependencies-on-dmioutpu.patch
new file mode 100644
index 0000000..edd3449
--- /dev/null
+++ b/SOURCES/0022-dmidecode-Add-missing-build-dependencies-on-dmioutpu.patch
@@ -0,0 +1,37 @@
+From 51bfbe2d5e8728e47f38eb44464f58ad674c4289 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Tue, 7 Apr 2020 11:55:41 +0200
+Subject: [PATCH 22/23] dmidecode: Add missing build dependencies on
+ dmioutput.h
+
+dmidecode.c and dmioem.c both include dmioutput.h so they must be
+rebuilt if that header file changes.
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ Makefile | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 97a1782fde3f..194a523bdb46 100644
+--- a/Makefile
++++ b/Makefile
+@@ -78,13 +78,13 @@ vpddecode : vpddecode.o vpdopt.o util.o
+ #
+ 
+ dmidecode.o : dmidecode.c version.h types.h util.h config.h dmidecode.h \
+-	      dmiopt.h dmioem.h
++	      dmiopt.h dmioem.h dmioutput.h
+ 	$(CC) $(CFLAGS) -c $< -o $@
+ 
+ dmiopt.o : dmiopt.c config.h types.h util.h dmidecode.h dmiopt.h
+ 	$(CC) $(CFLAGS) -c $< -o $@
+ 
+-dmioem.o : dmioem.c types.h dmidecode.h dmioem.h
++dmioem.o : dmioem.c types.h dmidecode.h dmioem.h dmioutput.h
+ 	$(CC) $(CFLAGS) -c $< -o $@
+ 
+ dmioutput.o : dmioutput.c types.h dmioutput.h
+-- 
+2.17.1
+
diff --git a/SOURCES/0023-Allow-overriding-build-settings-from-the-environment.patch b/SOURCES/0023-Allow-overriding-build-settings-from-the-environment.patch
new file mode 100644
index 0000000..183223b
--- /dev/null
+++ b/SOURCES/0023-Allow-overriding-build-settings-from-the-environment.patch
@@ -0,0 +1,55 @@
+From 5b3c8e9950262fc941bb5b3b3a1275720d47d62d Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Fri, 17 Apr 2020 17:14:15 +0200
+Subject: [PATCH 23/23] Allow overriding build settings from the environment
+
+Let packagers pass their own CC, CFLAGS and LDFLAGS settings. The
+settings which are specific to dmidecode are appended later so that
+they are applied no matter what.
+
+This should fix bug #55805:
+https://savannah.nongnu.org/bugs/?55805
+
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+---
+ Makefile | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 194a523bdb46..7aa729d8ca01 100644
+--- a/Makefile
++++ b/Makefile
+@@ -12,8 +12,13 @@
+ #   (at your option) any later version.
+ #
+ 
+-CC      = gcc
+-CFLAGS  = -W -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \
++CC     ?= gcc
++# Base CFLAGS can be overridden by environment
++CFLAGS ?= -O2
++# When debugging, disable -O2 and enable -g
++#CFLAGS ?= -g
++
++CFLAGS += -W -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \
+           -Wcast-align -Wwrite-strings -Wmissing-prototypes -Winline -Wundef
+ 
+ # Let lseek and mmap support 64-bit wide offsets
+@@ -22,12 +27,8 @@ CFLAGS += -D_FILE_OFFSET_BITS=64
+ #CFLAGS += -DBIGENDIAN
+ #CFLAGS += -DALIGNMENT_WORKAROUND
+ 
+-# When debugging, disable -O2 and enable -g.
+-CFLAGS += -O2
+-#CFLAGS += -g
+-
+-# Pass linker flags here
+-LDFLAGS =
++# Pass linker flags here (can be set from environment too)
++LDFLAGS ?=
+ 
+ DESTDIR =
+ prefix  = /usr/local
+-- 
+2.17.1
+
diff --git a/SPECS/dmidecode.spec b/SPECS/dmidecode.spec
index f87fca5..a677511 100644
--- a/SPECS/dmidecode.spec
+++ b/SPECS/dmidecode.spec
@@ -1,7 +1,7 @@
 Summary:        Tool to analyse BIOS DMI data
 Name:           dmidecode
 Version:        3.2
-Release:        5%{?dist}
+Release:        6%{?dist}
 Epoch:          1
 License:        GPLv2+
 Source0:        http://download.savannah.gnu.org/releases/%{name}/%{name}-%{version}.tar.xz
@@ -25,6 +25,29 @@ Patch14:        0010-dmidecode-Fix-System-Slot-Information-for-PCIe-SSD.patch
 Patch15:        0011-Typo.patch
 Patch16:        0001-dmidecode-Add-enumerated-values-from-SMBIOS-3.3.0.patch
 Patch17:        0002-dmidecode-Decode-system-slot-base-bus-width-and-peer.patch
+Patch18:        0001-dmidecode-Print-type-33-name-unconditionally.patch
+Patch19:        0002-dmidecode-Don-t-choke-on-invalid-processor-voltage.patch
+Patch20:        0003-dmidecode-Simplify-the-formatting-of-memory-error-st.patch
+Patch21:        0004-dmidecode-Fix-the-alignment-of-type-25-name.patch
+Patch22:        0005-dmidecode-Code-indentation-fixes.patch
+Patch23:        0006-dmidecode-Reduce-the-indentation-of-type-42-structur.patch
+Patch24:        0007-dmidecode-Move-type-42-warning-messages-to-stderr.patch
+Patch25:        0008-dmidecode-Refactor-ASCII-filtering-of-DMI-strings.patch
+Patch26:        0009-dmidecode-Add-helper-function-pr_comment.patch
+Patch27:        0010-dmidecode-Add-helper-function-pr_info.patch
+Patch28:        0011-dmidecode-Protect-dmidecode.h-against-double-inclusi.patch
+Patch29:        0012-dmidecode-Add-helper-function-pr_handle.patch
+Patch30:        0013-dmidecode-Add-helper-function-pr_handle_name.patch
+Patch31:        0014-dmidecode-Add-helper-function-pr_attr.patch
+Patch32:        0015-dmidecode-Add-helper-functions-pr_list_start-item-en.patch
+Patch33:        0016-dmidecode-Add-helper-function-pr_subattr.patch
+Patch34:        0017-dmidecode-Use-the-print-helpers-in-dump-mode-too.patch
+Patch35:        0018-dmidecode-Add-helper-function-for-separators.patch
+Patch36:        0019-dmidecode-Add-helper-function-for-structure-errors.patch
+Patch37:        0020-dmidecode-White-space-fixes.patch
+Patch38:        0021-dmidecode-Update-copyright-year.patch
+Patch39:        0022-dmidecode-Add-missing-build-dependencies-on-dmioutpu.patch
+Patch40:        0023-Allow-overriding-build-settings-from-the-environment.patch
 
 BuildRequires:  gcc make
 ExclusiveArch:  %{ix86} x86_64 ia64 aarch64
@@ -60,6 +83,29 @@ I/O ports (e.g. serial, parallel, USB).
 %patch15 -p1
 %patch16 -p1
 %patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+%patch21 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch26 -p1
+%patch27 -p1
+%patch28 -p1
+%patch29 -p1
+%patch30 -p1
+%patch31 -p1
+%patch32 -p1
+%patch33 -p1
+%patch34 -p1
+%patch35 -p1
+%patch36 -p1
+%patch37 -p1
+%patch38 -p1
+%patch39 -p1
+%patch40 -p1
 
 %build
 make %{?_smp_mflags} CFLAGS="%{optflags}" LDFLAGS="%{__global_ldflags}"
@@ -80,6 +126,11 @@ make %{?_smp_mflags} DESTDIR=%{buildroot} prefix=%{_prefix} install-bin install-
 %{_mandir}/man8/*
 
 %changelog
+* Wed May 20 2020 Lianbo Jiang <lijiang@redhat.com> - 1:3.2-6
+- Updated to the latest upstream(5b3c8e995026 ("Allow overriding
+  build settings from the environment"))
+- Resolves: rhbz#1796581
+
 * Sun Oct 27 2019 Lianbo Jiang <lijiang@redhat.com> - 1:3.2-5
 - Fix the "OUT OF SPEC" for type 9
 - Resolves: rhbz#1763678