master
c 346 lines 10.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows-events-xml.h"
4
5 #include <string.h>
6 #include <stdio.h>
7
8 #define INDENT_STEP 2
9 #define A_LOT_OF_SPACES " "
10
11 // Helper: Add indentation
12 static inline void buffer_add_xml_indent(BUFFER *buffer, const int level) {
13 size_t total_spaces = (size_t)level * INDENT_STEP;
14 const size_t step = sizeof(A_LOT_OF_SPACES) - 1;
15 while (total_spaces > 0) {
16 const size_t spaces_to_add = (total_spaces > step) ? step : total_spaces;
17 buffer_fast_strcat(buffer, A_LOT_OF_SPACES, spaces_to_add);
18 total_spaces -= spaces_to_add;
19 }
20 }
21
22 const char *append_the_rest(BUFFER *buffer, const char *xml, const char *end) {
23 if(xml >= end) return end;
24 buffer_fast_strcat(buffer, xml, end - xml);
25 return end;
26 }
27
28 static const char *parse_node(BUFFER *buffer, const char *xml, const char *end, int level);
29
30 // Helper: Parse the value (between > and <) and return the next position to parse
31 const char *parse_value_and_closing_tag(BUFFER *buffer, const char *xml, const char *end, int level) {
32 const char *start = xml;
33 bool has_subnodes = false;
34
35 // const char *tag_start = NULL, *tag_end = NULL;
36 while (xml < end) {
37 if(*xml == '<') {
38 if(xml + 1 < end && *(xml + 1) == '/') {
39 // a closing tag
40 xml += 2;
41
42 // tag_start = xml;
43
44 while(xml < end && *xml != '>')
45 xml++;
46
47 // tag_end = xml;
48
49 if(xml < end && *xml == '>')
50 xml++;
51
52 if(has_subnodes) {
53 buffer_putc(buffer, '\n');
54 buffer_add_xml_indent(buffer, level);
55 }
56
57 buffer_fast_strcat(buffer, start, xml - start);
58 return xml;
59 }
60 else {
61 // an opening tag
62 buffer_fast_strcat(buffer, start, xml - start);
63 xml = start = parse_node(buffer, xml, end, level + 1);
64 while(xml < end && isspace((uint8_t)*xml))
65 xml++;
66 has_subnodes = true;
67 }
68 }
69 else
70 xml++;
71 }
72
73 return append_the_rest(buffer, start, end);
74 }
75
76 // Parse a field value and return the next position to parse
77 const char *parse_field_value(BUFFER *buffer, const char *xml, const char *end) {
78 const char quote = *xml;
79
80 if(quote != '"' && quote != '\'')
81 return append_the_rest(buffer, xml, end);
82
83 const char *start = xml++;
84
85 while (xml < end && *xml != quote) {
86 if (*xml == '\\') {
87 xml++; // Skip escape character
88
89 if(xml < end)
90 xml++;
91
92 continue;
93 }
94
95 xml++;
96 }
97
98 if(xml < end && *xml == quote) {
99 xml++; // Move past the closing quote
100 buffer_fast_strcat(buffer, start, xml - start);
101 return xml;
102 }
103
104 return append_the_rest(buffer, start, end);
105 }
106
107 // Parse a field name and return the next position to parse
108 const char *parse_field(BUFFER *buffer, const char *xml, const char *end) {
109 while(isspace((uint8_t)*xml) && xml < end) xml++;
110
111 const char *start = xml;
112
113 while (*xml != '=' && xml < end)
114 xml++;
115
116 // Append the field name
117 buffer_fast_strcat(buffer, start, xml - start);
118
119 if(xml < end && *xml == '=') {
120 xml++;
121
122 buffer_putc(buffer, '=');
123
124 if(xml < end && (*xml == '"' || *xml == '\''))
125 xml = parse_field_value(buffer, xml, end);
126
127 return xml; // Return the next character to parse
128 }
129
130 return append_the_rest(buffer, start, end);
131 }
132
133 // Parse a node (handles fields and subnodes) and return the next position to parse
134 static inline const char *parse_node(BUFFER *buffer, const char *xml, const char *end, int level) {
135 if(*xml != '<')
136 return append_the_rest(buffer, xml, end);
137
138 const char *start = xml++; // skip the <
139
140 buffer_putc(buffer, '\n');
141 buffer_add_xml_indent(buffer, level);
142
143 // skip spaces before the tag name
144 while(xml < end && isspace((uint8_t)*xml)) xml++;
145
146 // Parse the tag name
147 // const char *tag_start = xml, *tag_end = NULL;
148 while (xml < end && *xml != '>' && *xml != '/') {
149 xml++;
150
151 if(xml < end && isspace((uint8_t)*xml)) {
152 xml++;
153 // tag_end = xml;
154
155 while(xml < end && isspace((uint8_t)*xml))
156 xml++;
157
158 if(xml < end && *xml == '/') {
159 // an opening tag that is self-closing
160 xml++;
161 if(xml < end && *xml == '>') {
162 xml++;
163 buffer_fast_strcat(buffer, start, xml - start);
164 return xml;
165 }
166 else
167 return append_the_rest(buffer, start, end);
168 }
169 else if(xml < end && *xml == '>') {
170 // the end of an opening tag
171 xml++;
172 buffer_fast_strcat(buffer, start, xml - start);
173 return parse_value_and_closing_tag(buffer, xml, end, level);
174 }
175 else {
176 buffer_fast_strcat(buffer, start, xml - start);
177 xml = start = parse_field(buffer, xml, end);
178 while(xml < end && isspace((uint8_t)*xml))
179 xml++;
180 }
181 }
182 }
183
184 bool self_closing_tag = false;
185 if(xml < end && *xml == '/') {
186 self_closing_tag = true;
187 xml++;
188 }
189
190 if(xml < end && *xml == '>') {
191 xml++;
192 buffer_fast_strcat(buffer, start, xml - start);
193
194 if(self_closing_tag)
195 return xml;
196
197 return parse_value_and_closing_tag(buffer, xml, end, level);
198 }
199
200 return append_the_rest(buffer, start, end);
201 }
202
203 static inline void buffer_pretty_print_xml_object(BUFFER *buffer, const char *xml, const char *end) {
204 while(xml < end) {
205 while(xml < end && isspace((uint8_t)*xml))
206 xml++;
207
208 if(xml < end && *xml == '<')
209 xml = parse_node(buffer, xml, end, 1);
210 else {
211 append_the_rest(buffer, xml, end);
212 return;
213 }
214 }
215 }
216
217 void buffer_pretty_print_xml(BUFFER *buffer, const char *xml, size_t xml_len) {
218 const char *end = xml + xml_len;
219 buffer_pretty_print_xml_object(buffer, xml, end);
220 }
221
222 // --------------------------------------------------------------------------------------------------------------------
223
224 bool buffer_extract_and_print_xml_with_cb(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[],
225 void (*cb)(BUFFER *, const char *, const char *, const char *)) {
226 if(!keys || !*keys[0]) {
227 buffer_pretty_print_xml(buffer, xml, xml_len);
228 return true;
229 }
230
231 const char *start = xml, *end = NULL;
232 for(size_t k = 0; keys[k] ; k++) {
233 if(!*keys[k]) continue;
234
235 size_t klen = strlen(keys[k]);
236 char *tag_open = mallocz(klen + 2);
237 tag_open[0] = '<';
238 strcpy(&tag_open[1], keys[k]);
239 tag_open[klen + 1] = '\0';
240
241 const char *new_start = strstr(start, tag_open);
242 freez(tag_open);
243 if(!new_start)
244 return false;
245
246 start = new_start + klen + 1;
247
248 if(*start != '>' && !isspace((uint8_t)*start))
249 return false;
250
251 if(*start != '>') {
252 start = strchr(start, '>');
253 if(!start) return false;
254 }
255 start++; // skip the >
256
257 char *tag_close = mallocz(klen + 4);
258 tag_close[0] = '<';
259 tag_close[1] = '/';
260 strcpy(&tag_close[2], keys[k]);
261 tag_close[klen + 2] = '>';
262 tag_close[klen + 3] = '\0';
263
264 const char *new_end = strstr(start, tag_close);
265 freez(tag_close);
266 if(!new_end || (end && new_end > end))
267 return false;
268
269 end = new_end;
270 }
271
272 if(!start || !end || start == end)
273 return false;
274
275 cb(buffer, prefix, start, end);
276 return true;
277 }
278
279 static void print_xml_cb(BUFFER *buffer, const char *prefix, const char *start, const char *end) {
280 if(prefix)
281 buffer_strcat(buffer, prefix);
282
283 buffer_pretty_print_xml_object(buffer, start, end);
284 }
285
286 bool buffer_extract_and_print_xml(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]) {
287 return buffer_extract_and_print_xml_with_cb(
288 buffer, xml, xml_len,
289 prefix, keys,
290 print_xml_cb);
291 }
292
293 static void print_value_cb(BUFFER *buffer, const char *prefix, const char *start, const char *end) {
294 if(prefix)
295 buffer_strcat(buffer, prefix);
296
297 buffer_need_bytes(buffer, end - start + 1);
298
299 char *started = &buffer->buffer[buffer->len];
300 char *d = started;
301 const char *s = start;
302
303 while(s < end && s) {
304 if(*s == '&' && s + 3 < end) {
305 if(*(s + 1) == '#') {
306 if(s + 4 < end && *(s + 2) == '1' && *(s + 4) == ';') {
307 if (*(s + 3) == '0') {
308 s += 5;
309 *d++ = '\n';
310 continue;
311 } else if (*(s + 3) == '3') {
312 s += 5;
313 // *d++ = '\r';
314 continue;
315 }
316 } else if (*(s + 2) == '9' && *(s + 3) == ';') {
317 s += 4;
318 *d++ = '\t';
319 continue;
320 }
321 }
322 else if(s + 3 < end && *(s + 2) == 't' && *(s + 3) == ';') {
323 if(*(s + 1) == 'l') {
324 s += 4;
325 *d++ = '<';
326 continue;
327 }
328 else if(*(s + 1) == 'g') {
329 s += 4;
330 *d++ = '>';
331 continue;
332 }
333 }
334 }
335 *d++ = *s++;
336 }
337 *d = '\0';
338 buffer->len += d - started;
339 }
340
341 bool buffer_xml_extract_and_print_value(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]) {
342 return buffer_extract_and_print_xml_with_cb(
343 buffer, xml, xml_len,
344 prefix, keys,
345 print_value_cb);
346 }