Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @f xml
15 : * @a Sjoerd Mullender, Niels Nes, Martin Kersten
16 : * @v 0.1
17 : * @+ MAL support for XQL
18 : * This module contains the primitives needed in the SQL
19 : * front-end to support SQL/XML.
20 : */
21 : #include "monetdb_config.h"
22 : #include "xml.h"
23 : #include "mal_interpreter.h"
24 :
25 : #ifdef HAVE_LIBXML
26 : #include <libxml/parser.h>
27 : #include <libxml/xmlmemory.h>
28 :
29 : /* The xml atom is used to represent XML data. It is implemented as a
30 : subtype of str. The first character of the string representation
31 : indicates the type of XML data. There are three possibilities:
32 : * D - an XML document (possibly including <?xml?> and DOCTYPE);
33 : * C - XML content, i.e. something that can occur inside an XML element;
34 : * A - XML name/attribute pair.
35 : */
36 :
37 : size_t
38 119 : XMLquotestring(const char *s, char *buf, size_t len)
39 : {
40 119 : size_t i = 0;
41 :
42 119 : assert(len > 0);
43 918 : while (*s && i + 6 < len) {
44 799 : if (*s == '&') {
45 0 : buf[i++] = '&';
46 0 : buf[i++] = 'a';
47 0 : buf[i++] = 'm';
48 0 : buf[i++] = 'p';
49 0 : buf[i++] = ';';
50 : } else if (*s == '<') {
51 0 : buf[i++] = '&';
52 0 : buf[i++] = 'l';
53 0 : buf[i++] = 't';
54 0 : buf[i++] = ';';
55 : } else if (*s == '>') {
56 0 : buf[i++] = '&';
57 0 : buf[i++] = 'g';
58 0 : buf[i++] = 't';
59 0 : buf[i++] = ';';
60 : } else if (*s == '"') {
61 0 : buf[i++] = '&';
62 0 : buf[i++] = 'q';
63 0 : buf[i++] = 'u';
64 0 : buf[i++] = 'o';
65 0 : buf[i++] = 't';
66 0 : buf[i++] = ';';
67 : } else if (*s == '\'') {
68 0 : buf[i++] = '&';
69 0 : buf[i++] = 'a';
70 0 : buf[i++] = 'p';
71 0 : buf[i++] = 'o';
72 0 : buf[i++] = 's';
73 0 : buf[i++] = ';';
74 799 : } else if ((*s & 0xFF) < 0x20) {
75 0 : int n = snprintf(buf, len - i, "&#%d;", *s & 0xFF);
76 :
77 0 : if (n < 0)
78 : break;
79 0 : i += n;
80 : } else {
81 799 : buf[i++] = *s;
82 : }
83 799 : s++;
84 : }
85 119 : if (i < len)
86 119 : buf[i] = 0;
87 : else
88 0 : buf[len - 1] = 0;
89 119 : return i;
90 : }
91 :
92 : size_t
93 0 : XMLunquotestring(const char **p, char q, char *buf)
94 : {
95 0 : const char *s = *p;
96 0 : size_t i = 0;
97 :
98 0 : while (*s && *s != q) {
99 0 : if (*s == '&') {
100 0 : s++;
101 0 : if (strncmp(s, "lt;", 3) == 0) {
102 0 : buf[i++] = '<';
103 0 : s += 3;
104 0 : } else if (strncmp(s, "gt;", 3) == 0) {
105 0 : buf[i++] = '>';
106 0 : s += 3;
107 0 : } else if (strncmp(s, "apos;", 5) == 0) {
108 0 : buf[i++] = '\'';
109 0 : s += 5;
110 0 : } else if (strncmp(s, "quot;", 5) == 0) {
111 0 : buf[i++] = '"';
112 0 : s += 5;
113 0 : } else if (strncmp(s, "amp;", 4) == 0) {
114 0 : buf[i++] = '&';
115 0 : s += 4;
116 0 : } else if (*s == '#') {
117 0 : char *e;
118 0 : int base;
119 0 : unsigned long n; /* type unsigned long returned by strtoul() */
120 :
121 0 : s++;
122 0 : if (*s == 'x' || *s == 'X') {
123 0 : s++;
124 0 : base = 16;
125 : } else
126 : base = 10;
127 0 : n = strtoul(s, &e, base);
128 0 : assert(e > s && *e == ';');
129 0 : assert(n <= 0x7FFFFFFF);
130 0 : s = e + 1;
131 0 : if (n <= 0x7F)
132 0 : buf[i++] = (char) n;
133 0 : else if (n <= 0x7FF) {
134 0 : buf[i++] = (char) (0xC0 | (n >> 6));
135 0 : buf[i++] = (char) (0x80 | (n & 0x3F));
136 0 : } else if (n <= 0xFFFF) {
137 0 : buf[i++] = (char) (0xE0 | (n >> 12));
138 0 : buf[i++] = (char) (0x80 | ((n >> 6) & 0x3F));
139 0 : buf[i++] = (char) (0x80 | (n & 0x3F));
140 0 : } else if (n <= 0x1FFFFF) {
141 0 : buf[i++] = (char) (0xF0 | (n >> 18));
142 0 : buf[i++] = (char) (0x80 | ((n >> 12) & 0x3F));
143 0 : buf[i++] = (char) (0x80 | ((n >> 6) & 0x3F));
144 0 : buf[i++] = (char) (0x80 | (n & 0x3F));
145 0 : } else if (n <= 0x3FFFFFF) {
146 0 : buf[i++] = (char) (0xF8 | (n >> 24));
147 0 : buf[i++] = (char) (0x80 | ((n >> 18) & 0x3F));
148 0 : buf[i++] = (char) (0x80 | ((n >> 12) & 0x3F));
149 0 : buf[i++] = (char) (0x80 | ((n >> 6) & 0x3F));
150 0 : buf[i++] = (char) (0x80 | (n & 0x3F));
151 0 : } else if (n <= 0x7FFFFFFF) {
152 0 : buf[i++] = (char) (0xFC | (n >> 30));
153 0 : buf[i++] = (char) (0x80 | ((n >> 24) & 0x3F));
154 0 : buf[i++] = (char) (0x80 | ((n >> 18) & 0x3F));
155 0 : buf[i++] = (char) (0x80 | ((n >> 12) & 0x3F));
156 0 : buf[i++] = (char) (0x80 | ((n >> 6) & 0x3F));
157 0 : buf[i++] = (char) (0x80 | (n & 0x3F));
158 : }
159 : } else {
160 : /* unrecognized entity: just leave it */
161 0 : buf[i++] = '&';
162 : }
163 : } else
164 0 : buf[i++] = *s++;
165 : }
166 0 : buf[i] = 0;
167 0 : *p = s;
168 0 : return i;
169 : }
170 :
171 : str
172 2 : XMLxml2str(str *s, xml *x)
173 : {
174 2 : if (strNil(*x)) {
175 1 : *s = GDKstrdup(str_nil);
176 1 : return MAL_SUCCEED;
177 : }
178 1 : assert(**x == 'A' || **x == 'C' || **x == 'D');
179 1 : *s = GDKstrdup(*x + 1);
180 1 : return MAL_SUCCEED;
181 : }
182 :
183 : str
184 2 : XMLstr2xml(xml *x, const char **val)
185 : {
186 2 : const char *t = *val;
187 2 : str buf;
188 2 : size_t len;
189 :
190 2 : if (strNil(t)) {
191 0 : *x = (xml) GDKstrdup(str_nil);
192 0 : if (*x == NULL)
193 0 : throw(MAL, "xml.xml", SQLSTATE(HY013) MAL_MALLOC_FAIL);
194 : return MAL_SUCCEED;
195 : }
196 2 : len = 6 * strlen(t) + 1;
197 2 : buf = GDKmalloc(len + 1);
198 2 : if (buf == NULL)
199 0 : throw(MAL, "xml.xml", SQLSTATE(HY013) MAL_MALLOC_FAIL);
200 2 : buf[0] = 'C';
201 2 : XMLquotestring(t, buf + 1, len);
202 2 : *x = buf;
203 2 : return MAL_SUCCEED;
204 : }
205 :
206 : str
207 0 : XMLxmltext(str *s, xml *x)
208 : {
209 0 : xmlDocPtr doc;
210 0 : xmlNodePtr elem;
211 0 : str content = NULL;
212 :
213 0 : if (strNil(*x)) {
214 0 : *s = GDKstrdup(str_nil);
215 0 : if (*s == NULL)
216 0 : throw(MAL, "xml.text", SQLSTATE(HY013) MAL_MALLOC_FAIL);
217 : return MAL_SUCCEED;
218 : }
219 0 : if (**x == 'D') {
220 0 : doc = xmlParseMemory(*x + 1, (int) strlen(*x + 1));
221 0 : elem = xmlDocGetRootElement(doc);
222 0 : content = (str) xmlNodeGetContent(elem);
223 0 : xmlFreeDoc(doc);
224 0 : } else if (**x == 'C') {
225 0 : doc = xmlParseMemory("<doc/>", 6);
226 0 : xmlParseInNodeContext(xmlDocGetRootElement(doc), *x + 1,
227 0 : (int) strlen(*x + 1), 0, &elem);
228 0 : content = (str) xmlNodeGetContent(elem);
229 0 : xmlFreeNodeList(elem);
230 0 : xmlFreeDoc(doc);
231 0 : } else if (**x == 'A') {
232 0 : const char *t = *x + 1;
233 0 : str p;
234 :
235 0 : p = content = GDKmalloc(strlen(*x) + 1);
236 0 : if (p == NULL)
237 0 : throw(MAL, "xml.text", SQLSTATE(HY013) MAL_MALLOC_FAIL);
238 0 : while (*t) {
239 0 : if (*t == '"' || *t == '\'') {
240 0 : char q = *t++;
241 :
242 0 : p += XMLunquotestring(&t, q, p);
243 : }
244 0 : t++;
245 : }
246 0 : *p = 0;
247 : }
248 0 : if (content == NULL) {
249 0 : *s = GDKstrdup("");
250 0 : if (*s == NULL)
251 0 : throw(MAL, "xml.text", SQLSTATE(HY013) MAL_MALLOC_FAIL);
252 : } else
253 0 : *s = (str) content;
254 : return MAL_SUCCEED;
255 : }
256 :
257 : str
258 0 : XMLxml2xml(xml *s, xml *x)
259 : {
260 0 : *s = GDKstrdup(*x);
261 0 : if (*s == NULL)
262 0 : throw(MAL, "xml.xml", SQLSTATE(HY013) MAL_MALLOC_FAIL);
263 : return MAL_SUCCEED;
264 : }
265 :
266 : str
267 0 : XMLdocument(xml *x, str *val)
268 : {
269 0 : xmlDocPtr doc;
270 :
271 0 : if (strNil(*val)) {
272 0 : *x = (xml) GDKstrdup(str_nil);
273 0 : if (*x == NULL)
274 0 : throw(MAL, "xml.document", SQLSTATE(HY013) MAL_MALLOC_FAIL);
275 : return MAL_SUCCEED;
276 : }
277 : /* call the libxml2 library to perform the test */
278 0 : doc = xmlParseMemory(*val, (int) strlen(*val));
279 0 : if (doc) {
280 0 : int len;
281 0 : xmlChar *buf;
282 :
283 0 : xmlDocDumpMemory(doc, &buf, &len);
284 0 : xmlFreeDoc(doc);
285 0 : *x = GDKmalloc(len + 2);
286 0 : if (*x == NULL)
287 0 : throw(MAL, "xml.document", SQLSTATE(HY013) MAL_MALLOC_FAIL);
288 0 : snprintf(*x, len + 2, "D%s", (char *) buf);
289 0 : GDKfree(buf);
290 0 : return MAL_SUCCEED;
291 : }
292 0 : throw(MAL, "xml.document", "Document parse error");
293 : }
294 :
295 : str
296 0 : XMLcontent(xml *x, str *val)
297 : {
298 0 : xmlDocPtr doc;
299 0 : xmlNodePtr elem;
300 0 : xmlParserErrors err;
301 0 : xmlBufferPtr buf;
302 0 : const xmlChar *s;
303 0 : size_t len;
304 :
305 0 : if (strNil(*val)) {
306 0 : *x = (xml) GDKstrdup(str_nil);
307 0 : if (*x == NULL)
308 0 : throw(MAL, "xml.content", SQLSTATE(HY013) MAL_MALLOC_FAIL);
309 : return MAL_SUCCEED;
310 : }
311 : /* call the libxml2 library to perform the test */
312 0 : doc = xmlParseMemory("<doc/>", 6);
313 0 : err = xmlParseInNodeContext(xmlDocGetRootElement(doc), *val,
314 0 : (int) strlen(*val), 0, &elem);
315 0 : if (err !=XML_ERR_OK) {
316 0 : xmlFreeDoc(doc);
317 0 : throw(MAL, "xml.content", "Content parse error");
318 : }
319 0 : buf = xmlBufferCreate();
320 0 : xmlNodeDump(buf, doc, elem, 0, 0);
321 0 : s = xmlBufferContent(buf);
322 0 : len = strlen((const char *) s) + 2;
323 0 : *x = GDKmalloc(len);
324 0 : if (*x == NULL)
325 0 : throw(MAL, "xml.content", SQLSTATE(HY013) MAL_MALLOC_FAIL);
326 0 : snprintf(*x, len, "C%s", (const char *) s);
327 0 : xmlBufferFree(buf);
328 0 : xmlFreeNodeList(elem);
329 0 : xmlFreeDoc(doc);
330 0 : return MAL_SUCCEED;
331 : }
332 :
333 : str
334 2 : XMLisdocument(bit *x, str *s)
335 : {
336 2 : xmlDocPtr doc;
337 :
338 : /* call the libxml2 library to perform the test */
339 2 : if (strNil(*s))
340 0 : *x = bit_nil;
341 : else {
342 2 : doc = xmlParseMemory(*s, (int) strlen(*s));
343 2 : *x = doc != NULL;
344 2 : if (doc)
345 2 : xmlFreeDoc(doc);
346 : }
347 2 : return MAL_SUCCEED;
348 : }
349 :
350 : str
351 1 : XMLcomment(xml *x, str *s)
352 : {
353 1 : size_t len;
354 1 : str buf;
355 :
356 1 : if (strNil(*s)) {
357 0 : *x = (xml) GDKstrdup(str_nil);
358 0 : if (*x == NULL)
359 0 : throw(MAL, "xml.comment", SQLSTATE(HY013) MAL_MALLOC_FAIL);
360 : return MAL_SUCCEED;
361 : }
362 1 : if (strstr(*s, "--") != NULL)
363 0 : throw(MAL, "xml.comment", "comment may not contain `--'");
364 1 : len = strlen(*s) + 9;
365 1 : buf = (str) GDKmalloc(len);
366 1 : if (buf == NULL)
367 0 : throw(MAL, "xml.comment", SQLSTATE(HY013) MAL_MALLOC_FAIL);
368 1 : snprintf(buf, len, "C<!--%s-->", *s);
369 1 : *x = buf;
370 1 : return MAL_SUCCEED;
371 : }
372 :
373 : str
374 0 : XMLparse(xml *x, str *doccont, str *val, str *option)
375 : {
376 0 : (void) option;
377 0 : if (strcmp(*doccont, "content") == 0)
378 0 : return XMLcontent(x, val);
379 0 : if (strcmp(*doccont, "document") == 0)
380 0 : return XMLdocument(x, val);
381 0 : throw(MAL, "xml.parse", "invalid parameter");
382 : }
383 :
384 : str
385 0 : XMLpi(xml *ret, str *target, str *value)
386 : {
387 0 : size_t len;
388 0 : str buf;
389 0 : str val = NULL;
390 :
391 0 : if (strNil(*target)) {
392 0 : *ret = GDKstrdup(str_nil);
393 0 : if (*ret == NULL)
394 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
395 : return MAL_SUCCEED;
396 : }
397 0 : if (xmlValidateName((xmlChar *) *target, 0) != 0
398 0 : || strcasecmp(*target, "xml") == 0)
399 0 : throw(MAL, "xml.attribute", "invalid processing instruction target");
400 0 : len = strlen(*target) + 6;
401 0 : if (strNil(*value) || **value == 0) {
402 0 : size_t n = 6 * strlen(*value) + 1;
403 :
404 0 : val = GDKmalloc(n);
405 0 : if (val == NULL)
406 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
407 0 : len += XMLquotestring(*value, val, n) + 1;
408 : }
409 0 : buf = GDKmalloc(len);
410 0 : if (buf == NULL) {
411 0 : if (val)
412 0 : GDKfree(val);
413 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
414 : }
415 0 : if (val == NULL) {
416 0 : snprintf(buf, len, "C<?%s?>", *target);
417 : } else {
418 0 : snprintf(buf, len, "C<?%s %s?>", *target, val);
419 0 : GDKfree(val);
420 : }
421 0 : *ret = buf;
422 0 : return MAL_SUCCEED;
423 : }
424 :
425 : str
426 0 : XMLroot(xml *ret, xml *val, str *version, str *standalone)
427 : {
428 0 : size_t len = 0, i = 0;
429 0 : str buf;
430 0 : bit isdoc = 0;
431 :
432 0 : if (strNil(*val)) {
433 0 : *ret = GDKstrdup(str_nil);
434 0 : if (*ret == NULL)
435 0 : throw(MAL, "xml.root", SQLSTATE(HY013) MAL_MALLOC_FAIL);
436 : return MAL_SUCCEED;
437 : }
438 0 : if (**val != 'C')
439 0 : throw(MAL, "xml.root", "value must be an XML node");
440 0 : len = 8;
441 0 : len = strlen(*val);
442 0 : if (!strNil(*version) && **version) {
443 0 : if (strcmp(*version, "1.0") != 0 && strcmp(*version, "1.1") != 0)
444 0 : throw(MAL, "xml.root", "illegal XML version");
445 0 : len += 11 + strlen(*version); /* strlen(" version=\"\"") */
446 : }
447 0 : if (!strNil(*standalone) && **standalone) {
448 0 : if (strcmp(*standalone, "yes") != 0 && strcmp(*standalone, "no") != 0)
449 0 : throw(MAL, "xml.root", "illegal XML standalone value");
450 0 : len += 14 + strlen(*standalone); /* strlen(" standalone=\"\"") */
451 : }
452 0 : buf = GDKmalloc(len);
453 0 : if (buf == NULL)
454 0 : throw(MAL, "xml.root", SQLSTATE(HY013) MAL_MALLOC_FAIL);
455 0 : strcpy(buf, "D<?xml");
456 0 : i = strlen(buf);
457 0 : if (!strNil(*version) && **version)
458 0 : i += snprintf(buf + i, len - i, " version=\"%s\"", *version);
459 0 : if (!strNil(*standalone) && **standalone)
460 0 : i += snprintf(buf + i, len - i, " standalone=\"%s\"", *standalone);
461 0 : snprintf(buf + i, len - i, "?>%s", *val + 1);
462 0 : buf++;
463 0 : XMLisdocument(&isdoc, &buf); /* check well-formedness */
464 0 : buf--;
465 0 : if (!isdoc) {
466 0 : GDKfree(buf);
467 0 : throw(MAL, "xml.root", "resulting document not well-formed");
468 : }
469 0 : *ret = buf;
470 0 : return MAL_SUCCEED;
471 : }
472 :
473 : str
474 1 : XMLattribute(xml *x, str *name, str *val)
475 : {
476 1 : str t = *val;
477 1 : str buf;
478 1 : size_t len;
479 :
480 2 : if (strNil(t) || strNil(*name)) {
481 0 : *x = (xml) GDKstrdup(str_nil);
482 0 : if (*x == NULL)
483 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
484 : return MAL_SUCCEED;
485 : }
486 1 : if (xmlValidateName((xmlChar *) *name, 0) != 0)
487 0 : throw(MAL, "xml.attribute", "invalid attribute name");
488 1 : len = 6 * strlen(t) + 1;
489 1 : buf = GDKmalloc(len);
490 1 : if (buf == NULL)
491 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
492 1 : len = XMLquotestring(t, buf, len);
493 1 : len += strlen(*name) + 5;
494 1 : *x = GDKmalloc(len);
495 1 : if (*x == NULL) {
496 0 : GDKfree(buf);
497 0 : throw(MAL, "xml.attribute", SQLSTATE(HY013) MAL_MALLOC_FAIL);
498 : }
499 1 : snprintf(*x, len, "A%s=\"%s\"", *name, buf);
500 1 : GDKfree(buf);
501 1 : return MAL_SUCCEED;
502 : }
503 :
504 : str
505 23 : XMLelement(xml *ret, str *name, xml *nspace, xml *attr, xml *val)
506 : {
507 23 : size_t len, i, namelen;
508 23 : str buf;
509 :
510 23 : if (strNil(*name))
511 0 : throw(MAL, "xml.element", "no element name specified");
512 23 : if (xmlValidateName((xmlChar *) *name, 0) != 0)
513 0 : throw(MAL, "xml.element", "invalid element name");
514 : /* len is composed of several parts:
515 : "C" + "<" + name + (nspace ? " " + nspace : "") + (attr ? " " + attr : "") + (val ? ">" + val + "</" + name + ">" : "/>")
516 : */
517 23 : namelen = strlen(*name);
518 23 : len = namelen + 5; /* "C", "<", "/", ">", terminating NUL */
519 44 : if (nspace && !strNil(*nspace)) {
520 0 : if (**nspace != 'A')
521 0 : throw(MAL, "xml.element", "illegal namespace");
522 0 : len += strlen(*nspace); /* " " + nspace (nspace contains initial 'A' which is replaced by space) */
523 : }
524 44 : if (attr && !strNil(*attr)) {
525 19 : if (**attr != 'A')
526 0 : throw(MAL, "xml.element", "illegal attribute");
527 19 : len += strlen(*attr); /* " " + attr (attr contains initial 'A' which is replaced by space) */
528 : }
529 46 : if (!strNil(*val) && **val != 0) {
530 21 : if (**val != 'C')
531 0 : throw(MAL, "xml.element", "illegal content");
532 21 : len += strlen(*val + 1) + namelen + 2; /* extra "<", ">", and name ("/" already counted) */
533 : }
534 23 : buf = GDKmalloc(len);
535 23 : if (buf == NULL)
536 0 : throw(MAL, "xml.element", SQLSTATE(HY013) MAL_MALLOC_FAIL);
537 25 : if (strNil(*val) && (!attr || strNil(*attr))) {
538 1 : strcpy(buf, str_nil);
539 : } else {
540 22 : i = snprintf(buf, len, "C<%s", *name);
541 42 : if (nspace && !strNil(*nspace))
542 0 : i += snprintf(buf + i, len - i, " %s", *nspace + 1);
543 42 : if (attr && !strNil(*attr))
544 19 : i += snprintf(buf + i, len - i, " %s", *attr + 1);
545 44 : if (!strNil(*val))
546 21 : i += snprintf(buf + i, len - i, ">%s</%s>", *val + 1, *name);
547 : else
548 1 : i += snprintf(buf + i, len - i, "/>");
549 : }
550 23 : *ret = buf;
551 23 : return MAL_SUCCEED;
552 : }
553 :
554 : str
555 2 : XMLelementSmall(xml *ret, str *name, xml *val)
556 : {
557 2 : return XMLelement(ret, name, NULL, NULL, val);
558 : }
559 :
560 : str
561 0 : XMLconcat(xml *ret, xml *left, xml *right)
562 : {
563 0 : size_t len;
564 0 : str buf;
565 :
566 : /* if either side is nil, return the other, otherwise concatenate */
567 0 : if (strNil(*left))
568 0 : buf = GDKstrdup(*right);
569 0 : else if (strNil(*right))
570 0 : buf = GDKstrdup(*left);
571 0 : else if (**left != **right)
572 0 : throw(MAL, "xml.concat", "arguments not compatible");
573 0 : else if (**left == 'A') {
574 0 : len = strlen(*left) + strlen(*right) + 1;
575 0 : buf = GDKmalloc(len);
576 0 : if (buf == NULL)
577 0 : throw(MAL, "xml.concat", SQLSTATE(HY013) MAL_MALLOC_FAIL);
578 0 : snprintf(buf, len, "A%s %s", *left + 1, *right + 1);
579 0 : } else if (**left == 'C') {
580 0 : len = strlen(*left) + strlen(*right) + 2;
581 0 : buf = GDKmalloc(len);
582 0 : if (buf == NULL)
583 0 : throw(MAL, "xml.concat", SQLSTATE(HY013) MAL_MALLOC_FAIL);
584 0 : snprintf(buf, len, "C%s%s", *left + 1, *right + 1);
585 : } else
586 0 : throw(MAL, "xml.concat",
587 : "can only concatenate attributes and element content");
588 0 : if (buf == NULL)
589 0 : throw(MAL, "xml.concat", SQLSTATE(HY013) MAL_MALLOC_FAIL);
590 0 : *ret = buf;
591 0 : return MAL_SUCCEED;
592 : }
593 :
594 : str
595 1 : XMLforest(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
596 : {
597 1 : xml *ret = getArgReference_TYPE(stk, p, 0, xml);
598 1 : int i;
599 1 : size_t len;
600 1 : str buf;
601 1 : xml x;
602 :
603 1 : (void) cntxt;
604 1 : (void) mb;
605 :
606 1 : len = 2;
607 3 : for (i = p->retc; i < p->argc; i++) {
608 2 : x = *getArgReference_TYPE(stk, p, i, xml);
609 4 : if (!strNil(x))
610 2 : if (*x != 'C')
611 0 : throw(MAL, "xml.forest", "arguments must be element content");
612 2 : len += strlen(x + 1);
613 : }
614 1 : buf = (str) GDKmalloc(len);
615 1 : if (buf == NULL)
616 0 : throw(MAL, "xml.forest", SQLSTATE(HY013) MAL_MALLOC_FAIL);
617 1 : *ret = buf;
618 1 : *buf++ = 'C';
619 1 : *buf = 0;
620 :
621 3 : for (i = p->retc; i < p->argc; i++) {
622 2 : x = *getArgReference_TYPE(stk, p, i, xml);
623 4 : if (!strNil(x)) {
624 2 : len = strlen(x + 1);
625 2 : strcpy(buf, x + 1);
626 2 : buf += len;
627 : }
628 : }
629 : return MAL_SUCCEED;
630 : }
631 :
632 : int TYPE_xml;
633 :
634 : static str
635 336 : XMLprelude(void)
636 : {
637 336 : TYPE_xml = ATOMindex("xml");
638 336 : xmlMemSetup(GDKfree, GDKmalloc, GDKrealloc, GDKstrdup);
639 336 : xmlInitParser();
640 336 : return MAL_SUCCEED;
641 : }
642 :
643 : str
644 334 : XMLepilogue(void *ret)
645 : {
646 334 : (void) ret;
647 334 : xmlCleanupParser();
648 334 : return MAL_SUCCEED;
649 : }
650 :
651 : static ssize_t
652 0 : XMLfromString(const char *src, size_t *len, void **X, bool external)
653 : {
654 0 : xml *x = (xml *) X;
655 0 : if (*x) {
656 0 : GDKfree(*x);
657 0 : *x = NULL;
658 : }
659 0 : if (external && strcmp(src, "nil") == 0) {
660 0 : *x = GDKstrdup(str_nil);
661 0 : if (*x == NULL)
662 : return -1;
663 : return 3;
664 0 : } else if (strNil(src)) {
665 0 : *x = GDKstrdup(str_nil);
666 0 : if (*x == NULL)
667 : return -1;
668 : return 1;
669 : } else {
670 0 : char *err = XMLstr2xml(x, &src);
671 0 : if (err !=MAL_SUCCEED) {
672 0 : GDKerror("%s", getExceptionMessageAndState(err));
673 0 : freeException(err);
674 0 : return -1;
675 : }
676 : }
677 0 : *len = strlen(*x) + 1;
678 0 : return (ssize_t) *len - 1;
679 : }
680 :
681 : static ssize_t
682 48 : XMLtoString(str *s, size_t *len, const void *SRC, bool external)
683 : {
684 48 : const char *src = SRC;
685 48 : size_t l;
686 :
687 48 : if (strNil(src))
688 1 : src = external ? "nil" : str_nil;
689 : else
690 47 : src++;
691 48 : l = strlen(src) + 1;
692 48 : if (l >= *len || *s == NULL) {
693 5 : GDKfree(*s);
694 5 : *s = GDKmalloc(l);
695 5 : if (*s == NULL)
696 : return -1;
697 5 : *len = l;
698 : }
699 48 : strcpy(*s, src);
700 48 : return (ssize_t) l - 1;
701 : }
702 :
703 : #else
704 :
705 : #define NO_LIBXML_FATAL "xml: MonetDB was built without libxml, but what you are trying to do requires it."
706 :
707 : static ssize_t
708 : XMLfromString(const char *src, size_t *len, void **x, bool external)
709 : {
710 : (void) src;
711 : (void) len;
712 : (void) x;
713 : (void) external;
714 : GDKerror("not implemented\n");
715 : return -1;
716 : }
717 :
718 : static ssize_t
719 : XMLtoString(str *s, size_t *len, const void *src, bool external)
720 : {
721 : (void) s;
722 : (void) len;
723 : (void) src;
724 : (void) external;
725 : GDKerror("not implemented\n");
726 : return -1;
727 : }
728 :
729 : str
730 : XMLxml2str(str *s, xml *x)
731 : {
732 : (void) s;
733 : (void) x;
734 : return createException(MAL, "xml.xml2str", SQLSTATE(HY005) NO_LIBXML_FATAL);
735 : }
736 :
737 : str
738 : XMLstr2xml(xml *x, const char **s)
739 : {
740 : (void) s;
741 : (void) x;
742 : return createException(MAL, "xml.xml2str", SQLSTATE(HY005) NO_LIBXML_FATAL);
743 : }
744 :
745 : str
746 : XMLxmltext(str *s, xml *x)
747 : {
748 : (void) s;
749 : (void) x;
750 : return createException(MAL, "xml.xmltext", SQLSTATE(HY005) NO_LIBXML_FATAL);
751 : }
752 :
753 : str
754 : XMLxml2xml(xml *x, xml *s)
755 : {
756 : (void) s;
757 : (void) x;
758 : return createException(MAL, "xml.xml2xml", SQLSTATE(HY005) NO_LIBXML_FATAL);
759 : }
760 :
761 : str
762 : XMLdocument(xml *x, str *s)
763 : {
764 : (void) s;
765 : (void) x;
766 : return createException(MAL, "xml.document",
767 : SQLSTATE(HY005) NO_LIBXML_FATAL);
768 : }
769 :
770 : str
771 : XMLcontent(xml *x, str *s)
772 : {
773 : (void) s;
774 : (void) x;
775 : return createException(MAL, "xml.content", SQLSTATE(HY005) NO_LIBXML_FATAL);
776 : }
777 :
778 : str
779 : XMLisdocument(bit *x, str *s)
780 : {
781 : (void) s;
782 : (void) x;
783 : return createException(MAL, "xml.isdocument",
784 : SQLSTATE(HY005) NO_LIBXML_FATAL);
785 : }
786 :
787 : str
788 : XMLcomment(xml *x, str *s)
789 : {
790 : (void) s;
791 : (void) x;
792 : return createException(MAL, "xml.comment", SQLSTATE(HY005) NO_LIBXML_FATAL);
793 : }
794 :
795 : str
796 : XMLpi(xml *x, str *target, str *s)
797 : {
798 : (void) s;
799 : (void) target;
800 : (void) x;
801 : return createException(MAL, "xml.pi", SQLSTATE(HY005) NO_LIBXML_FATAL);
802 : }
803 :
804 : str
805 : XMLroot(xml *x, xml *v, str *version, str *standalone)
806 : {
807 : (void) x;
808 : (void) v;
809 : (void) version;
810 : (void) standalone;
811 : return createException(MAL, "xml.root", SQLSTATE(HY005) NO_LIBXML_FATAL);
812 : }
813 :
814 : str
815 : XMLparse(xml *x, str *doccont, str *s, str *option)
816 : {
817 : (void) x;
818 : (void) doccont;
819 : (void) s;
820 : (void) option;
821 : return createException(MAL, "xml.parse", SQLSTATE(HY005) NO_LIBXML_FATAL);
822 : }
823 :
824 : str
825 : XMLattribute(xml *ret, str *name, str *val)
826 : {
827 : (void) ret;
828 : (void) name;
829 : (void) val;
830 : return createException(MAL, "xml.attribute",
831 : SQLSTATE(HY005) NO_LIBXML_FATAL);
832 : }
833 :
834 : str
835 : XMLelement(xml *ret, str *name, xml *nspace, xml *attr, xml *val)
836 : {
837 : (void) ret;
838 : (void) name;
839 : (void) nspace;
840 : (void) attr;
841 : (void) val;
842 : return createException(MAL, "xml.element", SQLSTATE(HY005) NO_LIBXML_FATAL);
843 : }
844 :
845 : str
846 : XMLelementSmall(xml *ret, str *name, xml *val)
847 : {
848 : (void) ret;
849 : (void) name;
850 : (void) val;
851 : return createException(MAL, "xml.elementSmall",
852 : SQLSTATE(HY005) NO_LIBXML_FATAL);
853 : }
854 :
855 : str
856 : XMLconcat(xml *ret, xml *left, xml *right)
857 : {
858 : (void) ret;
859 : (void) left;
860 : (void) right;
861 : return createException(MAL, "xml.concat", SQLSTATE(HY005) NO_LIBXML_FATAL);
862 : }
863 :
864 : str
865 : XMLforest(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
866 : {
867 : (void) cntxt;
868 : (void) mb;
869 : (void) stk;
870 : (void) p;
871 : return createException(MAL, "xml.forest", SQLSTATE(HY005) NO_LIBXML_FATAL);
872 : }
873 :
874 : size_t
875 : XMLquotestring(const char *s, char *buf, size_t len)
876 : {
877 : (void) s;
878 : (void) buf;
879 : (void) len;
880 : return 0;
881 : }
882 :
883 : size_t
884 : XMLunquotestring(const char **p, char q, char *buf)
885 : {
886 : (void) p;
887 : (void) q;
888 : (void) buf;
889 : return 0;
890 : }
891 :
892 : static str
893 : XMLprelude(void)
894 : {
895 : return MAL_SUCCEED; /* to not break init */
896 : }
897 :
898 : str
899 : XMLepilogue(void *ret)
900 : {
901 : (void) ret;
902 : return MAL_SUCCEED;
903 : }
904 :
905 : #endif /* HAVE_LIBXML */
906 :
907 : #include "mel.h"
908 : mel_atom xml_init_atoms[] = {
909 : { .name="xml", .basetype="str", .fromstr=XMLfromString, .tostr=XMLtoString, }, { .cmp=NULL }
910 : };
911 : mel_func xml_init_funcs[] = {
912 : command("xml", "xml", XMLstr2xml, false, "Cast the string to an xml compliant string", args(1,2, arg("",xml),arg("src",str))),
913 : command("xml", "str", XMLxml2str, false, "Cast the string to an xml compliant string", args(1,2, arg("",str),arg("src",xml))),
914 : command("xml", "text", XMLxmltext, false, "Extract text from an xml atom", args(1,2, arg("",str),arg("src",xml))),
915 : command("xml", "comment", XMLcomment, false, "Construct an comment struction ", args(1,2, arg("",xml),arg("val",str))),
916 : command("xml", "parse", XMLparse, false, "Parse the XML document or element string values ", args(1,4, arg("",xml),arg("doccont",str),arg("val",str),arg("option",str))),
917 : command("xml", "pi", XMLpi, false, "Construct a processing instruction", args(1,3, arg("",xml),arg("target",str),arg("val",str))),
918 : command("xml", "document", XMLdocument, false, "Check the value for compliance as XML document", args(1,2, arg("",xml),arg("val",str))),
919 : command("xml", "content", XMLcontent, false, "Check the value for compliance as content, i.e. it may contain multiple roots and character data.", args(1,2, arg("",xml),arg("val",str))),
920 : command("xml", "root", XMLroot, false, "Construct the root nodes", args(1,4, arg("",xml),arg("val",xml),arg("version",str),arg("standalone",str))),
921 : command("xml", "attribute", XMLattribute, false, "Construct an attribute value pair", args(1,3, arg("",xml),arg("name",str),arg("val",str))),
922 : command("xml", "element", XMLelement, false, "The basic building block for XML elements are namespaces, attributes and a sequence of xml elements. The name space and the attributes may be left unspecified(=nil:bat).", args(1,5, arg("",xml),arg("name",str),arg("ns",xml),arg("attr",xml),arg("s",xml))),
923 : command("xml", "element", XMLelementSmall, false, "The basic building block for XML elements are namespaces, attributes and a sequence of xml elements. The name space and the attributes may be left unspecified(=nil:bat).", args(1,3, arg("",xml),arg("name",str),arg("s",xml))),
924 : command("xml", "concat", XMLconcat, false, "Concatenate the xml values", args(1,3, arg("",xml),arg("val1",xml),arg("val2",xml))),
925 : pattern("xml", "forest", XMLforest, false, "Construct an element list", args(1,2, arg("",xml),vararg("val",xml))),
926 : command("xml", "isdocument", XMLisdocument, false, "Validate the string as a document", args(1,2, arg("",bit),arg("val",str))),
927 : command("xml", "epilogue", XMLepilogue, false, "", args(1,1, arg("",void))),
928 : command("calc", "xml", XMLstr2xml, false, "", args(1,2, arg("",xml),arg("src",str))),
929 : command("calc", "xml", XMLxml2xml, false, "", args(1,2, arg("",xml),arg("src",xml))),
930 : { .imp=NULL }
931 : };
932 : #include "mal_import.h"
933 : #ifdef _MSC_VER
934 : #undef read
935 : #pragma section(".CRT$XCU",read)
936 : #endif
937 329 : LIB_STARTUP_FUNC(init_xml_mal)
938 329 : { mal_module2("xml", xml_init_atoms, xml_init_funcs, XMLprelude, NULL); }
|