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, ">", 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, "<", 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, "&", 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, """, 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, "'", 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 |