• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 7252848206

18 Dec 2023 07:26PM UTC coverage: 32.067% (+0.001%) from 32.066%
7252848206

push

github

mcallegari
Code style review #1427

199 of 628 new or added lines in 101 files covered. (31.69%)

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.65
/engine/src/script.cpp
1
/*
2
  Q Light Controller Plus
3
  script.cpp
4

5
  Copyright (C) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#if !defined(Q_OS_IOS)
24
#include <QProcess>
25
#endif
26
#include <QDebug>
27
#include <QUrl>
28
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
29
#include <QRandomGenerator>
30
#endif
31
 
32
#include "genericfader.h"
33
#include "fadechannel.h"
34
#include "mastertimer.h"
35
#include "universe.h"
36
#include "script.h"
37
#include "doc.h"
38

39
#define KXMLQLCScriptCommand "Command"
40

41
const QString Script::startFunctionCmd = QString("startfunction");
42
const QString Script::stopFunctionCmd = QString("stopfunction");
43
const QString Script::blackoutCmd = QString("blackout");
44

45
const QString Script::waitCmd = QString("wait");
46
const QString Script::waitKeyCmd = QString("waitkey");
47

48
const QString Script::setFixtureCmd = QString("setfixture");
49
const QString Script::systemCmd = QString("systemcommand");
50

51
const QString Script::labelCmd = QString("label");
52
const QString Script::jumpCmd = QString("jump");
53

54
const QString Script::blackoutOn = QString("on");
55
const QString Script::blackoutOff = QString("off");
56

57
const QStringList knownKeywords(QStringList() << "ch" << "val" << "arg");
58

59
/****************************************************************************
60
 * Initialization
61
 ****************************************************************************/
62

63
Script::Script(Doc* doc) : Function(doc, Function::ScriptType)
2✔
64
    , m_currentCommand(0)
65
    , m_waitCount(0)
2✔
66
{
67
    setName(tr("New Script"));
2✔
68
}
2✔
69

70
Script::~Script()
3✔
71
{
72
}
3✔
73

74
QIcon Script::getIcon() const
×
75
{
76
    return QIcon(":/script.png");
×
77
}
78

79
quint32 Script::totalDuration()
×
80
{
81
    quint32 totalDuration = 0;
×
82

83
    for (int i = 0; i < m_lines.count(); i++)
×
84
    {
85
        QList <QStringList> tokens = m_lines[i];
×
86
        if (tokens.isEmpty() || tokens[0].size() < 2)
×
87
            continue;
×
88

89
        if (tokens[0][0] == Script::waitCmd)
×
90
        {
91
            bool ok = false;
×
92
            quint32 waitTime = getValueFromString(tokens[0][1], &ok);
×
93
            if (ok == true)
×
94
                totalDuration += waitTime;
×
95
        }
96
    }
97

98
    return totalDuration;
×
99
}
100

101
Function* Script::createCopy(Doc* doc, bool addToDoc)
×
102
{
103
    Q_ASSERT(doc != NULL);
×
104

105
    Function* copy = new Script(doc);
×
106
    if (copy->copyFrom(this) == false)
×
107
    {
108
        delete copy;
×
109
        copy = NULL;
×
110
    }
111
    if (addToDoc == true && doc->addFunction(copy) == false)
×
112
    {
113
        delete copy;
×
114
        copy = NULL;
×
115
    }
116

117
    return copy;
×
118
}
119

120
bool Script::copyFrom(const Function* function)
×
121
{
122
    const Script* script = qobject_cast<const Script*> (function);
×
123
    if (script == NULL)
×
124
        return false;
×
125

126
    setData(script->data());
×
127

128
    return Function::copyFrom(function);
×
129
}
130

131
/****************************************************************************
132
 * Script data
133
 ****************************************************************************/
134

135
bool Script::setData(const QString& str)
1✔
136
{
137
    m_data = str;
1✔
138
    m_syntaxErrorLines.clear();
1✔
139

140
    // Construct individual code lines from the data
141
    m_lines.clear();
1✔
142
    if (m_data.isEmpty() == false)
1✔
143
    {
144
        int i = 1;
1✔
145
        QStringList lines = m_data.split(QRegExp("(\r\n|\n\r|\r|\n)"));
3✔
146
        foreach (QString line, lines)
29✔
147
        {
148
            bool ok = false;
14✔
149
            if (line.isEmpty() == false)
14✔
150
            {
151
                m_lines << tokenizeLine(line + QString("\n"), &ok);
13✔
152
                if (ok == false)
13✔
153
                    m_syntaxErrorLines.append(i);
2✔
154
            }
155
            i++;
14✔
156
        }
157
    }
158

159
    // Map all labels to their individual line numbers for fast jumps
160
    m_labels.clear();
1✔
161
    for (int i = 0; i < m_lines.size(); i++)
14✔
162
    {
163
        QList <QStringList> line = m_lines[i];
26✔
164
        if (line.isEmpty() == false &&
26✔
165
            line.first().size() == 2 && line.first()[0] == Script::labelCmd)
26✔
166
        {
167
            m_labels[line.first()[1]] = i;
1✔
168
        }
169
    }
170

171
    return true;
1✔
172
}
173

174
bool Script::appendData(const QString &str)
1✔
175
{
176
    m_data.append(str + QString("\n"));
1✔
177
    m_lines << tokenizeLine(str + QString("\n"));
1✔
178

179
    return true;
1✔
180
}
181

182
QString Script::data() const
×
183
{
184
    return m_data;
×
185
}
186

187
QStringList Script::dataLines() const
×
188
{
189
    QStringList result = m_data.split(QRegExp("(\r\n|\n\r|\r|\n)"));
×
190
    while (result.count() && result.last().isEmpty())
×
191
        result.takeLast();
×
192

193
    return result;
×
194
}
195

196
QList<quint32> Script::functionList() const
7✔
197
{
198
    QList<quint32> list;
7✔
199

200
    for (int i = 0; i < m_lines.count(); i++)
14✔
201
    {
202
        QList <QStringList> tokens = m_lines[i];
7✔
203
        if (tokens.isEmpty() == true)
7✔
204
            continue;
×
205

206
        if (tokens[0].size() >= 2 && tokens[0][0] == Script::startFunctionCmd)
7✔
207
        {
208
            list.append(tokens[0][1].toUInt());
7✔
209
            list.append(i);
7✔
210
        }
211
    }
212

213
    return list;
7✔
214
}
215

216
QList<quint32> Script::fixtureList() const
×
217
{
218
    QList<quint32> list;
×
219

220
    for (int i = 0; i < m_lines.count(); i++)
×
221
    {
222
        QList <QStringList> tokens = m_lines[i];
×
223
        if (tokens.isEmpty() == true)
×
224
            continue;
×
225

226
        if (tokens[0].size() >= 2 && tokens[0][0] == Script::setFixtureCmd)
×
227
        {
228
            list.append(tokens[0][1].toUInt());
×
229
            list.append(i);
×
230
        }
231
    }
232

233
    return list;
×
234
}
235

236
QList<int> Script::syntaxErrorsLines()
×
237
{
238
    return m_syntaxErrorLines;
×
239
}
240

241
/****************************************************************************
242
 * Load & Save
243
 ****************************************************************************/
244

245
bool Script::loadXML(QXmlStreamReader &root)
×
246
{
247
    if (root.name() != KXMLQLCFunction)
×
248
    {
249
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
250
        return false;
×
251
    }
252

253
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::ScriptType))
×
254
    {
255
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
256
                   << "is not a script";
×
257
        return false;
×
258
    }
259

260
    /* Load script contents */
261
    while (root.readNextStartElement())
×
262
    {
263
        if (root.name() == KXMLQLCFunctionSpeed)
×
264
        {
265
            loadXMLSpeed(root);
×
266
        }
267
        else if (root.name() == KXMLQLCFunctionDirection)
×
268
        {
269
            loadXMLDirection(root);
×
270
        }
271
        else if (root.name() == KXMLQLCFunctionRunOrder)
×
272
        {
273
            loadXMLRunOrder(root);
×
274
        }
275
        else if (root.name() == KXMLQLCScriptCommand)
×
276
        {
277
            appendData(QUrl::fromPercentEncoding(root.readElementText().toUtf8()));
×
278
            //appendData(tag.text().toUtf8());
279
        }
280
        else
281
        {
282
            qWarning() << Q_FUNC_INFO << "Unknown script tag:" << root.name();
×
283
            root.skipCurrentElement();
×
284
        }
285
    }
286

287
    return true;
×
288
}
289

290
bool Script::saveXML(QXmlStreamWriter *doc)
×
291
{
292
    Q_ASSERT(doc != NULL);
×
293

294
    /* Function tag */
295
    doc->writeStartElement(KXMLQLCFunction);
×
296

297
    /* Common attributes */
298
    saveXMLCommon(doc);
×
299

300
    /* Speed */
301
    saveXMLSpeed(doc);
×
302

303
    /* Direction */
304
    saveXMLDirection(doc);
×
305

306
    /* Run order */
307
    saveXMLRunOrder(doc);
×
308

309
    /* Contents */
NEW
310
    foreach (QString cmd, dataLines())
×
311
    {
312
        doc->writeTextElement(KXMLQLCScriptCommand, QUrl::toPercentEncoding(cmd));
×
313
    }
314

315
    /* End the <Function> tag */
316
    doc->writeEndElement();
×
317

318
    return true;
×
319
}
320

321
/****************************************************************************
322
 * Running
323
 ****************************************************************************/
324

325
void Script::preRun(MasterTimer *timer)
1✔
326
{
327
    // Reset
328
    m_waitCount = 0;
1✔
329
    m_currentCommand = 0;
1✔
330
    m_startedFunctions.clear();
1✔
331

332
    Function::preRun(timer);
1✔
333
}
1✔
334

335
void Script::write(MasterTimer *timer, QList<Universe *> universes)
1✔
336
{
337
    if (stopped() || isPaused())
1✔
338
        return;
×
339

340
    incrementElapsed();
1✔
341

342
    if (waiting() == false)
1✔
343
    {
344
        // Not currently waiting for anything. Free to proceed to next command.
345
        while (m_currentCommand < m_lines.size() && stopped() == false)
4✔
346
        {
347
            bool continueLoop = executeCommand(m_currentCommand, timer, universes);
4✔
348
            m_currentCommand++;
4✔
349
            if (continueLoop == false)
4✔
350
                break; // Executed command told to skip to the next cycle
1✔
351
        }
352

353
        // In case wait() is the last command, don't stop the script prematurely
354
        if (m_currentCommand >= m_lines.size() && m_waitCount == 0)
1✔
355
            stop(FunctionParent::master());
×
356
    }
357

358
    // Handle GenericFader tasks (setltp/sethtp/setfixture)
359
    //if (m_fader != NULL)
360
    //    m_fader->write(universes);
361
}
362

363
void Script::postRun(MasterTimer *timer, QList<Universe *> universes)
1✔
364
{
365
    // Stop all functions started by this script
366
    foreach (Function *function, m_startedFunctions)
1✔
367
        function->stop(FunctionParent::master());
×
368

369
    m_startedFunctions.clear();
1✔
370

371
    dismissAllFaders();
1✔
372

373
    Function::postRun(timer, universes);
1✔
374
}
1✔
375

376
bool Script::waiting()
1✔
377
{
378
    if (m_waitCount > 0)
1✔
379
    {
380
        // Still waiting for at least one cycle.
381
        m_waitCount--;
×
382
        return true;
×
383
    }
384
    else
385
    {
386
        // Not waiting.
387
        return false;
1✔
388
    }
389
}
390

391
quint32 Script::getValueFromString(QString str, bool *ok)
×
392
{
393
    if (str.startsWith("random") == false)
×
394
    {
395
        *ok = true;
×
396
        return Function::stringToSpeed(str);
×
397
    }
398

399
    QString strippedStr = str.remove("random(");
×
400
    strippedStr.remove(")");
×
401
    if (strippedStr.contains(",") == false)
×
402
        return -1;
×
403

404
    QStringList valList = strippedStr.split(",");
×
405
    int min = Function::stringToSpeed(valList.at(0));
×
406
    int max = Function::stringToSpeed(valList.at(1));
×
407

408
    *ok = true;
×
409
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
410
    return qrand() % ((max + 1) - min) + min;
411
#else
412
      return QRandomGenerator::global()->generate() % ((max + 1) - min) + min;
×
413
#endif
414
}
415

416
bool Script::executeCommand(int index, MasterTimer* timer, QList<Universe *> universes)
4✔
417
{
418
    if (index < 0 || index >= m_lines.size())
4✔
419
    {
420
        qWarning() << "Invalid command index:" << index;
×
421
        return false;
×
422
    }
423

424
    QList <QStringList> tokens = m_lines[index];
8✔
425
    if (tokens.isEmpty() == true)
4✔
426
        return true; // Empty line
×
427

428
    bool continueLoop = true;
4✔
429
    QString error;
4✔
430
    if (tokens[0].size() < 2)
4✔
431
    {
432
        error = QString("Syntax error");
1✔
433
    }
434
    else if (tokens[0][0] == Script::startFunctionCmd)
3✔
435
    {
436
        error = handleStartFunction(tokens, timer);
1✔
437
    }
438
    else if (tokens[0][0] == Script::stopFunctionCmd)
2✔
439
    {
440
        error = handleStopFunction(tokens);
1✔
441
    }
442
    else if (tokens[0][0] == Script::blackoutCmd)
1✔
443
    {
444
        error = handleBlackout(tokens);
×
445
        continueLoop = false;
×
446
    }
447
    else if (tokens[0][0] == Script::waitCmd)
1✔
448
    {
449
        // Waiting should break out of the execution loop to prevent skipping
450
        // straight to the next command. If there is no error in wait parsing,
451
        // we must wait at least one cycle.
452
        error = handleWait(tokens);
×
453
        if (error.isEmpty() == true)
×
454
            continueLoop = false;
×
455
    }
456
    else if (tokens[0][0] == Script::waitKeyCmd)
1✔
457
    {
458
        // Waiting for a key should break out of the execution loop to prevent
459
        // skipping straight to the next command. If there is no error in waitkey
460
        // parsing,we must wait at least one cycle.
461
        error = handleWaitKey(tokens);
1✔
462
        if (error.isEmpty() == true)
1✔
463
            continueLoop = false;
1✔
464
    }
465
    else if (tokens[0][0] == Script::setFixtureCmd)
×
466
    {
467
        error = handleSetFixture(tokens, universes);
×
468
    }
469
    else if (tokens[0][0] == Script::systemCmd)
×
470
    {
471
        error = handleSystemCommand(tokens);
×
472
    }
473
    else if (tokens[0][0] == Script::labelCmd)
×
474
    {
475
        error = handleLabel(tokens);
×
476
    }
477
    else if (tokens[0][0] == Script::jumpCmd)
×
478
    {
479
        // Jumping can cause an infinite non-waiting loop, causing starvation
480
        // among other functions. Therefore, the script must relinquish its
481
        // time slot after each jump. If there is no error in jumping, the jump
482
        // must have happened.
483
        error = handleJump(tokens);
×
484
        if (error.isEmpty() == true)
×
485
            continueLoop = false;
×
486
    }
487
    else
488
    {
489
        error = QString("Unknown command: %1").arg(tokens[0][0]);
×
490
    }
491

492
    if (error.isEmpty() == false)
4✔
493
        qWarning() << QString("Script:%1, line:%2, error:%3").arg(name()).arg(index).arg(error);
3✔
494

495
    return continueLoop;
4✔
496
}
497

498
QString Script::handleStartFunction(const QList<QStringList>& tokens, MasterTimer* timer)
1✔
499
{
500
    qDebug() << Q_FUNC_INFO;
1✔
501

502
    if (tokens.size() > 1)
1✔
503
        return QString("Too many arguments");
×
504

505
    bool ok = false;
1✔
506
    quint32 id = tokens[0][1].toUInt(&ok);
1✔
507
    if (ok == false)
1✔
508
        return QString("Invalid function ID: %1").arg(tokens[0][1]);
×
509

510
    Doc* doc = qobject_cast<Doc*> (parent());
1✔
511
    Q_ASSERT(doc != NULL);
1✔
512

513
    Function* function = doc->function(id);
1✔
514
    if (function != NULL)
1✔
515
    {
516
        function->start(timer, FunctionParent::master());
×
517

518
        m_startedFunctions << function;
×
519
        return QString();
×
520
    }
521
    else
522
    {
523
        return QString("No such function (ID %1)").arg(id);
1✔
524
    }
525
}
526

527
QString Script::handleStopFunction(const QList <QStringList>& tokens)
1✔
528
{
529
    qDebug() << Q_FUNC_INFO;
1✔
530

531
    if (tokens.size() > 1)
1✔
532
        return QString("Too many arguments");
×
533

534
    bool ok = false;
1✔
535
    quint32 id = tokens[0][1].toUInt(&ok);
1✔
536
    if (ok == false)
1✔
537
        return QString("Invalid function ID: %1").arg(tokens[0][1]);
×
538

539
    Doc *doc = qobject_cast<Doc*> (parent());
1✔
540
    Q_ASSERT(doc != NULL);
1✔
541

542
    Function *function = doc->function(id);
1✔
543
    if (function != NULL)
1✔
544
    {
545
        function->stop(FunctionParent::master());
×
546

547
        m_startedFunctions.removeAll(function);
×
548
        return QString();
×
549
    }
550
    else
551
    {
552
        return QString("No such function (ID %1)").arg(id);
1✔
553
    }
554
}
555

556
QString Script::handleBlackout(const QList <QStringList>& tokens)
×
557
{
558
    qDebug() << Q_FUNC_INFO;
×
559

560
    if (tokens.size() > 1)
×
561
        return QString("Too many arguments");
×
562

563
    InputOutputMap::BlackoutRequest request = InputOutputMap::BlackoutRequestNone;
×
564

565
    if (tokens[0][1] == blackoutOn)
×
566
    {
567
        request = InputOutputMap::BlackoutRequestOn;
×
568
    }
569
    else if (tokens[0][1] == blackoutOff)
×
570
    {
571
        request = InputOutputMap::BlackoutRequestOff;
×
572
    }
573
    else
574
    {
575
        return QString("Invalid argument: %1").arg(tokens[0][1]);
×
576
    }
577

578
    Doc* doc = qobject_cast<Doc*> (parent());
×
579
    Q_ASSERT(doc != NULL);
×
580

581
    doc->inputOutputMap()->requestBlackout(request);
×
582

583
    return QString();
×
584
}
585

586
QString Script::handleWait(const QList<QStringList>& tokens)
×
587
{
588
    qDebug() << Q_FUNC_INFO;
×
589

590
    if (tokens.size() > 2)
×
591
        return QString("Too many arguments");
×
592

593
    bool ok = false;
×
594
    uint time = getValueFromString(tokens[0][1], &ok);
×
595

596
    qDebug() << "Wait time:" << time;
×
597

598
    m_waitCount = time / MasterTimer::tick();
×
599

600
    return QString();
×
601
}
602

603
QString Script::handleWaitKey(const QList<QStringList>& tokens)
1✔
604
{
605
    qDebug() << Q_FUNC_INFO << tokens;
1✔
606

607
    if (tokens.size() > 1)
1✔
608
        return QString("Too many arguments");
×
609

610
    QString key = QString(tokens[0][1]).remove("\"");
3✔
611
    qDebug() << "Ought to wait for" << key;
1✔
612

613
    return QString();
1✔
614
}
615

616
QString Script::handleSetFixture(const QList<QStringList>& tokens, QList<Universe *> universes)
×
617
{
618
    qDebug() << Q_FUNC_INFO;
×
619

620
    if (tokens.size() > 4)
×
621
        return QString("Too many arguments");
×
622

623
    bool ok = false;
×
624
    quint32 id = 0;
×
625
    quint32 ch = 0;
×
626
    uchar value = 0;
×
627
    double time = 0;
×
628

629
    id = getValueFromString(tokens[0][1], &ok);
×
630
    if (ok == false)
×
631
        return QString("Invalid fixture (ID: %1)").arg(tokens[0][1]);
×
632

633
    for (int i = 1; i < tokens.size(); i++)
×
634
    {
635
        QStringList list = tokens[i];
×
636
        list[0] = list[0].toLower().trimmed();
×
637
        if (list.size() == 2)
×
638
        {
639
            ok = false;
×
640
            if (list[0] == "val" || list[0] == "value")
×
641
                value = uchar(getValueFromString(list[1], &ok));
×
642
            else if (list[0] == "ch" || list[0] == "channel")
×
643
                ch = getValueFromString(list[1], &ok);
×
644
            else if (list[0] == "time")
×
645
                time = getValueFromString(list[1], &ok);
×
646
            else
647
                return QString("Unrecognized keyword: %1").arg(list[0]);
×
648

649
            if (ok == false)
×
650
                return QString("Invalid value (%1) for keyword: %2").arg(list[1]).arg(list[0]);
×
651
        }
652
    }
653

654
    Doc *doc = qobject_cast<Doc*> (parent());
×
655
    Q_ASSERT(doc != NULL);
×
656

657
    Fixture *fxi = doc->fixture(id);
×
658
    if (fxi != NULL)
×
659
    {
660
        if (ch < fxi->channels())
×
661
        {
662
            int address = fxi->address() + ch;
×
663
            if (address < 512)
×
664
            {
665
                quint32 universe = fxi->universe();
×
666
                QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
×
667
                if (fader.isNull())
×
668
                {
669
                    fader = universes[universe]->requestFader();
×
670
                    fader->adjustIntensity(getAttributeValue(Intensity));
×
671
                    fader->setBlendMode(blendMode());
×
672
                    fader->setParentFunctionID(this->id());
×
673
                    fader->setName(name());
×
674
                    m_fadersMap[universe] = fader;
×
675
                }
676

677
                FadeChannel *fc = fader->getChannelFader(doc, universes[universe], fxi->id(), ch);
×
678
                fc->setTarget(value);
×
679
                fc->setFadeTime(time);
×
680

681
                return QString();
×
682
            }
683
            else
684
            {
685
                return QString("Invalid address: %1").arg(address);
×
686
            }
687
        }
688
        else
689
        {
690
            return QString("Fixture (%1) has no channel number %2").arg(fxi->name()).arg(ch);
×
691
        }
692
    }
693
    else
694
    {
695
        return QString("No such fixture (ID: %1)").arg(id);
×
696
    }
697
}
698

699
QString Script::handleSystemCommand(const QList<QStringList> &tokens)
×
700
{
701
    qDebug() << Q_FUNC_INFO;
×
702

703
    QString programName = tokens[0][1];
×
704
    QStringList programArgs;
×
705
    for (int i = 1; i < tokens.size(); i++)
×
706
        programArgs << tokens[i][1];
×
707
#if !defined(Q_OS_IOS)
708
    QProcess *newProcess = new QProcess();
×
709

710
    // startDetached() enables to delete QProcess object without killing actual process
711
    qint64 pid;
712
    newProcess->setProgram(programName);
×
713
    newProcess->setArguments(programArgs);
×
714
    newProcess->startDetached(&pid);
×
715
    delete newProcess;
×
716
#endif
717
    return QString();
×
718
}
719

720
QString Script::handleLabel(const QList<QStringList>& tokens)
×
721
{
722
    // A label just exists. Not much to do here.
723
    qDebug() << Q_FUNC_INFO;
×
724

725
    if (tokens.size() > 1)
×
726
        return QString("Too many arguments");
×
727

728
    return QString();
×
729
}
730

731
QString Script::handleJump(const QList<QStringList>& tokens)
×
732
{
733
    qDebug() << Q_FUNC_INFO;
×
734

735
    if (tokens.size() > 1)
×
736
        return QString("Too many arguments");
×
737

738
    if (m_labels.contains(tokens[0][1]) == true)
×
739
    {
740
        int lineNumber = m_labels[tokens[0][1]];
×
741
        Q_ASSERT(lineNumber >= 0 && lineNumber < m_lines.size());
×
742
        m_currentCommand = lineNumber;
×
743
        return QString();
×
744
    }
745
    else
746
    {
747
        return QString("No such label: %1").arg(tokens[0][1]);
×
748
    }
749
}
750

751
QList <QStringList> Script::tokenizeLine(const QString& str, bool* ok)
14✔
752
{
753
    QList<QStringList> tokens;
14✔
754
    QString keyword;
28✔
755
    QString value;
28✔
756

757
    if (ok != NULL)
14✔
758
        *ok = true; // in case, this is set to false afterwards
13✔
759

760
    if (str.simplified().startsWith("//") == true || str.simplified().isEmpty() == true)
14✔
761
    {
762
        tokens << QStringList(); // Return an empty string list for commented lines
1✔
763
    }
764
    else
765
    {
766
        // Truncate everything after the first comment sign
767
        QString line = str;
26✔
768
        int left = 0;
13✔
769

770
        while (left != -1)
26✔
771
        {
772
            left = line.indexOf("//", left);
13✔
773
            if (left != -1)
13✔
774
            {
775
                // if we stumbled into a URL like http:// or ftp://
776
                // then it's not a comment !
777
                if (line.at(left - 1) != ':')
×
778
                    line.truncate(left);
×
779
                left += 2;
×
780
            }
781
        }
782

783
        left = 0;
13✔
784
        while (left < line.length())
27✔
785
        {
786
            // Find the next colon to get the keyword
787
            int right = line.indexOf(":", left);
16✔
788
            if (right == -1)
16✔
789
            {
790
                qDebug() << "Syntax error:" << line.mid(left);
1✔
791
                if (ok != NULL)
1✔
792
                    *ok = false;
1✔
793
                break;
1✔
794
            }
795
            else
796
            {
797
                // Keyword found
798
                keyword = line.mid(left, right - left);
15✔
799
                left = right + 1;
15✔
800
            }
801

802
            // Try to see if there is a value between quotes
803
            int quoteleft = -1;
15✔
804
            if (line.mid(left, 1) == "\"")
15✔
805
                quoteleft = left + 1;
2✔
806
            if (quoteleft != -1)
15✔
807
            {
808
                int quoteright = line.indexOf("\"", quoteleft + 1);
2✔
809
                if (quoteright != -1)
2✔
810
                {
811
                    // Don't include the "" in the string
812
                    value = line.mid(quoteleft, quoteright - quoteleft);
2✔
813
                    left = quoteright + 2;
2✔
814
                }
815
                else
816
                {
817
                    qDebug() << "Syntax error:" << line.mid(quoteleft);
×
818
                    if (ok != NULL)
×
819
                        *ok = false;
×
820
                    break;
×
821
                }
822
            }
823
            else
824
            {
825
                // No quotes. Find the next whitespace.
826
                right = line.indexOf(QRegExp("\\s"), left);
13✔
827
                if (right == -1)
13✔
828
                {
829
                    qDebug() << "Syntax error:" << line.mid(left);
×
830
                    if (ok != NULL)
×
831
                        *ok = false;
×
832
                    break;
×
833
                }
834
                else
835
                {
836
                    // Value found
837
                    value = line.mid(left, right - left);
13✔
838
                    left = right + 1;
13✔
839
                }
840
            }
841

842
            if (tokens.count() > 0 && knownKeywords.contains(keyword.trimmed()) == false)
15✔
843
            {
844
                qDebug() << "Syntax error. Unknown keyword detected:" << keyword.trimmed();
1✔
845
                if (ok != NULL)
1✔
846
                    *ok = false;
1✔
847
                break;
1✔
848
            }
849
            else
850
                tokens << (QStringList() << keyword.trimmed() << value.trimmed());
14✔
851
        }
852
    }
853

854
    qDebug() << "Tokens:" << tokens;
14✔
855

856
    return tokens;
28✔
857
}
858

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc