GCC Code Coverage Report


Directory: ./
File: pdserv/src/msrproto/XmlParser.cpp
Date: 2025-08-17 04:10:43
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 144 XmlParser::XmlParser(size_t bufMax): bufLenMax(bufMax)
37 {
38 144 parseState = FindElementStart;
39 144 buf = 0;
40 144 bufEnd = 0;
41 144 inputEnd = 0;
42 144 parsePos = 0;
43 144 name = 0;
44 144 }
45
46 /////////////////////////////////////////////////////////////////////////////
47 288 XmlParser::~XmlParser()
48 {
49
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 delete[] buf;
50 144 }
51
52 /////////////////////////////////////////////////////////////////////////////
53 8868 std::streamsize XmlParser::read(std::streambuf* sb)
54 {
55
2/2
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 8724 times.
8868 if (bufEnd == inputEnd) {
56
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 144 times.
144 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 144 times.
144 else if (bufEnd >= buf + bufLenMax) {
63 inputEnd = 0;
64 return 0;
65 }
66 else {
67 log_debug("allocate new buffer");
68 144 size_t bufLen = (bufEnd - buf) + bufIncrement;
69 144 char *newBuf = new char[bufLen];
70
71 144 moveData(newBuf);
72
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
144 delete[] buf;
74 144 buf = newBuf;
75 144 bufEnd = buf + bufLen;
76 }
77 }
78
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 8724 times.
✓ Branch 3 taken 846 times.
✓ Branch 4 taken 7878 times.
✓ Branch 7 taken 520 times.
✓ Branch 8 taken 326 times.
8724 else if (parseState == FindElementStart and parsePos == inputEnd) {
79 520 inputEnd = buf;
80 520 parsePos = buf;
81 }
82
83 8868 std::streamsize n = sb->sgetn(inputEnd, bufEnd - inputEnd);
84
2/2
✓ Branch 0 taken 8763 times.
✓ Branch 1 taken 105 times.
8868 if (n > 0)
85 8763 inputEnd += n;
86
87 8868 return n;
88 }
89
90 /////////////////////////////////////////////////////////////////////////////
91 144 void XmlParser::moveData(char* dst)
92 {
93 // Move data to new location
94 144 std::copy(name, const_cast<const char*>(inputEnd), dst);
95
96 // Update pointers
97 144 const ssize_t diff = dst - name;
98 144 parsePos += diff;
99 144 inputEnd += diff;
100 144 name += diff;
101 144 argument += diff;
102
103 144 for (AttributeList::iterator it = attribute.begin();
104
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 144 times.
144 it != attribute.end(); ++it) {
105 it->first += diff;
106 it->second += diff;
107 }
108 144 }
109
110 /////////////////////////////////////////////////////////////////////////////
111 bool XmlParser::invalid() const
112 {
113 return !inputEnd;
114 }
115
116 /////////////////////////////////////////////////////////////////////////////
117 11576 XmlParser::operator bool()
118 {
119
1/2
✓ Branch 2 taken 11576 times.
✗ Branch 3 not taken.
13785 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 11576 times.
✓ Branch 3 taken 1496 times.
✓ Branch 4 taken 2923 times.
✓ Branch 5 taken 2272 times.
✓ Branch 6 taken 4885 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
11576 switch (parseState) {
125 1496 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 1496 times.
✗ Branch 5 not taken.
1496 parsePos = std::find(parsePos, inputEnd, '<');
130
131
2/2
✓ Branch 2 taken 883 times.
✓ Branch 3 taken 613 times.
1496 if (parsePos + 2 >= inputEnd) {
132 // Smallest element needs at least 2 more characters,
133 // one for name and at least one for closing '>'
134 883 return false;
135 }
136
137 613 name = ++parsePos;
138
139
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 613 times.
613 if (!isalpha(*name)) {
140 // Name must start with an alpha character
141 break;
142 }
143
144 613 attribute.clear();
145
146 613 parseState = FindElementEnd;
147
148 /* FALLTHRU */
149
150 3536 case FindElementEnd:
151 // Clobber white space
152
2/2
✓ Branch 2 taken 1605 times.
✓ Branch 3 taken 2822 times.
5318 while (*parsePos == ' ') {
153 1605 *parsePos++ = '\0';
154
2/2
✓ Branch 2 taken 714 times.
✓ Branch 3 taken 891 times.
1605 if (parsePos == inputEnd)
155 714 return false;
156 }
157
158 // Element that ends in />
159
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2822 times.
2822 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 604 times.
✓ Branch 3 taken 2218 times.
2822 if (!isalpha(*parsePos)) {
174 604 parseState = FindElementStart;
175
1/2
✓ Branch 2 taken 604 times.
✗ Branch 3 not taken.
604 if (*parsePos == '>') {
176 604 *parsePos++ = '\0';
177
178 604 return true;
179 }
180 break;
181 }
182
183 2218 argument = parsePos;
184
185 2218 parseState = FindArgumentName;
186
187 /* FALLTHRU */
188
189 4490 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 12685 times.
✓ Branch 3 taken 3473 times.
27826 while (!strchr("= />", *parsePos))
193
2/2
✓ Branch 4 taken 1017 times.
✓ Branch 5 taken 11668 times.
12685 if (++parsePos == inputEnd)
194 1017 return false;
195
196
2/2
✓ Branch 2 taken 613 times.
✓ Branch 3 taken 2860 times.
3473 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 613 times.
✗ Branch 5 not taken.
1226 attribute.push_back(
200
1/2
✓ Branch 4 taken 613 times.
✗ Branch 5 not taken.
1226 std::make_pair(argument, (const char*)0));
201
202 613 parseState = FindElementEnd;
203 613 break;
204 }
205
206 // parsePos points to the '='
207
208 // Need at least one following character to continue
209
2/2
✓ Branch 2 taken 266 times.
✓ Branch 3 taken 2594 times.
2860 if (parsePos + 1 >= inputEnd)
210 266 return false;
211
212 2594 quote = parsePos[1];
213
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2594 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2594 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 989 times.
✓ Branch 3 taken 1605 times.
2594 else if (parsePos + 2 >= inputEnd)
221 // Quoted argument value. Need at least 2 following chars
222 // to continue
223 989 return false;
224
225 1605 *parsePos = '\0'; // Clobber '='
226
2/4
✓ Branch 8 taken 1605 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 1605 times.
✗ Branch 15 not taken.
1605 attribute.push_back(std::make_pair(argument, parsePos + 2));
227 1605 parsePos += 2;
228 1605 parseState = FindQuotedArgumentValue;
229
230 /* FALLTHRU */
231
232 6490 case FindQuotedArgumentValue:
233 6490 parsePos = std::find(parsePos, inputEnd, quote);
234
2/2
✓ Branch 2 taken 4894 times.
✓ Branch 3 taken 1596 times.
6490 if (parsePos + 1 >= inputEnd)
235 4894 return false;
236
237 1596 *parsePos++ = '\0';
238 1596 parseState = FindElementEnd;
239
240 /* FALLTHRU */
241
242 1596 case FindArgumentValue:
243
3/4
✓ Branch 2 taken 566 times.
✓ Branch 3 taken 1030 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 566 times.
1596 while (*parsePos != ' ' and *parsePos != '>') {
244 if (++parsePos == inputEnd)
245 return false;
246 }
247
248 1596 parseState = FindElementEnd;
249 1596 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 604 const char *XmlParser::tag() const
272 {
273 604 return name;
274 }
275
276 /////////////////////////////////////////////////////////////////////////////
277 3017 bool XmlParser::find(const char *name, const char **value) const
278 {
279
4/4
✓ Branch 4 taken 1376 times.
✓ Branch 5 taken 1641 times.
✓ Branch 7 taken 1376 times.
✓ Branch 8 taken 1641 times.
11590 for (AttributeList::const_iterator it = attribute.begin();
280
2/2
✓ Branch 6 taken 10214 times.
✓ Branch 7 taken 1376 times.
11590 it != attribute.end(); ++it) {
281
4/6
✗ Branch 2 not taken.
✓ Branch 3 taken 10214 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10214 times.
✓ Branch 6 taken 1641 times.
✓ Branch 7 taken 8573 times.
10214 if (!strcasecmp(name, it->first)) {
282
2/2
✓ Branch 0 taken 1502 times.
✓ Branch 1 taken 139 times.
1641 if (value)
283 1502 *value = it->second;
284 1641 return true;
285 }
286 }
287
288 1376 return false;
289 }
290
291 /////////////////////////////////////////////////////////////////////////////
292 183 bool XmlParser::isEqual(const char *name, const char *s) const
293 {
294 183 const char *value;
295
296
3/6
✓ Branch 2 taken 183 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 183 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 183 times.
✗ Branch 7 not taken.
183 if (find(name, &value) and value)
297
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 183 times.
183 return !strcasecmp(value, s);
298
299 return false;
300 }
301
302 /////////////////////////////////////////////////////////////////////////////
303 688 bool XmlParser::isTrue(const char *name) const
304 {
305 688 bool val;
306
4/6
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 574 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 114 times.
✓ Branch 6 taken 114 times.
✗ Branch 7 not taken.
688 return getBool(name, &val) and val;
307 }
308
309 /////////////////////////////////////////////////////////////////////////////
310 837 bool XmlParser::getBool(const char *name, bool* val) const
311 {
312 837 const char *str;
313
314
2/2
✓ Branch 2 taken 713 times.
✓ Branch 3 taken 124 times.
837 if (!(find(name, &str)))
315 713 return false;
316
317 // Binary attribute, e.g <xsad sync>
318
1/4
✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
124 *val = !(str
319
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 124 times.
248 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 124 return true;
324 }
325
326 /////////////////////////////////////////////////////////////////////////////
327 1279 bool XmlParser::getString(const char *name, std::string &s) const
328 {
329 1279 const char *value;
330
331 1279 s.clear();
332
333
5/6
✓ Branch 2 taken 793 times.
✓ Branch 3 taken 486 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 793 times.
✓ Branch 6 taken 486 times.
✓ Branch 7 taken 793 times.
1279 if (!(find(name, &value) and value))
334 486 return false;
335
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 793 times.
793 const char *pptr, *eptr = value + strlen(value);
337
3/4
✓ Branch 2 taken 847 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 54 times.
✓ Branch 6 taken 793 times.
901 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 793 times.
✗ Branch 3 not taken.
793 s.append(value, eptr - value);
371 793 return true;
372 }
373
374 /////////////////////////////////////////////////////////////////////////////
375 354 bool XmlParser::getUnsigned(const char *name, unsigned int &i) const
376 {
377 354 const char *value;
378
379
5/6
✓ Branch 2 taken 246 times.
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 246 times.
✓ Branch 6 taken 108 times.
✓ Branch 7 taken 246 times.
354 if (!(find(name, &value)) or !value)
380 108 return false;
381
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 246 times.
246 i = strtoul(value, 0, 0);
383 246 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