Merge pull request #428 from bebehei/refactor-notification_init

Refactor notification init
This commit is contained in:
Benedikt Heine 2017-12-18 00:55:59 +01:00 committed by GitHub
commit 35dbd00611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 463 additions and 175 deletions

View File

@ -128,10 +128,7 @@ static void on_get_capabilities(GDBusConnection *connection,
g_dbus_connection_flush(connection, NULL, NULL, NULL);
}
static void on_notify(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
static notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters)
{
gchar *appname = NULL;
@ -268,14 +265,15 @@ static void on_notify(GDBusConnection *connection,
fflush(stdout);
notification *n = notification_create();
n->id = replaces_id;
n->appname = appname;
n->summary = summary;
n->body = body;
n->icon = icon;
n->raw_icon = raw_icon;
n->timeout = timeout < 0 ? -1 : timeout * 1000;
n->markup = settings.markup;
n->progress = (progress < 0 || progress > 100) ? -1 : progress;
n->progress = progress;
n->urgency = urgency;
n->category = category;
n->dbus_client = g_strdup(sender);
@ -287,14 +285,20 @@ static void on_notify(GDBusConnection *connection,
}
n->actions = actions;
for (int i = 0; i < ColLast; i++) {
n->color_strings[i] = NULL;
}
n->color_strings[ColFG] = fgcolor;
n->color_strings[ColBG] = bgcolor;
n->colors[ColFG] = fgcolor;
n->colors[ColBG] = bgcolor;
notification_init(n);
int id = queues_notification_insert(n, replaces_id);
return n;
}
static void on_notify(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
notification *n = dbus_message_to_notification(sender, parameters);
int id = queues_notification_insert(n);
GVariant *reply = g_variant_new("(u)", id);
g_dbus_method_invocation_return_value(invocation, reply);

View File

@ -149,6 +149,7 @@ int dunst_main(int argc, char *argv[])
if (settings.startup_notification) {
notification *n = notification_create();
n->id = 0;
n->appname = g_strdup("dunst");
n->summary = g_strdup("startup");
n->body = g_strdup("dunst is up and running");
@ -157,7 +158,7 @@ int dunst_main(int argc, char *argv[])
n->markup = MARKUP_NO;
n->urgency = URG_LOW;
notification_init(n);
queues_notification_insert(n, 0);
queues_notification_insert(n);
// we do not call wakeup now, wake_up does not work here yet
}

View File

@ -17,7 +17,7 @@
#define ColBG 0
extern GSList *rules;
extern const char *color_strings[3][3];
extern const char *colors[3][3];
gboolean run(void *data);
void wake_up(void);

View File

@ -4,6 +4,8 @@
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "settings.h"
#include "utils.h"
@ -44,6 +46,178 @@ static char *markup_br2nl(char *str)
return str;
}
/*
* Remove HTML hyperlinks of a string.
*
* @str: The string to replace a tags
* @urls: (nullable): If any href-attributes found, an '\n' concatenated
* string of the URLs in format '[<text between tags>] <href>'
*/
void markup_strip_a(char **str, char **urls)
{
char *tag1 = NULL;
if (urls)
*urls = NULL;
while ((tag1 = strstr(*str, "<a"))) {
// use href=" as stated in the notification spec
char *href = strstr(tag1, "href=\"");
char *tag1_end = strstr(tag1, ">");
char *tag2 = strstr(tag1, "</a>");
// the tag is broken, ignore it
if (!tag1_end) {
fprintf(stderr,
"WARNING: Given link is broken: '%s'\n",
tag1);
string_replace_at(*str, tag1-*str, strlen(tag1), "");
break;
}
if (tag2 && tag2 < tag1_end) {
int repl_len = (tag2 - tag1) + strlen("</a>");
fprintf(stderr,
"WARNING: Given link is broken: '%.*s.'\n",
repl_len, tag1);
string_replace_at(*str, tag1-*str, repl_len, "");
break;
}
// search contents of href attribute
char *plain_url = NULL;
if (href && href < tag1_end) {
// shift href to the actual begin of the value
href = href+6;
const char *quote = strstr(href, "\"");
if (quote && quote < tag1_end) {
plain_url = g_strndup(href, quote-href);
}
}
// text between a tags
int text_len;
if (tag2)
text_len = tag2 - (tag1_end+1);
else
text_len = strlen(tag1_end+1);
char *text = g_strndup(tag1_end+1, text_len);
int repl_len = text_len + (tag1_end-tag1) + 1;
repl_len += tag2 ? strlen("</a>") : 0;
*str = string_replace_at(*str, tag1-*str, repl_len, text);
// if there had been a href attribute,
// add it to the URLs
if (plain_url && urls) {
text = string_replace_all("]", "", text);
text = string_replace_all("[", "", text);
char *url = g_strdup_printf("[%s] %s", text, plain_url);
*urls = string_append(*urls, url, "\n");
g_free(url);
}
g_free(plain_url);
g_free(text);
}
}
/*
* Remove img-tags of a string. If alt attribute given, use this as replacement.
*
* @str: The string to replace img tags
* @urls: (nullable): If any src-attributes found, an '\n' concatenated string of
* the URLs in format '[<alt>] <src>'
*/
void markup_strip_img(char **str, char **urls)
{
const char *start = *str;
if (urls)
*urls = NULL;
while ((start = strstr(*str, "<img"))) {
const char *end = strstr(start, ">");
// the tag is broken, ignore it
if (!end) {
fprintf(stderr, "WARNING: Given image is broken: '%s'\n", start);
string_replace_at(*str, start-*str, strlen(start), "");
break;
}
// use attribute=" as stated in the notification spec
const char *alt_s = strstr(start, "alt=\"");
const char *src_s = strstr(start, "src=\"");
char *text_alt = NULL;
char *text_src = NULL;
const char *src_e = NULL, *alt_e = NULL;
if (alt_s)
alt_e = strstr(alt_s + strlen("alt=\""), "\"");
if (src_s)
src_e = strstr(src_s + strlen("src=\""), "\"");
// Move pointer to the actual start
alt_s = alt_s ? alt_s + strlen("alt=\"") : NULL;
src_s = src_s ? src_s + strlen("src=\"") : NULL;
/* check if alt and src attribute are given
* If both given, check the alignment of all pointers */
if ( alt_s && alt_e
&& src_s && src_e
&& ( (alt_s < src_s && alt_e < src_s-strlen("src=\"") && src_e < end)
||(src_s < alt_s && src_e < alt_s-strlen("alt=\"") && alt_e < end)) ) {
text_alt = g_strndup(alt_s, alt_e-alt_s);
text_src = g_strndup(src_s, src_e-src_s);
/* check if single valid alt attribute is available */
} else if (alt_s && alt_e && alt_e < end && (!src_s || src_s < alt_s || alt_e < src_s - strlen("src=\""))) {
text_alt = g_strndup(alt_s, alt_e-alt_s);
/* check if single valid src attribute is available */
} else if (src_s && src_e && src_e < end && (!alt_s || alt_s < src_s || src_e < alt_s - strlen("alt=\""))) {
text_src = g_strndup(src_s, src_e-src_s);
} else {
fprintf(stderr,
"WARNING: Given image argument is broken: '%.*s'\n",
(int)(end-start), start);
}
// replacement text for alt
int repl_len = end - start + 1;
if (!text_alt)
text_alt = g_strdup("[image]");
*str = string_replace_at(*str, start-*str, repl_len, text_alt);
// if there had been a href attribute,
// add it to the URLs
if (text_src && urls) {
text_alt = string_replace_all("]", "", text_alt);
text_alt = string_replace_all("[", "", text_alt);
char *url = g_strdup_printf("[%s] %s", text_alt, text_src);
*urls = string_append(*urls, url, "\n");
g_free(url);
}
g_free(text_src);
g_free(text_alt);
}
}
/*
* Strip any markup from text; turn it in to plain text.
*
@ -96,6 +270,8 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
break;
case MARKUP_FULL:
str = markup_br2nl(str);
markup_strip_a(&str, NULL);
markup_strip_img(&str, NULL);
break;
}

View File

@ -5,6 +5,10 @@
#include "settings.h"
char *markup_strip(char *str);
void markup_strip_a(char **str, char **urls);
void markup_strip_img(char **str, char **urls);
char *markup_transform(char *str, enum markup_mode markup_mode);
#endif

View File

@ -95,10 +95,14 @@ char *extract_urls(const char *to_match)
*/
void open_browser(const char *in)
{
// remove prefix and test url
char *url = extract_urls(in);
if (!url)
return;
char *url = NULL;
// If any, remove leading [ linktext ] from URL
const char *end = strstr(in, "] ");
if (*in == '[' && end)
url = g_strdup(end + 2);
else
url = g_strdup(in);
int browser_pid1 = fork();

View File

@ -23,6 +23,10 @@
#include "utils.h"
#include "x11/x.h"
static void notification_extract_urls(notification *n);
static void notification_format_message(notification *n);
static void notification_dmenu_string(notification *n);
/*
* print a human readable representation
* of the given notification to stdout.
@ -40,15 +44,17 @@ void notification_print(notification *n)
printf("\turgency: %s\n", notification_urgency_to_string(n->urgency));
printf("\ttransient: %d\n", n->transient);
printf("\tformatted: '%s'\n", n->msg);
printf("\tfg: %s\n", n->color_strings[ColFG]);
printf("\tbg: %s\n", n->color_strings[ColBG]);
printf("\tframe: %s\n", n->color_strings[ColFrame]);
printf("\tfg: %s\n", n->colors[ColFG]);
printf("\tbg: %s\n", n->colors[ColBG]);
printf("\tframe: %s\n", n->colors[ColFrame]);
printf("\tid: %d\n", n->id);
if (n->urls) {
char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls));
printf("\turls:\n");
printf("\t{\n");
printf("\t\t%s\n", n->urls);
printf("\t\t%s\n", urls);
printf("\t}\n");
g_free(urls);
}
if (n->actions) {
@ -210,6 +216,9 @@ void notification_free(notification *n)
g_free(n->category);
g_free(n->text_to_render);
g_free(n->urls);
g_free(n->colors[ColFG]);
g_free(n->colors[ColBG]);
g_free(n->colors[ColFrame]);
actions_free(n->actions);
rawimage_free(n->raw_icon);
@ -248,95 +257,88 @@ void notification_replace_single_field(char **haystack,
g_free(input);
}
char *notification_extract_markup_urls(char **str_ptr)
{
char *start, *end, *replace_buf, *str, *urls = NULL, *url, *index_buf;
int linkno = 1;
str = *str_ptr;
while ((start = strstr(str, "<a href")) != NULL) {
end = strstr(start, ">");
if (end != NULL) {
replace_buf = g_strndup(start, end - start + 1);
url = extract_urls(replace_buf);
if (url != NULL) {
str = string_replace(replace_buf, "[", str);
index_buf = g_strdup_printf("[#%d]", linkno++);
if (urls == NULL) {
urls = g_strconcat(index_buf, " ", url, NULL);
} else {
char *tmp = urls;
urls = g_strconcat(tmp, "\n", index_buf, " ", url, NULL);
g_free(tmp);
}
index_buf[0] = ' ';
str = string_replace("</a>", index_buf, str);
g_free(index_buf);
g_free(url);
} else {
str = string_replace(replace_buf, "", str);
str = string_replace("</a>", "", str);
}
g_free(replace_buf);
} else {
break;
}
}
*str_ptr = str;
return urls;
}
/*
* Create notification struct and initialise everything to NULL,
* this function is guaranteed to return a valid pointer.
* Create notification struct and initialise all fields with either
* - the default (if it's not needed to be freed later)
* - its undefined representation (NULL, -1)
*
* This function is guaranteed to return a valid pointer.
* @Returns: The generated notification
*/
notification *notification_create(void)
{
return g_malloc0(sizeof(notification));
}
notification *n = g_malloc0(sizeof(notification));
void notification_init_defaults(notification *n)
{
assert(n != NULL);
if(n->appname == NULL) n->appname = g_strdup("unknown");
if(n->summary == NULL) n->summary = g_strdup("");
if(n->body == NULL) n->body = g_strdup("");
if(n->category == NULL) n->category = g_strdup("");
/* Unparameterized default values */
n->first_render = true;
n->markup = settings.markup;
n->format = settings.format;
n->timestamp = g_get_monotonic_time();
n->urgency = URG_NORM;
n->timeout = -1;
n->transient = false;
n->progress = -1;
return n;
}
/*
* Initialize the given notification
* Sanitize values of notification, apply all matching rules
* and generate derived fields.
*
* n should be a pointer to a notification allocated with
* notification_create, it is undefined behaviour to pass a notification
* allocated some other way.
* @n: the notification to sanitize
*/
void notification_init(notification *n)
{
assert(n != NULL);
/* default to empty string to avoid further NULL faults */
n->appname = n->appname ? n->appname : g_strdup("unknown");
n->summary = n->summary ? n->summary : g_strdup("");
n->body = n->body ? n->body : g_strdup("");
n->category = n->category ? n->category : g_strdup("");
//Prevent undefined behaviour by initialising required fields
notification_init_defaults(n);
/* sanitize urgency */
if (n->urgency < URG_MIN)
n->urgency = URG_LOW;
if (n->urgency > URG_MAX)
n->urgency = URG_CRIT;
n->script = NULL;
n->text_to_render = NULL;
/* Timeout processing */
if (n->timeout < 0)
n->timeout = settings.timeouts[n->urgency];
n->format = settings.format;
/* Icon handling */
if (n->icon && strlen(n->icon) <= 0)
g_clear_pointer(&n->icon, g_free);
if (!n->raw_icon && !n->icon)
n->icon = g_strdup(settings.icons[n->urgency]);
/* Color hints */
if (!n->colors[ColFG])
n->colors[ColFG] = g_strdup(xctx.colors[ColFG][n->urgency]);
if (!n->colors[ColBG])
n->colors[ColBG] = g_strdup(xctx.colors[ColBG][n->urgency]);
if (!n->colors[ColFrame])
n->colors[ColFrame] = g_strdup(xctx.colors[ColFrame][n->urgency]);
/* Sanitize misc hints */
if (n->progress < 0 || n->progress > 100)
n->progress = -1;
/* Process rules */
rule_apply_all(n);
if (n->icon != NULL && strlen(n->icon) <= 0) {
g_free(n->icon);
n->icon = NULL;
}
/* UPDATE derived fields */
notification_extract_urls(n);
notification_dmenu_string(n);
notification_format_message(n);
}
if (n->raw_icon == NULL && n->icon == NULL) {
n->icon = g_strdup(settings.icons[n->urgency]);
}
n->urls = notification_extract_markup_urls(&(n->body));
static void notification_format_message(notification *n)
{
g_clear_pointer(&n->msg, g_free);
n->msg = string_replace_all("\\n", "\n", g_strdup(n->format));
@ -438,45 +440,36 @@ void notification_init(notification *n)
g_free(n->msg);
n->msg = buffer;
}
}
n->dup_count = 0;
static void notification_extract_urls(notification *n)
{
g_clear_pointer(&n->urls, g_free);
/* urgency > URG_CRIT -> array out of range */
if (n->urgency < URG_MIN)
n->urgency = URG_LOW;
if (n->urgency > URG_MAX)
n->urgency = URG_CRIT;
char *urls_in = string_append(g_strdup(n->summary), n->body, " ");
if (!n->color_strings[ColFG]) {
n->color_strings[ColFG] = xctx.color_strings[ColFG][n->urgency];
}
char *urls_a = NULL;
char *urls_img = NULL;
markup_strip_a(&urls_in, &urls_a);
markup_strip_img(&urls_in, &urls_img);
// remove links and images first to not confuse
// plain urls extraction
char *urls_text = extract_urls(urls_in);
if (!n->color_strings[ColBG]) {
n->color_strings[ColBG] = xctx.color_strings[ColBG][n->urgency];
}
n->urls = string_append(n->urls, urls_a, "\n");
n->urls = string_append(n->urls, urls_img, "\n");
n->urls = string_append(n->urls, urls_text, "\n");
if (!n->color_strings[ColFrame]) {
n->color_strings[ColFrame] = xctx.color_strings[ColFrame][n->urgency];
}
n->timeout =
n->timeout < 0 ? settings.timeouts[n->urgency] : n->timeout;
n->start = 0;
n->timestamp = g_get_monotonic_time();
n->redisplayed = false;
n->first_render = true;
char *tmp = g_strconcat(n->summary, " ", n->body, NULL);
char *tmp_urls = extract_urls(tmp);
n->urls = string_append(n->urls, tmp_urls, "\n");
g_free(tmp_urls);
g_free(urls_in);
g_free(urls_a);
g_free(urls_img);
g_free(urls_text);
}
static void notification_dmenu_string(notification *n)
{
if (n->actions) {
n->actions->dmenu_str = NULL;
g_clear_pointer(&n->actions->dmenu_str, g_free);
for (int i = 0; i < n->actions->count; i += 2) {
char *human_readable = n->actions->actions[i + 1];
string_replace_char('[', '(', human_readable); // kill square brackets
@ -489,14 +482,11 @@ void notification_init(notification *n)
}
}
}
g_free(tmp);
}
void notification_update_text_to_render(notification *n)
{
g_free(n->text_to_render);
n->text_to_render = NULL;
g_clear_pointer(&n->text_to_render, g_free);
char *buf = NULL;

View File

@ -35,34 +35,44 @@ typedef struct _actions {
} Actions;
typedef struct _notification {
int id;
char *dbus_client;
char *appname;
char *summary;
char *body;
char *icon;
RawImage *raw_icon;
char *msg; /* formatted message */
char *category;
char *text_to_render;
const char *format;
char *dbus_client;
gint64 start;
gint64 timestamp;
gint64 timeout;
enum urgency urgency;
enum markup_mode markup;
bool redisplayed; /* has been displayed before? */
int id;
int dup_count;
int displayed_height;
const char *color_strings[3];
bool first_render;
bool transient;
int progress; /* percentage (-1: undefined) */
int history_ignore;
const char *script;
char *urls;
char *icon; /* plain icon information (may be a path or just a name) */
RawImage *raw_icon; /* passed icon data of notification, takes precedence over icon */
gint64 start; /* begin of current display */
gint64 timestamp; /* arrival time */
gint64 timeout; /* time to display */
Actions *actions;
enum markup_mode markup;
const char *format;
const char *script;
char *colors[3];
/* Hints */
bool transient; /* timeout albeit user is idle */
int progress; /* percentage (-1: undefined) */
int history_ignore; /* push to history or free directly */
/* internal */
bool redisplayed; /* has been displayed before? */
bool first_render; /* markup has been rendered before? */
int dup_count; /* amount of duplicate notifications stacked onto this */
int displayed_height;
/* derived fields */
char *msg; /* formatted message */
char *text_to_render; /* formatted message (with age and action indicators) */
char *urls; /* urllist delimited by '\n' */
} notification;
notification *notification_create(void);

View File

@ -51,7 +51,7 @@ unsigned int queues_length_history()
return history->length;
}
int queues_notification_insert(notification *n, int replaces_id)
int queues_notification_insert(notification *n)
{
/* do not display the message, if the message is empty */
@ -72,12 +72,11 @@ int queues_notification_insert(notification *n, int replaces_id)
return 0;
}
if (replaces_id == 0) {
if (n->id == 0) {
n->id = ++next_notification_id;
if (!settings.stack_duplicates || !queues_stack_duplicate(n))
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
} else {
n->id = replaces_id;
if (!queues_notification_replace_id(n))
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
}

View File

@ -34,13 +34,13 @@ unsigned int queues_length_history();
* Insert a fully initialized notification into queues
* Respects stack_duplicates, and notification replacement
*
* If replaces_id != 0, n replaces notification with id replaces_id
* If replaces_id == 0, n gets occupies a new position
* If n->id != 0, n replaces notification with id n->id
* If n->id == 0, n gets a new id assigned
*
* Returns the assigned notification id
* If returned id == 0, the message was dismissed
*/
int queues_notification_insert(notification *n, int replaces_id);
int queues_notification_insert(notification *n);
/*
* Replace the notification which matches the id field of

View File

@ -28,10 +28,14 @@ void rule_apply(rule_t *r, notification *n)
rawimage_free(n->raw_icon);
n->raw_icon = NULL;
}
if (r->fg)
n->color_strings[ColFG] = r->fg;
if (r->bg)
n->color_strings[ColBG] = r->bg;
if (r->fg) {
g_free(n->colors[ColFG]);
n->colors[ColFG] = g_strdup(r->fg);
}
if (r->bg) {
g_free(n->colors[ColBG]);
n->colors[ColBG] = g_strdup(r->bg);
}
if (r->format)
n->format = r->format;
if (r->script)

View File

@ -468,9 +468,9 @@ static colored_layout *r_init_shared(cairo_t *c, notification *n)
cl->icon = NULL;
}
cl->fg = x_string_to_color_t(n->color_strings[ColFG]);
cl->bg = x_string_to_color_t(n->color_strings[ColBG]);
cl->frame = x_string_to_color_t(n->color_strings[ColFrame]);
cl->fg = x_string_to_color_t(n->colors[ColFG]);
cl->bg = x_string_to_color_t(n->colors[ColBG]);
cl->frame = x_string_to_color_t(n->colors[ColFrame]);
cl->n = n;
@ -987,26 +987,26 @@ void x_setup(void)
x_shortcut_grab(&settings.context_ks);
x_shortcut_ungrab(&settings.context_ks);
xctx.color_strings[ColFG][URG_LOW] = settings.lowfgcolor;
xctx.color_strings[ColFG][URG_NORM] = settings.normfgcolor;
xctx.color_strings[ColFG][URG_CRIT] = settings.critfgcolor;
xctx.colors[ColFG][URG_LOW] = settings.lowfgcolor;
xctx.colors[ColFG][URG_NORM] = settings.normfgcolor;
xctx.colors[ColFG][URG_CRIT] = settings.critfgcolor;
xctx.color_strings[ColBG][URG_LOW] = settings.lowbgcolor;
xctx.color_strings[ColBG][URG_NORM] = settings.normbgcolor;
xctx.color_strings[ColBG][URG_CRIT] = settings.critbgcolor;
xctx.colors[ColBG][URG_LOW] = settings.lowbgcolor;
xctx.colors[ColBG][URG_NORM] = settings.normbgcolor;
xctx.colors[ColBG][URG_CRIT] = settings.critbgcolor;
if (settings.lowframecolor)
xctx.color_strings[ColFrame][URG_LOW] = settings.lowframecolor;
xctx.colors[ColFrame][URG_LOW] = settings.lowframecolor;
else
xctx.color_strings[ColFrame][URG_LOW] = settings.frame_color;
xctx.colors[ColFrame][URG_LOW] = settings.frame_color;
if (settings.normframecolor)
xctx.color_strings[ColFrame][URG_NORM] = settings.normframecolor;
xctx.colors[ColFrame][URG_NORM] = settings.normframecolor;
else
xctx.color_strings[ColFrame][URG_NORM] = settings.frame_color;
xctx.colors[ColFrame][URG_NORM] = settings.frame_color;
if (settings.critframecolor)
xctx.color_strings[ColFrame][URG_CRIT] = settings.critframecolor;
xctx.colors[ColFrame][URG_CRIT] = settings.critframecolor;
else
xctx.color_strings[ColFrame][URG_CRIT] = settings.frame_color;
xctx.colors[ColFrame][URG_CRIT] = settings.frame_color;
/* parse and set xctx.geometry and monitor position */
if (settings.geom[0] == '-') {

View File

@ -28,7 +28,7 @@ typedef struct _xctx {
Window win;
bool visible;
dimension_t geometry;
const char *color_strings[3][3];
const char *colors[3][3];
XScreenSaverInfo *screensaver_info;
dimension_t window_dim;
unsigned long sep_custom_col;

View File

@ -45,12 +45,108 @@ TEST test_markup_transform(void)
ASSERT_STR_EQ("<i>foo</i> bar baz", (ptr=markup_transform(g_strdup("<i>foo</i><br>bar\nbaz"), MARKUP_FULL)));
g_free(ptr);
// Test replacement of img and a tags, not renderable by pango
ASSERT_STR_EQ("foo bar bar baz", (ptr=markup_transform(g_strdup("<img alt=\"foo bar\"><br>bar\nbaz"), MARKUP_FULL)));
g_free(ptr);
ASSERT_STR_EQ("test ", (ptr=markup_transform(g_strdup("test <img alt=\"foo bar\""), MARKUP_FULL)));
g_free(ptr);
ASSERT_STR_EQ("test [image] image", (ptr=markup_transform(g_strdup("test <img src=\"nothing.jpg\"> image"), MARKUP_FULL)));
g_free(ptr);
ASSERT_STR_EQ("bar baz", (ptr=markup_transform(g_strdup("<a href=\"asdf\">bar</a> baz"), MARKUP_FULL)));
g_free(ptr);
PASS();
}
TEST helper_markup_strip_a (const char *in, const char *exp, const char *urls)
{
// out_urls is a return parameter and the content should be ignored
char *out_urls = (char *)0x04; //Chosen by a fair dice roll
char *out = g_strdup(in);
char *msg = g_strconcat("url: ", in, NULL);
markup_strip_a(&out, &out_urls);
ASSERT_STR_EQm(msg, exp, out);
if (urls) {
ASSERT_STR_EQm(msg, urls, out_urls);
} else {
ASSERT_EQm(msg, urls, out_urls);
}
g_free(out_urls);
g_free(out);
g_free(msg);
PASS();
}
TEST test_markup_strip_a(void)
{
RUN_TESTp(helper_markup_strip_a, "<a href=\"https://url.com\">valid</a> link", "valid link", "[valid] https://url.com");
RUN_TESTp(helper_markup_strip_a, "<a href=\"\">valid</a> link", "valid link", "[valid] ");
RUN_TESTp(helper_markup_strip_a, "<a>valid</a> link", "valid link", NULL);
RUN_TESTp(helper_markup_strip_a, "<a href=\"https://url.com\">valid link", "valid link", "[valid link] https://url.com");
RUN_TESTp(helper_markup_strip_a, "<a href=\"https://url.com\" invalid</a> link", " link", NULL);
RUN_TESTp(helper_markup_strip_a, "<a invalid</a> link", " link", NULL);
PASS();
}
TEST helper_markup_strip_img (const char *in, const char *exp, const char *urls)
{
// out_urls is a return parameter and the content should be ignored
char *out_urls = (char *)0x04; //Chosen by a fair dice roll
char *out = g_strdup(in);
char *msg = g_strconcat("url: ", in, NULL);
markup_strip_img(&out, &out_urls);
ASSERT_STR_EQm(msg, exp, out);
if (urls) {
ASSERT_STR_EQm(msg, urls, out_urls);
} else {
ASSERT_EQm(msg, urls, out_urls);
}
g_free(out_urls);
g_free(out);
g_free(msg);
PASS();
}
TEST test_markup_strip_img(void)
{
RUN_TESTp(helper_markup_strip_img, "v <img> img", "v [image] img", NULL);
RUN_TESTp(helper_markup_strip_img, "v <img alt=\"valid\" alt=\"invalid\"> img", "v valid img", NULL);
RUN_TESTp(helper_markup_strip_img, "v <img src=\"url.com\"> img", "v [image] img", "[image] url.com");
RUN_TESTp(helper_markup_strip_img, "v <img alt=\"valid\" src=\"url.com\"> img", "v valid img", "[valid] url.com");
RUN_TESTp(helper_markup_strip_img, "v <img src=\"url.com\" alt=\"valid\"> img", "v valid img", "[valid] url.com");
RUN_TESTp(helper_markup_strip_img, "v <img src=\"url.com\" alt=\"valid\" alt=\"i\"> img", "v valid img", "[valid] url.com");
RUN_TESTp(helper_markup_strip_img, "i <img alt=\"invalid src=\"https://url.com\"> img", "i [image] img", "[image] https://url.com");
RUN_TESTp(helper_markup_strip_img, "i <img alt=\"broken\" src=\"https://url.com > img", "i broken img", NULL);
RUN_TESTp(helper_markup_strip_img, "i <img alt=\"invalid src=\"https://url.com > img", "i [image] img", NULL);
RUN_TESTp(helper_markup_strip_img, "i <img src=\"url.com alt=\"broken\"> img", "i broken img", NULL);
RUN_TESTp(helper_markup_strip_img, "i <img src=\"url.com\" alt=\"invalid > img", "i [image] img", "[image] url.com");
RUN_TESTp(helper_markup_strip_img, "i <img src=\"url.com alt=\"invalid > img", "i [image] img", NULL);
RUN_TESTp(helper_markup_strip_img, "i <img src=\"url.com\" alt=\"invalid\" img", "i ", NULL);
PASS();
}
SUITE(suite_markup)
{
RUN_TEST(test_markup_strip);
RUN_TEST(test_markup_strip_a);
RUN_TEST(test_markup_strip_img);
RUN_TEST(test_markup_transform);
}