Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
45 changes: 45 additions & 0 deletions canfigger.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions canfigger.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
25 changes: 25 additions & 0 deletions examples/canfigger_parse_color_pair.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <stdint.h>
#include <stdio.h>
#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;
}
1 change: 1 addition & 0 deletions examples/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ test_cases = [
'multiple_attributes',
'no_trailing_newline',
'parse_color',
'parse_color_pair',
'parse_file',
'skip_attributes',
'unicode',
Expand Down
48 changes: 48 additions & 0 deletions tests/parse_color_pair.c
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 4 additions & 0 deletions tests/parse_color_pair.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
button = pair, #FFFF00, #000000
highlight = pair, #FF800080, #00000080
solo = pair, #FFFFFF
invalid = pair, notacolor, #000000
Loading