mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-15 07:29:41 +00:00
667 lines
12 KiB
C++
667 lines
12 KiB
C++
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
|
Contributed by Oracle.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
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, 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, 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "config.h"
|
|
#include <ctype.h>
|
|
|
|
#include "util.h"
|
|
#include "vec.h"
|
|
#include "DefaultHandler.h"
|
|
#include "SAXParser.h"
|
|
#include "SAXParserFactory.h"
|
|
#include "StringBuilder.h"
|
|
|
|
/*
|
|
* Private implementation of Attributes
|
|
*/
|
|
class AttributesP : public Attributes
|
|
{
|
|
public:
|
|
AttributesP ();
|
|
~AttributesP ();
|
|
int getLength ();
|
|
const char *getQName (int index);
|
|
const char *getValue (int index);
|
|
int getIndex (const char *qName);
|
|
const char *getValue (const char *qName);
|
|
void append (char *qName, char *value);
|
|
|
|
private:
|
|
Vector<char*> *names;
|
|
Vector<char*> *values;
|
|
};
|
|
|
|
AttributesP::AttributesP ()
|
|
{
|
|
names = new Vector<char*>;
|
|
values = new Vector<char*>;
|
|
}
|
|
|
|
AttributesP::~AttributesP ()
|
|
{
|
|
Destroy (names);
|
|
Destroy (values);
|
|
}
|
|
|
|
int
|
|
AttributesP::getLength ()
|
|
{
|
|
return names->size ();
|
|
}
|
|
|
|
const char *
|
|
AttributesP::getQName (int index)
|
|
{
|
|
if (index < 0 || index >= names->size ())
|
|
return NULL;
|
|
return names->fetch (index);
|
|
}
|
|
|
|
const char *
|
|
AttributesP::getValue (int index)
|
|
{
|
|
if (index < 0 || index >= values->size ())
|
|
return NULL;
|
|
return values->fetch (index);
|
|
}
|
|
|
|
int
|
|
AttributesP::getIndex (const char *qName)
|
|
{
|
|
for (int idx = 0; idx < names->size (); idx++)
|
|
if (strcmp (names->fetch (idx), qName) == 0)
|
|
return idx;
|
|
return -1;
|
|
}
|
|
|
|
const char *
|
|
AttributesP::getValue (const char *qName)
|
|
{
|
|
for (int idx = 0; idx < names->size (); idx++)
|
|
if (strcmp (names->fetch (idx), qName) == 0)
|
|
return values->fetch (idx);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
AttributesP::append (char *qName, char *value)
|
|
{
|
|
names->append (qName);
|
|
values->append (value);
|
|
}
|
|
|
|
/*
|
|
* Implementation of SAXException
|
|
*/
|
|
SAXException::SAXException ()
|
|
{
|
|
message = strdup ("null");
|
|
}
|
|
|
|
SAXException::SAXException (const char *_message)
|
|
{
|
|
if (_message == NULL)
|
|
message = strdup ("null");
|
|
else
|
|
message = strdup (_message);
|
|
}
|
|
|
|
SAXException::~SAXException ()
|
|
{
|
|
free (message);
|
|
}
|
|
|
|
char *
|
|
SAXException::getMessage ()
|
|
{
|
|
return message;
|
|
}
|
|
|
|
/*
|
|
* SAXParseException
|
|
*/
|
|
SAXParseException::SAXParseException (char *message, int _lineNumber, int _columnNumber)
|
|
: SAXException (message == NULL ? GTXT ("XML parse error") : message)
|
|
{
|
|
lineNumber = _lineNumber;
|
|
columnNumber = _columnNumber;
|
|
}
|
|
|
|
/*
|
|
* Private implementation of SAXParser
|
|
*/
|
|
class SAXParserP : public SAXParser
|
|
{
|
|
public:
|
|
SAXParserP ();
|
|
~SAXParserP ();
|
|
void reset ();
|
|
void parse (File*, DefaultHandler*);
|
|
|
|
bool
|
|
isNamespaceAware ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
isValidating ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
|
|
static const int CH_EOF = -1;
|
|
|
|
void nextch ();
|
|
bool isWSpace ();
|
|
void skipWSpaces ();
|
|
void scanString (const char *str);
|
|
char *parseName ();
|
|
char *parseString ();
|
|
char *decodeString (char *str);
|
|
Attributes *parseAttributes ();
|
|
void parseTag ();
|
|
void parseDocument ();
|
|
void parsePart (int idx);
|
|
|
|
DefaultHandler *dh;
|
|
int bufsz;
|
|
char *buffer;
|
|
int cntsz;
|
|
int idx;
|
|
int curch;
|
|
int line;
|
|
int column;
|
|
};
|
|
|
|
SAXParserP::SAXParserP ()
|
|
{
|
|
dh = NULL;
|
|
bufsz = 0x2000;
|
|
buffer = (char*) malloc (bufsz);
|
|
cntsz = 0;
|
|
idx = 0;
|
|
line = 1;
|
|
column = 0;
|
|
}
|
|
|
|
SAXParserP::~SAXParserP ()
|
|
{
|
|
free (buffer);
|
|
}
|
|
|
|
void
|
|
SAXParserP::reset ()
|
|
{
|
|
dh = NULL;
|
|
bufsz = 8192;
|
|
buffer = (char*) realloc (buffer, bufsz);
|
|
cntsz = 0;
|
|
idx = 0;
|
|
line = 1;
|
|
column = 0;
|
|
}
|
|
|
|
void
|
|
SAXParserP::parse (File *f, DefaultHandler *_dh)
|
|
{
|
|
if (_dh == NULL)
|
|
return;
|
|
dh = _dh;
|
|
FILE *file = (FILE*) f;
|
|
int rem = bufsz;
|
|
cntsz = 0;
|
|
idx = 0;
|
|
for (;;)
|
|
{
|
|
int n = (int) fread (buffer + cntsz, 1, rem, file);
|
|
if (ferror (file) || n <= 0)
|
|
break;
|
|
cntsz += n;
|
|
if (feof (file))
|
|
break;
|
|
rem -= n;
|
|
if (rem == 0)
|
|
{
|
|
int oldbufsz = bufsz;
|
|
bufsz = bufsz >= 0x100000 ? bufsz + 0x100000 : bufsz * 2;
|
|
buffer = (char*) realloc (buffer, bufsz);
|
|
rem = bufsz - oldbufsz;
|
|
}
|
|
}
|
|
nextch ();
|
|
parseDocument ();
|
|
}
|
|
|
|
static int
|
|
hex (char c)
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return (c - '0');
|
|
else if (c >= 'a' && c <= 'f')
|
|
return 10 + (c - 'a');
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
SAXParserP::nextch ()
|
|
{
|
|
curch = idx >= cntsz ? CH_EOF : buffer[idx++];
|
|
if (curch == '\n')
|
|
{
|
|
line += 1;
|
|
column = 0;
|
|
}
|
|
else
|
|
column += 1;
|
|
}
|
|
|
|
bool
|
|
SAXParserP::isWSpace ()
|
|
{
|
|
return curch == ' ' || curch == '\t' || curch == '\n' || curch == '\r';
|
|
}
|
|
|
|
void
|
|
SAXParserP::skipWSpaces ()
|
|
{
|
|
while (isWSpace ())
|
|
nextch ();
|
|
}
|
|
|
|
void
|
|
SAXParserP::scanString (const char *str)
|
|
{
|
|
if (str == NULL || *str == '\0')
|
|
return;
|
|
for (;;)
|
|
{
|
|
if (curch == CH_EOF)
|
|
break;
|
|
else if (curch == *str)
|
|
{
|
|
const char *p = str;
|
|
for (;;)
|
|
{
|
|
p += 1;
|
|
nextch ();
|
|
if (*p == '\0')
|
|
return;
|
|
if (curch != *p)
|
|
break;
|
|
}
|
|
}
|
|
nextch ();
|
|
}
|
|
}
|
|
|
|
char *
|
|
SAXParserP::parseName ()
|
|
{
|
|
StringBuilder *name = new StringBuilder ();
|
|
|
|
if ((curch >= 'A' && curch <= 'Z') || (curch >= 'a' && curch <= 'z'))
|
|
{
|
|
name->append ((char) curch);
|
|
nextch ();
|
|
while (isalnum (curch) != 0 || curch == '_')
|
|
{
|
|
name->append ((char) curch);
|
|
nextch ();
|
|
}
|
|
}
|
|
|
|
char *res = name->toString ();
|
|
delete name;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Replaces encoded XML characters with original characters
|
|
* Attention: this method reuses the same string that is passed as the argument
|
|
* @param str
|
|
* @return str
|
|
*/
|
|
char *
|
|
SAXParserP::decodeString (char * str)
|
|
{
|
|
// Check if string has %22% and replace it with double quotes
|
|
// Also replace all other special combinations.
|
|
char *from = str;
|
|
char *to = str;
|
|
if (strstr (from, "%") || strstr (from, "&"))
|
|
{
|
|
int len = strlen (from);
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
int nch = from[i];
|
|
// Process &...; combinations
|
|
if (nch == '&' && i + 3 < len)
|
|
{
|
|
if (from[i + 2] == 't' && from[i + 3] == ';')
|
|
{
|
|
// check < >
|
|
if (from[i + 1] == 'l')
|
|
{
|
|
nch = '<';
|
|
i += 3;
|
|
}
|
|
else if (from[i + 1] == 'g')
|
|
{
|
|
nch = '>';
|
|
i += 3;
|
|
}
|
|
}
|
|
else if (i + 4 < len && from[i + 4] == ';')
|
|
{
|
|
// check &
|
|
if (from[i + 1] == 'a' && from[i + 2] == 'm' && from[i + 3] == 'p')
|
|
{
|
|
nch = '&';
|
|
i += 4;
|
|
}
|
|
}
|
|
else if ((i + 5 < len) && (from[i + 5] == ';'))
|
|
{
|
|
// check ' "
|
|
if (from[i + 1] == 'a' && from[i + 2] == 'p'
|
|
&& from[i + 3] == 'o' && from[i + 4] == 's')
|
|
{
|
|
nch = '\'';
|
|
i += 5;
|
|
}
|
|
if (from[i + 1] == 'q' && from[i + 2] == 'u' && from[i + 3] == 'o' && from[i + 4] == 't')
|
|
{
|
|
nch = '"';
|
|
i += 5;
|
|
}
|
|
}
|
|
}
|
|
// Process %XX% combinations
|
|
if (nch == '%' && i + 3 < len && from[i + 3] == '%')
|
|
{
|
|
int ch = hex (from[i + 1]);
|
|
if (ch >= 0)
|
|
{
|
|
int ch2 = hex (from[i + 2]);
|
|
if (ch2 >= 0)
|
|
{
|
|
ch = ch * 16 + ch2;
|
|
nch = ch;
|
|
i += 3;
|
|
}
|
|
}
|
|
}
|
|
*to++ = (char) nch;
|
|
}
|
|
*to = '\0';
|
|
}
|
|
return str;
|
|
}
|
|
|
|
char *
|
|
SAXParserP::parseString ()
|
|
{
|
|
StringBuilder *str = new StringBuilder ();
|
|
int quote = '>';
|
|
if (curch == '"')
|
|
{
|
|
quote = curch;
|
|
nextch ();
|
|
}
|
|
for (;;)
|
|
{
|
|
if (curch == CH_EOF)
|
|
break;
|
|
if (curch == quote)
|
|
{
|
|
nextch ();
|
|
break;
|
|
}
|
|
str->append ((char) curch);
|
|
nextch ();
|
|
}
|
|
|
|
char *res = str->toString ();
|
|
// Decode XML characters
|
|
res = decodeString (res);
|
|
delete str;
|
|
return res;
|
|
}
|
|
|
|
Attributes *
|
|
SAXParserP::parseAttributes ()
|
|
{
|
|
AttributesP *attrs = new AttributesP ();
|
|
|
|
for (;;)
|
|
{
|
|
skipWSpaces ();
|
|
char *name = parseName ();
|
|
if (name == NULL || *name == '\0')
|
|
{
|
|
free (name);
|
|
break;
|
|
}
|
|
skipWSpaces ();
|
|
if (curch != '=')
|
|
{
|
|
SAXParseException *e = new SAXParseException (NULL, line, column);
|
|
dh->error (e);
|
|
scanString (">");
|
|
free (name);
|
|
return attrs;
|
|
}
|
|
nextch ();
|
|
skipWSpaces ();
|
|
char *value = parseString ();
|
|
attrs->append (name, value);
|
|
}
|
|
return attrs;
|
|
}
|
|
|
|
void
|
|
SAXParserP::parseTag ()
|
|
{
|
|
skipWSpaces ();
|
|
bool empty = false;
|
|
char *name = parseName ();
|
|
if (name == NULL || *name == '\0')
|
|
{
|
|
SAXParseException *e = new SAXParseException (NULL, line, column);
|
|
dh->error (e);
|
|
scanString (">");
|
|
free (name);
|
|
return;
|
|
}
|
|
|
|
Attributes *attrs = parseAttributes ();
|
|
if (curch == '/')
|
|
{
|
|
nextch ();
|
|
empty = true;
|
|
}
|
|
if (curch == '>')
|
|
nextch ();
|
|
else
|
|
{
|
|
empty = false;
|
|
SAXParseException *e = new SAXParseException (NULL, line, column);
|
|
dh->error (e);
|
|
scanString (">");
|
|
}
|
|
if (curch == CH_EOF)
|
|
{
|
|
free (name);
|
|
delete attrs;
|
|
return;
|
|
}
|
|
dh->startElement (NULL, NULL, name, attrs);
|
|
if (empty)
|
|
{
|
|
dh->endElement (NULL, NULL, name);
|
|
free (name);
|
|
delete attrs;
|
|
return;
|
|
}
|
|
|
|
StringBuilder *chars = new StringBuilder ();
|
|
bool wspaces = true;
|
|
for (;;)
|
|
{
|
|
if (curch == CH_EOF)
|
|
break;
|
|
else if (curch == '<')
|
|
{
|
|
if (chars->length () > 0)
|
|
{
|
|
char *str = chars->toString ();
|
|
// Decode XML characters
|
|
str = decodeString (str);
|
|
if (wspaces)
|
|
dh->ignorableWhitespace (str, 0, chars->length ());
|
|
else
|
|
dh->characters (str, 0, chars->length ());
|
|
free (str);
|
|
chars->setLength (0);
|
|
wspaces = true;
|
|
}
|
|
nextch ();
|
|
if (curch == '/')
|
|
{
|
|
nextch ();
|
|
char *ename = parseName ();
|
|
if (ename && *ename != '\0')
|
|
{
|
|
if (strcmp (name, ename) == 0)
|
|
{
|
|
skipWSpaces ();
|
|
if (curch == '>')
|
|
{
|
|
nextch ();
|
|
dh->endElement (NULL, NULL, name);
|
|
free (ename);
|
|
break;
|
|
}
|
|
SAXParseException *e = new SAXParseException (NULL, line, column);
|
|
dh->error (e);
|
|
}
|
|
else
|
|
{
|
|
SAXParseException *e = new SAXParseException (NULL, line, column);
|
|
dh->error (e);
|
|
}
|
|
scanString (">");
|
|
}
|
|
free (ename);
|
|
}
|
|
else
|
|
parseTag ();
|
|
}
|
|
else
|
|
{
|
|
if (!isWSpace ())
|
|
wspaces = false;
|
|
chars->append ((char) curch);
|
|
nextch ();
|
|
}
|
|
}
|
|
|
|
free (name);
|
|
delete attrs;
|
|
delete chars;
|
|
return;
|
|
}
|
|
|
|
void
|
|
SAXParserP::parseDocument ()
|
|
{
|
|
dh->startDocument ();
|
|
for (;;)
|
|
{
|
|
if (curch == CH_EOF)
|
|
break;
|
|
if (curch == '<')
|
|
{
|
|
nextch ();
|
|
if (curch == '?')
|
|
scanString ("?>");
|
|
else if (curch == '!')
|
|
scanString (">");
|
|
else
|
|
parseTag ();
|
|
}
|
|
else
|
|
nextch ();
|
|
}
|
|
dh->endDocument ();
|
|
}
|
|
|
|
/*
|
|
* Private implementation of SAXParserFactory
|
|
*/
|
|
class SAXParserFactoryP : public SAXParserFactory
|
|
{
|
|
public:
|
|
SAXParserFactoryP () { }
|
|
~SAXParserFactoryP () { }
|
|
SAXParser *newSAXParser ();
|
|
|
|
void
|
|
setFeature (const char *, bool) { }
|
|
|
|
bool
|
|
getFeature (const char *)
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
SAXParser *
|
|
SAXParserFactoryP::newSAXParser ()
|
|
{
|
|
return new SAXParserP ();
|
|
}
|
|
|
|
/*
|
|
* SAXParserFactory
|
|
*/
|
|
const char *SAXParserFactory::DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory";
|
|
|
|
SAXParserFactory *
|
|
SAXParserFactory::newInstance ()
|
|
{
|
|
return new SAXParserFactoryP ();
|
|
}
|
|
|
|
void
|
|
DefaultHandler::dump_startElement (const char *qName, Attributes *attrs)
|
|
{
|
|
fprintf (stderr, NTXT ("DefaultHandler::startElement qName='%s'\n"), STR (qName));
|
|
for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++)
|
|
{
|
|
const char *qn = attrs->getQName (i);
|
|
const char *vl = attrs->getValue (i);
|
|
fprintf (stderr, NTXT (" %d '%s' = '%s'\n"), i, STR (qn), STR (vl));
|
|
}
|
|
}
|