enum: Extend printf hook to print flags
authorThomas Egerer <thomas.egerer@secunet.com>
Thu, 22 Jan 2015 17:23:42 +0000 (18:23 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Mar 2015 12:41:49 +0000 (13:41 +0100)
Signed-off-by: Thomas Egerer <thomas.egerer@secunet.com>
src/libstrongswan/tests/suites/test_enum.c
src/libstrongswan/utils/enum.c
src/libstrongswan/utils/enum.h

index b48b51c..53ebd29 100644 (file)
@@ -58,6 +58,39 @@ ENUM_NEXT(test_enum_split_names, SPLIT5, SPLIT5, SPLIT4,
 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);
 
index f96fe29..089bebb 100644 (file)
@@ -60,20 +60,103 @@ bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val)
 }
 
 /**
+ * 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)
+       {
+               name = enum_flags_to_string(ed, val, buf, sizeof(buf));
+               if (name == NULL)
+               {
+                       snprintf(buf, sizeof(buf), "(0x%X)", val);
+                       name = buf;
+               }
+       }
+       else
        {
-               snprintf(buf, sizeof(buf), "(%d)", val);
-               name = buf;
+               name = enum_to_name(ed, val);
+               if (name == NULL)
+               {
+                       snprintf(buf, sizeof(buf), "(%d)", val);
+                       name = buf;
+               }
        }
        if (spec->minus)
        {
index 3c03c2a..b45ed4b 100644 (file)
 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.
  *
  * To print the string representation of enumeration values, the strings
@@ -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[];
@@ -107,6 +112,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.
  *
  * @param e            enum names for this enum value
@@ -146,6 +168,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.
  *
  * Arguments are: