1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
35 #include <utils/qtcassert.h>
37 #include <QtCore/QByteArray>
38 #include <QtCore/QRegExp>
39 #include <QtCore/QTextStream>
46 void skipCommas(const char *&from, const char *to)
48 while (*from == ',' && from != to)
52 QTextStream &operator<<(QTextStream &os, const GdbMi &mi)
54 return os << mi.toString();
57 void GdbMi::parseResultOrValue(const char *&from, const char *to)
59 while (from != to && isspace(*from))
62 //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
65 //qDebug() << "no valid result in " << QByteArray(from, to - from);
68 if (from == to || *from == '(')
70 const char *ptr = from;
71 while (ptr < to && *ptr != '=') {
72 //qDebug() << "adding" << QChar(*ptr) << "to name";
75 m_name = QByteArray(from, ptr - from);
77 if (from < to && *from == '=') {
83 QByteArray GdbMi::parseCString(const char *&from, const char *to)
86 //qDebug() << "parseCString: " << QByteArray(from, to - from);
88 qDebug() << "MI Parse Error, double quote expected";
89 ++from; // So we don't hang
92 const char *ptr = from;
97 result = QByteArray(from + 1, ptr - from - 2);
103 qDebug() << "MI Parse Error, unterminated backslash escape";
104 from = ptr; // So we don't hang
112 int idx = result.indexOf('\\');
114 char *dst = result.data() + idx;
115 const char *src = dst + 1, *end = result.data() + result.length();
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;
133 if (c < '0' || c > '7') {
137 prod = prod * 8 + c - '0';
138 if (++chars == 3 || src == end)
143 qDebug() << "MI Parse Error, unrecognized backslash escape";
155 } while (src != end);
157 result.truncate(dst - result.data());
163 void GdbMi::parseValue(const char *&from, const char *to)
165 //qDebug() << "parseValue: " << QByteArray(from, to - from);
168 parseTuple(from, to);
175 m_data = parseCString(from, to);
183 void GdbMi::parseTuple(const char *&from, const char *to)
185 //qDebug() << "parseTuple: " << QByteArray(from, to - from);
186 QTC_ASSERT(*from == '{', /**/);
188 parseTuple_helper(from, to);
191 void GdbMi::parseTuple_helper(const char *&from, const char *to)
193 skipCommas(from, to);
194 //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from);
202 child.parseResultOrValue(from, to);
203 //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
204 if (!child.isValid())
207 skipCommas(from, to);
211 void GdbMi::parseList(const char *&from, const char *to)
213 //qDebug() << "parseList: " << QByteArray(from, to - from);
214 QTC_ASSERT(*from == '[', /**/);
217 skipCommas(from, to);
224 child.parseResultOrValue(from, to);
227 skipCommas(from, to);
231 void GdbMi::setStreamOutput(const QByteArray &name, const QByteArray &content)
233 if (content.isEmpty())
236 child.m_type = Const;
238 child.m_data = content;
240 if (m_type == Invalid)
244 static QByteArray ind(int indent)
246 return QByteArray(2 * indent, ' ');
249 void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
251 for (int i = 0; i < m_children.size(); ++i) {
259 *str += m_children.at(i).toString(multiline, indent);
263 class MyString : public QString {
265 ushort at(int i) const { return constData()[i].unicode(); }
268 template<class ST, typename CT>
269 inline ST escapeCStringTpl(const ST &ba)
272 ret.reserve(ba.length() * 2);
273 for (int i = 0; i < ba.length(); ++i) {
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;
286 if (c < 32 || c == 127) {
288 ret += '0' + (c >> 6);
289 ret += '0' + ((c >> 3) & 7);
290 ret += '0' + (c & 7);
299 QString GdbMi::escapeCString(const QString &ba)
301 return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
304 QByteArray GdbMi::escapeCString(const QByteArray &ba)
306 return escapeCStringTpl<QByteArray, uchar>(ba);
309 QByteArray GdbMi::toString(bool multiline, int indent) const
315 result += ind(indent) + "Invalid\n";
320 if (!m_name.isEmpty())
321 result += m_name + '=';
322 result += '"' + escapeCString(m_data) + '"';
325 if (!m_name.isEmpty())
326 result += m_name + '=';
329 dumpChildren(&result, multiline, indent + 1);
330 result += '\n' + ind(indent) + '}';
333 dumpChildren(&result, multiline, indent + 1);
338 if (!m_name.isEmpty())
339 result += m_name + '=';
342 dumpChildren(&result, multiline, indent + 1);
343 result += '\n' + ind(indent) + ']';
346 dumpChildren(&result, multiline, indent + 1);
354 void GdbMi::fromString(const QByteArray &ba)
356 const char *from = ba.constBegin();
357 const char *to = ba.constEnd();
358 parseResultOrValue(from, to);
361 void GdbMi::fromStringMultiple(const QByteArray &ba)
363 const char *from = ba.constBegin();
364 const char *to = ba.constEnd();
365 parseTuple_helper(from, to);
368 GdbMi GdbMi::findChild(const char *name) const
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);
376 //////////////////////////////////////////////////////////////////////////////////
380 //////////////////////////////////////////////////////////////////////////////////
382 QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass)
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";
394 QByteArray GdbResponse::toString() const
398 result = QByteArray::number(token);
400 result += stringFromResultClass(resultClass);
402 result += ',' + data.toString();
408 //////////////////////////////////////////////////////////////////////////////////
412 //////////////////////////////////////////////////////////////////////////////////
414 void extractGdbVersion(const QString &msg,
415 int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb)
417 const QChar dot(QLatin1Char('.'));
422 foreach (QChar c, msg) {
423 if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
428 else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
433 else if (!build.isEmpty() && !build.endsWith(dot))
438 *isMacGdb = msg.contains(QLatin1String("Apple version"));
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();
446 *gdbBuildVersion = build.section(dot, 0, 0).toInt();
449 *gdbBuildVersion = build.section(dot, 1, 1).toInt();
452 } // namespace Internal
453 } // namespace Debugger