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/>.
29 int isWhiteSpaceStringN(char * source
, int tagPos
);
30 CELL
* makeTagSymbolCell(char * tagStart
, int tagLen
);
31 void performXmlCallback(CELL
* cell
, char * start
);
42 CELL typeCells
[5] = {{0}, {0}, {0}, {0}, {0}};
44 static char * xmlError
;
45 static char xmlMsg
[64];
46 static char * sourceOrg
;
49 static SYMBOL
* XMLcontext
;
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
66 TAG_STACK
* tagStack
= NULL
;
68 CELL
* xmlCallback
= NULL
;
70 /* setup type tage default cells, if done already just relink */
71 CELL
* setupTypeTagCells(void)
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
)
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
;
115 XMLcontext
= currentContext
;
118 optionsFlag
= OPTION_NO_OPTION
;
136 CELL
* p_XMLtypeTags(CELL
* params
)
141 if(params
== nilCell
)
142 return(copyCell(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
)
165 errorCell
= getCell(CELL_EXPRESSION
);
166 cell
= stuffString(xmlError
);
167 errorCell
->contents
= (UINT
)cell
;
168 cell
->next
= stuffInteger((UINT
)(source
- sourceOrg
));
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)
191 int closingFlag
= FALSE
;
194 lastNode
= node
= getCell(CELL_EXPRESSION
);
196 while(!xmlError
&& !closingFlag
)
198 if((tagPos
= find("<", source
)) == -1) break;
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)
216 if(*source
== '<' && *(source
+ 1) == '?')
218 parseProcessingInstruction();
222 if(memcmp(source
, "<!--", 4) == 0)
224 if(optionsFlag
& OPTION_NO_COMMENTS
)
227 lastNode
= appendNode(lastNode
, parseTag("-->"));
230 if(memcmp(source
, "<![CDATA[", 9) == 0)
232 lastNode
= appendNode(lastNode
, parseTag("]]>"));
236 if(*source
== '<' && *(source
+ 1) == '/')
243 lastNode
= appendNode(lastNode
, parseTag(">"));
259 int closeTag
, squareTag
;
263 if((closeTag
= find(">", source
)) == -1)
265 xmlError
= "error in DTD: expected '>'";
269 squareTag
= find("[", source
);
270 if(squareTag
!= -1 && squareTag
< closeTag
)
277 if((closePos
= find(closeTagStr
, source
)) == -1)
279 snprintf(xmlMsg
, 63, "expected: %s", closeTagStr
);
283 if(*(source
+ closePos
- 1) != ']')
285 source
= source
+ closePos
+ strlen(closeTagStr
);
288 source
= source
+ closePos
+ strlen(closeTagStr
);
293 void parseProcessingInstruction(void)
297 if((closeTag
= find("?>", source
)) == -1)
299 xmlError
= "expecting closing tag sequence '?>'";
303 source
= source
+ closeTag
+ 2;
307 void parseClosing(void)
311 TAG_STACK
* oldTagStack
;
313 if((closeTag
= find(">", source
)) == -1)
315 xmlError
= "missing closing >";
321 xmlError
= "closing tag has no opening";
325 tagName
= tagStack
->name
;
326 if(strncmp(source
+ 2, tagName
, strlen(tagName
)) != 0)
328 xmlError
= "closing tag doesn't match";
334 oldTagStack
= tagStack
;
335 tagStack
= tagStack
->next
;
337 freeMemory(oldTagStack
);
339 source
= source
+ closeTag
+ 1;
343 CELL
* parseTag(char * closeTagStr
)
353 closeTag
= find(closeTagStr
, source
);
354 if(*(source
+ closeTag
- 1) == '/')
356 if(memcmp(closeTagStr
,"]]>",3) != 0)
365 snprintf(xmlMsg
, 63, "expected closing tag: %s", closeTagStr
);
370 if(memcmp(source
, "<!--", 4) == 0)
372 if(optionsFlag
& OPTION_NO_COMMENTS
)
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";
393 newSrc
= source
+ closeTag
+ strlen(closeTagStr
);
396 cell
= parseNormalTag(source
+ closeTag
, newSrc
);
400 /* call back with closed tag expression found
401 and opening start and end of source of this
406 performXmlCallback(cell
, tagStart
);
412 void performXmlCallback(CELL
* result
, char * tagStart
)
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
));
425 next
->next
= stuffInteger((UINT
)(source
- tagStart
));
426 ((CELL
*)list
->contents
)->next
= cell
;
428 if(!evaluateExpressionSafe(list
, &errNo
))
431 longjmp(errorJump
, errNo
);
436 CELL
* parseNormalTag(char * endSrc
, char * newSrc
)
445 ++source
; /* skip '/' */
447 while(*source
<= ' ' && source
< endSrc
) ++source
; /* skip whitespace */
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
;
467 if(*source
== '/' && *(source
+ 1) == '>')
470 if(optionsFlag
& OPTION_TAGS_TO_SYMBOLS
)
471 tagCell
= makeTagSymbolCell(tagStart
, tagLen
);
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
;
487 if(optionsFlag
& OPTION_TAGS_TO_SYMBOLS
)
488 tagCell
= makeTagSymbolCell(tagStart
, tagLen
);
490 tagCell
= stuffStringN(tagStart
, tagLen
);
492 return makeElementNode(tagCell
, attributes
, childs
);
496 CELL
* makeTagSymbolCell(char * tagStart
, int tagLen
)
501 name
= (char *)callocMemory(tagLen
+ 1);
502 memcpy(name
, tagStart
, tagLen
);
503 cell
= stuffSymbol(translateCreateSymbol(name
, CELL_NIL
, XMLcontext
, 0));
509 CELL
* parseAttributes(char * endSrc
)
520 attributes
= getCell(CELL_EXPRESSION
);
523 while(!xmlError
&& source
< endSrc
)
525 while(*source
<= ' ' && source
< endSrc
) source
++; /* strip leading space */
528 while(*source
> ' ' && *source
!= '=' && source
< endSrc
) source
++, nameLen
++; /* get end */
529 if(nameLen
== 0) break;
530 while(*source
<= ' ' && source
< endSrc
) source
++; /* strip leading space */
533 xmlError
= "expected '=' in attributes";
534 deleteList(attributes
);
538 while(*source
<= ' ' && source
< endSrc
) source
++; /* strip spaces */
539 if(*source
!= '\"' && *source
!= '\'')
541 xmlError
= "attribute values must be delimited by \" or \' ";
542 deleteList(attributes
);
549 while(*source
!= quoteChar
&& source
< endSrc
) source
++, valLen
++;
550 if(*source
!= quoteChar
) valLen
= -1;
552 if(nameLen
== 0 || valLen
== -1)
554 xmlError
= "incorrect attribute";
555 deleteList(attributes
);
558 att
= getCell(CELL_EXPRESSION
);
559 if(optionsFlag
& OPTION_TAGS_TO_SYMBOLS
)
560 cell
= makeTagSymbolCell(namePos
, nameLen
);
562 cell
= stuffStringN(namePos
, nameLen
);
563 cell
->next
= stuffStringN(valPos
, valLen
);
564 att
->contents
= (UINT
)cell
;
566 attributes
->contents
= (UINT
)att
;
576 CELL
* appendNode(CELL
* node
, CELL
* newNode
)
578 if(node
->contents
== (UINT
)nilCell
)
579 node
->contents
= (UINT
)newNode
;
581 node
->next
= newNode
;
587 CELL
* makeTextNode(int type
, CELL
* contents
)
592 /* unwrap text node if nil xml-type-tag */
593 if(typeCells
[type
].type
== CELL_NIL
)
596 newNode
= getCell(CELL_EXPRESSION
);
597 cell
= copyCell(&typeCells
[type
]);
598 newNode
->contents
= (UINT
)cell
;
599 cell
->next
= contents
;
605 CELL
* makeElementNode(CELL
* tagNode
, CELL
* attributesNode
, CELL
* childrenNode
)
610 /* unwrap children node, if nil in xml-type-tag */
611 if(typeCells
[XML_ELEMENT
].type
== CELL_NIL
)
614 childrenNode
= (CELL
*)childrenNode
->contents
;
615 cell
->contents
= (UINT
)nilCell
;
619 newNode
= getCell(CELL_EXPRESSION
);
620 if(typeCells
[XML_ELEMENT
].type
== CELL_NIL
)
621 newNode
->contents
= (UINT
)tagNode
;
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
);
637 tagNode
->next
= attributesNode
;
638 attributesNode
->next
= childrenNode
;
645 int find(char * key
, char * source
)
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
);