diff --git a/i3bar.c b/i3bar.c index 30fb989b..ba57b344 100644 --- a/i3bar.c +++ b/i3bar.c @@ -23,6 +23,7 @@ #include "log.h" #include "map.h" #include "term.h" +#include /* See https://i3wm.org/docs/i3bar-protocol.html for details */ @@ -104,6 +105,26 @@ static void i3bar_print_term(const struct bar *bar) fflush(stdout); } +void amp_escape(const char *value, char *buf, size_t size) +{ + int i, k = 0; + for(i = 0; value[i] != '\0'; ) + { + buf[k] = value[i]; + + if(value[i] == '&') + { + buf[++k] = 'a'; + buf[++k] = 'm'; + buf[++k] = 'p'; + buf[++k] = ';'; + } + + ++i; ++k; + } + buf[k] = 0; +} + static int i3bar_print_pair(const char *key, const char *value, void *data) { unsigned int index = i3bar_indexof(key); @@ -143,11 +164,91 @@ static int i3bar_print_pair(const char *key, const char *value, void *data) if ((*pcount)++) fprintf(stdout, ","); + // escape [& -> &] + if(strncmp(key, "full_text", strlen(key)) == 0 && strchr(value, '&') != NULL) + { + static char buf1[BUFSIZ]={0}; + amp_escape(value, buf1, sizeof(buf1)); + value = buf1; + } + fprintf(stdout, "\"%s\":%s", key, value); return 0; } +static const char* i3bar_get_value_by_ref(const char *value, struct block *block) +{ + if(value == NULL || block == NULL) + return NULL; + + if(value[0] == '#') // color + { + return value; + } + if(value[0] == '&') // reference + { + const char *ref = map_get(block->env, value + 1); + if(ref != NULL) + { + // reccursive reference lookup + return i3bar_get_value_by_ref(ref, block); + } + else + { + block_debug(block, "Cannot found reference %s.", value); + return value; + } + } + + return value; +} + +static int i3bar_parse_pattern(struct block *block, const char *pattern, char *res) +{ + char pattern_copy[BUFSIZ] = {0}; + + // TODO: parametrize wia block->conf + char delim[] = "%"; + + // copy, because strtok modifies input string + // TODO: use strdup + strcpy(pattern_copy, pattern); + + char *key = strtok(pattern_copy, delim); + bool token = false; + while(key != NULL) + { + if(token) + { + const char *value = map_get(block->env, key); + if(value != NULL) + { + const char *final_value = i3bar_get_value_by_ref(value, block); + if(final_value) + strcat(res, final_value); + else + { + block_error(block, "Something wrong with %s", value); // should not hit there + return 2; + } + } + else + { + block_debug(block, "Key %s is not defined.", key); + return 1; + } + } + else + strcat(res, key); + + token = !token; + key = strtok(NULL, delim); + } + + return 0; +} + static int i3bar_print_block(struct block *block, void *data) { const char *full_text = map_get(block->env, "full_text"); @@ -161,6 +262,21 @@ static int i3bar_print_block(struct block *block, void *data) return 0; } + char *full_text_copy = NULL; + + const char *full_text_pattern = map_get(block->config, "full_text_pattern"); + if(full_text_pattern) + { + char buf[BUFSIZ] = {0}; + + if(i3bar_parse_pattern(block, full_text_pattern, buf) == 0) + { + full_text_copy = strdup(full_text); + if(full_text_copy != NULL) + map_set(block->env, "full_text", buf); + } + } + if ((*mcount)++) fprintf(stdout, ","); @@ -168,6 +284,12 @@ static int i3bar_print_block(struct block *block, void *data) err = map_for_each(block->env, i3bar_print_pair, &pcount); fprintf(stdout, "}"); + if(full_text_copy) + { + map_set(block->env, "full_text", full_text_copy); + free(full_text_copy); + } + return err; }