Skip to content

Commit

Permalink
Add a function to split strings on spaces, respecting quotes.
Browse files Browse the repository at this point in the history
  • Loading branch information
linuxdude42 committed Jun 20, 2020
1 parent 43086b0 commit 6f8d618
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 6 deletions.
32 changes: 32 additions & 0 deletions mythtv/libs/libmythbase/mythmiscutil.cpp
Expand Up @@ -45,6 +45,8 @@ using namespace std;
#include <QUrl>
#include <QHostAddress>
#include <QDataStream>
#include <QRegularExpression>
#include <QRegularExpressionMatchIterator>

// Myth headers
#include "mythcorecontext.h"
Expand Down Expand Up @@ -1237,4 +1239,34 @@ QString MythFormatTime(int secs, QString fmt)
return QTime::fromMSecsSinceStartOfDay(secs*1000).toString(fmt);
}

QStringList MythSplitCommandString(const QString &line)
{
// Match anything that starts at the beginning of the line or a separator
// Match 1: Any number of characters other than a quote or a separator
// A literal "
// Any number or characters other than a quote or a separator
// A literal "
// Any number or characters other than a quote or a separator
// This should match:
// "abc"
// "abc def"
// abc"def"
// abc"def ghi"jkl
// Match 2: One or more characters other than a separator
// This should match:
// abc
QRegularExpression re(R"((?:^| )([^ "]*"[^"]*"[^ "]*|[^ ]+))");
QRegularExpressionMatchIterator it = re.globalMatch(line.trimmed());

QStringList fields;
while (it.hasNext())
{
QRegularExpressionMatch match = it.next();
if (match.hasMatch())
fields.push_back(match.captured(1));
}

return fields;
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */
2 changes: 2 additions & 0 deletions mythtv/libs/libmythbase/mythmiscutil.h
Expand Up @@ -152,4 +152,6 @@ inline void rdtsc(uint64_t &x)
inline void rdtsc(uint64_t &x) { x = 0ULL; }
#endif // !MMX

MBASE_PUBLIC QStringList MythSplitCommandString(const QString &line);

#endif // MYTHMISCUTIL_H_
1 change: 1 addition & 0 deletions mythtv/libs/libmythbase/test/test_mythmiscutil/.gitignore
@@ -0,0 +1 @@
test_mythmiscutil
@@ -0,0 +1,63 @@
/*
* Class TestMiscUtil
*
* Copyright (c) David Hampton 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <iostream>
#include "test_mythmiscutil.h"

void TestMiscUtil::test_parse_cmdline_data(void)
{
QTest::addColumn<QString>("input");
QTest::addColumn<QStringList>("expectedOutput");

QTest::newRow("simple")
<< R"(This is a test string)"
<< QStringList({"This", "is", "a", "test", "string"});
QTest::newRow("simplequotes")
<< R"(cmd "whatever" "goes" here)"
<< QStringList({R"(cmd)",
R"("whatever")",
R"("goes")",
R"(here)"});
QTest::newRow("multiword")
<< R"(cmd "whatever" "multi-word argument" arg3)"
<< QStringList({R"(cmd)",
R"("whatever")",
R"("multi-word argument")",
R"(arg3)"});
QTest::newRow("mixedargs")
<< R"(cmd --arg1="whatever" --arg2="multi-word argument" --arg3)"
<< QStringList({R"(cmd)",
R"(--arg1="whatever")",
R"(--arg2="multi-word argument")",
R"(--arg3)"});
}

void TestMiscUtil::test_parse_cmdline(void)
{
QFETCH(QString, input);
QFETCH(QStringList, expectedOutput);

QStringList output = MythSplitCommandString(input);
// std::cerr << "Expected: " << qPrintable(expectedOutput.join("|")) << std::endl;
// std::cerr << "Actual: " << qPrintable(output.join("|")) << std::endl;
QCOMPARE(output, expectedOutput);
}


QTEST_APPLESS_MAIN(TestMiscUtil)
33 changes: 33 additions & 0 deletions mythtv/libs/libmythbase/test/test_mythmiscutil/test_mythmiscutil.h
@@ -0,0 +1,33 @@
/*
* Class TestMiscUtil
*
* Copyright (c) David Hampton 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <QtTest/QtTest>
#include <iostream>

#include "mythmiscutil.h"

class TestMiscUtil : public QObject
{
Q_OBJECT

private slots:
static void test_parse_cmdline_data(void);
static void test_parse_cmdline(void);
};
@@ -0,0 +1,26 @@
include ( ../../../../settings.pro )

QT += testlib

TEMPLATE = app
TARGET = test_mythmiscutil
DEPENDPATH += . ../.. ../../logging
INCLUDEPATH += . ../.. ../../logging
LIBS += -L../.. -lmythbase-$$LIBVERSION
LIBS += -Wl,$$_RPATH_$${PWD}/../..

contains(QMAKE_CXX, "g++") {
QMAKE_CXXFLAGS += -O0 -fprofile-arcs -ftest-coverage
QMAKE_LFLAGS += -fprofile-arcs
}

QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../..

# Input
HEADERS += test_mythmiscutil.h
SOURCES += test_mythmiscutil.cpp

QMAKE_CLEAN += $(TARGET)
QMAKE_CLEAN += ; ( cd $(OBJECTS_DIR) && rm -f *.gcov *.gcda *.gcno )

LIBS += $$EXTRA_LIBS $$LATE_LIBS
13 changes: 7 additions & 6 deletions mythtv/programs/mythexternrecorder/MythExternRecApp.cpp
Expand Up @@ -21,6 +21,7 @@
#include <thread>
#include <csignal>
#include "commandlineparser.h"
#include "mythmiscutil.h"
#include "MythExternRecApp.h"

#include <QElapsedTimer>
Expand Down Expand Up @@ -271,7 +272,7 @@ Q_SLOT void MythExternRecApp::Cleanup(void)
if (m_cleanup.isEmpty())
return;

QStringList args = m_cleanup.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(m_cleanup);
QString cmd = args.takeFirst();

LOG(VB_RECORD, LOG_WARNING, LOC +
Expand Down Expand Up @@ -303,7 +304,7 @@ Q_SLOT void MythExternRecApp::DataStarted(void)
if (m_onDataStart.isEmpty())
return;

QStringList args = m_onDataStart.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(m_onDataStart);
QString cmd = args.takeFirst();
cmd.replace("%CHANNUM%", m_tunedChannel);

Expand Down Expand Up @@ -342,7 +343,7 @@ Q_SLOT void MythExternRecApp::LoadChannels(const QString & serial)

if (!m_scanCommand.isEmpty())
{
QStringList args = m_scanCommand.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(m_scanCommand);
QString cmd = args.takeFirst();
cmd.replace("%CHANCONF%", m_channelsIni);

Expand Down Expand Up @@ -457,7 +458,7 @@ Q_SLOT void MythExternRecApp::NextChannel(const QString & serial)

void MythExternRecApp::NewEpisodeStarting(const QString & channum)
{
QStringList args = m_newEpisodeCommand.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(m_newEpisodeCommand);
QString cmd = args.takeFirst();
cmd.replace("%CHANNUM%", channum);

Expand Down Expand Up @@ -572,7 +573,7 @@ Q_SLOT void MythExternRecApp::TuneChannel(const QString & serial,

if (!m_tuneCommand.isEmpty())
{
QStringList args = tunecmd.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(tunecmd);
QString cmd = args.takeFirst();
m_tuningChannel = channum;
m_tuneProc.start(cmd, args);
Expand Down Expand Up @@ -690,7 +691,7 @@ Q_SLOT void MythExternRecApp::StartStreaming(const QString & serial)
return;
}

QStringList args = m_command.split(QRegularExpression("\\s+"));
QStringList args = MythSplitCommandString(m_command);
QString cmd = args.takeFirst();
m_proc.start(cmd, args, QIODevice::ReadOnly|QIODevice::Unbuffered);
m_proc.setTextModeEnabled(false);
Expand Down

0 comments on commit 6f8d618

Please sign in to comment.