/* nl-xml.c - newLISP XML interface Copyright (C) 2007 Lutz Mueller This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "newlisp.h" #include "protos.h" #define XML_NONE 0 #define XML_TEXT 1 #define XML_CDATA 2 #define XML_COMMENT 3 #define XML_ELEMENT 4 int isWhiteSpaceStringN(char * source, int tagPos); CELL * makeTagSymbolCell(char * tagStart, int tagLen); void performXmlCallback(CELL * cell, char * start); char * typeNames[] = { "none", "TEXT", "CDATA", "COMMENT", "ELEMENT" }; CELL typeCells[5] = {{0}, {0}, {0}, {0}, {0}}; static char * xmlError; static char xmlMsg[64]; static char * sourceOrg; static char * source; static SYMBOL * XMLcontext; UINT optionsFlag; #define OPTION_NO_OPTION 0 #define OPTION_NO_WHITESPACE 1 #define OPTION_NO_EMPTY_ATTRIBUTES 2 #define OPTION_NO_COMMENTS 4 #define OPTION_TAGS_TO_SYMBOLS 8 #define OPTION_SXML_ATTRIBUTES 16 typedef struct { char * name; void * next; } TAG_STACK; TAG_STACK * tagStack = NULL; CELL * xmlCallback = NULL; /* setup type tage default cells, if done already just relink */ CELL * setupTypeTagCells(void) { int i; /* if never done, initialize defaults */ if(typeCells[0].contents == 0) for(i = 0; i < 5; i++) { typeCells[i].type = (i == 0) ? CELL_EXPRESSION : CELL_STRING; typeCells[i].next = nilCell; typeCells[i].aux = (i == 0) ? (UINT)nilCell : strlen(typeNames[i]) + 1; typeCells[i].contents = (UINT)typeNames[i]; } /* link cells in a list */ typeCells[0].contents = (UINT)&typeCells[1]; for(i = 1; i < 4; i++) typeCells[i].next = &typeCells[i+1]; return(&typeCells[0]); } CELL * p_XMLparse(CELL * params) { CELL * result; if(xmlCallback != NULL) errorProc(ERR_NOT_REENTRANT); params = getString(params, &source); if(params != nilCell) { params = getInteger(params, &optionsFlag); if(params != nilCell) { XMLcontext = getCreateContext(params, TRUE); if(params->next != nilCell) xmlCallback = params->next; else xmlCallback = NULL; } else XMLcontext = currentContext; } else optionsFlag = OPTION_NO_OPTION; setupTypeTagCells(); xmlError = NULL; sourceOrg = source; deleteTagStack(); result = parseDoc(); deleteTagStack(); if(xmlError != NULL) return nilCell; else return result; } CELL * p_XMLtypeTags(CELL * params) { int i; CELL * cell; if(params == nilCell) return(copyCell(setupTypeTagCells())); setupTypeTagCells(); for(i = 1; i < 5; i++) { cell = evaluateExpression(params); memcpy(&typeCells[i], cell, sizeof(CELL)); params = params->next; } return(copyCell(setupTypeTagCells())); } CELL * p_XMLerror(CELL * params) { CELL * errorCell; CELL * cell; if(xmlError == NULL) return(nilCell); errorCell = getCell(CELL_EXPRESSION); cell = stuffString(xmlError); errorCell->contents = (UINT)cell; cell->next = stuffInteger((UINT)(source - sourceOrg)); return errorCell; } void deleteTagStack(void) { TAG_STACK * oldTagStack; while(tagStack != NULL) { oldTagStack = tagStack; freeMemory(tagStack->name); tagStack = tagStack->next; freeMemory(oldTagStack); } } CELL * parseDoc(void) { CELL * node; CELL * lastNode; int closingFlag = FALSE; int tagPos; lastNode = node = getCell(CELL_EXPRESSION); while(!xmlError && !closingFlag) { if((tagPos = find("<", source)) == -1) break; if(tagPos > 0) { if( (tagStack != NULL) || (node->contents != (UINT)nilCell)) { if((optionsFlag & OPTION_NO_WHITESPACE) && isWhiteSpaceStringN(source, tagPos)) {;} else lastNode = appendNode(lastNode, makeTextNode(XML_TEXT, stuffStringN(source, tagPos))); } source = source + tagPos; } if(strncmp(source, ""); else lastNode = appendNode(lastNode, parseTag("-->")); continue; } if(memcmp(source, "")); continue; } if(*source == '<' && *(source + 1) == '/') { closingFlag = TRUE; parseClosing(); continue; } lastNode = appendNode(lastNode, parseTag(">")); } if(xmlError != NULL) { deleteList(node); return nilCell; } return node; } void parseDTD(void) { int closeTag, squareTag; int closePos = 0; char * closeTagStr; if((closeTag = find(">", source)) == -1) { xmlError = "error in DTD: expected '>'"; return; } squareTag = find("[", source); if(squareTag != -1 && squareTag < closeTag) closeTagStr = "]>"; else closeTagStr = ">"; while(!xmlError) { if((closePos = find(closeTagStr, source)) == -1) { snprintf(xmlMsg, 63, "expected: %s", closeTagStr); xmlError = xmlMsg; return; } if(*(source + closePos - 1) != ']') break; source = source + closePos + strlen(closeTagStr); } source = source + closePos + strlen(closeTagStr); return; } void parseProcessingInstruction(void) { int closeTag; if((closeTag = find("?>", source)) == -1) { xmlError = "expecting closing tag sequence '?>'"; return; } source = source + closeTag + 2; } void parseClosing(void) { int closeTag; char * tagName; TAG_STACK * oldTagStack; if((closeTag = find(">", source)) == -1) { xmlError = "missing closing >"; return; } if(tagStack == NULL) { xmlError = "closing tag has no opening"; return; } tagName = tagStack->name; if(strncmp(source + 2, tagName, strlen(tagName)) != 0) { xmlError = "closing tag doesn't match"; return; } /* pop tagStack */ freeMemory(tagName); oldTagStack = tagStack; tagStack = tagStack->next; freeMemory(oldTagStack); source = source + closeTag + 1; } CELL * parseTag(char * closeTagStr) { char * newSrc; char * tagStart; int closeTag; CELL * cell; tagStart = source; cell = NULL; closeTag = find(closeTagStr, source); if(*(source + closeTag - 1) == '/') { if(memcmp(closeTagStr,"]]>",3) != 0) { --closeTag; closeTagStr = "/>"; } } if(closeTag == -1) { snprintf(xmlMsg, 63, "expected closing tag: %s", closeTagStr); xmlError = xmlMsg; return nilCell; } if(memcmp(source, "