GCC Code Coverage Report


Directory: ./
File: pdserv/src/msrproto/XmlParser.cpp
Date: 2023-11-12 04:06:57
Exec Total Coverage
Lines: 155 195 79.5%
Branches: 133 241 55.2%

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 120 XmlParser::XmlParser(size_t bufMax): bufLenMax(bufMax)
39 {
40 120 parseState = FindElementStart;
41 120 buf = 0;
42 120 bufEnd = 0;
43 120 inputEnd = 0;
44 120 parsePos = 0;
45 120 name = 0;
46 120 }
47
48 /////////////////////////////////////////////////////////////////////////////
49 240 XmlParser::~XmlParser()
50 {
51
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 delete[] buf;
52 120 }
53
54 /////////////////////////////////////////////////////////////////////////////
55 6134 std::streamsize XmlParser::read(std::streambuf* sb)
56 {
57
2/2
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 6014 times.
6134 if (bufEnd == inputEnd) {
58
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 120 times.
120 if (name > buf + 1) {
59 // Name is not the second character in the buffer.
60 log_debug("shift up data");
61
62 moveData(buf);
63 }
64
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 120 times.
120 else if (bufEnd >= buf + bufLenMax) {
65 inputEnd = 0;
66 return 0;
67 }
68 else {
69 log_debug("allocate new buffer");
70 120 size_t bufLen = (bufEnd - buf) + bufIncrement;
71 120 char *newBuf = new char[bufLen];
72
73 120 moveData(newBuf);
74
75
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 120 times.
120 delete[] buf;
76 120 buf = newBuf;
77 120 bufEnd = buf + bufLen;
78 }
79 }
80
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6014 times.
✓ Branch 3 taken 612 times.
✓ Branch 4 taken 5402 times.
✓ Branch 7 taken 401 times.
✓ Branch 8 taken 211 times.
6014 else if (parseState == FindElementStart and parsePos == inputEnd) {
81 401 inputEnd = buf;
82 401 parsePos = buf;
83 }
84
85 6134 std::streamsize n = sb->sgetn(inputEnd, bufEnd - inputEnd);
86
2/2
✓ Branch 0 taken 6041 times.
✓ Branch 1 taken 93 times.
6134 if (n > 0)
87 6041 inputEnd += n;
88
89 // log_debug("string is %zi %zi %p %s", count, n, name, std::string(name, inputEnd - name).c_str());
90
91 6134 return n;
92 }
93
94 /////////////////////////////////////////////////////////////////////////////
95 120 void XmlParser::moveData(char* dst)
96 {
97 // Move data to new location
98 120 std::copy(name, const_cast<const char*>(inputEnd), dst);
99
100 // Update pointers
101 120 const ssize_t diff = dst - name;
102 120 parsePos += diff;
103 120 inputEnd += diff;
104 120 name += diff;
105 120 argument += diff;
106
107 120 for (AttributeList::iterator it = attribute.begin();
108
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 120 times.
120 it != attribute.end(); ++it) {
109 it->first += diff;
110 it->second += diff;
111 }
112 120 }
113
114 /////////////////////////////////////////////////////////////////////////////
115 bool XmlParser::invalid() const
116 {
117 return !inputEnd;
118 }
119
120 /////////////////////////////////////////////////////////////////////////////
121 8162 XmlParser::operator bool()
122 {
123
1/2
✓ Branch 2 taken 8162 times.
✗ Branch 3 not taken.
9811 while (parsePos < inputEnd) {
124 // log_debug("parsepos=%zi state=%i \"%s\"",
125 // parsePos - buf, parseState,
126 // std::string(name, inputEnd - name).c_str()
127 // );
128
5/9
✗ Branch 1 not taken.
✓ Branch 2 taken 8162 times.
✓ Branch 3 taken 1118 times.
✓ Branch 4 taken 2091 times.
✓ Branch 5 taken 1478 times.
✓ Branch 6 taken 3475 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
8162 switch (parseState) {
129 1118 case FindElementStart:
130 // At the end of this state, name set up correctly
131 // and there is at least one valid character following name
132
133
1/2
✓ Branch 4 taken 1118 times.
✗ Branch 5 not taken.
1118 parsePos = std::find(parsePos, inputEnd, '<');
134
135
2/2
✓ Branch 2 taken 637 times.
✓ Branch 3 taken 481 times.
1118 if (parsePos + 2 >= inputEnd) {
136 // Smallest element needs at least 2 more characters,
137 // one for name and at least one for closing '>'
138 637 return false;
139 }
140
141 481 name = ++parsePos;
142
143
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 481 times.
481 if (!isalpha(*name)) {
144 // Name must start with an alpha character
145 break;
146 }
147
148 481 attribute.clear();
149
150 481 parseState = FindElementEnd;
151
152 /* FALLTHRU */
153
154 2572 case FindElementEnd:
155 // Clobber white space
156
2/2
✓ Branch 2 taken 1177 times.
✓ Branch 3 taken 2130 times.
4042 while (*parsePos == ' ') {
157 1177 *parsePos++ = '\0';
158
2/2
✓ Branch 2 taken 442 times.
✓ Branch 3 taken 735 times.
1177 if (parsePos == inputEnd)
159 442 return false;
160 }
161
162 // Element that ends in />
163
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2130 times.
2130 if (*parsePos == '/') {
164 if (parsePos + 1 >= inputEnd)
165 return false;
166 *parsePos++ = '\0';
167
168 parseState = FindElementStart;
169
170 if (*parsePos++ == '>') {
171 return true;
172 }
173 break;
174 }
175
176 // Element that ends in >
177
2/2
✓ Branch 2 taken 472 times.
✓ Branch 3 taken 1658 times.
2130 if (!isalpha(*parsePos)) {
178 472 parseState = FindElementStart;
179
1/2
✓ Branch 2 taken 472 times.
✗ Branch 3 not taken.
472 if (*parsePos == '>') {
180 472 *parsePos++ = '\0';
181
182 472 return true;
183 }
184 break;
185 }
186
187 1658 argument = parsePos;
188
189 1658 parseState = FindArgumentName;
190
191 /* FALLTHRU */
192
193 3136 case FindArgumentName:
194 // In this state, the argument name is searched for. It is
195 // terminated by a '=', ' ', '/' or '>'
196
2/2
✓ Branch 2 taken 9641 times.
✓ Branch 3 taken 2469 times.
21084 while (!strchr("= />", *parsePos))
197
2/2
✓ Branch 4 taken 667 times.
✓ Branch 5 taken 8974 times.
9641 if (++parsePos == inputEnd)
198 667 return false;
199
200
2/2
✓ Branch 2 taken 481 times.
✓ Branch 3 taken 1988 times.
2469 if (*parsePos != '=') {
201 // Found an attribute without a value. Consider it to
202 // be a boolean which evaluates to true
203
1/2
✓ Branch 4 taken 481 times.
✗ Branch 5 not taken.
962 attribute.push_back(
204
1/2
✓ Branch 4 taken 481 times.
✗ Branch 5 not taken.
962 std::make_pair(argument, (const char*)0));
205
206 481 parseState = FindElementEnd;
207 481 break;
208 }
209
210 // parsePos points to the '='
211
212 // Need at least one following character to continue
213
2/2
✓ Branch 2 taken 196 times.
✓ Branch 3 taken 1792 times.
1988 if (parsePos + 1 >= inputEnd)
214 196 return false;
215
216 1792 quote = parsePos[1];
217
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1792 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1792 if (quote != '"' and quote != '\'') {
218 // Argument value is not surrounded with quotes
219 *parsePos++ = '\0';
220 attribute.push_back(std::make_pair(argument, parsePos++));
221 parseState = FindArgumentValue;
222 break;
223 }
224
2/2
✓ Branch 2 taken 615 times.
✓ Branch 3 taken 1177 times.
1792 else if (parsePos + 2 >= inputEnd)
225 // Quoted argument value. Need at least 2 following chars
226 // to continue
227 615 return false;
228
229 1177 *parsePos = '\0'; // Clobber '='
230
2/4
✓ Branch 8 taken 1177 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 1177 times.
✗ Branch 15 not taken.
1177 attribute.push_back(std::make_pair(argument, parsePos + 2));
231 1177 parsePos += 2;
232 1177 parseState = FindQuotedArgumentValue;
233
234 /* FALLTHRU */
235
236 4652 case FindQuotedArgumentValue:
237 4652 parsePos = std::find(parsePos, inputEnd, quote);
238
2/2
✓ Branch 2 taken 3484 times.
✓ Branch 3 taken 1168 times.
4652 if (parsePos + 1 >= inputEnd)
239 3484 return false;
240
241 1168 *parsePos++ = '\0';
242 1168 parseState = FindElementEnd;
243
244 /* FALLTHRU */
245
246 1168 case FindArgumentValue:
247
3/4
✓ Branch 2 taken 434 times.
✓ Branch 3 taken 734 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 434 times.
1168 while (*parsePos != ' ' and *parsePos != '>') {
248 if (++parsePos == inputEnd)
249 return false;
250 }
251
252 1168 parseState = FindElementEnd;
253 1168 break;
254
255 case FindNameEnd:
256 // At the end of this state, name is terminated with a
257 // closing \0
258
259 // Name is any set of characters not in " />"
260 while (!strchr(" />", *parsePos)) {
261 if (++parsePos == inputEnd)
262 return false;
263 }
264
265 parseState = FindElementEnd;
266
267 break;
268 }
269 }
270
271 return false;
272 }
273
274 /////////////////////////////////////////////////////////////////////////////
275 472 const char *XmlParser::tag() const
276 {
277 472 return name;
278 }
279
280 /////////////////////////////////////////////////////////////////////////////
281 2249 bool XmlParser::find(const char *name, const char **value) const
282 {
283
4/4
✓ Branch 4 taken 1036 times.
✓ Branch 5 taken 1213 times.
✓ Branch 7 taken 1036 times.
✓ Branch 8 taken 1213 times.
8178 for (AttributeList::const_iterator it = attribute.begin();
284
2/2
✓ Branch 6 taken 7142 times.
✓ Branch 7 taken 1036 times.
8178 it != attribute.end(); ++it) {
285
4/6
✗ Branch 2 not taken.
✓ Branch 3 taken 7142 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7142 times.
✓ Branch 6 taken 1213 times.
✓ Branch 7 taken 5929 times.
7142 if (!strcasecmp(name, it->first)) {
286
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 115 times.
1213 if (value)
287 1098 *value = it->second;
288 1213 return true;
289 }
290 }
291
292 1036 return false;
293 }
294
295 /////////////////////////////////////////////////////////////////////////////
296 127 bool XmlParser::isEqual(const char *name, const char *s) const
297 {
298 127 const char *value;
299
300
4/8
✓ Branch 2 taken 127 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 127 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 127 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 127 times.
✗ Branch 9 not taken.
127 if (find(name, &value) and value)
301
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 127 times.
127 return !strcasecmp(value, s);
302
303 return false;
304 }
305
306 /////////////////////////////////////////////////////////////////////////////
307 452 bool XmlParser::isTrue(const char *name) const
308 {
309 452 bool val;
310
5/8
✓ Branch 2 taken 452 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 354 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 98 times.
✓ Branch 8 taken 98 times.
✗ Branch 9 not taken.
452 return getBool(name, &val) and val;
311 }
312
313 /////////////////////////////////////////////////////////////////////////////
314 577 bool XmlParser::getBool(const char *name, bool* val) const
315 {
316 577 const char *str;
317
318
3/4
✓ Branch 2 taken 577 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 469 times.
✓ Branch 5 taken 108 times.
577 if (!(find(name, &str)))
319 469 return false;
320
321 // Binary attribute, e.g <xsad sync>
322
1/4
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
108 *val = !(str
323
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 108 times.
216 and strcasecmp(str, "1") // Binary attribute, e.g <xsad sync="1">
324 and strcasecmp(str, "true") // Binary attribute, e.g <xsad sync="true">
325 and strcasecmp(str, "on")); // Binary attribute, e.g <xsad sync="on">
326
327 108 return true;
328 }
329
330 /////////////////////////////////////////////////////////////////////////////
331 1035 bool XmlParser::getString(const char *name, std::string &s) const
332 {
333 1035 const char *value;
334
335 1035 s.clear();
336
337
6/8
✓ Branch 2 taken 1035 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 601 times.
✓ Branch 5 taken 434 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 601 times.
✓ Branch 8 taken 434 times.
✓ Branch 9 taken 601 times.
1035 if (!(find(name, &value) and value))
338 434 return false;
339
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 601 times.
601 const char *pptr, *eptr = value + strlen(value);
341
3/4
✓ Branch 2 taken 655 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 54 times.
✓ Branch 6 taken 601 times.
709 while ((pptr = std::find(value, eptr, '&')) != eptr) {
342 // FIXME: maybe also check for escape char, e.g. \" ??
343 // this is difficult, because the quote character is not available
344 // here any more :(
345
346
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
54 s.append(value, pptr - value);
347 54 size_t len = eptr - pptr;
348
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)) {
349 s.append(1, '>');
350 value = pptr + 4;
351 }
352
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)) {
353
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '<');
354 18 value = pptr + 4;
355 }
356
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)) {
357
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '&');
358 18 value = pptr + 5;
359 }
360
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)) {
361
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 s.append(1, '"');
362 18 value = pptr + 6;
363 }
364 else if (len >= 6 and !strncmp(pptr, "&apos;", 6)) {
365 s.append(1, '\'');
366 value = pptr + 6;
367 }
368 else {
369 s.append(1, '&');
370 value = pptr + 1;
371 }
372 }
373
374
1/2
✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
601 s.append(value, eptr - value);
375 601 return true;
376 }
377
378 /////////////////////////////////////////////////////////////////////////////
379 226 bool XmlParser::getUnsigned(const char *name, unsigned int &i) const
380 {
381 226 const char *value;
382
383
6/8
✓ Branch 2 taken 226 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 150 times.
✓ Branch 5 taken 76 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 150 times.
✓ Branch 8 taken 76 times.
✓ Branch 9 taken 150 times.
226 if (!(find(name, &value)) or !value)
384 76 return false;
385
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
150 i = strtoul(value, 0, 0);
387 150 return true;
388 }
389
390 /////////////////////////////////////////////////////////////////////////////
391 12 bool XmlParser::getUnsignedList(const char *name,
392 std::list<unsigned int> &intList) const
393 {
394 12 const char *value;
395
396
4/8
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
12 if (!(find(name, &value)) or !value)
397 return false;
398
399
2/4
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
24 std::istringstream is(value);
400
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
12 is.imbue(std::locale::classic());
401
402
3/4
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 12 times.
24 while (is) {
403 12 unsigned int i;
404 12 char comma;
405
406
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 is >> i;
407
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 if (is)
408
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 intList.push_back(i);
409
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 is >> comma;
410 }
411
412 12 return true;
413 }
414