GCC Code Coverage Report


Directory: ./
File: pdserv-1.1.0/src/msrproto/XmlParser.cpp
Date: 2026-10-18 04:09:42
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