diff --git a/ChangeLog.txt b/ChangeLog.txt index cfcf5da..089a80b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,6 +5,7 @@ + Add canfigger_get_double_attrs(): parse node attributes as a double array + Add canfigger_parse_color_hex(): parse #RRGGBB / #RRGGBBAA into uint8_t RGBA components + Add canfigger_parse_color(): parse a color node in hex or integer-attribute format + + Add canfigger_parse_color_pair(): parse a foreground/background hex color pair from one node 2026-05-11 diff --git a/canfigger.c b/canfigger.c index e845509..5ce05fc 100644 --- a/canfigger.c +++ b/canfigger.c @@ -632,6 +632,51 @@ canfigger_parse_color(const struct Canfigger *node, uint8_t *r, uint8_t *g, } +int +canfigger_parse_color_pair(const struct Canfigger *node, + uint8_t *r1, uint8_t *g1, uint8_t *b1, uint8_t *a1, + uint8_t *r2, uint8_t *g2, uint8_t *b2, uint8_t *a2) +{ + if (!node || !node->attributes || !node->attributes->str + || !r1 || !g1 || !b1 || !a1 || !r2 || !g2 || !b2 || !a2) + return 0; + + char buf[10]; /* #RRGGBBAA + NUL */ + char *p = node->attributes->str; + + while (*p == ' ' || *p == '\t') + p++; + char *nl = strchr(p, '\n'); + size_t len = nl ? (size_t) (nl - p) : strlen(p); + while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) + len--; + if (len == 0 || len >= sizeof(buf)) + return 0; + memcpy(buf, p, len); + buf[len] = '\0'; + if (!canfigger_parse_color_hex(buf, r1, g1, b1, a1)) + return 0; + + if (!nl) + return 1; + p = nl + 1; + while (*p == ' ' || *p == '\t') + p++; + nl = strchr(p, '\n'); + len = nl ? (size_t) (nl - p) : strlen(p); + while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) + len--; + if (len == 0 || len >= sizeof(buf)) + return 1; + memcpy(buf, p, len); + buf[len] = '\0'; + if (!canfigger_parse_color_hex(buf, r2, g2, b2, a2)) + return 1; + + return 2; +} + + #ifndef _WIN32 static char * xdg_base_dir(const char *xdg_env, const char *fallback) diff --git a/canfigger.h b/canfigger.h index 00c0f82..44f2ee5 100644 --- a/canfigger.h +++ b/canfigger.h @@ -394,6 +394,41 @@ extern "C" int canfigger_parse_color(const struct Canfigger *node, uint8_t * r, uint8_t * g, uint8_t * b, uint8_t * a); +/** + * @brief Parse a foreground/background color pair from a config node. + * + * Reads two hex color strings from the node's attributes — the first as the + * foreground color, the second as the background color. Both must be in + * @c \#RRGGBB or @c \#RRGGBBAA format. + * + * Conventional config line form: + * @code + * button = pair, #FFFF00, #000000 + * @endcode + * The value (@c pair) is a caller-chosen type tag; this function ignores it + * and reads directly from the attributes. + * + * @param node Config node to read (may be NULL). + * @param r1 Output: foreground red. + * @param g1 Output: foreground green. + * @param b1 Output: foreground blue. + * @param a1 Output: foreground alpha (255 if no alpha digits). + * @param r2 Output: background red. + * @param g2 Output: background green. + * @param b2 Output: background blue. + * @param a2 Output: background alpha (255 if no alpha digits). + * @return 2 if both colors were parsed, 1 if only the first was parsed, + * 0 on failure. + * + * @since 0.3.3 + * + * @snippet examples/canfigger_parse_color_pair.c canfigger_parse_color_pair + */ + int canfigger_parse_color_pair(const struct Canfigger *node, + uint8_t * r1, uint8_t * g1, uint8_t * b1, + uint8_t * a1, uint8_t * r2, uint8_t * g2, + uint8_t * b2, uint8_t * a2); + #ifdef __cplusplus } #endif diff --git a/examples/canfigger_parse_color_pair.c b/examples/canfigger_parse_color_pair.c new file mode 100644 index 0000000..f75e6d8 --- /dev/null +++ b/examples/canfigger_parse_color_pair.c @@ -0,0 +1,25 @@ +#include +#include +#include "canfigger.h" + +int +main(void) +{ + //! [canfigger_parse_color_pair] + /* + * Config line: button = pair, #FFFF00, #000000 + * First attribute is foreground, second is background. + */ + struct Canfigger *list = canfigger_parse_file("colors.conf", ','); + while (list) + { + uint8_t r1, g1, b1, a1, r2, g2, b2, a2; + if (canfigger_parse_color_pair(list, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 2) + fprintf(stderr, "%s: fg=rgba(%u,%u,%u,%u) bg=rgba(%u,%u,%u,%u)\n", + list->key, r1, g1, b1, a1, r2, g2, b2, a2); + canfigger_free_current_key_node_advance(&list); + } + //! [canfigger_parse_color_pair] + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index 34ed57d..17f9cde 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -6,6 +6,7 @@ snippet_examples = [ 'canfigger_get_double_attrs', 'canfigger_parse_color_hex', 'canfigger_parse_color', + 'canfigger_parse_color_pair', ] foreach ex : snippet_examples diff --git a/tests/meson.build b/tests/meson.build index 32dcbb1..58317f8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -12,6 +12,7 @@ test_cases = [ 'multiple_attributes', 'no_trailing_newline', 'parse_color', + 'parse_color_pair', 'parse_file', 'skip_attributes', 'unicode', diff --git a/tests/parse_color_pair.c b/tests/parse_color_pair.c new file mode 100644 index 0000000..00624e8 --- /dev/null +++ b/tests/parse_color_pair.c @@ -0,0 +1,48 @@ +#include "test.h" + +int +main(void) +{ + struct Canfigger *list = + canfigger_parse_file(SOURCE_DIR "/parse_color_pair.conf", ','); + assert(list); + + uint8_t r1, g1, b1, a1, r2, g2, b2, a2; + + /* button = pair, #FFFF00, #000000 — both RGB, alpha defaults to 255 */ + assert(strcmp(list->key, "button") == 0); + assert(canfigger_parse_color_pair(list, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 2); + assert(r1 == 0xFF && g1 == 0xFF && b1 == 0x00 && a1 == 0xFF); + assert(r2 == 0x00 && g2 == 0x00 && b2 == 0x00 && a2 == 0xFF); + canfigger_free_current_key_node_advance(&list); + + /* highlight = pair, #FF800080, #00000080 — both RGBA */ + assert(strcmp(list->key, "highlight") == 0); + assert(canfigger_parse_color_pair(list, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 2); + assert(r1 == 0xFF && g1 == 0x80 && b1 == 0x00 && a1 == 0x80); + assert(r2 == 0x00 && g2 == 0x00 && b2 == 0x00 && a2 == 0x80); + canfigger_free_current_key_node_advance(&list); + + /* solo = pair, #FFFFFF — only one color, returns 1 */ + assert(strcmp(list->key, "solo") == 0); + assert(canfigger_parse_color_pair(list, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 1); + assert(r1 == 0xFF && g1 == 0xFF && b1 == 0xFF && a1 == 0xFF); + canfigger_free_current_key_node_advance(&list); + + /* invalid = pair, notacolor, #000000 — first attr not hex, returns 0 */ + assert(strcmp(list->key, "invalid") == 0); + assert(canfigger_parse_color_pair(list, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 0); + canfigger_free_current_key_node_advance(&list); + + assert(list == NULL); + + /* NULL node */ + assert(canfigger_parse_color_pair(NULL, &r1, &g1, &b1, &a1, + &r2, &g2, &b2, &a2) == 0); + + return 0; +} diff --git a/tests/parse_color_pair.conf b/tests/parse_color_pair.conf new file mode 100644 index 0000000..464bb15 --- /dev/null +++ b/tests/parse_color_pair.conf @@ -0,0 +1,4 @@ +button = pair, #FFFF00, #000000 +highlight = pair, #FF800080, #00000080 +solo = pair, #FFFFFF +invalid = pair, notacolor, #000000