1 /* Copyright (C) 2016-2019 Shengyu Zhang <i@silverrainz.me>
2 *
3 * This file is part of Srain.
4 *
5 * Srain is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib.h>
20
21 #include "srain.h"
22 #include "ret.h"
23
24 #include "markup_renderer.h"
25
26 struct _SrnMarkupRenderer {
27 GMarkupParser parser;
28 GString *str;
29 void *user_data;
30 bool is_parsing;
31 };
32
33 #define ROOT_TAG "root"
34
35 static void start_element(GMarkupParseContext *context, const gchar *element_name,
36 const gchar **attribute_names, const gchar **attribute_values,
37 gpointer user_data, GError **error);
38 static void end_element(GMarkupParseContext *context, const gchar *element_name,
39 gpointer user_data, GError **error);
40 static void text(GMarkupParseContext *context, const gchar *text, gsize text_len,
41 gpointer user_data, GError **error);
42 static void passthrough(GMarkupParseContext *context,
43 const gchar *passthrough_text, gsize text_len, gpointer user_data,
44 GError **error);
45 static void error(GMarkupParseContext *context, GError *error,
46 gpointer user_data);
47
srn_markup_renderer_new(void)48 SrnMarkupRenderer *srn_markup_renderer_new(void) {
49 SrnMarkupRenderer *self;
50
51 self = g_malloc0(sizeof(SrnMarkupRenderer));
52 self->parser.start_element = start_element;
53 self->parser.end_element = end_element;
54 self->parser.text = text;
55 self->parser.passthrough = passthrough;
56 self->parser.error = error;
57 self->is_parsing = FALSE;
58
59 return self;
60 }
61
srn_markup_renderer_free(SrnMarkupRenderer * self)62 void srn_markup_renderer_free(SrnMarkupRenderer *self) {
63 g_warn_if_fail(!self->is_parsing);
64 g_warn_if_fail(!self->str);
65 g_warn_if_fail(!self->user_data);
66 g_free(self);
67 }
68
srn_markup_renderer_render(SrnMarkupRenderer * self,const char * markup_in,char ** markup_out,void * user_data)69 SrnRet srn_markup_renderer_render(SrnMarkupRenderer *self,
70 const char *markup_in, char **markup_out, void *user_data) {
71 GError *err;
72 GMarkupParseContext *parse_ctx;
73 SrnRet ret;
74
75 g_return_val_if_fail(!self->is_parsing, SRN_ERR);
76
77 self->str = g_string_new(NULL);
78 self->user_data = user_data;
79 self->is_parsing = TRUE;
80
81 ret = SRN_OK;
82 err = NULL;
83 parse_ctx = g_markup_parse_context_new(&self->parser, 0, self, NULL);
84 g_markup_parse_context_parse(parse_ctx, "<" ROOT_TAG ">", -1, NULL);
85 g_markup_parse_context_parse(parse_ctx, markup_in, -1, &err);
86 g_markup_parse_context_parse(parse_ctx, "</" ROOT_TAG ">", -1, NULL);
87 g_markup_parse_context_end_parse(parse_ctx, NULL);
88 g_markup_parse_context_free(parse_ctx);
89
90 if (err){
91 ret = RET_ERR("Markup parse error: %s", err->message);
92 g_error_free(err);
93 }
94
95 if (markup_out) {
96 *markup_out = self->str->str;
97 g_string_free(self->str, FALSE);
98 } else {
99 g_string_free(self->str, TRUE);
100 }
101
102 self->str = NULL;
103 self->user_data = NULL;
104 self->is_parsing = FALSE;
105
106 return ret;
107 }
108
srn_markup_renderer_get_markup_parser(SrnMarkupRenderer * self)109 GMarkupParser* srn_markup_renderer_get_markup_parser(SrnMarkupRenderer *self) {
110 return &self->parser;
111 }
112
srn_markup_renderer_get_markup(SrnMarkupRenderer * self)113 GString* srn_markup_renderer_get_markup(SrnMarkupRenderer *self) {
114 g_warn_if_fail(self->is_parsing);
115 return self->str;
116 }
117
srn_markup_renderer_get_user_data(SrnMarkupRenderer * self)118 void* srn_markup_renderer_get_user_data(SrnMarkupRenderer *self) {
119 g_warn_if_fail(self->is_parsing);
120 return self->user_data;
121 }
122
123 /* GLib Markup parser callbacks */
124
start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)125 static void start_element(GMarkupParseContext *context, const gchar *element_name,
126 const gchar **attribute_names, const gchar **attribute_values,
127 gpointer user_data, GError **error){
128 SrnMarkupRenderer *self;
129
130 self = user_data;
131
132 // Ignore root tag
133 if (g_strcmp0(element_name, ROOT_TAG) == 0){
134 return;
135 }
136
137 self->str = g_string_append_c(self->str, '<');
138 self->str = g_string_append(self->str, element_name);
139 while (*attribute_names != NULL){
140 char *escaped_value = g_markup_escape_text(*attribute_values, -1);
141 g_string_append_printf(self->str, " %s=\"%s\"",
142 *attribute_names, escaped_value);
143 g_free(escaped_value);
144 attribute_names++;
145 attribute_values++;
146 }
147 self->str = g_string_append_c(self->str, '>');
148 }
149
end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)150 static void end_element(GMarkupParseContext *context, const gchar *element_name,
151 gpointer user_data, GError **error){
152 SrnMarkupRenderer *self;
153
154 self = user_data;
155
156 if (g_strcmp0(element_name, ROOT_TAG) == 0){
157 return;
158 }
159
160 g_string_append_printf(self->str, "</%s>", element_name);
161 }
162
163 /* NOTE: text is not nul-terminated */
text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)164 static void text(GMarkupParseContext *context, const gchar *text, gsize text_len,
165 gpointer user_data, GError **error){
166 char *markup;
167 SrnMarkupRenderer *self;
168
169 self = user_data;
170
171 markup = g_markup_escape_text(text, text_len);
172 self->str = g_string_append(self->str, markup);
173 g_free(markup);
174 }
175
176 /* Called for strings that should be re-saved verbatim in this same
177 * position, but are not otherwise interpretable. At the moment
178 * this includes comments and processing instructions.
179 */
passthrough(GMarkupParseContext * context,const gchar * passthrough_text,gsize text_len,gpointer user_data,GError ** error)180 static void passthrough(GMarkupParseContext *context,
181 const gchar *passthrough_text, gsize text_len, gpointer user_data,
182 GError **error){
183 /* Ignore comments for now */
184 }
185
error(GMarkupParseContext * context,GError * error,gpointer user_data)186 static void error(GMarkupParseContext *context, GError *error,
187 gpointer user_data){
188 /* No need to deal with error here */
189 }
190