Blob Blame History Raw
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