GCC Code Coverage Report


Directory: ./
File: pdserv-1.1.0/src/msrproto/XmlParser.cpp
Date: 2024-12-15 04:08:34
Exec Total Coverage
Lines: 1 223 0.4%
Branches: 0 233 0.0%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * $Id$
4 *
5 * Copyright 2010 Richard Hacker (lerichi at gmx dot net)
6 *
7 * This file is part of the pdserv library.
8 *
9 * The pdserv library is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation, either version 3 of the License, or (at
12 * your option) any later version.
13 *
14 * The pdserv library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 * License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>.
21 *
22 *****************************************************************************/
23
24 #include "XmlParser.h"
25 #include "Session.h"
26 #include "../Debug.h"
27
28 #include <algorithm>
29 #include <cstring>
30 #include <sstream>
31
32 #undef log_debug
33 #define log_debug(...)
34
35 using namespace MsrProto;
36
37 /////////////////////////////////////////////////////////////////////////////
38 XmlParser::XmlParser(size_t bufMax): bufLenMax(bufMax)
39 {
40 parseState = FindStart;
41 buf = new char[bufIncrement];
42 bufLen = bufIncrement;
43
44 parsePos = buf;
45 inputEnd = buf;
46 }
47
48 /////////////////////////////////////////////////////////////////////////////
49 XmlParser::XmlParser(const char *begin, const char *end):
50 bufLenMax(end - begin)
51 {
52 parseState = FindStart;
53 buf = new char[bufLenMax];
54 bufLen = bufLenMax;
55
56 parsePos = buf;
57 inputEnd = buf;
58
59 std::copy(begin, end, buf);
60 newData(bufLen);
61 }
62
63 /////////////////////////////////////////////////////////////////////////////
64 XmlParser::~XmlParser()
65 {
66 delete[] buf;
67 }
68
69 /////////////////////////////////////////////////////////////////////////////
70 size_t XmlParser::free()
71 {
72 return (buf + bufLen) - inputEnd;
73 }
74
75 /////////////////////////////////////////////////////////////////////////////
76 char *XmlParser::bufptr()
77 {
78 return inputEnd;
79 }
80
81 /////////////////////////////////////////////////////////////////////////////
82 void XmlParser::newData(size_t n)
83 {
84 inputEnd += n;
85 }
86
87 /////////////////////////////////////////////////////////////////////////////
88 XmlParser::Element XmlParser::nextElement()
89 {
90 bool finished = false;
91
92 for (; parsePos < inputEnd and !finished; ++parsePos) {
93 // log_debug("state %i, char %c", parseState, *parsePos);
94
95 switch (parseState) {
96 case FindStart:
97 if (*parsePos == '<') {
98 // Found beginning
99 attribute.clear();
100 element = parsePos;
101 parseState = ExpectToken;
102 }
103 else {
104 // Move forward in the buffer until '<' is found
105 parsePos = std::find(parsePos+1, inputEnd, '<') - 1;
106 // log_debug("Found < at %zu", parsePos - buf);
107 }
108 break;
109
110 case ExpectToken:
111 // Expect an alpha character here
112 if (isalpha(*parsePos)) {
113 parseState = FindTokenEnd;
114 attribute.push_back(
115 std::make_pair(parsePos,(const char*)0));
116 }
117 else
118 parseState = FindStart;
119 break;
120
121 case SkipSpace:
122 // Check for end of XML Element
123 if (isalpha(*parsePos)) {
124 // Found next token
125 parseState = ExpectToken;
126
127 // Decrement parsePos because the pointer gets incremented
128 // automatically at the loop end
129 --parsePos;
130 break;
131 }
132
133 if (*parsePos == '/')
134 parseState = ExpectGT;
135 else if (*parsePos == '>')
136 finished = true;
137 else if (!isspace(*parsePos))
138 parseState = FindStart;
139
140 *parsePos = '\0'; // Clobber spaces
141 break;
142
143 case FindTokenEnd:
144 // Moves forward until non-alpha is found
145 if (isalpha(*parsePos) or *parsePos == '_')
146 break;
147
148 if (*parsePos == '=') {
149 parseState = GetAttribute;
150 *parsePos = '\0';
151 }
152 else {
153 parseState = SkipSpace;
154 --parsePos;
155 }
156
157
158 break;
159
160 case GetAttribute:
161 {
162 const char *attrValuePos = parsePos;
163 if (*parsePos == '\'' or *parsePos == '"') {
164 quote = *parsePos;
165 ++attrValuePos;
166 parseState = GetQuotedAttribute;
167 }
168 else {
169 parseState = GetUnquotedAttribute;
170 }
171
172 attribute.rbegin()->second = attrValuePos;
173 }
174
175 break;
176
177 case GetQuotedAttribute:
178 {
179 char *quotePos = std::find(parsePos, inputEnd, quote);
180 char *escape = std::find(parsePos, quotePos, '\\');
181
182 parsePos = quotePos;
183
184 // log_debug("quotePos=%zu escape=%zu",
185 // quotePos - buf, escape - quotePos);
186 if (escape < quotePos) {
187 parsePos = escape;
188 escapeState = GetQuotedAttribute;
189 parseState = SkipEscapeChar;
190 }
191 else if (quotePos < inputEnd) {
192 *parsePos = '\0';
193
194 parseState = SkipSpace;
195 }
196 else
197 --parsePos;
198 }
199 break;
200
201 case GetUnquotedAttribute:
202 if (*parsePos == '>') {
203 *parsePos = '\0';
204 finished = true;
205 }
206 else if (*parsePos == '/')
207 parseState = MaybeGT;
208 else if (isspace(*parsePos)) {
209 *parsePos = '\0';
210 parseState = SkipSpace;
211 }
212
213 break;
214
215 case ExpectGT:
216 parseState = FindStart;
217 finished = *parsePos == '>';
218
219 break;
220
221 case MaybeGT:
222 if (*parsePos == '>') {
223 *(parsePos - 1) = '\0';
224 finished = true;
225 }
226 else
227 parseState = GetUnquotedAttribute;
228 break;
229
230 case SkipEscapeChar:
231 parseState = escapeState;
232 break;
233 }
234 }
235
236 if (finished) {
237 log_debug("found something");
238 parseState = FindStart;
239 return Element(element+1, &attribute);
240 }
241
242 if (parseState == FindStart) {
243 parsePos = inputEnd = buf;
244 log_debug("resetting pointers");
245 }
246 else if (inputEnd == buf + bufLen) {
247 if (element > buf) {
248 size_t n = element - buf;
249
250 std::copy(buf + n, inputEnd, buf);
251
252 parsePos -= n;
253 inputEnd -= n;
254 element = buf;
255 for (AttributeList::iterator it = attribute.begin();
256 it != attribute.end(); ++it) {
257 it->first -= n;
258 it->second -= n;
259 }
260 log_debug("move data to buf start");
261 }
262 else {
263 // Note: element points to beginning of buf here
264
265 bufLen += bufIncrement;
266
267 if (bufLen > bufLenMax) {
268 // Overrun max buffer
269 bufLen = bufIncrement;
270 parseState = FindStart;
271
272 element = buf;
273 parsePos = buf;
274 inputEnd = buf;
275 }
276
277 buf = new char[bufLen];
278 std::copy(element, inputEnd, buf);
279
280 parsePos = buf + (parsePos - element);
281 inputEnd = buf + (inputEnd - element);
282 for (AttributeList::iterator it = attribute.begin();
283 it != attribute.end(); ++it) {
284 it->first = buf + (it->first - element);
285 it->second = buf + (it->second - element);
286 }
287
288 delete[] element;
289 element = buf;
290 log_debug("increased buf to %zu", bufLen);
291 }
292 }
293
294 log_debug("empty");
295 return Element(0,0);
296 }
297
298 /////////////////////////////////////////////////////////////////////////////
299 /////////////////////////////////////////////////////////////////////////////
300 XmlParser::Element::Element(
301 const char *command, const XmlParser::AttributeList* attr):
302 command(command), attribute(attr)
303 {
304 }
305
306 /////////////////////////////////////////////////////////////////////////////
307 XmlParser::Element::operator bool() const
308 {
309 return command != 0;
310 }
311
312 /////////////////////////////////////////////////////////////////////////////
313 const char *XmlParser::Element::getCommand() const
314 {
315 return command;
316 }
317
318 /////////////////////////////////////////////////////////////////////////////
319 bool XmlParser::Element::find(const char *name, const char *&value) const
320 {
321 for (AttributeList::const_iterator it = attribute->begin();
322 it != attribute->end(); ++it) {
323 if (!strcasecmp(name, it->first)) {
324 value = it->second;
325 return true;
326 }
327 }
328
329 return false;
330 }
331
332 /////////////////////////////////////////////////////////////////////////////
333 bool XmlParser::Element::isEqual(const char *name, const char *s) const
334 {
335 const char *value;
336
337 if (find(name, value) and value)
338 return !strcasecmp(value, s);
339
340 return false;
341 }
342
343 /////////////////////////////////////////////////////////////////////////////
344 bool XmlParser::Element::isTrue(const char *name) const
345 {
346 const char *value;
347
348 if (!(find(name, value)))
349 return false;
350
351 // Binary attribute, e.g <xsad sync>
352 if (!value)
353 return true;
354
355 size_t len = strlen(value);
356
357 // Binary attribute, e.g <xsad sync=1>
358 if (len == 1)
359 return *value == '1';
360
361 // Binary attribute, e.g <xsad sync="true">
362 if (len == 4)
363 return !strncasecmp(value, "true", 4);
364
365 // Binary attribute, e.g <xsad sync='on'/>
366 if (len == 2)
367 return !strncasecmp(value, "on", 2);
368
369 return false;
370 }
371
372 /////////////////////////////////////////////////////////////////////////////
373 bool XmlParser::Element::getString(const char *name, std::string &s) const
374 {
375 const char *value;
376
377 s.clear();
378
379 if (!(find(name, value) and value))
380 return false;
381
382 const char *pptr, *eptr = value + strlen(value);
383 while ((pptr = std::find(value, eptr, '&')) != eptr) {
384 // FIXME: maybe also check for escape char, e.g. \" ??
385 // this is difficult, because the quote character is not available
386 // here any more :(
387
388 s.append(value, pptr - value);
389 size_t len = eptr - pptr;
390 if (len >= 4 and !strncmp(pptr, "&gt;", 4)) {
391 s.append(1, '>');
392 value = pptr + 4;
393 }
394 else if (len >= 4 and !strncmp(pptr, "&lt;", 4)) {
395 s.append(1, '<');
396 value = pptr + 4;
397 }
398 else if (len >= 5 and !strncmp(pptr, "&amp;", 5)) {
399 s.append(1, '&');
400 value = pptr + 5;
401 }
402 else if (len >= 6 and !strncmp(pptr, "&quot;", 6)) {
403 s.append(1, '"');
404 value = pptr + 6;
405 }
406 else if (len >= 6 and !strncmp(pptr, "&apos;", 6)) {
407 s.append(1, '\'');
408 value = pptr + 6;
409 }
410 else {
411 s.append(1, '&');
412 value = pptr + 1;
413 }
414 }
415
416 s.append(value, eptr - value);
417 return true;
418 }
419
420 /////////////////////////////////////////////////////////////////////////////
421 bool XmlParser::Element::getUnsigned(const char *name, unsigned int &i) const
422 {
423 const char *value;
424
425 if (!(find(name, value)) or !value)
426 return false;
427
428 i = strtoul(value, 0, 0);
429 return true;
430 }
431
432 /////////////////////////////////////////////////////////////////////////////
433 bool XmlParser::Element::getUnsignedList(const char *name,
434 std::list<unsigned int> &intList) const
435 {
436 const char *value;
437
438 if (!(find(name, value)) or !value)
439 return false;
440
441 std::istringstream is(value);
442 is.imbue(std::locale::classic());
443
444 while (is) {
445 unsigned int i;
446 char comma;
447
448 is >> i;
449 if (is)
450 intList.push_back(i);
451 is >> comma;
452 }
453
454 return true;
455 3 }
456