| /* Copyright (C) 2021-2024 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)); |
| } |
| } |