1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
3 | * |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation, either version 3 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "gdbmiresultparser.h" |
18 | |
19 | #include <QFileInfo> |
20 | #include <QList> |
21 | #include <QDebug> |
22 | |
23 | GDBMIResultParser::GDBMIResultParser() |
24 | { |
25 | mResultTypes.insert("-break-insert" ,GDBMIResultType::Breakpoint); |
26 | //mResultTypes.insert("BreakpointTable",GDBMIResultType::BreakpointTable); |
27 | mResultTypes.insert("-stack-list-frames" ,GDBMIResultType::FrameStack); |
28 | mResultTypes.insert("-stack-list-variables" , GDBMIResultType::LocalVariables); |
29 | //mResultTypes.insert("frame",GDBMIResultType::Frame); |
30 | mResultTypes.insert("-data-disassemble" ,GDBMIResultType::Disassembly); |
31 | mResultTypes.insert("-data-evaluate-expression" ,GDBMIResultType::Evaluation); |
32 | // mResultTypes.insert("register-names",GDBMIResultType::RegisterNames); |
33 | // mResultTypes.insert("register-values",GDBMIResultType::RegisterValues); |
34 | mResultTypes.insert("-data-read-memory" ,GDBMIResultType::Memory); |
35 | mResultTypes.insert("-data-list-register-names" ,GDBMIResultType::RegisterNames); |
36 | mResultTypes.insert("-data-list-register-values" ,GDBMIResultType::RegisterValues); |
37 | mResultTypes.insert("-var-create" ,GDBMIResultType::CreateVar); |
38 | mResultTypes.insert("-var-list-children" ,GDBMIResultType::ListVarChildren); |
39 | mResultTypes.insert("-var-update" ,GDBMIResultType::UpdateVarValue); |
40 | } |
41 | |
42 | bool GDBMIResultParser::parse(const QByteArray &record, const QString& command, GDBMIResultType &type, ParseObject& multiValues) |
43 | { |
44 | const char* p = record.data(); |
45 | bool result = parseMultiValues(p,multiValues); |
46 | if (!result) |
47 | return false; |
48 | // if (*p!=0) |
49 | // return false; |
50 | if (!mResultTypes.contains(command)) |
51 | return false; |
52 | type = mResultTypes[command]; |
53 | return true; |
54 | } |
55 | |
56 | bool GDBMIResultParser::parseAsyncResult(const QByteArray &record, QByteArray &result, ParseObject &multiValue) |
57 | { |
58 | const char* p =record.data(); |
59 | if (*p!='*') |
60 | return false; |
61 | p++; |
62 | const char* start=p; |
63 | while (*p && *p!=',') |
64 | p++; |
65 | result = QByteArray(start,p-start); |
66 | if (*p==0) |
67 | return true; |
68 | p++; |
69 | return parseMultiValues(p,multiValue); |
70 | } |
71 | |
72 | bool GDBMIResultParser::parseMultiValues(const char* p, ParseObject &multiValue) |
73 | { |
74 | while (*p) { |
75 | QByteArray propName; |
76 | ParseValue propValue; |
77 | bool result = parseNameAndValue(p,propName,propValue); |
78 | if (result) { |
79 | multiValue[propName]=propValue; |
80 | } else { |
81 | return false; |
82 | } |
83 | skipSpaces(p); |
84 | if (*p==0) |
85 | break; |
86 | if (*p!=',') |
87 | return false; |
88 | p++; //skip ',' |
89 | skipSpaces(p); |
90 | } |
91 | return true; |
92 | } |
93 | |
94 | bool GDBMIResultParser::parseNameAndValue(const char *&p, QByteArray &name, ParseValue &value) |
95 | { |
96 | skipSpaces(p); |
97 | const char* nameStart =p; |
98 | while (*p!=0 && isNameChar(*p)) { |
99 | p++; |
100 | } |
101 | if (*p==0) |
102 | return false; |
103 | name = QByteArray(nameStart,p-nameStart); |
104 | skipSpaces(p); |
105 | if (*p!='=') |
106 | return false; |
107 | p++; |
108 | return parseValue(p,value); |
109 | } |
110 | |
111 | bool GDBMIResultParser::parseValue(const char *&p, ParseValue &value) |
112 | { |
113 | skipSpaces(p); |
114 | bool result; |
115 | switch (*p) { |
116 | case '{': { |
117 | ParseObject obj; |
118 | result = parseObject(p,obj); |
119 | value = obj; |
120 | break; |
121 | } |
122 | case '[': { |
123 | QList<ParseValue> array; |
124 | result = parseArray(p,array); |
125 | value = array; |
126 | break; |
127 | } |
128 | case '"': { |
129 | QByteArray s; |
130 | result = parseStringValue(p,s); |
131 | value = s; |
132 | break; |
133 | } |
134 | default: |
135 | return false; |
136 | } |
137 | if (!result) |
138 | return false; |
139 | skipSpaces(p); |
140 | return true; |
141 | } |
142 | |
143 | bool GDBMIResultParser::parseStringValue(const char *&p, QByteArray& stringValue) |
144 | { |
145 | if (*p!='"') |
146 | return false; |
147 | p++; |
148 | stringValue.clear(); |
149 | while (*p!=0) { |
150 | if (*p == '"') { |
151 | break; |
152 | } else if (*p=='\\' && *(p+1)!=0) { |
153 | p++; |
154 | switch (*p) { |
155 | case '\'': |
156 | stringValue+=0x27; |
157 | p++; |
158 | break; |
159 | case '"': |
160 | stringValue+=0x22; |
161 | p++; |
162 | break; |
163 | case '?': |
164 | stringValue+=0x3f; |
165 | p++; |
166 | break; |
167 | case '\\': |
168 | stringValue+=0x5c; |
169 | p++; |
170 | break; |
171 | case 'a': |
172 | stringValue+=0x07; |
173 | p++; |
174 | break; |
175 | case 'b': |
176 | stringValue+=0x08; |
177 | p++; |
178 | break; |
179 | case 'f': |
180 | stringValue+=0x0c; |
181 | p++; |
182 | break; |
183 | case 'n': |
184 | stringValue+=0x0a; |
185 | p++; |
186 | break; |
187 | case 'r': |
188 | stringValue+=0x0d; |
189 | p++; |
190 | break; |
191 | case 't': |
192 | stringValue+=0x09; |
193 | p++; |
194 | break; |
195 | case 'v': |
196 | stringValue+=0x0b; |
197 | p++; |
198 | break; |
199 | case '0': |
200 | case '1': |
201 | case '2': |
202 | case '3': |
203 | case '4': |
204 | case '5': |
205 | case '6': |
206 | case '7': |
207 | { |
208 | int i=0; |
209 | for (i=0;i<3;i++) { |
210 | if (*(p+i)<'0' || *(p+i)>'7') |
211 | break; |
212 | } |
213 | QByteArray numStr(p,i); |
214 | bool ok; |
215 | unsigned char ch = numStr.toInt(&ok,8); |
216 | stringValue+=ch; |
217 | p+=i; |
218 | break; |
219 | } |
220 | } |
221 | } else { |
222 | stringValue+=*p; |
223 | p++; |
224 | } |
225 | } |
226 | if (*p=='"') { |
227 | p++; //skip '"' |
228 | return true; |
229 | } |
230 | return false; |
231 | } |
232 | |
233 | bool GDBMIResultParser::parseObject(const char *&p, ParseObject &obj) |
234 | { |
235 | if (*p!='{') |
236 | return false; |
237 | p++; |
238 | |
239 | if (*p!='}') { |
240 | while (*p!=0) { |
241 | QByteArray propName; |
242 | ParseValue propValue; |
243 | bool result = parseNameAndValue(p,propName,propValue); |
244 | if (result) { |
245 | obj[propName]=propValue; |
246 | } else { |
247 | return false; |
248 | } |
249 | skipSpaces(p); |
250 | if (*p=='}') |
251 | break; |
252 | if (*p!=',') { |
253 | return false; |
254 | } |
255 | p++; //skip ',' |
256 | skipSpaces(p); |
257 | } |
258 | } |
259 | if (*p=='}') { |
260 | p++; //skip '}' |
261 | return true; |
262 | } |
263 | return false; |
264 | } |
265 | |
266 | bool GDBMIResultParser::parseArray(const char *&p, QList<GDBMIResultParser::ParseValue> &array) |
267 | { |
268 | if (*p!='[') |
269 | return false; |
270 | p++; |
271 | if (*p!=']') { |
272 | while (*p!=0) { |
273 | skipSpaces(p); |
274 | if (*p=='{' || *p=='"' || *p=='[') { |
275 | ParseValue val; |
276 | bool result = parseValue(p,val); |
277 | if (result) { |
278 | array.append(val); |
279 | } else { |
280 | return false; |
281 | } |
282 | } else { |
283 | QByteArray name; |
284 | ParseValue val; |
285 | bool result = parseNameAndValue(p,name,val); |
286 | if (result) { |
287 | array.append(val); |
288 | } else { |
289 | return false; |
290 | } |
291 | } |
292 | skipSpaces(p); |
293 | if (*p==']') |
294 | break; |
295 | if (*p!=',') |
296 | return false; |
297 | p++; //skip ',' |
298 | skipSpaces(p); |
299 | } |
300 | } |
301 | if (*p==']') { |
302 | p++; //skip ']' |
303 | return true; |
304 | } |
305 | return false; |
306 | } |
307 | |
308 | bool GDBMIResultParser::isNameChar(char ch) |
309 | { |
310 | if (ch=='-') |
311 | return true; |
312 | if (ch=='_') |
313 | return true; |
314 | if (ch>='a' && ch<='z') |
315 | return true; |
316 | if (ch>='A' && ch<='Z') |
317 | return true; |
318 | return false; |
319 | } |
320 | |
321 | bool GDBMIResultParser::isSpaceChar(char ch) |
322 | { |
323 | switch(ch) { |
324 | case ' ': |
325 | case '\t': |
326 | return true; |
327 | } |
328 | return false; |
329 | } |
330 | |
331 | void GDBMIResultParser::skipSpaces(const char *&p) |
332 | { |
333 | while (*p!=0 && isSpaceChar(*p)) |
334 | p++; |
335 | } |
336 | |
337 | const QByteArray &GDBMIResultParser::ParseValue::value() const |
338 | { |
339 | return mValue; |
340 | } |
341 | |
342 | const QList<::GDBMIResultParser::ParseValue> &GDBMIResultParser::ParseValue::array() const |
343 | { |
344 | return mArray; |
345 | } |
346 | |
347 | const GDBMIResultParser::ParseObject &GDBMIResultParser::ParseValue::object() const |
348 | { |
349 | return mObject; |
350 | } |
351 | |
352 | int GDBMIResultParser::ParseValue::intValue(int defaultValue) const |
353 | { |
354 | //Q_ASSERT(mType == ParseValueType::Value); |
355 | bool ok; |
356 | int value = QString(mValue).toInt(&ok); |
357 | if (ok) |
358 | return value; |
359 | else |
360 | return defaultValue; |
361 | } |
362 | |
363 | int GDBMIResultParser::ParseValue::hexValue(int defaultValue) const |
364 | { |
365 | //Q_ASSERT(mType == ParseValueType::Value); |
366 | bool ok; |
367 | int value = QString(mValue).toInt(&ok,16); |
368 | if (ok) |
369 | return value; |
370 | else |
371 | return defaultValue; |
372 | } |
373 | |
374 | QString GDBMIResultParser::ParseValue::pathValue() const |
375 | { |
376 | //Q_ASSERT(mType == ParseValueType::Value); |
377 | return QFileInfo(QString::fromLocal8Bit(mValue)).absoluteFilePath(); |
378 | } |
379 | |
380 | QString GDBMIResultParser::ParseValue::utf8PathValue() const |
381 | { |
382 | return QFileInfo(QString::fromUtf8(mValue)).absoluteFilePath(); |
383 | } |
384 | |
385 | GDBMIResultParser::ParseValueType GDBMIResultParser::ParseValue::type() const |
386 | { |
387 | return mType; |
388 | } |
389 | |
390 | bool GDBMIResultParser::ParseValue::isValid() const |
391 | { |
392 | return mType!=ParseValueType::NotAssigned; |
393 | } |
394 | |
395 | GDBMIResultParser::ParseValue::ParseValue(): |
396 | mType(ParseValueType::NotAssigned) { |
397 | |
398 | } |
399 | |
400 | GDBMIResultParser::ParseValue::ParseValue(const QByteArray &value): |
401 | mValue(value), |
402 | mType(ParseValueType::Value) |
403 | { |
404 | } |
405 | |
406 | GDBMIResultParser::ParseValue::ParseValue(const ParseObject &object): |
407 | mObject(object), |
408 | mType(ParseValueType::Object) |
409 | { |
410 | } |
411 | |
412 | GDBMIResultParser::ParseValue::ParseValue(const QList<ParseValue> &array): |
413 | mArray(array), |
414 | mType(ParseValueType::Array) |
415 | { |
416 | } |
417 | |
418 | GDBMIResultParser::ParseValue::ParseValue(const ParseValue &value): |
419 | mValue(value.mValue), |
420 | mArray(value.mArray), |
421 | mObject(value.mObject), |
422 | mType(value.mType) |
423 | { |
424 | } |
425 | |
426 | GDBMIResultParser::ParseValue &GDBMIResultParser::ParseValue::operator=(const GDBMIResultParser::ParseValue &value) |
427 | { |
428 | mType = value.mType; |
429 | mValue = value.mValue; |
430 | mArray = value.mArray; |
431 | mObject = value.mObject; |
432 | return *this; |
433 | } |
434 | |
435 | GDBMIResultParser::ParseValue &GDBMIResultParser::ParseValue::operator=(const QByteArray &value) |
436 | { |
437 | Q_ASSERT(mType == ParseValueType::NotAssigned); |
438 | mType = ParseValueType::Value; |
439 | mValue = value; |
440 | return *this; |
441 | } |
442 | |
443 | GDBMIResultParser::ParseValue &GDBMIResultParser::ParseValue::operator=(const ParseObject& object) |
444 | { |
445 | Q_ASSERT(mType == ParseValueType::NotAssigned); |
446 | mType = ParseValueType::Object; |
447 | mObject = object; |
448 | return *this; |
449 | } |
450 | |
451 | GDBMIResultParser::ParseValue &GDBMIResultParser::ParseValue::operator=(const QList<ParseValue>& array) |
452 | { |
453 | Q_ASSERT(mType == ParseValueType::NotAssigned); |
454 | mType = ParseValueType::Array; |
455 | mArray = array; |
456 | return *this; |
457 | } |
458 | |
459 | |
460 | GDBMIResultParser::ParseObject::ParseObject() |
461 | { |
462 | |
463 | } |
464 | |
465 | GDBMIResultParser::ParseObject::ParseObject(const ParseObject &object): |
466 | mProps(object.mProps) |
467 | { |
468 | |
469 | } |
470 | |
471 | GDBMIResultParser::ParseValue GDBMIResultParser::ParseObject::operator[](const QByteArray &name) const |
472 | { |
473 | if (mProps.contains(name)) { |
474 | ParseValue value(mProps[name]); |
475 | return value; |
476 | } |
477 | return ParseValue(); |
478 | } |
479 | |
480 | GDBMIResultParser::ParseObject &GDBMIResultParser::ParseObject::operator=(const ParseObject &object) |
481 | { |
482 | mProps = object.mProps; |
483 | return *this; |
484 | } |
485 | |
486 | GDBMIResultParser::ParseValue &GDBMIResultParser::ParseObject::operator[](const QByteArray &name) { |
487 | if (!mProps.contains(name)) |
488 | mProps[name]=ParseValue(); |
489 | return mProps[name]; |
490 | } |
491 | |
492 | |