1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
36 #include <utils/qtcassert.h>
38 #include <QtCore/QByteArray>
39 #include <QtCore/QRegExp>
40 #include <QtCore/QTextStream>
47 void skipCommas(const char *&from, const char *to)
49 while (*from == ',' && from != to)
53 QTextStream &operator<<(QTextStream &os, const GdbMi &mi)
55 return os << mi.toString();
58 void GdbMi::parseResultOrValue(const char *&from, const char *to)
60 while (from != to && isspace(*from))
63 //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
66 //qDebug() << "no valid result in " << QByteArray(from, to - from);
69 if (from == to || *from == '(')
71 const char *ptr = from;
72 while (ptr < to && *ptr != '=') {
73 //qDebug() << "adding" << QChar(*ptr) << "to name";
76 m_name = QByteArray(from, ptr - from);
78 if (from < to && *from == '=') {
84 QByteArray GdbMi::parseCString(const char *&from, const char *to)
87 //qDebug() << "parseCString: " << QByteArray(from, to - from);
89 qDebug() << "MI Parse Error, double quote expected";
90 ++from; // So we don't hang
93 const char *ptr = from;
98 result = QByteArray(from + 1, ptr - from - 2);
104 qDebug() << "MI Parse Error, unterminated backslash escape";
105 from = ptr; // So we don't hang
113 int idx = result.indexOf('\\');
115 char *dst = result.data() + idx;
116 const char *src = dst + 1, *end = result.data() + result.length();
120 case 'a': *dst++ = '\a'; break;
121 case 'b': *dst++ = '\b'; break;
122 case 'f': *dst++ = '\f'; break;
123 case 'n': *dst++ = '\n'; break;
124 case 'r': *dst++ = '\r'; break;
125 case 't': *dst++ = '\t'; break;
126 case 'v': *dst++ = '\v'; break;
127 case '"': *dst++ = '"'; break;
128 case '\\': *dst++ = '\\'; break;
134 if (c < '0' || c > '7') {
138 prod = prod * 8 + c - '0';
139 if (++chars == 3 || src == end)
144 qDebug() << "MI Parse Error, unrecognized backslash escape";
156 } while (src != end);
158 result.truncate(dst - result.data());
164 void GdbMi::parseValue(const char *&from, const char *to)
166 //qDebug() << "parseValue: " << QByteArray(from, to - from);
169 parseTuple(from, to);
176 m_data = parseCString(from, to);
184 void GdbMi::parseTuple(const char *&from, const char *to)
186 //qDebug() << "parseTuple: " << QByteArray(from, to - from);
187 QTC_ASSERT(*from == '{', /**/);
189 parseTuple_helper(from, to);
192 void GdbMi::parseTuple_helper(const char *&from, const char *to)
194 skipCommas(from, to);
195 //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from);
203 child.parseResultOrValue(from, to);
204 //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
205 if (!child.isValid())
208 skipCommas(from, to);
212 void GdbMi::parseList(const char *&from, const char *to)
214 //qDebug() << "parseList: " << QByteArray(from, to - from);
215 QTC_ASSERT(*from == '[', /**/);
218 skipCommas(from, to);
225 child.parseResultOrValue(from, to);
228 skipCommas(from, to);
232 void GdbMi::setStreamOutput(const QByteArray &name, const QByteArray &content)
234 if (content.isEmpty())
237 child.m_type = Const;
239 child.m_data = content;
241 if (m_type == Invalid)
245 static QByteArray ind(int indent)
247 return QByteArray(2 * indent, ' ');
250 void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
252 for (int i = 0; i < m_children.size(); ++i) {
260 *str += m_children.at(i).toString(multiline, indent);
264 class MyString : public QString {
266 ushort at(int i) const { return constData()[i].unicode(); }
269 template<class ST, typename CT>
270 inline ST escapeCStringTpl(const ST &ba)
273 ret.reserve(ba.length() * 2);
274 for (int i = 0; i < ba.length(); ++i) {
277 case '\\': ret += "\\\\"; break;
278 case '\a': ret += "\\a"; break;
279 case '\b': ret += "\\b"; break;
280 case '\f': ret += "\\f"; break;
281 case '\n': ret += "\\n"; break;
282 case '\r': ret += "\\r"; break;
283 case '\t': ret += "\\t"; break;
284 case '\v': ret += "\\v"; break;
285 case '"': ret += "\\\""; break;
287 if (c < 32 || c == 127) {
289 ret += '0' + (c >> 6);
290 ret += '0' + ((c >> 3) & 7);
291 ret += '0' + (c & 7);
300 QString GdbMi::escapeCString(const QString &ba)
302 return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
305 QByteArray GdbMi::escapeCString(const QByteArray &ba)
307 return escapeCStringTpl<QByteArray, uchar>(ba);
310 QByteArray GdbMi::toString(bool multiline, int indent) const
316 result += ind(indent) + "Invalid\n";
321 if (!m_name.isEmpty())
322 result += m_name + '=';
323 result += '"' + escapeCString(m_data) + '"';
326 if (!m_name.isEmpty())
327 result += m_name + '=';
330 dumpChildren(&result, multiline, indent + 1);
331 result += '\n' + ind(indent) + '}';
334 dumpChildren(&result, multiline, indent + 1);
339 if (!m_name.isEmpty())
340 result += m_name + '=';
343 dumpChildren(&result, multiline, indent + 1);
344 result += '\n' + ind(indent) + ']';
347 dumpChildren(&result, multiline, indent + 1);
355 void GdbMi::fromString(const QByteArray &ba)
357 const char *from = ba.constBegin();
358 const char *to = ba.constEnd();
359 parseResultOrValue(from, to);
362 void GdbMi::fromStringMultiple(const QByteArray &ba)
364 const char *from = ba.constBegin();
365 const char *to = ba.constEnd();
366 parseTuple_helper(from, to);
369 GdbMi GdbMi::findChild(const char *name) const
371 for (int i = 0; i < m_children.size(); ++i)
372 if (m_children.at(i).m_name == name)
373 return m_children.at(i);
377 //////////////////////////////////////////////////////////////////////////////////
381 //////////////////////////////////////////////////////////////////////////////////
383 QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass)
385 switch (resultClass) {
386 case GdbResultDone: return "done";
387 case GdbResultRunning: return "running";
388 case GdbResultConnected: return "connected";
389 case GdbResultError: return "error";
390 case GdbResultExit: return "exit";
391 default: return "unknown";
395 QByteArray GdbResponse::toString() const
399 result = QByteArray::number(token);
401 result += stringFromResultClass(resultClass);
403 result += ',' + data.toString();
409 //////////////////////////////////////////////////////////////////////////////////
413 //////////////////////////////////////////////////////////////////////////////////
415 void extractGdbVersion(const QString &msg,
416 int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb)
418 const QChar dot(QLatin1Char('.'));
423 foreach (QChar c, msg) {
424 if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
429 else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
434 else if (!build.isEmpty() && !build.endsWith(dot))
439 *isMacGdb = msg.contains(QLatin1String("Apple version"));
441 *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
442 + 100 * cleaned.section(dot, 1, 1).toInt()
443 + 1 * cleaned.section(dot, 2, 2).toInt();
444 if (cleaned.count(dot) >= 3)
445 *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
447 *gdbBuildVersion = build.section(dot, 0, 0).toInt();
450 *gdbBuildVersion = build.section(dot, 1, 1).toInt();
453 } // namespace Internal
454 } // namespace Debugger