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