GCC Code Coverage Report


Directory: ./
File: pdserv/src/msrproto/XmlParser.cpp
Date: 2026-05-10 04:09:05
Exec Total Coverage
Lines: 155 195 79.5%
Branches: 127 229 55.5%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright 2010 Richard Hacker (lerichi at gmx dot net)
4 *
5 * This file is part of the pdserv library.
6 *
7 * The pdserv library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation, either version 3 of the License, or (at
10 * your option) any later version.
11 *
12 * The pdserv library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ****************************************************************************/
21
22 #include "XmlParser.h"
23 #include "Session.h"
24 #include "../Debug.h"
25
26 #include <algorithm>
27 #include <cstring>
28 #include <sstream>
29
30 #undef log_debug
31 #define log_debug(...)
32
33 using namespace MsrProto;
34
35 /////////////////////////////////////////////////////////////////////////////
36 145 XmlParser::XmlParser(size_t bufMax): bufLenMax(bufMax)
37 {
38 145 parseState = FindElementStart;
39 145 buf = 0;
40 145 bufEnd = 0;
41 145 inputEnd = 0;
42 145 parsePos = 0;
43 145 name = 0;
44 145 }
45
46 /////////////////////////////////////////////////////////////////////////////
47 290 XmlParser::~XmlParser()
48 {
49
1/2
✓ Branch 1 taken 145 times.
✗ Branch 2 not taken.
145 delete[] buf;
50 145 }
51
52 /////////////////////////////////////////////////////////////////////////////
53 7963 std::streamsize XmlParser::read(std::streambuf* sb)
54 {
55
2/2
✓ Branch 2 taken 145 times.
✓ Branch 3 taken 7818 times.
7963 if (bufEnd == inputEnd) {
56
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 145 times.
145 if (name > buf + 1) {
57 // Name is not the second character in the buffer.
58 log_debug("shift up data");
59
60 moveData(buf);
61 }
62
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 145 times.
145 else if (bufEnd >= buf + bufLenMax) {
63 inputEnd = 0;
64 return 0;
65 }
66 else {
67 log_debug("allocate new buffer");
68 145 size_t bufLen = (bufEnd - buf) + bufIncrement;
69 145 char *newBuf = new char[bufLen];
70
71 145 moveData(newBuf);
72
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 145 times.
145 delete[] buf;
74 145 buf = newBuf;
75 145 bufEnd = buf + bufLen;
76 }
77 }
78
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 7818 times.
✓ Branch 3 taken 807 times.
✓ Branch 4 taken 7011 times.
✓ Branch 7 taken 515 times.
✓ Branch 8 taken 292 times.
7818 else if (parseState == FindElementStart and parsePos == inputEnd) {
79 515 inputEnd = buf;
80 515 parsePos = buf;
81 }
82
83 7963 std::streamsize n = sb->sgetn(inputEnd, bufEnd - inputEnd);
84
2/2
✓ Branch 0 taken 7857 times.
✓ Branch 1 taken 106 times.
7963 if (n > 0)
85 7857 inputEnd += n;
86
87 7963 return n;
88 }
89
90 /////////////////////////////////////////////////////////////////////////////
91 145 void XmlParser::moveData(char* dst)
92 {
93 // Move data to new location
94 145 std::copy(name, const_cast<const char*>(inputEnd), dst);
95
96 // Update pointers
97 145 const ssize_t diff = dst - name;
98 145 parsePos += diff;
99 145 inputEnd += diff;
100 145 name += diff;
101 145 argument += diff;
102
103 145 for (AttributeList::iterator it = attribute.begin();
104
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 145 times.
145 it != attribute.end(); ++it) {
105 it->first += diff;
106 it->second += diff;
107 }
108 145 }
109
110 /////////////////////////////////////////////////////////////////////////////
111 bool XmlParser::invalid() const
112 {
113 return !inputEnd;
114 }
115
116 /////////////////////////////////////////////////////////////////////////////
117 10691 XmlParser::operator bool()
118 {
119
1/2
✓ Branch 2 taken 10691 times.
✗ Branch 3 not taken.
12917 while (parsePos < inputEnd) {
120 // log_debug("parsepos=%zi state=%i \"%s\"",
121 // parsePos - buf, parseState,
122 // std::string(name, inputEnd - name).c_str()
123 // );
124
5/9
✗ Branch 1 not taken.
✓ Branch 2 taken 10691 times.
✓ Branch 3 taken 1461 times.
✓ Branch 4 taken 2857 times.
✓ Branch 5 taken 2018 times.
✓ Branch 6 taken 4355 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
10691 switch (parseState) {
125 1461 case FindElementStart:
126 // At the end of this state, name set up correctly
127 // and there is at least one valid character following name
128
129
1/2
✓ Branch 4 taken 1461 times.
✗ Branch 5 not taken.
1461 parsePos = std::find(parsePos, inputEnd, '<');
130
131
2/2
✓ Branch 2 taken 844 times.
✓ Branch 3 taken 617 times.
1461 if (parsePos + 2 >= inputEnd) {
132 // Smallest element needs at least 2 more characters,
133 // one for name and at least one for closing '>'
134 844 return false;
135 }
136
137 617 name = ++parsePos;
138
139
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 617 times.
617 if (!isalpha(*name)) {
140 // Name must start with an alpha character
141 break;
142 }
143
144 617 attribute.clear();
145
146 617 parseState = FindElementEnd;
147
148 /* FALLTHRU */
149
150 3474 case FindElementEnd:
151 // Clobber white space
152
2/2
✓ Branch 2 taken 1618 times.
✓ Branch 3 taken 2843 times.
5448 while (*parsePos == ' ') {
153 1618 *parsePos++ = '\0';
154
2/2
✓ Branch 2 taken 631 times.
✓ Branch 3 taken 987 times.
1618 if (parsePos == inputEnd)
155 631 return false;
156 }
157
158 // Element that ends in />
159
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2843 times.
2843 if (*parsePos == '/') {
160 if (parsePos + 1 >= inputEnd)
161 return false;
162 *parsePos++ = '\0';
163
164 parseState = FindElementStart;
165
166 if (*parsePos++ == '>') {
167 return true;
168 }
169 break;
170 }
171
172 // Element that ends in >
173
2/2
✓ Branch 2 taken 608 times.
✓ Branch 3 taken 2235 times.
2843 if (!isalpha(*parsePos)) {
174 608 parseState = FindElementStart;
175
1/2
✓ Branch 2 taken 608 times.
✗ Branch 3 not taken.
608 if (*parsePos == '>') {
176 608 *parsePos++ = '\0';
177
178 608 return true;
179 }
180 break;
181 }
182
183 2235 argument = parsePos;
184
185 2235 parseState = FindArgumentName;
186
187 /* FALLTHRU */
188
189 4253 case FindArgumentName:
190 // In this state, the argument name is searched for. It is
191 // terminated by a '=', ' ', '/' or '>'
192
2/2
✓ Branch 2 taken 12763 times.
✓ Branch 3 taken 3344 times.
27961 while (!strchr("= />", *parsePos))
193
2/2
✓ Branch 4 taken 909 times.
✓ Branch 5 taken 11854 times.
12763 if (++parsePos == inputEnd)
194 909 return false;
195
196
2/2
✓ Branch 2 taken 617 times.
✓ Branch 3 taken 2727 times.
3344 if (*parsePos != '=') {
197 // Found an attribute without a value. Consider it to
198 // be a boolean which evaluates to true
199
1/2
✓ Branch 4 taken 617 times.
✗ Branch 5 not taken.
1234 attribute.push_back(
200
1/2
✓ Branch 4 taken 617 times.
✗ Branch 5 not taken.
1234 std::make_pair(argument, (const char*)0));
201
202 617 parseState = FindElementEnd;
203 617 break;
204 }
205
206 // parsePos points to the '='
207
208 // Need at least one following character to continue
209
2/2
✓ Branch 2 taken 223 times.
✓ Branch 3 taken 2504 times.
2727 if (parsePos + 1 >= inputEnd)
210 223 return false;
211
212 2504 quote = parsePos[1];
213
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2504 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2504 if (quote != '"' and quote != '\'') {
214 // Argument value is not surrounded with quotes
215 *parsePos++ = '\0';
216 attribute.push_back(std::make_pair(argument, parsePos++));
217 parseState = FindArgumentValue;
218 break;
219 }
220
2/2
✓ Branch 2 taken 886 times.
✓ Branch 3 taken 1618 times.
2504 else if (parsePos + 2 >= inputEnd)
221 // Quoted argument value. Need at least 2 following chars
222 // to continue
223 886 return false;
224
225 1618 *parsePos = '\0'; // Clobber '='
226
2/4
✓ Branch 8 taken 1618 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 1618 times.
✗ Branch 15 not taken.
1618 attribute.push_back(std::make_pair(argument, parsePos + 2));
227 1618 parsePos += 2;
228 1618 parseState = FindQuotedArgumentValue;
229
230 /* FALLTHRU */
231
232 5973 case FindQuotedArgumentValue:
233 5973 parsePos = std::find(parsePos, inputEnd, quote);
234
2/2
✓ Branch 2 taken 4364 times.
✓ Branch 3 taken 1609 times.
5973 if (parsePos + 1 >= inputEnd)
235 4364 return false;
236
237 1609 *parsePos++ = '\0';
238 1609 parseState = FindElementEnd;
239
240 /* FALLTHRU */
241
242 1609 case FindArgumentValue:
243
3/4
✓ Branch 2 taken 570 times.
✓ Branch 3 taken 1039 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 570 times.
1609 while (*parsePos != ' ' and *parsePos != '>') {
244 if (++parsePos == inputEnd)
245 return false;
246 }
247
248 1609 parseState = FindElementEnd;
249 1609 break;
250
251 case FindNameEnd:
252 // At the end of this state, name is terminated with a
253 // closing \0
254
255 // Name is any set of characters not in " />"
256 while (!strchr(" />", *parsePos)) {
257 if (++parsePos == inputEnd)
258 return false;
259 }
260
261 parseState = FindElementEnd;
262
263 break;
264 }
265 }
266
267 return false;
268 }
269
270 /////////////////////////////////////////////////////////////////////////////
271 608 const char *XmlParser::tag() const
272 {
273 608 return name;
274 }
275
276 /////////////////////////////////////////////////////////////////////////////
277 3037 bool XmlParser::find(const char *name, const char **value) const
278 {
279
4/4
✓ Branch 4 taken 1381 times.
✓ Branch 5 taken 1656 times.
✓ Branch 7 taken 1381 times.
✓ Branch 8 taken 1656 times.
11666 for (AttributeList::const_iterator it = attribute.begin();
280
2/2
✓ Branch 6 taken 10285 times.
✓ Branch 7 taken 1381 times.
11666 it != attribute.end(); ++it) {
281
4/6
✗ Branch 2 not taken.
✓ Branch 3 taken 10285 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10285 times.
✓ Branch 6 taken 1656 times.
✓ Branch 7 taken 8629 times.
10285 if (!strcasecmp(name, it->first)) {
282
2/2
✓ Branch 0 taken 1516 times.
✓ Branch 1 taken 140 times.
1656 if (value)
283 1516 *value = it->second;
284 1656 return true;
285 }
286 }
287
288 1381 return false;
289 }
290
291 /////////////////////////////////////////////////////////////////////////////
292 184 bool XmlParser::isEqual(const char *name, const char *s) const
293 {
294 184 const char *value;
295
296
3/6
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 184 times.
✗ Branch 7 not taken.
184 if (find(name, &value) and value)
297
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 184 times.
184 return !strcasecmp(value, s);
298
299 return false;
300 }
301
302 /////////////////////////////////////////////////////////////////////////////
303 693 bool XmlParser::isTrue(const char *name) const
304 {
305 693 bool val;
306
4/6
✓ Branch 2 taken 117 times.
✓ Branch 3 taken 576 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 117 times.
✓ Branch 6 taken 117 times.
✗ Branch 7 not taken.
693 return getBool(name, &val) and val;
307 }
308
309 /////////////////////////////////////////////////////////////////////////////
310 843 bool XmlParser::getBool(const char *name, bool* val) const
311 {
312 843 const char *str;
313
314
2/2
✓ Branch 2 taken 716 times.
✓ Branch 3 taken 127 times.
843 if (!(find(name, &str)))
315 716 return false;
316
317 // Binary attribute, e.g <xsad sync>
318
1/4
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
127 *val = !(str
319
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 127 times.
254 and strcasecmp(str, "1") // Binary attr., e.g <xsad sync="1">
320 and strcasecmp(str, "true") // Binary attr., e.g sync="true">
321 and strcasecmp(str, "on")); // Binary attr., e.g <xsad sync="on">
322
323 127 return true;
324 }
325
326 /////////////////////////////////////////////////////////////////////////////
327 1290 bool XmlParser::getString(const char *name, std::string &s) const
328 {
329 1290 const char *value;
330
331 1290 s.clear();
332
333
5/6
✓ Branch 2 taken 802 times.
✓ Branch 3 taken 488 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 802 times.
✓ Branch 6 taken 488 times.
✓ Branch 7 taken 802 times.
1290 if (!(find(name, &value) and value))
334 488 return false;
335
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 802 times.
802 const char *pptr, *eptr = value + strlen(value);
337
3/4
✓ Branch 2 taken 856 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 54 times.
✓ Branch 6 taken 802 times.
910 while ((pptr = std::find(value, eptr, '&')) != eptr) {
338 // FIXME: maybe also check for escape char, e.g. \" ??
339 // this is difficult, because the quote character is not available
340 // here any more :(
341
342
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
54 s.append(value, pptr - value);
343 54 size_t len = eptr - pptr;
344
3/6
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 54 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 54 times.
54 if (len >= 4 and !strncmp(pptr, "&gt;", 4)) {
345 s.append(1, '>');
346 value = pptr + 4;
347 }
348
4/6
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 54 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 36 times.
54 else if (len >= 4 and !strncmp(pptr, "&lt;", 4)) {
349
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '<');
350 18 value = pptr + 4;
351 }
352
4/6
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 18 times.
36 else if (len >= 5 and !strncmp(pptr, "&amp;", 5)) {
353
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '&');
354 18 value = pptr + 5;
355 }
356
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 else if (len >= 6 and !strncmp(pptr, "&quot;", 6)) {
357
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '"');
358 18 value = pptr + 6;
359 }
360 else if (len >= 6 and !strncmp(pptr, "&apos;", 6)) {
361 s.append(1, '\'');
362 value = pptr + 6;
363 }
364 else {
365 s.append(1, '&');
366 value = pptr + 1;
367 }
368 }
369
370
1/2
✓ Branch 2 taken 802 times.
✗ Branch 3 not taken.
802 s.append(value, eptr - value);
371 802 return true;
372 }
373
374 /////////////////////////////////////////////////////////////////////////////
375 355 bool XmlParser::getUnsigned(const char *name, unsigned int &i) const
376 {
377 355 const char *value;
378
379
5/6
✓ Branch 2 taken 247 times.
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 247 times.
✓ Branch 6 taken 108 times.
✓ Branch 7 taken 247 times.
355 if (!(find(name, &value)) or !value)
380 108 return false;
381
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 247 times.
247 i = strtoul(value, 0, 0);
383 247 return true;
384 }
385
386 /////////////////////////////////////////////////////////////////////////////
387 44 bool XmlParser::getUnsignedList(const char *name,
388 std::list<unsigned int> &intList) const
389 {
390 44 const char *value;
391
392
3/6
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 44 times.
44 if (!(find(name, &value)) or !value)
393 return false;
394
395
2/4
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 44 times.
✗ Branch 10 not taken.
88 std::istringstream is(value);
396
2/4
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
44 is.imbue(std::locale::classic());
397
398
3/4
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 44 times.
88 while (is) {
399 44 unsigned int i;
400 44 char comma;
401
402
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 is >> i;
403
2/4
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
44 if (is)
404
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 intList.push_back(i);
405
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 is >> comma;
406 }
407
408 44 return true;
409 }
410