/* nl-xml-json.c - newLISP XML and JSON interface Copyright (C) 2016 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" #ifdef XML_SUPPORT #define XML_TEXT 0 #define XML_CDATA 1 #define XML_COMMENT 2 #define XML_ELEMENT 3 int isWhiteSpaceStringN(char * source, int tagPos); CELL * makeTagSymbolCell(char * tagStart, int tagLen); void performXmlCallback(CELL * cell, char * start); char * typeNames[] = { "TEXT", "CDATA", "COMMENT", "ELEMENT" }; CELL * xmlTags = NULL; CELL * typeCell[4]; 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 tag default cells, if done already just relink */ CELL * setupTypeTagCells(void) { int i; if(xmlTags == NULL) { xmlTags = getCell(CELL_EXPRESSION); for(i = 0; i < 4; i++) typeCell[i] = stuffString(typeNames[i]); } /* link cells in a list */ xmlTags->contents = (UINT)typeCell[0]; for(i = 0; i < 3; i++) typeCell[i]->next = typeCell[i+1]; return(xmlTags); } CELL * p_XMLtypeTags(CELL * params) { int i; if(params == nilCell) return(copyCell(setupTypeTagCells())); if(xmlTags != NULL) deleteList(xmlTags); xmlTags = getCell(CELL_EXPRESSION); for(i = 0; i < 4; i++) { typeCell[i] = copyCell(evaluateExpression(params)); params = params->next; } return(copyCell(setupTypeTagCells())); } 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(XMLcontext == NULL) return(errorProc(ERR_SYMBOL_OR_CONTEXT_EXPECTED)); 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(); xmlCallback = NULL; if(xmlError != NULL) return nilCell; else return result; } CELL * p_XMLerror(CELL * params) { CELL * errorCell; CELL * cell; if(xmlError == NULL) return(nilCell); cell = stuffString(xmlError); errorCell = makeCell(CELL_EXPRESSION, (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, "