talkatu/talkatu

Update the minimum meson to 0.56.0

2021-08-03, Gary Kramlich
13ee9f88fa95
Update the minimum meson to 0.56.0

Testing Done:
ran `meson --wipe` and `ninja` and verified there were no warnings.

Reviewed at https://reviews.imfreedom.org/r/867/
/*
* Talkatu - GTK widgets for chat applications
* Copyright (C) 2017-2020 Gary Kramlich <grim@reaperworld.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, see <https://www.gnu.org/licenses/>.
*/
#include <gumbo.h>
#include "talkatuhtmlrenderer.h"
/**
* SECTION:talkatuhtmlrenderer
* @Title: HTML Renderer
* @Short_description: An Expat-like HTML Renderer
*
* #TalkatuHtmlRenderer is an abstract class that will parse HTML and call the
* registered instance methods for each element allowing subclasses to render
* the HTML however they like.
*/
/**
* TALKATU_TYPE_HTML_RENDERER:
*
* The standard _get_type macro for #TalkatuHtmlRenderer.
*/
/**
* TalkatuHtmlRendererClass:
* @reset: The method to call to reset the renderer. This allows the renderer
* to be reused.
* @element_start: The method to call when an element is found. The attribute
* names and values are passed in as a %NULL terminated array of
* strings.
* @element_finish: The method to call when all children of an element have been
* processed.
* @text: The method to call when can text or character data is found.
* @comment: The method to call when a comment is found. The passed in comment
* is the contents only and does not contain the start (<!--) and end
* (-->) tags.
*
* An abstract class that will walk an HTML document and call the instance
* methods of the child class for each node that is found.
*/
#define _GUMBO_NODE_IS_CONTAINER(node) \
((node)->type == GUMBO_NODE_ELEMENT || \
(node)->type == GUMBO_NODE_TEMPLATE)
static GumboNode *
talkatu_html_renderer_find_next_sibling(GumboNode *node) {
GumboNode *next = NULL;
if(node->parent == NULL) {
return NULL;
}
/* As long as we have a parent, we can use it with our node's
* `index_within_parent` to figure out if we have any more siblings.
*/
if(_GUMBO_NODE_IS_CONTAINER(node->parent)) {
GumboElement element = node->parent->v.element;
if(node->index_within_parent != element.children.length - 1) {
next = element.children.data[node->index_within_parent+1];
}
}
return next;
}
/******************************************************************************
* Helper Implementations
*****************************************************************************/
static void
talkatu_html_renderer_element_start(TalkatuHtmlRenderer *renderer,
const gchar *name,
GumboElement *element)
{
TalkatuHtmlRendererClass *klass = TALKATU_HTML_RENDERER_GET_CLASS(renderer);
if(klass->element_start) {
const gchar **names = NULL, **values = NULL;
guint length = element->attributes.length;
if(length > 0) {
guint i = 0;
names = g_new(const gchar *, length + 1);
values = g_new(const gchar *, length + 1);
for(i = 0; i < length; i++) {
GumboAttribute *attr = NULL;
attr = (GumboAttribute *)element->attributes.data[i];
names[i] = attr->name;
values[i] = attr->value;
}
/* add our terminating null values to the end */
names[i] = NULL;
values[i] = NULL;
}
klass->element_start(renderer, name, names, values);
g_free(names);
g_free(values);
}
}
static void
talkatu_html_renderer_element_finish(TalkatuHtmlRenderer *renderer,
const gchar *name)
{
TalkatuHtmlRendererClass *klass = TALKATU_HTML_RENDERER_GET_CLASS(renderer);
if(klass->element_finish) {
klass->element_finish(renderer, name);
}
}
static void
talkatu_html_renderer_text(TalkatuHtmlRenderer *renderer, const gchar *text) {
TalkatuHtmlRendererClass *klass = TALKATU_HTML_RENDERER_GET_CLASS(renderer);
if(klass->text) {
klass->text(renderer, text);
}
}
static void
talkatu_html_renderer_comment(TalkatuHtmlRenderer *renderer,
const gchar *comment)
{
TalkatuHtmlRendererClass *klass = TALKATU_HTML_RENDERER_GET_CLASS(renderer);
if(klass->comment) {
klass->comment(renderer, comment);
}
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
G_DEFINE_ABSTRACT_TYPE(TalkatuHtmlRenderer, talkatu_html_renderer,
G_TYPE_OBJECT)
static void
talkatu_html_renderer_init(TalkatuHtmlRenderer *renderer) {
}
static void
talkatu_html_renderer_class_init(TalkatuHtmlRendererClass *klass) {
}
/******************************************************************************
* Public API
*****************************************************************************/
/**
* talkatu_html_renderer_render:
* @renderer: The #TalkatuHtmlRenderer instance.
* @html: The HTML text to render.
*
* Renders the given @html calling the #TalkatuHtmlRendererClass functions as
* necessary.
*/
void
talkatu_html_renderer_render(TalkatuHtmlRenderer *renderer, const gchar *html) {
GList *stack = NULL;
GumboOutput *output = NULL;
output = gumbo_parse(html);
stack = g_list_prepend(stack, output->root);
/* We create a stack with the first node and then process according to the
* node type.
*
* For non-element nodes, we call the text/comment function as appropriate
* and then look for their siblings. If the node has a sibling, we remove
* the current node from the stack and replace it with its sibling.
*
* For element nodes, we call element_start and push the first child to the
* stack if the node has children and immediately start processing the
* child. If the element does not have children, we call element_finish,
* remove it from the stack and look for a sibling to push to the stack.
*
* If the node does not have a sibling, we call element_finish on its parent
* and remove it from the stack. Then we check for its parent and repeat
* the process until we have found a sibling or have exhausted the stack.
*/
while(stack != NULL) {
GumboNode *node = (GumboNode *)stack->data;
GumboNode *next = NULL;
const gchar *tagname = NULL;
switch(node->type) {
case GUMBO_NODE_DOCUMENT:
/* this is here to stop a warning from gcc. We could add a
* default case, but then if a new type is added or something we
* would mask the warning that it would generate.
*/
break;
case GUMBO_NODE_ELEMENT:
case GUMBO_NODE_TEMPLATE:
tagname = gumbo_normalized_tagname(node->v.element.tag);
talkatu_html_renderer_element_start(renderer, tagname,
&node->v.element);
if(node->v.element.children.length > 0) {
/* if we have at least one child, we throw it on the stack
* and start processing that node.
*/
node = (GumboNode *)(&node->v.element.children)->data[0];
stack = g_list_prepend(stack, node);
continue;
} else {
/* We have no children so we just call the finish method. */
talkatu_html_renderer_element_finish(renderer, tagname);
}
break;
case GUMBO_NODE_CDATA:
case GUMBO_NODE_TEXT:
case GUMBO_NODE_WHITESPACE:
talkatu_html_renderer_text(renderer, node->v.text.text);
break;
case GUMBO_NODE_COMMENT:
talkatu_html_renderer_comment(renderer, node->v.text.text);
break;
}
/* check if we have a sibling */
next = talkatu_html_renderer_find_next_sibling(node);
/* remove the node from the stack */
stack = g_list_remove(stack, node);
/* if the node was the last, we need to end element_finish for the
* parent and pop the parent from the stack as well.
*/
if(next != NULL) {
stack = g_list_prepend(stack, next);
} else if(node->parent != NULL && _GUMBO_NODE_IS_CONTAINER(node->parent)) {
/* Our node has no other siblings, so we need to finish the parent
* element.
*/
GumboElement parent_element = node->parent->v.element;
tagname = gumbo_normalized_tagname(parent_element.tag);
talkatu_html_renderer_element_finish(renderer, tagname);
/* while we still have elements on the list, pop them off until we
* find one that still has children we haven't visited yet.
*/
while(stack != NULL) {
GumboElement element;
node = (GumboNode *)stack->data;
next = talkatu_html_renderer_find_next_sibling(node);
if(next != NULL) {
/* we found a sibling, so drop the top most item and
* put the sibling on the top of the stack.
*/
stack = g_list_remove(stack, node);
stack = g_list_prepend(stack, next);
break;
}
if(node->parent->type != GUMBO_NODE_DOCUMENT) {
element = node->parent->v.element;
tagname = gumbo_normalized_tagname(element.tag);
talkatu_html_renderer_element_finish(renderer, tagname);
}
/* If this node doesn't have a sibling, then pop it off the
* stack.
*/
stack = g_list_remove(stack, node);
}
}
}
gumbo_destroy_output(&kGumboDefaultOptions, output);
}
/**
* talkatu_html_renderer_reset:
* @renderer: The #TalkatuHtmlRenderer instance.
*
* Resets @renderer back to a clean state so that it can render new HTML.
*/
void
talkatu_html_renderer_reset(TalkatuHtmlRenderer *renderer) {
TalkatuHtmlRendererClass *klass = NULL;
g_return_if_fail(TALKATU_IS_HTML_RENDERER(renderer));
klass = TALKATU_HTML_RENDERER_GET_CLASS(renderer);
if(klass && klass->reset) {
klass->reset(renderer);
}
}