GCC Code Coverage Report


Directory: ./
File: pdserv/src/msrproto/XmlParser.cpp
Date: 2024-12-15 04:08:34
Exec Total Coverage
Lines: 155 195 79.5%
Branches: 127 229 55.5%

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 144 XmlParser::XmlParser(size_t bufMax): bufLenMax(bufMax)
39 {
40 144 parseState = FindElementStart;
41 144 buf = 0;
42 144 bufEnd = 0;
43 144 inputEnd = 0;
44 144 parsePos = 0;
45 144 name = 0;
46 144 }
47
48 /////////////////////////////////////////////////////////////////////////////
49 288 XmlParser::~XmlParser()
50 {
51
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 delete[] buf;
52 144 }
53
54 /////////////////////////////////////////////////////////////////////////////
55 8779 std::streamsize XmlParser::read(std::streambuf* sb)
56 {
57
2/2
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 8635 times.
8779 if (bufEnd == inputEnd) {
58
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 144 times.
144 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 144 times.
144 else if (bufEnd >= buf + bufLenMax) {
65 inputEnd = 0;
66 return 0;
67 }
68 else {
69 log_debug("allocate new buffer");
70 144 size_t bufLen = (bufEnd - buf) + bufIncrement;
71 144 char *newBuf = new char[bufLen];
72
73 144 moveData(newBuf);
74
75
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
144 delete[] buf;
76 144 buf = newBuf;
77 144 bufEnd = buf + bufLen;
78 }
79 }
80
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 8635 times.
✓ Branch 3 taken 817 times.
✓ Branch 4 taken 7818 times.
✓ Branch 7 taken 519 times.
✓ Branch 8 taken 298 times.
8635 else if (parseState == FindElementStart and parsePos == inputEnd) {
81 519 inputEnd = buf;
82 519 parsePos = buf;
83 }
84
85 8779 std::streamsize n = sb->sgetn(inputEnd, bufEnd - inputEnd);
86
2/2
✓ Branch 0 taken 8674 times.
✓ Branch 1 taken 105 times.
8779 if (n > 0)
87 8674 inputEnd += n;
88
89 // log_debug("string is %zi %zi %p %s", count, n, name, std::string(name, inputEnd - name).c_str());
90
91 8779 return n;
92 }
93
94 /////////////////////////////////////////////////////////////////////////////
95 144 void XmlParser::moveData(char* dst)
96 {
97 // Move data to new location
98 144 std::copy(name, const_cast<const char*>(inputEnd), dst);
99
100 // Update pointers
101 144 const ssize_t diff = dst - name;
102 144 parsePos += diff;
103 144 inputEnd += diff;
104 144 name += diff;
105 144 argument += diff;
106
107 144 for (AttributeList::iterator it = attribute.begin();
108
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 144 times.
144 it != attribute.end(); ++it) {
109 it->first += diff;
110 it->second += diff;
111 }
112 144 }
113
114 /////////////////////////////////////////////////////////////////////////////
115 bool XmlParser::invalid() const
116 {
117 return !inputEnd;
118 }
119
120 /////////////////////////////////////////////////////////////////////////////
121 11487 XmlParser::operator bool()
122 {
123
1/2
✓ Branch 2 taken 11487 times.
✗ Branch 3 not taken.
13696 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 11487 times.
✓ Branch 3 taken 1467 times.
✓ Branch 4 taken 2908 times.
✓ Branch 5 taken 2269 times.
✓ Branch 6 taken 4843 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
11487 switch (parseState) {
129 1467 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 1467 times.
✗ Branch 5 not taken.
1467 parsePos = std::find(parsePos, inputEnd, '<');
134
135
2/2
✓ Branch 2 taken 854 times.
✓ Branch 3 taken 613 times.
1467 if (parsePos + 2 >= inputEnd) {
136 // Smallest element needs at least 2 more characters,
137 // one for name and at least one for closing '>'
138 854 return false;
139 }
140
141 613 name = ++parsePos;
142
143
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 613 times.
613 if (!isalpha(*name)) {
144 // Name must start with an alpha character
145 break;
146 }
147
148 613 attribute.clear();
149
150 613 parseState = FindElementEnd;
151
152 /* FALLTHRU */
153
154 3521 case FindElementEnd:
155 // Clobber white space
156
2/2
✓ Branch 2 taken 1605 times.
✓ Branch 3 taken 2822 times.
5333 while (*parsePos == ' ') {
157 1605 *parsePos++ = '\0';
158
2/2
✓ Branch 2 taken 699 times.
✓ Branch 3 taken 906 times.
1605 if (parsePos == inputEnd)
159 699 return false;
160 }
161
162 // Element that ends in />
163
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2822 times.
2822 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 604 times.
✓ Branch 3 taken 2218 times.
2822 if (!isalpha(*parsePos)) {
178 604 parseState = FindElementStart;
179
1/2
✓ Branch 2 taken 604 times.
✗ Branch 3 not taken.
604 if (*parsePos == '>') {
180 604 *parsePos++ = '\0';
181
182 604 return true;
183 }
184 break;
185 }
186
187 2218 argument = parsePos;
188
189 2218 parseState = FindArgumentName;
190
191 /* FALLTHRU */
192
193 4487 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 12685 times.
✓ Branch 3 taken 3456 times.
27795 while (!strchr("= />", *parsePos))
197
2/2
✓ Branch 4 taken 1031 times.
✓ Branch 5 taken 11654 times.
12685 if (++parsePos == inputEnd)
198 1031 return false;
199
200
2/2
✓ Branch 2 taken 613 times.
✓ Branch 3 taken 2843 times.
3456 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 613 times.
✗ Branch 5 not taken.
1226 attribute.push_back(
204
1/2
✓ Branch 4 taken 613 times.
✗ Branch 5 not taken.
1226 std::make_pair(argument, (const char*)0));
205
206 613 parseState = FindElementEnd;
207 613 break;
208 }
209
210 // parsePos points to the '='
211
212 // Need at least one following character to continue
213
2/2
✓ Branch 2 taken 260 times.
✓ Branch 3 taken 2583 times.
2843 if (parsePos + 1 >= inputEnd)
214 260 return false;
215
216 2583 quote = parsePos[1];
217
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2583 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2583 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 978 times.
✓ Branch 3 taken 1605 times.
2583 else if (parsePos + 2 >= inputEnd)
225 // Quoted argument value. Need at least 2 following chars
226 // to continue
227 978 return false;
228
229 1605 *parsePos = '\0'; // Clobber '='
230
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));
231 1605 parsePos += 2;
232 1605 parseState = FindQuotedArgumentValue;
233
234 /* FALLTHRU */
235
236 6448 case FindQuotedArgumentValue:
237 6448 parsePos = std::find(parsePos, inputEnd, quote);
238
2/2
✓ Branch 2 taken 4852 times.
✓ Branch 3 taken 1596 times.
6448 if (parsePos + 1 >= inputEnd)
239 4852 return false;
240
241 1596 *parsePos++ = '\0';
242 1596 parseState = FindElementEnd;
243
244 /* FALLTHRU */
245
246 1596 case FindArgumentValue:
247
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 != '>') {
248 if (++parsePos == inputEnd)
249 return false;
250 }
251
252 1596 parseState = FindElementEnd;
253 1596 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 604 const char *XmlParser::tag() const
276 {
277 604 return name;
278 }
279
280 /////////////////////////////////////////////////////////////////////////////
281 3017 bool XmlParser::find(const char *name, const char **value) const
282 {
283
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();
284
2/2
✓ Branch 6 taken 10214 times.
✓ Branch 7 taken 1376 times.
11590 it != attribute.end(); ++it) {
285
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)) {
286
2/2
✓ Branch 0 taken 1502 times.
✓ Branch 1 taken 139 times.
1641 if (value)
287 1502 *value = it->second;
288 1641 return true;
289 }
290 }
291
292 1376 return false;
293 }
294
295 /////////////////////////////////////////////////////////////////////////////
296 183 bool XmlParser::isEqual(const char *name, const char *s) const
297 {
298 183 const char *value;
299
300
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)
301
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);
302
303 return false;
304 }
305
306 /////////////////////////////////////////////////////////////////////////////
307 688 bool XmlParser::isTrue(const char *name) const
308 {
309 688 bool val;
310
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;
311 }
312
313 /////////////////////////////////////////////////////////////////////////////
314 837 bool XmlParser::getBool(const char *name, bool* val) const
315 {
316 837 const char *str;
317
318
2/2
✓ Branch 2 taken 713 times.
✓ Branch 3 taken 124 times.
837 if (!(find(name, &str)))
319 713 return false;
320
321 // Binary attribute, e.g <xsad sync>
322
1/4
✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
124 *val = !(str
323
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 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 124 return true;
328 }
329
330 /////////////////////////////////////////////////////////////////////////////
331 1279 bool XmlParser::getString(const char *name, std::string &s) const
332 {
333 1279 const char *value;
334
335 1279 s.clear();
336
337
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))
338 486 return false;
339
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 793 times.
793 const char *pptr, *eptr = value + strlen(value);
341
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) {
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 793 times.
✗ Branch 3 not taken.
793 s.append(value, eptr - value);
375 793 return true;
376 }
377
378 /////////////////////////////////////////////////////////////////////////////
379 354 bool XmlParser::getUnsigned(const char *name, unsigned int &i) const
380 {
381 354 const char *value;
382
383
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)
384 108 return false;
385
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 246 times.
246 i = strtoul(value, 0, 0);
387 246 return true;
388 }
389
390 /////////////////////////////////////////////////////////////////////////////
391 44 bool XmlParser::getUnsignedList(const char *name,
392 std::list<unsigned int> &intList) const
393 {
394 44 const char *value;
395
396
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)
397 return false;
398
399
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);
400
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());
401
402
3/4
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 44 times.
88 while (is) {
403 44 unsigned int i;
404 44 char comma;
405
406
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 is >> i;
407
2/4
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
44 if (is)
408
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 intList.push_back(i);
409
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 is >> comma;
410 }
411
412 44 return true;
413 }
414