enum: Extend printf hook to print flags
Signed-off-by: Thomas Egerer <[email protected]>
This commit is contained in:
committed by
Martin Willi
parent
53013b5f0c
commit
4dc72f5e57
@@ -57,6 +57,39 @@ ENUM_NEXT(test_enum_split_names, SPLIT5, SPLIT5, SPLIT4,
|
||||
"SPLIT5");
|
||||
ENUM_END(test_enum_split_names, SPLIT5);
|
||||
|
||||
/*******************************************************************************
|
||||
* enum flags
|
||||
*/
|
||||
enum {
|
||||
FLAG1 = (1 << 0),
|
||||
FLAG2 = (1 << 1),
|
||||
FLAG3 = (1 << 2),
|
||||
FLAG4 = (1 << 3),
|
||||
FLAG5 = (1 << 4),
|
||||
FLAG6 = (1 << 5),
|
||||
FLAG7 = (1 << 6),
|
||||
FLAG8 = (1 << 7),
|
||||
FLAG9 = (1 << 8),
|
||||
FLAG10 = (1 << 9),
|
||||
FLAG11 = (1 << 10),
|
||||
FLAG12 = (1 << 11),
|
||||
} test_enum_flags;
|
||||
|
||||
ENUM_FLAGS(test_enum_flags_names, FLAG1, FLAG5,
|
||||
"FLAG1", "FLAG2", "FLAG3", "FLAG4", "FLAG5");
|
||||
|
||||
ENUM_FLAGS(test_enum_flags_incomplete_names, FLAG3, FLAG4,
|
||||
"FLAG3", "FLAG4");
|
||||
|
||||
ENUM_FLAGS(test_enum_flags_null_names, FLAG1, FLAG4,
|
||||
"FLAG1", NULL, "FLAG3", NULL);
|
||||
|
||||
ENUM_FLAGS(test_enum_flags_overflow_names, FLAG1, FLAG12,
|
||||
"OVERFLOWFLAGLONGNAME1", "OVERFLOWFLAGLONGNAME2", "OVERFLOWFLAGLONGNAME3",
|
||||
"OVERFLOWFLAGLONGNAME4", "OVERFLOWFLAGLONGNAME5", "OVERFLOWFLAGLONGNAME6",
|
||||
"OVERFLOWFLAGLONGNAME7", "OVERFLOWFLAGLONGNAME8", "OVERFLOWFLAGLONGNAME9",
|
||||
"OVERFLOWFLAGLONGNAME10", "OVERFLOWFLAGLONGNAME11", "OVERFLOWFLAGLONGNAME12");
|
||||
|
||||
/*******************************************************************************
|
||||
* enum_to_name
|
||||
*/
|
||||
@@ -198,11 +231,52 @@ static struct {
|
||||
{256, "(256)"},
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* flag_to_name
|
||||
*/
|
||||
|
||||
static struct {
|
||||
int val;
|
||||
char *str;
|
||||
} printf_tests_flags[] = {
|
||||
{0, "(unset)"},
|
||||
{FLAG1, "FLAG1"},
|
||||
{FLAG2, "FLAG2"},
|
||||
{FLAG3, "FLAG3"},
|
||||
{FLAG4, "FLAG4"},
|
||||
{FLAG5, "FLAG5"},
|
||||
{FLAG1 | FLAG3, "FLAG1 | FLAG3"},
|
||||
{FLAG1 | FLAG3 | 32, "FLAG1 | FLAG3 | (0x20)"},
|
||||
{FLAG1 | FLAG3 | 32 | 64, "FLAG1 | FLAG3 | (0x20) | (0x40)"},
|
||||
{0x20, "(0x20)"},
|
||||
{0x80000000, "(0x80000000)"},
|
||||
{0xFFFFF, "FLAG1 | FLAG2 | FLAG3 | FLAG4 | "
|
||||
"FLAG5 | (0x20) | (0x40) | (0x80) | "
|
||||
"(0x100) | (0x200) | (0x400) | (0x800) | "
|
||||
"(0x1000) | (0x2000) | (0x4000) | (0x8000) | "
|
||||
"(0x10000) | (0x20000) | (0x40000) | (0x80000)"},
|
||||
}, printf_tests_flags_incomplete[] = {
|
||||
{FLAG1, "(0x1)"},
|
||||
{FLAG1 | FLAG2 | FLAG3, "(0x1) | (0x2) | FLAG3"},
|
||||
{FLAG3 | FLAG4 | FLAG5, "FLAG3 | FLAG4 | (0x10)"},
|
||||
}, printf_tests_flags_null[] = {
|
||||
{FLAG1 | FLAG2 | FLAG3 | FLAG4, "FLAG1 | FLAG3"},
|
||||
}, printf_tests_flags_overflow[] = {
|
||||
{0xFFFFFFFF, "(0xFFFFFFFF)"},
|
||||
}, printf_tests_flags_noflagenum[] = {
|
||||
{-1, "(-1)"},
|
||||
{6435, "(6435)"},
|
||||
}, enum_flags_to_string_tests[] = {
|
||||
{-1, NULL},
|
||||
{6435, NULL},
|
||||
};
|
||||
|
||||
START_TEST(test_enum_printf_hook_cont)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_cont_names, printf_tests_cont[_i].val);
|
||||
snprintf(buf, sizeof(buf), "%N",
|
||||
test_enum_cont_names, printf_tests_cont[_i].val);
|
||||
ck_assert_str_eq(printf_tests_cont[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
@@ -211,11 +285,89 @@ START_TEST(test_enum_printf_hook_split)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_split_names, printf_tests_split[_i].val);
|
||||
snprintf(buf, sizeof(buf), "%N",
|
||||
test_enum_split_names, printf_tests_split[_i].val);
|
||||
ck_assert_str_eq(printf_tests_split[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_flags)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_flags_names,
|
||||
printf_tests_flags[_i].val);
|
||||
ck_assert_str_eq(printf_tests_flags[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_flags_incomplete)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_flags_incomplete_names,
|
||||
printf_tests_flags_incomplete[_i].val);
|
||||
ck_assert_str_eq(printf_tests_flags_incomplete[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_flags_null)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_flags_null_names,
|
||||
printf_tests_flags_null[_i].val);
|
||||
ck_assert_str_eq(printf_tests_flags_null[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_flags_overflow)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_flags_overflow_names,
|
||||
printf_tests_flags_overflow[_i].val);
|
||||
ck_assert_str_eq(printf_tests_flags_overflow[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_flags_noflagenum)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%N", test_enum_cont_names,
|
||||
printf_tests_flags_noflagenum[_i].val);
|
||||
ck_assert_str_eq(printf_tests_flags_noflagenum[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_flags_to_string)
|
||||
{
|
||||
char buf[1], *str;
|
||||
|
||||
str = enum_flags_to_string(test_enum_flags_names,
|
||||
enum_flags_to_string_tests[_i].val, buf, sizeof(buf));
|
||||
if (str)
|
||||
{
|
||||
ck_assert_str_eq(enum_flags_to_string_tests[_i].str, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(str == enum_flags_to_string_tests[_i].str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_flags_to_string_noflagenum)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
enum_flags_to_string(test_enum_cont_names,
|
||||
printf_tests_flags_noflagenum[_i].val, buf, sizeof(buf));
|
||||
ck_assert_str_eq(printf_tests_flags_noflagenum[_i].str, buf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_enum_printf_hook_width)
|
||||
{
|
||||
char buf[128];
|
||||
@@ -246,9 +398,19 @@ Suite *enum_suite_create()
|
||||
tcase_add_loop_test(tc, test_enum_from_name_split, 0, countof(enum_tests_split));
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("enum_flags_to_string");
|
||||
tcase_add_loop_test(tc, test_enum_flags_to_string, 0, countof(enum_flags_to_string_tests));
|
||||
tcase_add_loop_test(tc, test_enum_flags_to_string_noflagenum, 0, countof(printf_tests_flags_noflagenum));
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("enum_printf_hook");
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_cont, 0, countof(printf_tests_cont));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_split, 0, countof(printf_tests_split));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_flags, 0, countof(printf_tests_flags));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_flags_incomplete, 0, countof(printf_tests_flags_incomplete));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_flags_null, 0, countof(printf_tests_flags_null));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_flags_overflow, 0, countof(printf_tests_flags_overflow));
|
||||
tcase_add_loop_test(tc, test_enum_printf_hook_flags_noflagenum, 0, countof(printf_tests_flags_noflagenum));
|
||||
tcase_add_test(tc, test_enum_printf_hook_width);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
|
||||
@@ -59,21 +59,104 @@ bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of a flag name using offset calculation
|
||||
*/
|
||||
static int find_flag_pos(u_int val, u_int first)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
while (val != 0x01)
|
||||
{
|
||||
val = val >> 1;
|
||||
offset++;
|
||||
}
|
||||
return first - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t len)
|
||||
{
|
||||
char *pos = buf, *delim = "";
|
||||
int i, wr;
|
||||
|
||||
if (e->next != ENUM_FLAG_MAGIC)
|
||||
{
|
||||
if (snprintf(buf, len, "(%d)", (int)val) >= len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (snprintf(buf, len, "(unset)") >= len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; val; i++)
|
||||
{
|
||||
u_int flag = 1 << i;
|
||||
|
||||
if (val & flag)
|
||||
{
|
||||
char *name = NULL, hex[32];
|
||||
|
||||
if (flag >= (u_int)e->first && flag <= (u_int)e->last)
|
||||
{
|
||||
name = e->names[find_flag_pos(e->first, i)];
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(hex, sizeof(hex), "(0x%X)", flag);
|
||||
name = hex;
|
||||
}
|
||||
if (name)
|
||||
{
|
||||
wr = snprintf(pos, len, "%s%s", delim, name);
|
||||
if (wr >= len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
len -= wr;
|
||||
pos += wr;
|
||||
delim = " | ";
|
||||
}
|
||||
val &= ~flag;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* See header.
|
||||
*/
|
||||
int enum_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
|
||||
const void *const *args)
|
||||
{
|
||||
enum_name_t *ed = *((enum_name_t**)(args[0]));
|
||||
int val = *((int*)(args[1]));
|
||||
char *name, buf[32];
|
||||
char *name, buf[512];
|
||||
|
||||
name = enum_to_name(ed, val);
|
||||
if (name == NULL)
|
||||
if (ed->next == ENUM_FLAG_MAGIC)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "(%d)", val);
|
||||
name = buf;
|
||||
name = enum_flags_to_string(ed, val, buf, sizeof(buf));
|
||||
if (name == NULL)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "(0x%X)", val);
|
||||
name = buf;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
name = enum_to_name(ed, val);
|
||||
if (name == NULL)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "(%d)", val);
|
||||
name = buf;
|
||||
}
|
||||
}
|
||||
if (spec->minus)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
|
||||
typedef struct enum_name_t enum_name_t;
|
||||
|
||||
/**
|
||||
* Magic enum_name_t pointer indicating this is an enum name for flags
|
||||
*/
|
||||
#define ENUM_FLAG_MAGIC ((enum_name_t*)~(uintptr_t)0)
|
||||
|
||||
/**
|
||||
* Struct to store names for enums.
|
||||
*
|
||||
@@ -58,7 +63,7 @@ struct enum_name_t {
|
||||
int first;
|
||||
/** value of the last enum string */
|
||||
int last;
|
||||
/** next enum_name_t in list */
|
||||
/** next enum_name_t in list, or ENUM_FLAG_MAGIC */
|
||||
enum_name_t *next;
|
||||
/** array of strings containing names from first to last */
|
||||
char *names[];
|
||||
@@ -106,6 +111,23 @@ struct enum_name_t {
|
||||
*/
|
||||
#define ENUM(name, first, last, ...) ENUM_BEGIN(name, first, last, __VA_ARGS__); ENUM_END(name, last)
|
||||
|
||||
/**
|
||||
* Define a enum name with only one range for flags.
|
||||
*
|
||||
* Using an enum list for flags would be overkill. Hence we use a single
|
||||
* range with all values in range. The next pointer is abused to mark
|
||||
* that the enum name is for flags only. Use NULL if a particular flag
|
||||
* is not meant to be printed.
|
||||
*
|
||||
* @param name name of the enum_name list
|
||||
* @param first enum value of the first enum string
|
||||
* @param last enum value of the last enum string
|
||||
* @param ... a list of strings
|
||||
*/
|
||||
#define ENUM_FLAGS(name, first, last, ...) \
|
||||
static enum_name_t name##last = {first, last, ENUM_FLAG_MAGIC, { __VA_ARGS__ }}; \
|
||||
ENUM_END(name, last)
|
||||
|
||||
/**
|
||||
* Convert a enum value to its string representation.
|
||||
*
|
||||
@@ -145,6 +167,17 @@ char *enum_to_name(enum_name_t *e, int val);
|
||||
*/
|
||||
bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val);
|
||||
|
||||
/**
|
||||
* Convert a enum value containing flags to its string representation.
|
||||
*
|
||||
* @param e enum names for this enum value suitable for flags
|
||||
* @param val enum value to get string for
|
||||
* @param buf buffer to write flag string to
|
||||
* @param len buffer size
|
||||
* @return buf, NULL if buffer too small
|
||||
*/
|
||||
char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t buflen);
|
||||
|
||||
/**
|
||||
* printf hook function for enum_names_t.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user