Initial commit of newLISP.
[newlisp.git] / nl-xml.c
blob9316e38f2333fefcbeac117aed069c71117c308d
1 /* nl-xml.c - newLISP XML interface
3 Copyright (C) 2008 Lutz Mueller
5 This program 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.
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.
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/>.
20 #include "newlisp.h"
21 #include "protos.h"
23 #define XML_NONE 0
24 #define XML_TEXT 1
25 #define XML_CDATA 2
26 #define XML_COMMENT 3
27 #define XML_ELEMENT 4
29 int isWhiteSpaceStringN(char * source, int tagPos);
30 CELL * makeTagSymbolCell(char * tagStart, int tagLen);
31 void performXmlCallback(CELL * cell, char * start);
33 char * typeNames[] =
35 "none",
36 "TEXT",
37 "CDATA",
38 "COMMENT",
39 "ELEMENT"
42 CELL typeCells[5] = {{0}, {0}, {0}, {0}, {0}};
44 static char * xmlError;
45 static char xmlMsg[64];
46 static char * sourceOrg;
47 static char * source;
49 static SYMBOL * XMLcontext;
51 UINT optionsFlag;
52 #define OPTION_NO_OPTION 0
53 #define OPTION_NO_WHITESPACE 1
54 #define OPTION_NO_EMPTY_ATTRIBUTES 2
55 #define OPTION_NO_COMMENTS 4
56 #define OPTION_TAGS_TO_SYMBOLS 8
57 #define OPTION_SXML_ATTRIBUTES 16
60 typedef struct
62 char * name;
63 void * next;
64 } TAG_STACK;
66 TAG_STACK * tagStack = NULL;
68 CELL * xmlCallback = NULL;
70 /* setup type tage default cells, if done already just relink */
71 CELL * setupTypeTagCells(void)
73 int i;
75 /* if never done, initialize defaults */
76 if(typeCells[0].contents == 0)
77 for(i = 0; i < 5; i++)
79 typeCells[i].type = (i == 0) ? CELL_EXPRESSION : CELL_STRING;
80 typeCells[i].next = nilCell;
81 typeCells[i].aux = (i == 0) ? (UINT)nilCell : strlen(typeNames[i]) + 1;
82 typeCells[i].contents = (UINT)typeNames[i];
85 /* link cells in a list */
86 typeCells[0].contents = (UINT)&typeCells[1];
87 for(i = 1; i < 4; i++)
88 typeCells[i].next = &typeCells[i+1];
90 return(&typeCells[0]);
93 CELL * p_XMLparse(CELL * params)
95 CELL * result;
97 if(xmlCallback != NULL)
98 errorProc(ERR_NOT_REENTRANT);
100 params = getString(params, &source);
101 if(params != nilCell)
103 params = getInteger(params, &optionsFlag);
104 if(params != nilCell)
106 XMLcontext = getCreateContext(params, TRUE);
107 if(XMLcontext == NULL)
108 return(errorProc(ERR_SYMBOL_OR_CONTEXT_EXPECTED));
109 if(params->next != nilCell)
110 xmlCallback = params->next;
111 else
112 xmlCallback = NULL;
114 else
115 XMLcontext = currentContext;
117 else
118 optionsFlag = OPTION_NO_OPTION;
120 setupTypeTagCells();
122 xmlError = NULL;
123 sourceOrg = source;
124 deleteTagStack();
126 result = parseDoc();
127 deleteTagStack();
129 if(xmlError != NULL)
130 return nilCell;
131 else
132 return result;
136 CELL * p_XMLtypeTags(CELL * params)
138 int i;
139 CELL * cell;
141 if(params == nilCell)
142 return(copyCell(setupTypeTagCells()));
144 setupTypeTagCells();
146 for(i = 1; i < 5; i++)
148 cell = evaluateExpression(params);
149 memcpy(&typeCells[i], cell, sizeof(CELL));
150 params = params->next;
153 return(copyCell(setupTypeTagCells()));
157 CELL * p_XMLerror(CELL * params)
159 CELL * errorCell;
160 CELL * cell;
162 if(xmlError == NULL)
163 return(nilCell);
165 errorCell = getCell(CELL_EXPRESSION);
166 cell = stuffString(xmlError);
167 errorCell->contents = (UINT)cell;
168 cell->next = stuffInteger((UINT)(source - sourceOrg));
170 return errorCell;
173 void deleteTagStack(void)
175 TAG_STACK * oldTagStack;
177 while(tagStack != NULL)
179 oldTagStack = tagStack;
180 freeMemory(tagStack->name);
181 tagStack = tagStack->next;
182 freeMemory(oldTagStack);
187 CELL * parseDoc(void)
189 CELL * node;
190 CELL * lastNode;
191 int closingFlag = FALSE;
192 int tagPos;
194 lastNode = node = getCell(CELL_EXPRESSION);
196 while(!xmlError && !closingFlag)
198 if((tagPos = find("<", source)) == -1) break;
199 if(tagPos > 0)
201 if( (tagStack != NULL) || (node->contents != (UINT)nilCell))
203 if((optionsFlag & OPTION_NO_WHITESPACE) && isWhiteSpaceStringN(source, tagPos))
205 else lastNode = appendNode(lastNode, makeTextNode(XML_TEXT, stuffStringN(source, tagPos)));
207 source = source + tagPos;
210 if(strncmp(source, "<!DOCTYPE", 9) == 0)
212 parseDTD();
213 continue;
216 if(*source == '<' && *(source + 1) == '?')
218 parseProcessingInstruction();
219 continue;
222 if(memcmp(source, "<!--", 4) == 0)
224 if(optionsFlag & OPTION_NO_COMMENTS)
225 parseTag("-->");
226 else
227 lastNode = appendNode(lastNode, parseTag("-->"));
228 continue;
230 if(memcmp(source, "<![CDATA[", 9) == 0)
232 lastNode = appendNode(lastNode, parseTag("]]>"));
233 continue;
236 if(*source == '<' && *(source + 1) == '/')
238 closingFlag = TRUE;
239 parseClosing();
240 continue;
243 lastNode = appendNode(lastNode, parseTag(">"));
247 if(xmlError != NULL)
249 deleteList(node);
250 return nilCell;
253 return node;
257 void parseDTD(void)
259 int closeTag, squareTag;
260 int closePos = 0;
261 char * closeTagStr;
263 if((closeTag = find(">", source)) == -1)
265 xmlError = "error in DTD: expected '>'";
266 return;
269 squareTag = find("[", source);
270 if(squareTag != -1 && squareTag < closeTag)
271 closeTagStr = "]>";
272 else
273 closeTagStr = ">";
275 while(!xmlError)
277 if((closePos = find(closeTagStr, source)) == -1)
279 snprintf(xmlMsg, 63, "expected: %s", closeTagStr);
280 xmlError = xmlMsg;
281 return;
283 if(*(source + closePos - 1) != ']')
284 break;
285 source = source + closePos + strlen(closeTagStr);
288 source = source + closePos + strlen(closeTagStr);
289 return;
293 void parseProcessingInstruction(void)
295 int closeTag;
297 if((closeTag = find("?>", source)) == -1)
299 xmlError = "expecting closing tag sequence '?>'";
300 return;
303 source = source + closeTag + 2;
307 void parseClosing(void)
309 int closeTag;
310 char * tagName;
311 TAG_STACK * oldTagStack;
313 if((closeTag = find(">", source)) == -1)
315 xmlError = "missing closing >";
316 return;
319 if(tagStack == NULL)
321 xmlError = "closing tag has no opening";
322 return;
325 tagName = tagStack->name;
326 if(strncmp(source + 2, tagName, strlen(tagName)) != 0)
328 xmlError = "closing tag doesn't match";
329 return;
332 /* pop tagStack */
333 freeMemory(tagName);
334 oldTagStack = tagStack;
335 tagStack = tagStack->next;
337 freeMemory(oldTagStack);
339 source = source + closeTag + 1;
343 CELL * parseTag(char * closeTagStr)
345 char * newSrc;
346 char * tagStart;
347 int closeTag;
348 CELL * cell;
350 tagStart = source;
352 cell = NULL;
353 closeTag = find(closeTagStr, source);
354 if(*(source + closeTag - 1) == '/')
356 if(memcmp(closeTagStr,"]]>",3) != 0)
358 --closeTag;
359 closeTagStr = "/>";
363 if(closeTag == -1)
365 snprintf(xmlMsg, 63, "expected closing tag: %s", closeTagStr);
366 xmlError = xmlMsg;
367 return nilCell;
370 if(memcmp(source, "<!--", 4) == 0)
372 if(optionsFlag & OPTION_NO_COMMENTS)
373 cell = nilCell;
374 else
376 cell = stuffStringN(source + 4, closeTag - 4);
377 cell = makeTextNode(XML_COMMENT, cell);
381 if(memcmp(source, "<![CDATA[", 9) == 0)
383 cell = stuffStringN(source + 9, closeTag - 9);
384 cell = makeTextNode(XML_CDATA, cell);
387 if(*source == '<' && *(source + 1) == '/')
389 xmlError = "closing node has no opening";
390 return nilCell;
393 newSrc = source + closeTag + strlen(closeTagStr);
395 if(cell == NULL)
396 cell = parseNormalTag(source + closeTag, newSrc);
397 else
398 source = newSrc;
400 /* call back with closed tag expression found
401 and opening start and end of source of this
402 tag expression
405 if(xmlCallback)
406 performXmlCallback(cell, tagStart);
408 return(cell);
412 void performXmlCallback(CELL * result, char * tagStart)
414 CELL * list;
415 CELL * cell;
416 CELL * next;
417 int errNo;
419 list = getCell(CELL_EXPRESSION);
420 list->contents = (UINT)copyCell(xmlCallback);
421 cell = getCell(CELL_QUOTE);
422 cell->contents = (UINT)copyCell(result);
423 cell->next = stuffInteger((UINT)(tagStart - sourceOrg));
424 next = cell->next;
425 next->next = stuffInteger((UINT)(source - tagStart));
426 ((CELL*)list->contents)->next = cell;
427 pushResult(list);
428 if(!evaluateExpressionSafe(list, &errNo))
430 deleteTagStack();
431 longjmp(errorJump, errNo);
436 CELL * parseNormalTag(char * endSrc, char * newSrc)
438 char * tagStart;
439 int tagLen;
440 CELL * attributes;
441 CELL * childs;
442 CELL * tagCell;
443 TAG_STACK * tag;
445 ++source; /* skip '/' */
447 while(*source <= ' ' && source < endSrc) ++source; /* skip whitespace */
449 tagStart = source;
450 tagLen = 0;
451 while(*source > ' ' && source < endSrc) ++source, ++tagLen; /* find tag end */
453 attributes = parseAttributes(endSrc);
454 if(optionsFlag & OPTION_SXML_ATTRIBUTES)
456 childs = (CELL*)attributes->contents;
457 if(! (childs == nilCell && (optionsFlag & OPTION_NO_EMPTY_ATTRIBUTES)))
459 attributes->contents = (UINT)stuffSymbol(atSymbol);
460 ((CELL*)(attributes->contents))->next = childs;
464 if(xmlError)
465 return nilCell;
467 if(*source == '/' && *(source + 1) == '>')
469 source = newSrc;
470 if(optionsFlag & OPTION_TAGS_TO_SYMBOLS)
471 tagCell = makeTagSymbolCell(tagStart, tagLen);
472 else
473 tagCell = stuffStringN(tagStart, tagLen);
474 return makeElementNode(tagCell, attributes, getCell(CELL_EXPRESSION));
477 /* push tag on tagstack */
478 tag = (TAG_STACK*)allocMemory(sizeof(TAG_STACK));
479 tag->name = (char *)callocMemory(tagLen + 1);
480 memcpy(tag->name, tagStart, tagLen);
481 tag->next = tagStack;
482 tagStack = tag;
484 source = newSrc;
485 childs = parseDoc();
487 if(optionsFlag & OPTION_TAGS_TO_SYMBOLS)
488 tagCell = makeTagSymbolCell(tagStart, tagLen);
489 else
490 tagCell = stuffStringN(tagStart, tagLen);
492 return makeElementNode(tagCell, attributes, childs);
496 CELL * makeTagSymbolCell(char * tagStart, int tagLen)
498 char * name;
499 CELL * cell;
501 name = (char *)callocMemory(tagLen + 1);
502 memcpy(name, tagStart, tagLen);
503 cell = stuffSymbol(translateCreateSymbol(name, CELL_NIL, XMLcontext, 0));
504 freeMemory(name);
505 return(cell);
509 CELL * parseAttributes(char * endSrc)
511 CELL * attributes;
512 CELL * att;
513 CELL * cell;
514 CELL * lastAtt;
515 char * namePos;
516 char * valPos;
517 char quoteChar;
518 int nameLen, valLen;
520 attributes = getCell(CELL_EXPRESSION);
521 lastAtt = NULL;
523 while(!xmlError && source < endSrc)
525 while(*source <= ' ' && source < endSrc) source++; /* strip leading space */
526 namePos = source;
527 nameLen = 0;
528 while(*source > ' ' && *source != '=' && source < endSrc) source++, nameLen++; /* get end */
529 if(nameLen == 0) break;
530 while(*source <= ' ' && source < endSrc) source++; /* strip leading space */
531 if(*source != '=')
533 xmlError = "expected '=' in attributes";
534 deleteList(attributes);
535 return nilCell;
537 else source++;
538 while(*source <= ' ' && source < endSrc) source++; /* strip spaces */
539 if(*source != '\"' && *source != '\'')
541 xmlError = "attribute values must be delimited by \" or \' ";
542 deleteList(attributes);
543 return nilCell;
545 quoteChar = *source;
546 source++;
547 valPos = source;
548 valLen = 0;
549 while(*source != quoteChar && source < endSrc) source++, valLen++;
550 if(*source != quoteChar) valLen = -1;
551 else source++;
552 if(nameLen == 0 || valLen == -1)
554 xmlError = "incorrect attribute";
555 deleteList(attributes);
556 return nilCell;
558 att = getCell(CELL_EXPRESSION);
559 if(optionsFlag & OPTION_TAGS_TO_SYMBOLS)
560 cell = makeTagSymbolCell(namePos, nameLen);
561 else
562 cell = stuffStringN(namePos, nameLen);
563 cell->next = stuffStringN(valPos, valLen);
564 att->contents = (UINT)cell;
565 if(lastAtt == NULL)
566 attributes->contents = (UINT)att;
567 else
568 lastAtt->next = att;
569 lastAtt = att;
572 return attributes;
576 CELL * appendNode(CELL * node, CELL * newNode)
578 if(node->contents == (UINT)nilCell)
579 node->contents = (UINT)newNode;
580 else
581 node->next = newNode;
583 return newNode;
587 CELL * makeTextNode(int type, CELL * contents)
589 CELL * newNode;
590 CELL * cell;
592 /* unwrap text node if nil xml-type-tag */
593 if(typeCells[type].type == CELL_NIL)
594 return(contents);
596 newNode = getCell(CELL_EXPRESSION);
597 cell = copyCell(&typeCells[type]);
598 newNode->contents = (UINT)cell;
599 cell->next = contents;
601 return newNode;
605 CELL * makeElementNode(CELL * tagNode, CELL * attributesNode, CELL * childrenNode)
607 CELL * newNode;
608 CELL * cell;
610 /* unwrap children node, if nil in xml-type-tag */
611 if(typeCells[XML_ELEMENT].type == CELL_NIL)
613 cell = childrenNode;
614 childrenNode = (CELL *)childrenNode->contents;
615 cell->contents = (UINT)nilCell;
616 deleteList(cell);
619 newNode = getCell(CELL_EXPRESSION);
620 if(typeCells[XML_ELEMENT].type == CELL_NIL)
621 newNode->contents = (UINT)tagNode;
622 else
624 cell = copyCell(&typeCells[XML_ELEMENT]);
625 newNode->contents = (UINT)cell;
626 cell->next = tagNode;
629 if( (attributesNode->contents == (UINT)nilCell) &&
630 (optionsFlag & OPTION_NO_EMPTY_ATTRIBUTES))
632 tagNode->next = childrenNode;
633 deleteList(attributesNode);
635 else
637 tagNode->next = attributesNode;
638 attributesNode->next = childrenNode;
641 return newNode;
645 int find(char * key, char * source)
647 char * ptr;
649 ptr = strstr(source, key);
650 if(ptr == NULL) return -1;
652 return(ptr - source);
656 int isWhiteSpaceStringN(char * source, int tagPos)
658 while(tagPos--) if((unsigned char)*source++ > 32) return(FALSE);
659 return(TRUE);
662 /* eof */