Patrick Brosi 7 év óta
commit
7dd5a308e8
5 módosított fájl, 798 hozzáadás és 0 törlés
  1. 20 0
      .gitignore
  2. 2 0
      CMakeLists.txt
  3. 399 0
      File.cpp
  4. 105 0
      File.h
  5. 272 0
      NamedEnts.h

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
1
+Makefile
2
+CMakeCache.txt
3
+*.o
4
+*.a
5
+cmake_install.cmake
6
+*~
7
+CMakeFiles
8
+*.cfg
9
+*.cmake
10
+/build/
11
+*.pyc
12
+compile_commands.json
13
+.vimrc
14
+.clang_complete
15
+/.idea
16
+*.vs
17
+[._]*.s[a-w][a-z]
18
+[._]s[a-w][a-z]
19
+*.cppr
20
+*.hr

+ 2 - 0
CMakeLists.txt

@@ -0,0 +1,2 @@
1
+file(GLOB_RECURSE xml_SRC *.cpp)
2
+add_library(xml ${xml_SRC})

+ 399 - 0
File.cpp

@@ -0,0 +1,399 @@
1
+// Copyright 2017 Patrick Brosi
2
+// info@patrickbrosi.de
3
+
4
+#include <fcntl.h>
5
+#include <sys/stat.h>
6
+#include <sys/types.h>
7
+#include <unistd.h>
8
+#include <cassert>
9
+#include <cstring>
10
+#include <fstream>
11
+#include <iostream>
12
+#include <map>
13
+#include "xml/File.h"
14
+#include "xml/NamedEnts.h"
15
+
16
+using namespace xml;
17
+
18
+// _____________________________________________________________________________
19
+File::File(const std::string& path)
20
+    : _file(0), _c(0), _lastBytes(0), _which(0), _path(path), _totReadBef(0) {
21
+  _buffer = new char*[2];
22
+  _buffer[0] = new char[BUFFER_S + 1];
23
+  _buffer[1] = new char[BUFFER_S + 1];
24
+
25
+  reset();
26
+}
27
+
28
+// _____________________________________________________________________________
29
+File::~File() {
30
+  delete[] _buffer[0];
31
+  delete[] _buffer[1];
32
+  delete[] _buffer;
33
+  close(_file);
34
+}
35
+
36
+// _____________________________________________________________________________
37
+void File::reset() {
38
+  _which = 0;
39
+  _s.s = NONE;
40
+  _s.hanging = 0;
41
+  _totReadBef = 0;
42
+
43
+  if (_file) close(_file);
44
+  _file = open(_path.c_str(), O_RDONLY);
45
+  posix_fadvise(_file, 0, 0, POSIX_FADV_SEQUENTIAL);
46
+
47
+  _lastBytes = read(_file, _buffer[_which], BUFFER_S);
48
+  _lastNewData = _lastBytes;
49
+  _c = _buffer[_which];
50
+  while (!_s.tagStack.empty()) _s.tagStack.pop();
51
+  _s.tagStack.push("[root]");
52
+  _prevs = _s;
53
+}
54
+
55
+// _____________________________________________________________________________
56
+size_t File::level() const { return _s.tagStack.size() - _s.hanging; }
57
+
58
+// _____________________________________________________________________________
59
+ParserState File::state() { return _prevs; }
60
+
61
+// _____________________________________________________________________________
62
+void File::setState(const ParserState& s) {
63
+  _s = s;
64
+  _prevs = s;
65
+
66
+  lseek(_file, _s.off, SEEK_SET);
67
+  _totReadBef = _s.off;
68
+  _lastBytes = read(_file, _buffer[_which], BUFFER_S);
69
+  _lastNewData = _lastBytes;
70
+  _c = _buffer[_which];
71
+
72
+  next();
73
+}
74
+
75
+// _____________________________________________________________________________
76
+const Tag& File::get() const { return _ret; }
77
+
78
+// _____________________________________________________________________________
79
+bool File::next() {
80
+  // avoid too much stack copying
81
+  if (_prevs.tagStack.size() != _s.tagStack.size() ||
82
+      _prevs.tagStack.top() != _s.tagStack.top()) {
83
+    _prevs.tagStack = _s.tagStack;
84
+  }
85
+  _prevs.s = _s.s;
86
+  _prevs.hanging = _s.hanging;
87
+  _prevs.off =
88
+      _totReadBef + (_c - _buffer[_which]) - (_lastBytes - _lastNewData);
89
+
90
+  if (_s.hanging) _s.hanging--;
91
+  _ret.name = 0;
92
+  _ret.attrs.clear();
93
+  void* i;
94
+  while (_lastBytes) {
95
+    for (; _c - _buffer[_which] < _lastBytes; ++_c) {
96
+      char c = *_c;
97
+      switch (_s.s) {
98
+        case NONE:
99
+          if (std::isspace(c))
100
+            continue;
101
+          else if (c == '<') {
102
+            _s.s = IN_TAG_TENTATIVE;
103
+            continue;
104
+          } else {
105
+            _s.s = IN_TEXT;
106
+            continue;
107
+          }
108
+        case IN_TEXT:
109
+          throw XmlFileException("text nodes not yet supported");
110
+        case IN_TAG_TENTATIVE:
111
+          if (c == '/') {
112
+            _s.s = IN_TAG_NAME_CLOSE;
113
+            _tmp = _c + 1;
114
+            continue;
115
+          } else if (c == '?') {
116
+            _s.s = IN_TAG_NAME_META;
117
+            continue;
118
+          } else if (std::isalnum(c) || c == '-' || c == '_' || c == '.') {
119
+            _s.s = IN_TAG_NAME;
120
+            _ret.name = _c;
121
+            continue;
122
+          }
123
+
124
+        case IN_TAG:
125
+          if (std::isspace(c))
126
+            continue;
127
+          else if (std::isalnum(c) || c == '-' || c == '_' || c == '.') {
128
+            _s.s = IN_ATTRKEY;
129
+            _tmp = _c;
130
+            continue;
131
+          } else if (c == '/') {
132
+            _s.s = AW_CLOSING;
133
+            continue;
134
+          } else if (c == '>') {
135
+            _s.hanging++;
136
+            _s.tagStack.push(_ret.name);
137
+            _s.s = WS_SKIP;
138
+            continue;
139
+          } else {
140
+            throw XmlFileException("expected valid tag");
141
+          }
142
+
143
+        case IN_ATTRVAL_SQ:
144
+          i = memchr(_c, '\'', _lastBytes - (_c - _buffer[_which]));
145
+          if (!i) {
146
+            _c = _buffer[_which] + _lastBytes;
147
+            continue;
148
+          } else {
149
+            _c = (char*)i;
150
+            _s.s = IN_TAG;
151
+            *_c = 0;
152
+            _ret.attrs[_tmp] = _tmp2;
153
+            continue;
154
+          }
155
+
156
+        case IN_ATTRVAL_DQ:
157
+          i = memchr(_c, '"', _lastBytes - (_c - _buffer[_which]));
158
+          if (!i) {
159
+            _c = _buffer[_which] + _lastBytes;
160
+            continue;
161
+          } else {
162
+            _c = (char*)i;
163
+            _s.s = IN_TAG;
164
+            *_c = 0;
165
+            _ret.attrs[_tmp] = _tmp2;
166
+            continue;
167
+          }
168
+
169
+        case AW_IN_ATTRVAL:
170
+          if (std::isspace(c))
171
+            continue;
172
+          else if (c == '\'') {
173
+            _s.s = IN_ATTRVAL_SQ;
174
+            _tmp2 = _c + 1;
175
+            continue;
176
+          } else if (c == '"') {
177
+            _s.s = IN_ATTRVAL_DQ;
178
+            _tmp2 = _c + 1;
179
+            continue;
180
+          } else {
181
+            throw XmlFileException("expected attribute value");
182
+          }
183
+
184
+        case IN_ATTRKEY:
185
+          if (std::isspace(c)) {
186
+            *_c = 0;
187
+            _s.s = AFTER_ATTRKEY;
188
+            continue;
189
+          } else if (std::isalnum(c) || c == '-' || c == '_' || c == '.') {
190
+            continue;
191
+          } else if (c == '=') {
192
+            *_c = 0;
193
+            _s.s = AW_IN_ATTRVAL;
194
+            continue;
195
+          }
196
+
197
+          std::cerr << "ERROR 5" << std::endl;
198
+          exit(0);
199
+
200
+        case AFTER_ATTRKEY:
201
+          if (std::isspace(c))
202
+            continue;
203
+          else if (c == '=') {
204
+            _s.s = AW_IN_ATTRVAL;
205
+            continue;
206
+          } else {
207
+            // TODO: error
208
+            continue;
209
+          }
210
+
211
+        case IN_TAG_NAME:
212
+          if (std::isspace(c)) {
213
+            *_c = 0;
214
+            _s.s = IN_TAG;
215
+            continue;
216
+          } else if (c == '>') {
217
+            *_c = 0;
218
+            _s.hanging++;
219
+            _s.tagStack.push(_ret.name);
220
+            _s.s = WS_SKIP;
221
+            continue;
222
+          } else if (c == '/') {
223
+            *_c = 0;
224
+            _s.s = AW_CLOSING;
225
+            continue;
226
+          } else if (std::isalnum(c) || c == '-' || c == '_' || c == '.') {
227
+            continue;
228
+          }
229
+
230
+        case IN_TAG_NAME_META:
231
+          // TODO: read meta tags!
232
+          if (c == '>') {
233
+            _s.s = NONE;
234
+            continue;
235
+          }
236
+
237
+          continue;
238
+
239
+        case IN_TAG_NAME_CLOSE:
240
+          if (std::isspace(c)) {
241
+            *_c = 0;
242
+            _s.s = IN_TAG_CLOSE;
243
+            continue;
244
+          } else if (std::isalnum(c) || c == '-' || c == '_' || c == '.') {
245
+            continue;
246
+          } else if (c == '>') {
247
+            *_c = 0;
248
+            if (_tmp != _s.tagStack.top()) {
249
+              throw XmlFileException("closing wrong tag");
250
+            }
251
+            _s.tagStack.pop();
252
+            _s.s = NONE;
253
+            continue;
254
+          }
255
+
256
+        case IN_TAG_CLOSE:
257
+          if (std::isspace(c))
258
+            continue;
259
+          else if (c == '>') {
260
+            if (_tmp != _s.tagStack.top()) {
261
+              throw XmlFileException("closing wrong tag");
262
+            }
263
+            _s.tagStack.pop();
264
+            _s.s = NONE;
265
+            continue;
266
+          } else {
267
+            throw XmlFileException("expected '>'");
268
+          }
269
+
270
+        case AW_CLOSING:
271
+          if (c == '>') {
272
+            _s.s = WS_SKIP;
273
+            continue;
274
+          }
275
+
276
+        case WS_SKIP:
277
+          if (std::isspace(c))
278
+            continue;
279
+          else {
280
+            _s.s = NONE;
281
+            return true;
282
+          }
283
+      }
284
+    }
285
+
286
+    // buffer ended, read new stuff, but copy remaining if needed
287
+    size_t off = 0;
288
+    if (_s.s == IN_TAG_NAME) {  //|| IN_TAG_NAME_META) {
289
+      off = _lastBytes - (_ret.name - _buffer[_which]);
290
+      memmove(_buffer[!_which], _ret.name, off);
291
+      _ret.name = _buffer[!_which];
292
+    } else if (_s.s == IN_TAG_NAME_CLOSE || _s.s == IN_ATTRKEY ||
293
+               _s.s == IN_TEXT) {
294
+      off = _lastBytes - (_tmp - _buffer[_which]);
295
+      memmove(_buffer[!_which], _tmp, off);
296
+      _tmp = _buffer[!_which];
297
+    } else if (_s.s == IN_ATTRVAL_SQ || _s.s == IN_ATTRVAL_DQ) {
298
+      off = _lastBytes - (_tmp2 - _buffer[_which]);
299
+      memmove(_buffer[!_which], _tmp2, off);
300
+      _tmp2 = _buffer[!_which];
301
+    }
302
+
303
+    assert(off <= BUFFER_S);
304
+
305
+    size_t readb = read(_file, _buffer[!_which] + off, BUFFER_S - off);
306
+    if (!readb) break;
307
+    _totReadBef += _lastNewData;
308
+    _which = !_which;
309
+    _lastNewData = readb;
310
+    _lastBytes = _lastNewData + off;
311
+    _c = _buffer[_which] + off;
312
+  }
313
+
314
+  if (_s.tagStack.top() != "[root]") {
315
+    // TODO error
316
+    throw XmlFileException("XML tree not complete");
317
+  } else {
318
+    _s.tagStack.pop();
319
+  }
320
+  _s.s = NONE;
321
+  _ret.name = "[root]";
322
+  return false;
323
+}
324
+
325
+// _____________________________________________________________________________
326
+std::string File::decode(const std::string& str) { return decode(str.c_str()); }
327
+
328
+// _____________________________________________________________________________
329
+std::string File::decode(const char* str) {
330
+  const char* c = strchr(str, '&');
331
+  if (!c) return str;
332
+
333
+  char decRet[strlen(str) + 1];
334
+  const char* last = str;
335
+  char* dstPt = decRet;
336
+
337
+  for (; c != 0; c = strchr(c + 1, '&')) {
338
+    memcpy(dstPt, last, c - last);
339
+    dstPt += c - last;
340
+    last = c;
341
+
342
+    if (*(c + 1) == '#') {
343
+      uint64_t cp = -1;
344
+      char* tail;
345
+      errno = 0;
346
+      if (*(c + 2) == 'x' || *(c + 2) == 'X')
347
+        cp = strtoul(c + 3, &tail, 16);
348
+      else
349
+        cp = strtoul(c + 2, &tail, 10);
350
+
351
+      if (*tail == ';' && cp <= 0x1FFFFF && !errno) {
352
+        dstPt += utf8(cp, dstPt);
353
+        last = tail + 1;
354
+      }
355
+    } else {
356
+      const char* e = strchr(c, ';');
357
+      if (e) {
358
+        char ent[e - 1 - c + 1];
359
+        memcpy(ent, c + 1, e - 1 - c);
360
+        ent[e - 1 - c] = 0;
361
+        const auto it = xml::ENTITIES.find(ent);
362
+        if (it != xml::ENTITIES.end()) {
363
+          const char* utf8 = it->second;
364
+          memcpy(dstPt, utf8, strlen(utf8));
365
+          dstPt += strlen(utf8);
366
+          last += strlen(ent) + 2;
367
+        }
368
+      }
369
+    }
370
+  }
371
+
372
+  strcpy(dstPt, last);
373
+  return decRet;
374
+}
375
+
376
+// _____________________________________________________________________________
377
+size_t File::utf8(size_t cp, char* out) {
378
+  if (cp <= 0x7F) {
379
+    out[0] = cp & 0x7F;
380
+    return 1;
381
+  } else if (cp <= 0x7FF) {
382
+    out[0] = 0xC0 | (cp >> 6);
383
+    out[1] = 0x80 | (cp & 0x3F);
384
+    return 2;
385
+  } else if (cp <= 0xFFFF) {
386
+    out[0] = 0xE0 | (cp >> 12);
387
+    out[1] = 0x80 | ((cp >> 6) & 0x3F);
388
+    out[2] = 0x80 | (cp & 0x3F);
389
+    return 3;
390
+  } else if (cp <= 0x1FFFFF) {
391
+    out[0] = 0xF0 | (cp >> 18);
392
+    out[1] = 0x80 | ((cp >> 12) & 0x3F);
393
+    out[2] = 0x80 | ((cp >> 6) & 0x3F);
394
+    out[3] = 0x80 | (cp & 0x3F);
395
+    return 4;
396
+  }
397
+
398
+  return 0;
399
+}

+ 105 - 0
File.h

@@ -0,0 +1,105 @@
1
+// Copyright 2017 Patrick Brosi
2
+// info@patrickbrosi.de
3
+
4
+#ifndef XML_FILE_H_
5
+#define XML_FILE_H_
6
+
7
+#include <cstring>
8
+#include <fstream>
9
+#include <map>
10
+#include <stack>
11
+#include <string>
12
+
13
+namespace xml {
14
+
15
+const static size_t BUFFER_S = 16 * 1024;
16
+
17
+class XmlFileException : public std::exception {
18
+ public:
19
+  XmlFileException(std::string msg) : _msg(msg) {}
20
+  ~XmlFileException() throw() {}
21
+
22
+  virtual const char* what() const throw() { return _msg.c_str(); };
23
+
24
+ private:
25
+  std::string _msg;
26
+};
27
+
28
+enum State {
29
+  NONE,
30
+  IN_TAG_NAME,
31
+  IN_TAG_NAME_META,
32
+  IN_TAG,
33
+  IN_TAG_CLOSE,
34
+  IN_TAG_NAME_CLOSE,
35
+  IN_TAG_TENTATIVE,
36
+  IN_ATTRKEY,
37
+  AFTER_ATTRKEY,
38
+  AW_IN_ATTRVAL,
39
+  IN_ATTRVAL_SQ,
40
+  IN_ATTRVAL_DQ,
41
+  IN_TEXT,
42
+  AW_CLOSING,
43
+  WS_SKIP
44
+};
45
+
46
+struct AttrCmp {
47
+  bool operator()(const char* const& a, const char* const& b) const {
48
+    return std::strcmp(a, b) < 0;
49
+  }
50
+};
51
+
52
+struct ParserState {
53
+  ParserState() : s(NONE), hanging(0), off(0){};
54
+  std::stack<std::string> tagStack;
55
+  State s;
56
+  size_t hanging;
57
+  int64_t off;
58
+};
59
+
60
+typedef std::map<const char*, const char*, AttrCmp> AttrMap;
61
+
62
+struct Tag {
63
+  const char* name;
64
+  AttrMap attrs;
65
+};
66
+
67
+class File {
68
+ public:
69
+  File(const std::string& path);
70
+  ~File();
71
+
72
+  const Tag& get() const;
73
+
74
+  bool next();
75
+  size_t level() const;
76
+  void reset();
77
+  ParserState state();
78
+  void setState(const ParserState& s);
79
+  static std::string decode(const char* str);
80
+  static std::string decode(const std::string& str);
81
+
82
+ private:
83
+  int _file;
84
+  ParserState _s;
85
+  ParserState _prevs;
86
+  char** _buffer;
87
+  char* _c;
88
+  int64_t _lastBytes;
89
+
90
+  const char* _tmp;
91
+  const char* _tmp2;
92
+
93
+  size_t _which;
94
+  std::string _path;
95
+
96
+  int64_t _totReadBef;
97
+  int64_t _lastNewData;
98
+
99
+  Tag _ret;
100
+
101
+  static size_t utf8(size_t cp, char* out);
102
+};
103
+}
104
+
105
+#endif  // XML_FILE_H_

+ 272 - 0
NamedEnts.h

@@ -0,0 +1,272 @@
1
+// Copyright 2017 Patrick Brosi
2
+// info@patrickbrosi.de
3
+
4
+#ifndef XML_NAMEDENTS_H_
5
+#define XML_NAMEDENTS_H_
6
+
7
+#include <map>
8
+#include <string>
9
+
10
+namespace xml {
11
+
12
+// see
13
+// http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
14
+std::map<std::string, const char*> ENTITIES = {
15
+  {"aacute", "á"},
16
+  {"Aacute", "Á"},
17
+  {"acirc", "â"},
18
+  {"Acirc", "Â"},
19
+  {"acute", "´"},
20
+  {"aelig", "æ"},
21
+  {"AElig", "Æ"},
22
+  {"agrave", "à"},
23
+  {"Agrave", "À"},
24
+  {"alefsym", "ℵ"},
25
+  {"alpha", "α"},
26
+  {"Alpha", "Α"},
27
+  {"amp", "&"},
28
+  {"and", "∧"},
29
+  {"ang", "∠"},
30
+  {"apos", "'"},
31
+  {"aring", "å"},
32
+  {"Aring", "Å"},
33
+  {"asymp", "≈"},
34
+  {"atilde", "ã"},
35
+  {"Atilde", "Ã"},
36
+  {"auml", "ä"},
37
+  {"Auml", "Ä"},
38
+  {"bdquo", "„"},
39
+  {"beta", "β"},
40
+  {"Beta", "Β"},
41
+  {"brvbar", "¦"},
42
+  {"bull", "•"},
43
+  {"cap", "∩"},
44
+  {"ccedil", "ç"},
45
+  {"Ccedil", "Ç"},
46
+  {"cedil", "¸"},
47
+  {"cent", "¢"},
48
+  {"chi", "χ"},
49
+  {"Chi", "Χ"},
50
+  {"circ", "ˆ"},
51
+  {"clubs", "♣"},
52
+  {"cong", "≅"},
53
+  {"copy", "©"},
54
+  {"crarr", "↵"},
55
+  {"cup", "∪"},
56
+  {"curren", "¤"},
57
+  {"dagger", "†"},
58
+  {"Dagger", "‡"},
59
+  {"darr", "↓"},
60
+  {"dArr", "⇓"},
61
+  {"deg", "°"},
62
+  {"delta", "δ"},
63
+  {"Delta", "Δ"},
64
+  {"diams", "♦"},
65
+  {"divide", "÷"},
66
+  {"eacute", "é"},
67
+  {"Eacute", "É"},
68
+  {"ecirc", "ê"},
69
+  {"Ecirc", "Ê"},
70
+  {"egrave", "è"},
71
+  {"Egrave", "È"},
72
+  {"empty", "∅"},
73
+  {"emsp", "\xE2\x80\x83"},
74
+  {"ensp", "\xE2\x80\x82"},
75
+  {"epsilon", "ε"},
76
+  {"Epsilon", "Ε"},
77
+  {"equiv", "≡"},
78
+  {"eta", "η"},
79
+  {"Eta", "Η"},
80
+  {"eth", "ð"},
81
+  {"ETH", "Ð"},
82
+  {"euml", "ë"},
83
+  {"Euml", "Ë"},
84
+  {"euro", "€"},
85
+  {"exist", "∃"},
86
+  {"fnof", "ƒ"},
87
+  {"forall", "∀"},
88
+  {"frac12", "½"},
89
+  {"frac14", "¼"},
90
+  {"frac34", "¾"},
91
+  {"frasl", "⁄"},
92
+  {"gamma", "γ"},
93
+  {"Gamma", "Γ"},
94
+  {"ge", "≥"},
95
+  {"gt", ">"},
96
+  {"harr", "↔"},
97
+  {"hArr", "⇔"},
98
+  {"hearts", "♥"},
99
+  {"hellip", "…"},
100
+  {"iacute", "í"},
101
+  {"Iacute", "Í"},
102
+  {"icirc", "î"},
103
+  {"Icirc", "Î"},
104
+  {"iexcl", "¡"},
105
+  {"igrave", "ì"},
106
+  {"Igrave", "Ì"},
107
+  {"image", "ℑ"},
108
+  {"infin", "∞"},
109
+  {"int", "∫"},
110
+  {"iota", "ι"},
111
+  {"Iota", "Ι"},
112
+  {"iquest", "¿"},
113
+  {"isin", "∈"},
114
+  {"iuml", "ï"},
115
+  {"Iuml", "Ï"},
116
+  {"kappa", "κ"},
117
+  {"Kappa", "Κ"},
118
+  {"lambda", "λ"},
119
+  {"Lambda", "Λ"},
120
+  {"lang", "〈"},
121
+  {"laquo", "«"},
122
+  {"larr", "←"},
123
+  {"lArr", "⇐"},
124
+  {"lceil", "⌈"},
125
+  {"ldquo", "“"},
126
+  {"le", "≤"},
127
+  {"lfloor", "⌊"},
128
+  {"lowast", "∗"},
129
+  {"loz", "◊"},
130
+  {"lrm", "\xE2\x80\x8E"},
131
+  {"lsaquo", "‹"},
132
+  {"lsquo", "‘"},
133
+  {"lt", "<"},
134
+  {"macr", "¯"},
135
+  {"mdash", "—"},
136
+  {"micro", "µ"},
137
+  {"middot", "·"},
138
+  {"minus", "−"},
139
+  {"mu", "μ"},
140
+  {"Mu", "Μ"},
141
+  {"nabla", "∇"},
142
+  {"nbsp", "\xC2\xA0"},
143
+  {"ndash", "–"},
144
+  {"ne", "≠"},
145
+  {"ni", "∋"},
146
+  {"not", "¬"},
147
+  {"notin", "∉"},
148
+  {"nsub", "⊄"},
149
+  {"ntilde", "ñ"},
150
+  {"Ntilde", "Ñ"},
151
+  {"nu", "ν"},
152
+  {"Nu", "Ν"},
153
+  {"oacute", "ó"},
154
+  {"Oacute", "Ó"},
155
+  {"ocirc", "ô"},
156
+  {"Ocirc", "Ô"},
157
+  {"oelig", "œ"},
158
+  {"OElig", "Œ"},
159
+  {"ograve", "ò"},
160
+  {"Ograve", "Ò"},
161
+  {"oline", "‾"},
162
+  {"omega", "ω"},
163
+  {"Omega", "Ω"},
164
+  {"omicron", "ο"},
165
+  {"Omicron", "Ο"},
166
+  {"oplus", "⊕"},
167
+  {"or", "∨"},
168
+  {"ordf", "ª"},
169
+  {"ordm", "º"},
170
+  {"oslash", "ø"},
171
+  {"Oslash", "Ø"},
172
+  {"otilde", "õ"},
173
+  {"Otilde", "Õ"},
174
+  {"otimes", "⊗"},
175
+  {"ouml", "ö"},
176
+  {"Ouml", "Ö"},
177
+  {"para", "¶"},
178
+  {"part", "∂"},
179
+  {"permil", "‰"},
180
+  {"perp", "⊥"},
181
+  {"phi", "φ"},
182
+  {"Phi", "Φ"},
183
+  {"piv", "ϖ"},
184
+  {"pi", "π"},
185
+  {"Pi", "Π"},
186
+  {"plusmn", "±"},
187
+  {"pound", "£"},
188
+  {"prime", "′"},
189
+  {"Prime", "″"},
190
+  {"prod", "∏"},
191
+  {"prop", "∝"},
192
+  {"psi", "ψ"},
193
+  {"Psi", "Ψ"},
194
+  {"quot", "\""},
195
+  {"radic", "√"},
196
+  {"rang", "〉"},
197
+  {"raquo", "»"},
198
+  {"rarr", "→"},
199
+  {"rArr", "⇒"},
200
+  {"rceil", "⌉"},
201
+  {"rdquo", "”"},
202
+  {"real", "ℜ"},
203
+  {"reg", "®"},
204
+  {"rfloor", "⌋"},
205
+  {"rho", "ρ"},
206
+  {"Rho", "Ρ"},
207
+  {"rlm", "\xE2\x80\x8F"},
208
+  {"rsaquo", "›"},
209
+  {"rsquo", "’"},
210
+  {"sbquo", "‚"},
211
+  {"scaron", "š"},
212
+  {"Scaron", "Š"},
213
+  {"sdot", "⋅"},
214
+  {"sect", "§"},
215
+  {"shy", "\xC2\xAD"},
216
+  {"sigmaf", "ς"},
217
+  {"sigma", "σ"},
218
+  {"Sigma", "Σ"},
219
+  {"sim", "∼"},
220
+  {"spades", "♠"},
221
+  {"sub", "⊂"},
222
+  {"sube", "⊆"},
223
+  {"sum", "∑"},
224
+  {"sup", "⊃"},
225
+  {"sup1", "¹"},
226
+  {"sup2", "²"},
227
+  {"sup3", "³"},
228
+  {"supe", "⊇"},
229
+  {"szlig", "ß"},
230
+  {"tau", "τ"},
231
+  {"Tau", "Τ"},
232
+  {"there4", "∴"},
233
+  {"thetasym", "ϑ"},
234
+  {"theta", "θ"},
235
+  {"Theta", "Θ"},
236
+  {"thinsp", "\xE2\x80\x89"},
237
+  {"thorn", "þ"},
238
+  {"THORN", "Þ"},
239
+  {"tilde", "˜"},
240
+  {"times", "×"},
241
+  {"trade", "™"},
242
+  {"uacute", "ú"},
243
+  {"Uacute", "Ú"},
244
+  {"uarr", "↑"},
245
+  {"uArr", "⇑"},
246
+  {"ucirc", "û"},
247
+  {"Ucirc", "Û"},
248
+  {"ugrave", "ù"},
249
+  {"Ugrave", "Ù"},
250
+  {"uml", "¨"},
251
+  {"upsih", "ϒ"},
252
+  {"upsilon", "υ"},
253
+  {"Upsilon", "Υ"},
254
+  {"uuml", "ü"},
255
+  {"Uuml", "Ü"},
256
+  {"weierp", "℘"},
257
+  {"xi", "ξ"},
258
+  {"Xi", "Ξ"},
259
+  {"yacute", "ý"},
260
+  {"Yacute", "Ý"},
261
+  {"yen", "¥"},
262
+  {"yuml", "ÿ"},
263
+  {"Yuml", "Ÿ"},
264
+  {"zeta", "ζ"},
265
+  {"Zeta", "Ζ"},
266
+  {"zwj", "\xE2\x80\x8D"},
267
+  {"zwnj", "\xE2\x80\x8C"}
268
+};
269
+}
270
+
271
+#endif  // XML_NAMEDENTS_H_
272
+