debuggerprotocol.cpp 24 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

30
#include "debuggerprotocol.h"
hjk's avatar
hjk committed
31

32 33
#include <QCoreApplication>
#include <QDateTime>
34
#include <QDebug>
35
#include <QHostAddress>
36
#if QT_VERSION >= 0x050200
37
#include <QTimeZone>
38
#endif
con's avatar
con committed
39

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
40 41
#include <ctype.h>

con's avatar
con committed
42 43 44
namespace Debugger {
namespace Internal {

45 46 47 48 49 50 51 52 53 54 55
uchar fromhex(uchar c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'z')
        return 10 + c - 'a';
    if (c >= 'A' && c <= 'Z')
        return 10 + c - 'A';
    return -1;
}

56 57 58 59 60 61
void skipCommas(const char *&from, const char *to)
{
    while (*from == ',' && from != to)
        ++from;
}

62
QTextStream &operator<<(QTextStream &os, const GdbMi &mi)
con's avatar
con committed
63 64 65 66
{
    return os << mi.toString();
}

67
void GdbMi::parseResultOrValue(const char *&from, const char *to)
con's avatar
con committed
68
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
69
    while (from != to && isspace(*from))
con's avatar
con committed
70 71
        ++from;

72
    //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
con's avatar
con committed
73 74
    parseValue(from, to);
    if (isValid()) {
75
        //qDebug() << "no valid result in " << QByteArray(from, to - from);
con's avatar
con committed
76 77 78 79
        return;
    }
    if (from == to || *from == '(')
        return;
80
    const char *ptr = from;
con's avatar
con committed
81 82 83 84 85 86 87 88 89 90 91 92
    while (ptr < to && *ptr != '=') {
        //qDebug() << "adding" << QChar(*ptr) << "to name";
        ++ptr;
    }
    m_name = QByteArray(from, ptr - from);
    from = ptr;
    if (from < to && *from == '=') {
        ++from;
        parseValue(from, to);
    }
}

93
QByteArray GdbMi::parseCString(const char *&from, const char *to)
con's avatar
con committed
94 95
{
    QByteArray result;
96
    //qDebug() << "parseCString: " << QByteArray(from, to - from);
con's avatar
con committed
97 98
    if (*from != '"') {
        qDebug() << "MI Parse Error, double quote expected";
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
99
        ++from; // So we don't hang
con's avatar
con committed
100 101
        return QByteArray();
    }
102
    const char *ptr = from;
con's avatar
con committed
103 104 105 106 107 108 109
    ++ptr;
    while (ptr < to) {
        if (*ptr == '"') {
            ++ptr;
            result = QByteArray(from + 1, ptr - from - 2);
            break;
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110
        if (*ptr == '\\') {
con's avatar
con committed
111
            ++ptr;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
112 113 114 115 116 117
            if (ptr == to) {
                qDebug() << "MI Parse Error, unterminated backslash escape";
                from = ptr; // So we don't hang
                return QByteArray();
            }
        }
con's avatar
con committed
118 119
        ++ptr;
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
120
    from = ptr;
con's avatar
con committed
121

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    int idx = result.indexOf('\\');
    if (idx >= 0) {
        char *dst = result.data() + idx;
        const char *src = dst + 1, *end = result.data() + result.length();
        do {
            char c = *src++;
            switch (c) {
                case 'a': *dst++ = '\a'; break;
                case 'b': *dst++ = '\b'; break;
                case 'f': *dst++ = '\f'; break;
                case 'n': *dst++ = '\n'; break;
                case 'r': *dst++ = '\r'; break;
                case 't': *dst++ = '\t'; break;
                case 'v': *dst++ = '\v'; break;
                case '"': *dst++ = '"'; break;
                case '\\': *dst++ = '\\'; break;
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
                case 'x': {
                        c = *src++;
                        int chars = 0;
                        uchar prod = 0;
                        while (true) {
                            uchar val = fromhex(c);
                            if (val == uchar(-1))
                                break;
                            prod = prod * 16 + val;
                            if (++chars == 3 || src == end)
                                break;
                            c = *src++;
                        }
                        if (!chars) {
                            qDebug() << "MI Parse Error, unrecognized hex escape";
                            return QByteArray();
                        }
                        *dst++ = prod;
                        break;
                    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
                default:
                    {
                        int chars = 0;
                        uchar prod = 0;
                        forever {
                            if (c < '0' || c > '7') {
                                --src;
                                break;
                            }
                            prod = prod * 8 + c - '0';
                            if (++chars == 3 || src == end)
                                break;
                            c = *src++;
                        }
                        if (!chars) {
                            qDebug() << "MI Parse Error, unrecognized backslash escape";
                            return QByteArray();
                        }
                        *dst++ = prod;
                    }
            }
            while (src != end) {
                char c = *src++;
                if (c == '\\')
                    break;
                *dst++ = c;
            }
        } while (src != end);
        *dst = 0;
        result.truncate(dst - result.data());
con's avatar
con committed
188 189 190 191 192
    }

    return result;
}

193
void GdbMi::parseValue(const char *&from, const char *to)
con's avatar
con committed
194
{
195
    //qDebug() << "parseValue: " << QByteArray(from, to - from);
con's avatar
con committed
196
    switch (*from) {
hjk's avatar
hjk committed
197 198 199 200 201 202 203 204 205 206 207 208
        case '{':
            parseTuple(from, to);
            break;
        case '[':
            parseList(from, to);
            break;
        case '"':
            m_type = Const;
            m_data = parseCString(from, to);
            break;
        default:
            break;
con's avatar
con committed
209 210 211 212
    }
}


213
void GdbMi::parseTuple(const char *&from, const char *to)
con's avatar
con committed
214
{
215
    //qDebug() << "parseTuple: " << QByteArray(from, to - from);
216
    //QTC_CHECK(*from == '{');
con's avatar
con committed
217 218 219 220
    ++from;
    parseTuple_helper(from, to);
}

221
void GdbMi::parseTuple_helper(const char *&from, const char *to)
con's avatar
con committed
222
{
223
    skipCommas(from, to);
224
    //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from);
con's avatar
con committed
225 226 227 228 229 230 231 232 233 234 235 236
    m_type = Tuple;
    while (from < to) {
        if (*from == '}') {
            ++from;
            break;
        }
        GdbMi child;
        child.parseResultOrValue(from, to);
        //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
        if (!child.isValid())
            return;
        m_children += child;
237
        skipCommas(from, to);
con's avatar
con committed
238 239 240
    }
}

241
void GdbMi::parseList(const char *&from, const char *to)
con's avatar
con committed
242
{
243
    //qDebug() << "parseList: " << QByteArray(from, to - from);
244
    //QTC_CHECK(*from == '[');
con's avatar
con committed
245 246
    ++from;
    m_type = List;
247
    skipCommas(from, to);
con's avatar
con committed
248 249 250 251 252 253 254 255 256
    while (from < to) {
        if (*from == ']') {
            ++from;
            break;
        }
        GdbMi child;
        child.parseResultOrValue(from, to);
        if (child.isValid())
            m_children += child;
257
        skipCommas(from, to);
con's avatar
con committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    }
}

static QByteArray ind(int indent)
{
    return QByteArray(2 * indent, ' ');
}

void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
{
    for (int i = 0; i < m_children.size(); ++i) {
        if (i != 0) {
            *str += ',';
            if (multiline)
                *str += '\n';
        }
        if (multiline)
            *str += ind(indent);
        *str += m_children.at(i).toString(multiline, indent);
    }
}

hjk's avatar
hjk committed
280
QByteArray GdbMi::escapeCString(const QByteArray &ba)
281
{
hjk's avatar
hjk committed
282
    QByteArray ret;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
283 284
    ret.reserve(ba.length() * 2);
    for (int i = 0; i < ba.length(); ++i) {
hjk's avatar
hjk committed
285
        const uchar c = ba.at(i);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
286
        switch (c) {
hjk's avatar
hjk committed
287 288 289 290 291 292 293 294 295
            case '\\': ret += "\\\\"; break;
            case '\a': ret += "\\a"; break;
            case '\b': ret += "\\b"; break;
            case '\f': ret += "\\f"; break;
            case '\n': ret += "\\n"; break;
            case '\r': ret += "\\r"; break;
            case '\t': ret += "\\t"; break;
            case '\v': ret += "\\v"; break;
            case '"': ret += "\\\""; break;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
296 297
            default:
                if (c < 32 || c == 127) {
hjk's avatar
hjk committed
298 299 300 301
                    ret += '\\';
                    ret += ('0' + (c >> 6));
                    ret += ('0' + ((c >> 3) & 7));
                    ret += ('0' + (c & 7));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
302
                } else {
hjk's avatar
hjk committed
303
                    ret += c;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
304 305 306 307
                }
        }
    }
    return ret;
308 309
}

con's avatar
con committed
310 311 312 313
QByteArray GdbMi::toString(bool multiline, int indent) const
{
    QByteArray result;
    switch (m_type) {
hjk's avatar
hjk committed
314 315 316 317 318 319
        case Invalid:
            if (multiline)
                result += ind(indent) + "Invalid\n";
            else
                result += "Invalid";
            break;
320
        case Const:
hjk's avatar
hjk committed
321
            if (!m_name.isEmpty())
322 323
                result += m_name + '=';
            result += '"' + escapeCString(m_data) + '"';
hjk's avatar
hjk committed
324 325 326
            break;
        case Tuple:
            if (!m_name.isEmpty())
327
                result += m_name + '=';
hjk's avatar
hjk committed
328 329 330
            if (multiline) {
                result += "{\n";
                dumpChildren(&result, multiline, indent + 1);
331
                result += '\n' + ind(indent) + '}';
hjk's avatar
hjk committed
332
            } else {
333
                result += '{';
hjk's avatar
hjk committed
334
                dumpChildren(&result, multiline, indent + 1);
335
                result += '}';
hjk's avatar
hjk committed
336 337 338 339
            }
            break;
        case List:
            if (!m_name.isEmpty())
340
                result += m_name + '=';
hjk's avatar
hjk committed
341 342 343
            if (multiline) {
                result += "[\n";
                dumpChildren(&result, multiline, indent + 1);
344
                result += '\n' + ind(indent) + ']';
hjk's avatar
hjk committed
345
            } else {
346
                result += '[';
hjk's avatar
hjk committed
347
                dumpChildren(&result, multiline, indent + 1);
348
                result += ']';
hjk's avatar
hjk committed
349 350
            }
            break;
con's avatar
con committed
351 352 353 354 355 356
    }
    return result;
}

void GdbMi::fromString(const QByteArray &ba)
{
357 358
    const char *from = ba.constBegin();
    const char *to = ba.constEnd();
con's avatar
con committed
359 360 361
    parseResultOrValue(from, to);
}

362 363 364 365 366 367 368
void GdbMi::fromStringMultiple(const QByteArray &ba)
{
    const char *from = ba.constBegin();
    const char *to = ba.constEnd();
    parseTuple_helper(from, to);
}

369
GdbMi GdbMi::operator[](const char *name) const
con's avatar
con committed
370
{
371
    for (int i = 0, n = m_children.size(); i < n; ++i)
con's avatar
con committed
372 373 374 375 376
        if (m_children.at(i).m_name == name)
            return m_children.at(i);
    return GdbMi();
}

377 378 379 380 381 382 383
qulonglong GdbMi::toAddress() const
{
    QByteArray ba = m_data;
    if (ba.endsWith('L'))
        ba.chop(1);
    if (ba.startsWith('*') || ba.startsWith('@'))
        ba = ba.mid(1);
384
    return ba.toULongLong(0, 0);
385 386
}

con's avatar
con committed
387 388
//////////////////////////////////////////////////////////////////////////////////
//
hjk's avatar
hjk committed
389
// GdbResponse
con's avatar
con committed
390 391 392
//
//////////////////////////////////////////////////////////////////////////////////

393
QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass)
con's avatar
con committed
394 395 396 397 398 399 400 401 402
{
    switch (resultClass) {
        case GdbResultDone: return "done";
        case GdbResultRunning: return "running";
        case GdbResultConnected: return "connected";
        case GdbResultError: return "error";
        case GdbResultExit: return "exit";
        default: return "unknown";
    }
403
}
con's avatar
con committed
404

hjk's avatar
hjk committed
405
QByteArray GdbResponse::toString() const
con's avatar
con committed
406 407 408 409 410 411 412 413 414 415 416 417
{
    QByteArray result;
    if (token != -1)
        result = QByteArray::number(token);
    result += '^';
    result += stringFromResultClass(resultClass);
    if (data.isValid())
        result += ',' + data.toString();
    result += '\n';
    return result;
}

418 419 420 421 422 423 424 425

//////////////////////////////////////////////////////////////////////////////////
//
// GdbResponse
//
//////////////////////////////////////////////////////////////////////////////////

void extractGdbVersion(const QString &msg,
426
    int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb)
427 428 429
{
    const QChar dot(QLatin1Char('.'));

430 431 432 433
    const bool ignoreParenthesisContent = msg.contains(QLatin1String("rubenvb"));
    const QChar parOpen(QLatin1Char('('));
    const QChar parClose(QLatin1Char(')'));

434 435 436
    QString cleaned;
    QString build;
    bool inClean = true;
437
    bool inParenthesis = false;
438 439 440
    foreach (QChar c, msg) {
        if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
            inClean = false;
441 442 443 444 445 446 447 448
        if (ignoreParenthesisContent) {
            if (!inParenthesis && c == parOpen)
                inParenthesis = true;
            if (inParenthesis && c == parClose)
                inParenthesis = false;
            if (inParenthesis)
                continue;
        }
449 450 451 452 453 454 455 456 457 458 459 460 461 462
        if (inClean) {
            if (c.isDigit())
                cleaned.append(c);
            else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
                cleaned.append(dot);
        } else {
            if (c.isDigit())
                build.append(c);
            else if (!build.isEmpty() && !build.endsWith(dot))
                build.append(dot);
        }
    }

    *isMacGdb = msg.contains(QLatin1String("Apple version"));
463
    *isQnxGdb = msg.contains(QLatin1String("qnx"));
464 465 466 467 468 469 470 471 472 473 474 475 476

    *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
                  + 100 * cleaned.section(dot, 1, 1).toInt()
                    + 1 * cleaned.section(dot, 2, 2).toInt();
    if (cleaned.count(dot) >= 3)
        *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
    else
        *gdbBuildVersion = build.section(dot, 0, 0).toInt();

    if (*isMacGdb)
        *gdbBuildVersion = build.section(dot, 1, 1).toInt();
}

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
//////////////////////////////////////////////////////////////////////////////////
//
// Decoding
//
//////////////////////////////////////////////////////////////////////////////////

static QString quoteUnprintableLatin1(const QByteArray &ba)
{
    QString res;
    char buf[10];
    for (int i = 0, n = ba.size(); i != n; ++i) {
        const unsigned char c = ba.at(i);
        if (isprint(c)) {
            res += QLatin1Char(c);
        } else {
            qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
            res += QLatin1String(buf);
        }
    }
    return res;
}

static QDate dateFromData(int jd)
{
    return jd ? QDate::fromJulianDay(jd) : QDate();
}

static QTime timeFromData(int ms)
{
    return ms == -1 ? QTime() : QTime(0, 0, 0, 0).addMSecs(ms);
}

509
#if QT_VERSION >= 0x050200
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
// Stolen and adapted from qdatetime.cpp
static void getDateTime(qint64 msecs, int status, QDate *date, QTime *time)
{
    enum {
        SECS_PER_DAY = 86400,
        MSECS_PER_DAY = 86400000,
        SECS_PER_HOUR = 3600,
        MSECS_PER_HOUR = 3600000,
        SECS_PER_MIN = 60,
        MSECS_PER_MIN = 60000,
        TIME_T_MAX = 2145916799,  // int maximum 2037-12-31T23:59:59 UTC
        JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1)
    };

    // Status of date/time
    enum StatusFlag {
        NullDate            = 0x01,
        NullTime            = 0x02,
        ValidDate           = 0x04,
        ValidTime           = 0x08,
        ValidDateTime       = 0x10,
        TimeZoneCached      = 0x20,
        SetToStandardTime   = 0x40,
        SetToDaylightTime   = 0x80
    };

    qint64 jd = JULIAN_DAY_FOR_EPOCH;
    qint64 ds = 0;

    if (qAbs(msecs) >= MSECS_PER_DAY) {
        jd += (msecs / MSECS_PER_DAY);
        msecs %= MSECS_PER_DAY;
    }

    if (msecs < 0) {
        ds = MSECS_PER_DAY - msecs - 1;
        jd -= ds / MSECS_PER_DAY;
        ds = ds % MSECS_PER_DAY;
        ds = MSECS_PER_DAY - ds - 1;
    } else {
        ds = msecs;
    }

    *date = (status & NullDate) ? QDate() : QDate::fromJulianDay(jd);
    *time = (status & NullTime) ? QTime() : QTime::fromMSecsSinceStartOfDay(ds);
}
556
#endif
557

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
QString decodeData(const QByteArray &ba, int encoding)
{
    switch (encoding) {
        case Unencoded8Bit: // 0
            return quoteUnprintableLatin1(ba);
        case Base64Encoded8BitWithQuotes: { // 1, used for QByteArray
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
            rc += quoteUnprintableLatin1(QByteArray::fromBase64(ba));
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded16BitWithQuotes: { // 2, used for QString
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            QString rc = doubleQuote;
            rc += QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded32BitWithQuotes: { // 3
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
            rc += QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4);
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded16Bit: { // 4, without quotes (see 2)
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            return QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
        }
        case Base64Encoded8Bit: { // 5, without quotes (see 1)
            return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
        }
        case Hex2EncodedLatin1WithQuotes: { // 6, %02x encoded 8 bit Latin1 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
599
            return doubleQuote + QString::fromLatin1(decodedBa, decodedBa.size()) + doubleQuote;
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
        }
        case Hex4EncodedLittleEndianWithQuotes: { // 7, %04x encoded 16 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
        }
        case Hex8EncodedLittleEndianWithQuotes: { // 8, %08x encoded 32 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
        }
        case Hex2EncodedUtf8WithQuotes: { // 9, %02x encoded 8 bit UTF-8 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUtf8(decodedBa) + doubleQuote;
        }
        case Hex8EncodedBigEndian: { // 10, %08x encoded 32 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            QByteArray decodedBa = QByteArray::fromHex(ba);
            for (int i = 0; i < decodedBa.size() - 3; i += 4) {
                char c = decodedBa.at(i);
                decodedBa[i] = decodedBa.at(i + 3);
                decodedBa[i + 3] = c;
                c = decodedBa.at(i + 1);
                decodedBa[i + 1] = decodedBa.at(i + 2);
                decodedBa[i + 2] = c;
            }
            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
        }
        case Hex4EncodedBigEndianWithQuotes: { // 11, %04x encoded 16 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            QByteArray decodedBa = QByteArray::fromHex(ba);
            for (int i = 0; i < decodedBa.size(); i += 2) {
                char c = decodedBa.at(i);
                decodedBa[i] = decodedBa.at(i + 1);
                decodedBa[i + 1] = c;
            }
            return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
        }
        case Hex4EncodedLittleEndianWithoutQuotes: { // 12, see 7, without quotes
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
        }
        case Hex2EncodedLocal8BitWithQuotes: { // 13, %02x encoded 8 bit UTF-8 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromLocal8Bit(decodedBa) + doubleQuote;
        }
        case JulianDate: { // 14, an integer count
            const QDate date = dateFromData(ba.toInt());
655
            return date.isValid() ? date.toString(Qt::TextDate) : QLatin1String("(invalid)");
656 657 658
        }
        case MillisecondsSinceMidnight: {
            const QTime time = timeFromData(ba.toInt());
659
            return time.isValid() ? time.toString(Qt::TextDate) : QLatin1String("(invalid)");
660 661 662 663 664
        }
        case JulianDateAndMillisecondsSinceMidnight: {
            const int p = ba.indexOf('/');
            const QDate date = dateFromData(ba.left(p).toInt());
            const QTime time = timeFromData(ba.mid(p + 1 ).toInt());
665 666
            const QDateTime dateTime = QDateTime(date, time);
            return dateTime.isValid() ? dateTime.toString(Qt::TextDate) : QLatin1String("(invalid)");
667
        }
668 669 670 671 672 673 674 675 676 677 678 679
        case IPv6AddressAndHexScopeId: { // 27, 16 hex-encoded bytes, "%" and the string-encoded scope
            const int p = ba.indexOf('%');
            QHostAddress ip6(QString::fromLatin1(p == -1 ? ba : ba.left(p)));
            if (ip6.isNull())
                break;

            const QByteArray scopeId = p == -1 ? QByteArray() : QByteArray::fromHex(ba.mid(p + 1));
            if (!scopeId.isEmpty())
                ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const ushort *>(scopeId.constData()),
                                                  scopeId.length() / 2));
            return ip6.toString();
        }
680 681 682 683
        case Hex2EncodedUtf8WithoutQuotes: { // 28, %02x encoded 8 bit UTF-8 data without quotes
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return QString::fromUtf8(decodedBa);
        }
684
        case DateTimeInternal: { // 29, DateTimeInternal: msecs, spec, offset, tz, status
685
#if QT_VERSION >= 0x050200
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
            int p0 = ba.indexOf('/');
            int p1 = ba.indexOf('/', p0 + 1);
            int p2 = ba.indexOf('/', p1 + 1);
            int p3 = ba.indexOf('/', p2 + 1);

            qint64 msecs = ba.left(p0).toLongLong();
            ++p0;
            Qt::TimeSpec spec = Qt::TimeSpec(ba.mid(p0, p1 - p0).toInt());
            ++p1;
            qulonglong offset = ba.mid(p1, p2 - p1).toInt();
            ++p2;
            QByteArray timeZoneId = QByteArray::fromHex(ba.mid(p2, p3 - p2));
            ++p3;
            int status = ba.mid(p3).toInt();

            QDate date;
            QTime time;
            getDateTime(msecs, status, &date, &time);

            QDateTime dateTime;
            if (spec == Qt::OffsetFromUTC) {
                dateTime = QDateTime(date, time, spec, offset);
            } else if (spec == Qt::TimeZone) {
                if (!QTimeZone::isTimeZoneIdAvailable(timeZoneId))
                    return QLatin1String("<unavailable>");
                dateTime = QDateTime(date, time, QTimeZone(timeZoneId));
            } else {
                dateTime = QDateTime(date, time, spec);
            }
            return dateTime.toString();
716 717 718 719
#else
            // "Very plain".
            return QString::fromLatin1(ba);
#endif
720
        }
721 722 723 724 725
    }
    qDebug() << "ENCODING ERROR: " << encoding;
    return QCoreApplication::translate("Debugger", "<Encoding error>");
}

con's avatar
con committed
726 727
} // namespace Internal
} // namespace Debugger