OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / gdb / gdbmi.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "gdbmi.h"
34
35 #include <utils/qtcassert.h>
36
37 #include <QtCore/QByteArray>
38 #include <QtCore/QRegExp>
39 #include <QtCore/QTextStream>
40
41 #include <ctype.h>
42
43 namespace Debugger {
44 namespace Internal {
45
46 void skipCommas(const char *&from, const char *to)
47 {
48     while (*from == ',' && from != to)
49         ++from;
50 }
51
52 QTextStream &operator<<(QTextStream &os, const GdbMi &mi)
53 {
54     return os << mi.toString();
55 }
56
57 void GdbMi::parseResultOrValue(const char *&from, const char *to)
58 {
59     while (from != to && isspace(*from))
60         ++from;
61
62     //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
63     parseValue(from, to);
64     if (isValid()) {
65         //qDebug() << "no valid result in " << QByteArray(from, to - from);
66         return;
67     }
68     if (from == to || *from == '(')
69         return;
70     const char *ptr = from;
71     while (ptr < to && *ptr != '=') {
72         //qDebug() << "adding" << QChar(*ptr) << "to name";
73         ++ptr;
74     }
75     m_name = QByteArray(from, ptr - from);
76     from = ptr;
77     if (from < to && *from == '=') {
78         ++from;
79         parseValue(from, to);
80     }
81 }
82
83 QByteArray GdbMi::parseCString(const char *&from, const char *to)
84 {
85     QByteArray result;
86     //qDebug() << "parseCString: " << QByteArray(from, to - from);
87     if (*from != '"') {
88         qDebug() << "MI Parse Error, double quote expected";
89         ++from; // So we don't hang
90         return QByteArray();
91     }
92     const char *ptr = from;
93     ++ptr;
94     while (ptr < to) {
95         if (*ptr == '"') {
96             ++ptr;
97             result = QByteArray(from + 1, ptr - from - 2);
98             break;
99         }
100         if (*ptr == '\\') {
101             ++ptr;
102             if (ptr == to) {
103                 qDebug() << "MI Parse Error, unterminated backslash escape";
104                 from = ptr; // So we don't hang
105                 return QByteArray();
106             }
107         }
108         ++ptr;
109     }
110     from = ptr;
111
112     int idx = result.indexOf('\\');
113     if (idx >= 0) {
114         char *dst = result.data() + idx;
115         const char *src = dst + 1, *end = result.data() + result.length();
116         do {
117             char c = *src++;
118             switch (c) {
119                 case 'a': *dst++ = '\a'; break;
120                 case 'b': *dst++ = '\b'; break;
121                 case 'f': *dst++ = '\f'; break;
122                 case 'n': *dst++ = '\n'; break;
123                 case 'r': *dst++ = '\r'; break;
124                 case 't': *dst++ = '\t'; break;
125                 case 'v': *dst++ = '\v'; break;
126                 case '"': *dst++ = '"'; break;
127                 case '\\': *dst++ = '\\'; break;
128                 default:
129                     {
130                         int chars = 0;
131                         uchar prod = 0;
132                         forever {
133                             if (c < '0' || c > '7') {
134                                 --src;
135                                 break;
136                             }
137                             prod = prod * 8 + c - '0';
138                             if (++chars == 3 || src == end)
139                                 break;
140                             c = *src++;
141                         }
142                         if (!chars) {
143                             qDebug() << "MI Parse Error, unrecognized backslash escape";
144                             return QByteArray();
145                         }
146                         *dst++ = prod;
147                     }
148             }
149             while (src != end) {
150                 char c = *src++;
151                 if (c == '\\')
152                     break;
153                 *dst++ = c;
154             }
155         } while (src != end);
156         *dst = 0;
157         result.truncate(dst - result.data());
158     }
159
160     return result;
161 }
162
163 void GdbMi::parseValue(const char *&from, const char *to)
164 {
165     //qDebug() << "parseValue: " << QByteArray(from, to - from);
166     switch (*from) {
167         case '{':
168             parseTuple(from, to);
169             break;
170         case '[':
171             parseList(from, to);
172             break;
173         case '"':
174             m_type = Const;
175             m_data = parseCString(from, to);
176             break;
177         default:
178             break;
179     }
180 }
181
182
183 void GdbMi::parseTuple(const char *&from, const char *to)
184 {
185     //qDebug() << "parseTuple: " << QByteArray(from, to - from);
186     QTC_ASSERT(*from == '{', /**/);
187     ++from;
188     parseTuple_helper(from, to);
189 }
190
191 void GdbMi::parseTuple_helper(const char *&from, const char *to)
192 {
193     skipCommas(from, to);
194     //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from);
195     m_type = Tuple;
196     while (from < to) {
197         if (*from == '}') {
198             ++from;
199             break;
200         }
201         GdbMi child;
202         child.parseResultOrValue(from, to);
203         //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
204         if (!child.isValid())
205             return;
206         m_children += child;
207         skipCommas(from, to);
208     }
209 }
210
211 void GdbMi::parseList(const char *&from, const char *to)
212 {
213     //qDebug() << "parseList: " << QByteArray(from, to - from);
214     QTC_ASSERT(*from == '[', /**/);
215     ++from;
216     m_type = List;
217     skipCommas(from, to);
218     while (from < to) {
219         if (*from == ']') {
220             ++from;
221             break;
222         }
223         GdbMi child;
224         child.parseResultOrValue(from, to);
225         if (child.isValid())
226             m_children += child;
227         skipCommas(from, to);
228     }
229 }
230
231 void GdbMi::setStreamOutput(const QByteArray &name, const QByteArray &content)
232 {
233     if (content.isEmpty())
234         return;
235     GdbMi child;
236     child.m_type = Const;
237     child.m_name = name;
238     child.m_data = content;
239     m_children += child;
240     if (m_type == Invalid)
241         m_type = Tuple;
242 }
243
244 static QByteArray ind(int indent)
245 {
246     return QByteArray(2 * indent, ' ');
247 }
248
249 void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
250 {
251     for (int i = 0; i < m_children.size(); ++i) {
252         if (i != 0) {
253             *str += ',';
254             if (multiline)
255                 *str += '\n';
256         }
257         if (multiline)
258             *str += ind(indent);
259         *str += m_children.at(i).toString(multiline, indent);
260     }
261 }
262
263 class MyString : public QString {
264 public:
265     ushort at(int i) const { return constData()[i].unicode(); }
266 };
267
268 template<class ST, typename CT>
269 inline ST escapeCStringTpl(const ST &ba)
270 {
271     ST ret;
272     ret.reserve(ba.length() * 2);
273     for (int i = 0; i < ba.length(); ++i) {
274         CT c = ba.at(i);
275         switch (c) {
276             case '\\': ret += "\\\\"; break;
277             case '\a': ret += "\\a"; break;
278             case '\b': ret += "\\b"; break;
279             case '\f': ret += "\\f"; break;
280             case '\n': ret += "\\n"; break;
281             case '\r': ret += "\\r"; break;
282             case '\t': ret += "\\t"; break;
283             case '\v': ret += "\\v"; break;
284             case '"': ret += "\\\""; break;
285             default:
286                 if (c < 32 || c == 127) {
287                     ret += '\\';
288                     ret += '0' + (c >> 6);
289                     ret += '0' + ((c >> 3) & 7);
290                     ret += '0' + (c & 7);
291                 } else {
292                     ret += c;
293                 }
294         }
295     }
296     return ret;
297 }
298
299 QString GdbMi::escapeCString(const QString &ba)
300 {
301     return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
302 }
303
304 QByteArray GdbMi::escapeCString(const QByteArray &ba)
305 {
306     return escapeCStringTpl<QByteArray, uchar>(ba);
307 }
308
309 QByteArray GdbMi::toString(bool multiline, int indent) const
310 {
311     QByteArray result;
312     switch (m_type) {
313         case Invalid:
314             if (multiline)
315                 result += ind(indent) + "Invalid\n";
316             else
317                 result += "Invalid";
318             break;
319         case Const:
320             if (!m_name.isEmpty())
321                 result += m_name + '=';
322             result += '"' + escapeCString(m_data) + '"';
323             break;
324         case Tuple:
325             if (!m_name.isEmpty())
326                 result += m_name + '=';
327             if (multiline) {
328                 result += "{\n";
329                 dumpChildren(&result, multiline, indent + 1);
330                 result += '\n' + ind(indent) + '}';
331             } else {
332                 result += '{';
333                 dumpChildren(&result, multiline, indent + 1);
334                 result += '}';
335             }
336             break;
337         case List:
338             if (!m_name.isEmpty())
339                 result += m_name + '=';
340             if (multiline) {
341                 result += "[\n";
342                 dumpChildren(&result, multiline, indent + 1);
343                 result += '\n' + ind(indent) + ']';
344             } else {
345                 result += '[';
346                 dumpChildren(&result, multiline, indent + 1);
347                 result += ']';
348             }
349             break;
350     }
351     return result;
352 }
353
354 void GdbMi::fromString(const QByteArray &ba)
355 {
356     const char *from = ba.constBegin();
357     const char *to = ba.constEnd();
358     parseResultOrValue(from, to);
359 }
360
361 void GdbMi::fromStringMultiple(const QByteArray &ba)
362 {
363     const char *from = ba.constBegin();
364     const char *to = ba.constEnd();
365     parseTuple_helper(from, to);
366 }
367
368 GdbMi GdbMi::findChild(const char *name) const
369 {
370     for (int i = 0; i < m_children.size(); ++i)
371         if (m_children.at(i).m_name == name)
372             return m_children.at(i);
373     return GdbMi();
374 }
375
376 //////////////////////////////////////////////////////////////////////////////////
377 //
378 // GdbResponse
379 //
380 //////////////////////////////////////////////////////////////////////////////////
381
382 QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass)
383 {
384     switch (resultClass) {
385         case GdbResultDone: return "done";
386         case GdbResultRunning: return "running";
387         case GdbResultConnected: return "connected";
388         case GdbResultError: return "error";
389         case GdbResultExit: return "exit";
390         default: return "unknown";
391     }
392 }
393
394 QByteArray GdbResponse::toString() const
395 {
396     QByteArray result;
397     if (token != -1)
398         result = QByteArray::number(token);
399     result += '^';
400     result += stringFromResultClass(resultClass);
401     if (data.isValid())
402         result += ',' + data.toString();
403     result += '\n';
404     return result;
405 }
406
407
408 //////////////////////////////////////////////////////////////////////////////////
409 //
410 // GdbResponse
411 //
412 //////////////////////////////////////////////////////////////////////////////////
413
414 void extractGdbVersion(const QString &msg,
415     int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb)
416 {
417     const QChar dot(QLatin1Char('.'));
418
419     QString cleaned;
420     QString build;
421     bool inClean = true;
422     foreach (QChar c, msg) {
423         if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
424             inClean = false;
425         if (inClean) {
426             if (c.isDigit())
427                 cleaned.append(c);
428             else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
429                 cleaned.append(dot);
430         } else {
431             if (c.isDigit())
432                 build.append(c);
433             else if (!build.isEmpty() && !build.endsWith(dot))
434                 build.append(dot);
435         }
436     }
437
438     *isMacGdb = msg.contains(QLatin1String("Apple version"));
439
440     *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
441                   + 100 * cleaned.section(dot, 1, 1).toInt()
442                     + 1 * cleaned.section(dot, 2, 2).toInt();
443     if (cleaned.count(dot) >= 3)
444         *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
445     else
446         *gdbBuildVersion = build.section(dot, 0, 0).toInt();
447
448     if (*isMacGdb)
449         *gdbBuildVersion = build.section(dot, 1, 1).toInt();
450 }
451
452 } // namespace Internal
453 } // namespace Debugger