--- /dev/null
+/*
+ xmlparser.c
+
+ Copyright (C) 2010-2019 Amf
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "chroma.h"
+#include "xmlparser.h"
+#include "util.h"
+
+// #define PARSER_DEBUG
+
+/* A simple state machine based XML parser - not foolproof, but enough. */
+
+struct parser* parser_new(char * filename)
+{
+ struct parser* pparser;
+
+ pparser = malloc(sizeof(struct parser));
+ if(pparser == NULL)
+ fatal(gettext("Out of memory in parser_new()"));
+
+ pparser->file = fopen(filename, "r");
+ if(pparser->file == NULL)
+ {
+ free(pparser);
+ return NULL;
+ }
+
+ pparser->state = 0;
+ pparser->depth = 0;
+ pparser->line = 1;
+ pparser->result = 0;
+ pparser->size = 256;
+
+ pparser->buffer = malloc(pparser->size);
+ if(pparser->buffer == NULL)
+ fatal(gettext("Out of memory in parser_new()"));
+
+ return pparser;
+}
+
+void parser_delete(struct parser* pparser)
+{
+ int i;
+
+ if(pparser == NULL)
+ return;
+
+ for(i = 0; i < pparser->depth; i ++)
+ {
+ free(pparser->stack[i]);
+ }
+ fclose(pparser->file);
+ free(pparser->buffer);
+ free(pparser);
+}
+
+void parser_push(struct parser* pparser, char *buffer)
+{
+ int i;
+ int j;
+ int k;
+
+ pparser->stack[pparser->depth] = malloc(strlen(buffer) + 1);
+ if(pparser->stack[pparser->depth] == NULL)
+ fatal(gettext("Out of memory in parser_push()"));
+ if(pparser->depth >= PARSER_MAXDEPTH)
+ fatal(gettext("Stack overflow in parser_push()"));
+
+ /* Entity translation */
+ j = 0;
+ for(i = 0; i < strlen(buffer); i ++)
+ {
+ if(buffer[i] == '&')
+ {
+ k = 0;
+ while(buffer[i + k] != 0 && buffer[i + k] != ';')
+ k ++;
+
+ /* Other than these five, all other entities are stripped */
+ if(k == 5 && strncmp(buffer + i + 1, "quot", 4) == 0)
+ pparser->stack[pparser->depth][j++] = '"';
+ if(k == 5 && strncmp(buffer + i + 1, "apos", 4) == 0)
+ pparser->stack[pparser->depth][j++] = '\'';
+ if(k == 4 && strncmp(buffer + i + 1, "amp", 3) == 0)
+ pparser->stack[pparser->depth][j++] = '&';
+ if(k == 3 && strncmp(buffer + i + 1, "gt", 2) == 0)
+ pparser->stack[pparser->depth][j++] = '>';
+ if(k == 3 && strncmp(buffer + i + 1, "lt", 2) == 0)
+ pparser->stack[pparser->depth][j++] = '<';
+
+ i += k;
+ }
+ else
+ {
+ pparser->stack[pparser->depth][j++] = buffer[i];
+ }
+
+ }
+ pparser->stack[pparser->depth][j++] = 0;
+
+ pparser->depth ++;
+}
+
+void parser_pop(struct parser* pparser)
+{
+ if(pparser->depth > 0)
+ {
+ free(pparser->stack[pparser->depth - 1]);
+ pparser->depth --;
+ }
+}
+
+int parser_parse(struct parser* pparser)
+{
+ int result;
+ int state;
+ int i, j;
+ char c;
+ char *tmp;
+
+ if(pparser == NULL)
+ return PARSER_END;
+
+ switch(pparser->result)
+ {
+ case PARSER_ATTRIBUTE:
+ parser_pop(pparser);
+ parser_pop(pparser);
+ break;
+
+ case PARSER_CONTENT:
+ case PARSER_ELEMENT_END:
+ case PARSER_ERROR:
+ parser_pop(pparser);
+ break;
+
+ default:
+ break;
+ }
+
+ result = PARSER_CONTINUE;
+ state = pparser->state;
+ i = 0;
+
+ /* XML parsing state machine */
+ while(result == PARSER_CONTINUE)
+ {
+ c = fgetc(pparser->file);
+ if(c == '\r')
+ continue;
+ if(c == '\n')
+ pparser->line ++;
+ if(c == '\t')
+ c = ' ';
+ if(feof(pparser->file))
+ state = -1;
+
+ switch(state)
+ {
+ /* State -1 : end of file detected */
+ case -1:
+ result = PARSER_END;
+ break;
+
+ /* State 0 : outside of <> */
+ case 0:
+ if(c == '<')
+ state = 1;
+ if(c == '\n')
+ continue;
+ break;
+
+ /* State 1 : reading element name */
+ case 1:
+ if(c == '>')
+ state = 0;
+ if(c == ' ' || c == '\n')
+ state = 2;
+ if(c == '<')
+ state = 2; /* ERROR, unless <!--<; not caught */
+ break;
+
+ /* State 2 : reading attribute name */
+ case 2:
+ if(c == '>')
+ state = 0;
+ if(c == '=')
+ state = 3;
+ if(c == ' ' || c == '\n')
+ continue;
+ break;
+
+ /* State 3 : searching for start of attribute value */
+ case 3:
+ if(c == '>')
+ state = 0; /* ERROR */
+ if(c == '"')
+ state = 4;
+ break;
+
+ /* State 4 : reading attribute value */
+ case 4:
+ if(c == '>')
+ state = 0; /* ERROR */
+ if(c == '"')
+ state = 2;
+ if(c == '\n')
+ continue;
+ break;
+
+ /* States 5, 6, 7 : searching for end of comment */
+ case 5:
+ if(c == '-')
+ state = 6;
+ break;
+ case 6:
+ if(c == '-')
+ state = 7;
+ else
+ state = 5;
+ break;
+ case 7:
+ if(c == '>')
+ state = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Have we changed state? */
+ if(state != pparser->state)
+ {
+ pparser->buffer[i++] = 0;
+
+ switch(pparser->state)
+ {
+ case 0:
+ /* We've found content */
+ i = 0;
+ while(pparser->buffer[i] == ' ')
+ i ++;
+
+ /* If there's more than just whitespace, return it */
+ if(pparser->buffer[i] != 0)
+ {
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_CONTENT;
+ }
+ break;
+
+ case 1:
+ /* We've found </element> */
+ if(pparser->buffer[0] == '/')
+ {
+ if(pparser->depth > 0 &&
+ strcmp(pparser->stack[pparser->depth - 1], pparser->buffer + 1) == 0)
+ {
+ result = PARSER_ELEMENT_END;
+ }
+ else
+ {
+ /* Error */
+ parser_push(pparser, pparser->buffer);
+ sprintf(pparser->buffer, gettext("Badly nested tags on line %d (expecting '/%s', found '%s')"), pparser->line, pparser->stack[pparser->depth - 2], pparser->stack[pparser->depth - 1]);
+ parser_pop(pparser);
+
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ERROR;
+ }
+ }
+ /* We've found the start of a comment */
+ else if(strcmp(pparser->buffer, "!--") == 0)
+ {
+ state = 5;
+ }
+ /* We've found <element */
+ else if(strlen(pparser->buffer) > 0 && pparser->buffer[strlen(pparser->buffer) - 1] != '/')
+ {
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ELEMENT_START;
+ }
+ /* We've found <element/> */
+ else if(strlen(pparser->buffer) > 0)
+ {
+ /* Lose / suffix */
+ pparser->buffer[strlen(pparser->buffer) - 1] = 0;
+
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ELEMENT_END;
+ }
+ break;
+
+ case 2:
+ /* We've found /> */
+ if(pparser->buffer[0] == '/')
+ {
+ result = PARSER_ELEMENT_END;
+ }
+ /* We've found attribute= */
+ else if(state == 3)
+ {
+ /* Push attribute name */
+ parser_push(pparser, pparser->buffer);
+ }
+ break;
+
+ case 3:
+ /* We've found attribute=>, which is an error */
+ if(state != 4)
+ {
+ sprintf(pparser->buffer, gettext("Malformed attribute on line %d (in '%s')"), pparser->line, pparser->stack[pparser->depth -1]);
+
+ /* Pop unused attribute name */
+ parser_pop(pparser);
+
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ERROR;
+ }
+ break;
+
+ case 4:
+ /* We've found attribute=">, which is an error */
+ if(state == 0)
+ {
+ sprintf(pparser->buffer, gettext("Malformed attribute on line %d (in '%s')"), pparser->line, pparser->stack[pparser->depth -1]);
+
+ /* Pop unused attribute name */
+ parser_pop(pparser);
+
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ERROR;
+ }
+ /* We've found attribute="value" */
+ else
+ {
+ parser_push(pparser, pparser->buffer);
+ result = PARSER_ATTRIBUTE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ pparser->state = state;
+ i = 0;
+ }
+ else
+ {
+ if(i > pparser->size - 2)
+ {
+ pparser->size *= 2;
+ tmp = malloc(pparser->size);
+ if(tmp == NULL)
+ fatal(gettext("Out of memory in parser_parse()"));
+ for(j = 0; j < i; j ++)
+ tmp[j] = pparser->buffer[j];
+
+ free(pparser->buffer);
+ pparser->buffer=tmp;
+
+ }
+ else
+ pparser->buffer[i++] = c;
+ }
+
+
+ }
+
+ pparser->result = result;
+
+#ifdef PARSER_DEBUG
+ printf("[%d]:",result);
+ for(i = 0; i < pparser->depth; i ++)
+ {
+ printf("'%s'%s", pparser->stack[i], i == pparser->depth -1 ? "" : ", ");
+ }
+ printf("\n");
+#endif
+
+ return result;
+}
+