diff --git a/src/markup.c b/src/markup.c new file mode 100644 index 0000000..05ef1ff --- /dev/null +++ b/src/markup.c @@ -0,0 +1,110 @@ +/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */ + +#define _GNU_SOURCE +#include "markup.h" + +#include +#include + +#include "settings.h" +#include "utils.h" + +static char *markup_quote(char *str) +{ + assert(str != NULL); + + str = string_replace_all("&", "&", str); + str = string_replace_all("\"", """, str); + str = string_replace_all("'", "'", str); + str = string_replace_all("<", "<", str); + str = string_replace_all(">", ">", str); + + return str; +} + +static char *markup_unquote(char *str) +{ + assert(str != NULL); + + str = string_replace_all(""", "\"", str); + str = string_replace_all("'", "'", str); + str = string_replace_all("<", "<", str); + str = string_replace_all(">", ">", str); + str = string_replace_all("&", "&", str); + + return str; +} + +static char *markup_br2nl(char *str) +{ + assert(str != NULL); + + str = string_replace_all("
", "\n", str); + str = string_replace_all("
", "\n", str); + str = string_replace_all("
", "\n", str); + return str; +} + +/* + * Strip any markup from text; turn it in to plain text. + * + * For well-formed markup, the following two commands should be + * roughly equivalent: + * + * out = markup_strip(in); + * pango_parse_markup(in, -1, 0, NULL, &out, NULL, NULL); + * + * However, `pango_parse_markup()` balks at invalid markup; + * `markup_strip()` shouldn't care if there is invalid markup. + */ +char *markup_strip(char *str) +{ + if (str == NULL) { + return NULL; + } + + /* strip all tags */ + string_strip_delimited(str, '<', '>'); + + /* unquote the remainder */ + str = markup_unquote(str); + + return str; +} + +/* + * Transform the string in accordance with `markup_mode` and + * `settings.ignore_newline` + */ +char *markup_transform(char *str, enum markup_mode markup_mode) +{ + if (str == NULL) { + return NULL; + } + + switch (markup_mode) { + case MARKUP_NULL: + /* `assert(false)`, but with a meaningful error message */ + assert(markup_mode != MARKUP_NULL); + break; + case MARKUP_NO: + str = markup_quote(str); + break; + case MARKUP_STRIP: + str = markup_br2nl(str); + str = markup_strip(str); + str = markup_quote(str); + break; + case MARKUP_FULL: + str = markup_br2nl(str); + break; + } + + if (settings.ignore_newline) { + str = string_replace_all("\n", " ", str); + } + + return str; +} + +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/markup.h b/src/markup.h new file mode 100644 index 0000000..8304e2d --- /dev/null +++ b/src/markup.h @@ -0,0 +1,11 @@ +/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */ +#ifndef DUNST_MARKUP_H +#define DUNST_MARKUP_H + +#include "settings.h" + +char *markup_strip(char *str); +char *markup_transform(char *str, enum markup_mode markup_mode); + +#endif +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/notification.c b/src/notification.c index 141fdfe..930a0f8 100644 --- a/src/notification.c +++ b/src/notification.c @@ -16,6 +16,7 @@ #include "dbus.h" #include "dunst.h" +#include "markup.h" #include "menu.h" #include "rules.h" #include "settings.h" @@ -196,46 +197,6 @@ void notification_free(notification * n) g_free(n); } -/* - * Strip any markup from text - */ -char *notification_strip_markup(char *str) -{ - if (str == NULL) { - return NULL; - } - - /* strip all tags */ - string_strip_delimited(str, '<', '>'); - - /* unquote the remainder */ - str = string_replace_all(""", "\"", str); - str = string_replace_all("'", "'", str); - str = string_replace_all("&", "&", str); - str = string_replace_all("<", "<", str); - str = string_replace_all(">", ">", str); - - return str; -} - -/* - * Quote a text string for rendering with pango - */ -char *notification_quote_markup(char *str) -{ - if (str == NULL) { - return NULL; - } - - str = string_replace_all("&", "&", str); - str = string_replace_all("\"", """, str); - str = string_replace_all("'", "'", str); - str = string_replace_all("<", "<", str); - str = string_replace_all(">", ">", str); - - return str; -} - /* * Replace all occurrences of "needle" with a quoted "replacement", * according to the markup settings. @@ -245,32 +206,7 @@ char *notification_replace_format(const char *needle, const char *replacement, char* tmp; char* ret; - if (markup_mode == MARKUP_NO) { - tmp = g_strdup(replacement); - tmp = notification_quote_markup(tmp); - } else { - tmp = g_strdup(replacement); - if (settings.ignore_newline) { - tmp = string_replace_all("
", " ", tmp); - tmp = string_replace_all("
", " ", tmp); - tmp = string_replace_all("
", " ", tmp); - } else { - tmp = string_replace_all("
", "\n", tmp); - tmp = string_replace_all("
", "\n", tmp); - tmp = string_replace_all("
", "\n", tmp); - } - - if (markup_mode != MARKUP_FULL ) { - tmp = notification_strip_markup(tmp); - tmp = notification_quote_markup(tmp); - } - - } - - if (settings.ignore_newline) { - tmp = string_replace_all("\n", " ", tmp); - } - + tmp = markup_transform(g_strdup(replacement), markup_mode); ret = string_replace_all(needle, tmp, haystack); g_free(tmp); diff --git a/src/notification.h b/src/notification.h index 777ce73..5d0fa5d 100644 --- a/src/notification.h +++ b/src/notification.h @@ -69,8 +69,6 @@ int notification_is_duplicate(const notification *a, const notification *b); void notification_run_script(notification * n); int notification_close(notification * n, int reason); void notification_print(notification * n); -char *notification_strip_markup(char *str); -char *notification_quote_markup(char *str); char *notification_replace_format(const char *needle, const char *replacement, char *haystack, enum markup_mode markup); void notification_update_text_to_render(notification *n); int notification_get_ttl(notification *n); diff --git a/src/x.c b/src/x.c index 310c341..dbb074a 100644 --- a/src/x.c +++ b/src/x.c @@ -22,6 +22,7 @@ #include #include "dunst.h" +#include "markup.h" #include "notification.h" #include "settings.h" #include "utils.h" @@ -475,7 +476,7 @@ static colored_layout *r_create_layout_from_notification(cairo_t *c, notificatio pango_layout_set_attributes(cl->l, cl->attr); } else { /* remove markup and display plain message instead */ - n->text_to_render = notification_strip_markup(n->text_to_render); + n->text_to_render = markup_strip(n->text_to_render); cl->text = NULL; cl->attr = NULL; pango_layout_set_text(cl->l, n->text_to_render, -1); diff --git a/test/markup.c b/test/markup.c new file mode 100644 index 0000000..cbd8bad --- /dev/null +++ b/test/markup.c @@ -0,0 +1,57 @@ +#include "greatest.h" + +#include +#include + +#include "src/markup.h" + +TEST test_markup_strip(void) +{ + char *ptr; + + ASSERT_STR_EQ(""", (ptr=markup_strip(g_strdup("&quot;")))); + g_free(ptr); + ASSERT_STR_EQ("'", (ptr=markup_strip(g_strdup("&apos;")))); + g_free(ptr); + ASSERT_STR_EQ("<", (ptr=markup_strip(g_strdup("&lt;")))); + g_free(ptr); + ASSERT_STR_EQ(">", (ptr=markup_strip(g_strdup("&gt;")))); + g_free(ptr); + ASSERT_STR_EQ("&", (ptr=markup_strip(g_strdup("&amp;")))); + g_free(ptr); + ASSERT_STR_EQ(">A ", (ptr=markup_strip(g_strdup(">A foo
bar\nbaz"), MARKUP_NO))); + g_free(ptr); + ASSERT_STR_EQ("foo\nbar\nbaz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_STRIP))); + g_free(ptr); + ASSERT_STR_EQ("foo\nbar\nbaz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_FULL))); + g_free(ptr); + + settings.ignore_newline = true; + ASSERT_STR_EQ("<i>foo</i><br>bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_NO))); + g_free(ptr); + ASSERT_STR_EQ("foo bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_STRIP))); + g_free(ptr); + ASSERT_STR_EQ("foo bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_FULL))); + g_free(ptr); + + PASS(); +} + +SUITE(suite_markup) +{ + RUN_TEST(test_markup_strip); + RUN_TEST(test_markup_transform); +} + +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/test.c b/test/test.c index 417ba19..90959a6 100644 --- a/test/test.c +++ b/test/test.c @@ -3,6 +3,7 @@ SUITE_EXTERN(suite_utils); SUITE_EXTERN(suite_option_parser); SUITE_EXTERN(suite_notification); +SUITE_EXTERN(suite_markup); GREATEST_MAIN_DEFS(); @@ -11,6 +12,7 @@ int main(int argc, char *argv[]) { RUN_SUITE(suite_utils); RUN_SUITE(suite_option_parser); RUN_SUITE(suite_notification); + RUN_SUITE(suite_markup); GREATEST_MAIN_END(); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */