summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Keller <rainer.keller@digia.com>2013-04-17 13:28:36 +0200
committerRainer Keller <rainer.keller@digia.com>2013-04-17 13:28:36 +0200
commitc47a4ed04da4d8d8f618187f671ed8dcb4ae824a (patch)
tree8831331988aa97f0e97623377df5076d8ece6a46
parent1312d95b33edc640046abe519f2b6be464ddb6d7 (diff)
Parse extended portlist
-rw-r--r--appcontroller.pro2
-rw-r--r--main.cpp29
-rw-r--r--portlist.cpp243
-rw-r--r--portlist.h67
4 files changed, 324 insertions, 17 deletions
diff --git a/appcontroller.pro b/appcontroller.pro
index 13ffdd9..e850245 100644
--- a/appcontroller.pro
+++ b/appcontroller.pro
@@ -2,10 +2,12 @@ QT-=gui
QT+=network
HEADERS=\
process.h \
+ portlist.h \
SOURCES=\
main.cpp \
process.cpp \
+ portlist.cpp \
target.path = $$[INSTALL_ROOT]/system/bin
INSTALLS+=target
diff --git a/main.cpp b/main.cpp
index c7e1b62..5ab493a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,4 +1,5 @@
#include "process.h"
+#include "portlist.h"
#include <QCoreApplication>
#include <QTcpServer>
#include <QProcess>
@@ -95,13 +96,13 @@ static void stop()
connectSocket();
}
-static int findFirstFreePort(int start, int end)
+static int findFirstFreePort(Utils::PortList range)
{
QTcpServer s;
- for (; start <= end; start++) {
- if (s.listen(QHostAddress::Any, start))
- return start;
+ while (range.hasMore()) {
+ if (s.listen(QHostAddress::Any, range.getNext()))
+ return s.serverPort();
}
return -1;
}
@@ -134,30 +135,24 @@ int main(int argc, char **argv)
}
defaultArgs.append(args);
break;
- } else if (args[0] == "--start-debug") {
+ } else if (args[0] == "--debug") {
debug = true;
- if (args.size() < 4) {
- qWarning("--start-debug requires arguments: start-port-range end-port-range and executable");
+ if (args.size() < 3) {
+ qWarning("--debug requires arguments: port-range and executable");
return 1;
}
- int range_start = args[1].toUInt();
- int range_end = args[2].toUInt();
- binary = args[3];
+ Utils::PortList range = Utils::PortList::fromString(args[1]);
+ binary = args[2];
args.removeFirst();
args.removeFirst();
- args.removeFirst();
- if (range_start == 0 || range_end == 0 || range_start > range_end) {
- qWarning("Invalid port range");
- return 1;
- }
if (binary.isEmpty()) {
qWarning("App path is empty");
return 1;
}
- int port = findFirstFreePort(range_start, range_end);
+ int port = findFirstFreePort(range);
if (port < 0) {
- qWarning("Could not find an unsued port in range");
+ qWarning("Could not find an unused port in range");
return 1;
}
defaultArgs.push_front("localhost:" + QString::number(port));
diff --git a/portlist.cpp b/portlist.cpp
new file mode 100644
index 0000000..054a9a2
--- /dev/null
+++ b/portlist.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+** GNU Lesser General Public License Usage
+** 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "portlist.h"
+
+#include <QList>
+#include <QPair>
+#include <QString>
+
+#include <cctype>
+
+namespace Utils {
+namespace Internal {
+namespace {
+
+typedef QPair<int, int> Range;
+
+class PortsSpecParser
+{
+ struct ParseException {
+ ParseException(const char *error) : error(error) {}
+ const char * const error;
+ };
+
+public:
+ PortsSpecParser(const QString &portsSpec)
+ : m_pos(0), m_portsSpec(portsSpec) { }
+
+ /*
+ * Grammar: Spec -> [ ElemList ]
+ * ElemList -> Elem [ ',' ElemList ]
+ * Elem -> Port [ '-' Port ]
+ */
+ bool parse()
+ {
+ if (!atEnd()) {
+ if (!parseElemList()) {
+ qWarning("Malformed ports specification");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ const PortList &portList() const
+ {
+ return m_portList;
+ }
+
+private:
+ bool parseElemList()
+ {
+ if (atEnd()) {
+ qWarning("Element list empty.");
+ return false;
+ }
+ if (!parseElem())
+ return false;
+ if (atEnd())
+ return true;
+ if (nextChar() != ',') {
+ qWarning("Element followed by something else than a comma.");
+ return false;
+ }
+ ++m_pos;
+ return parseElemList();
+ }
+
+ bool parseElem()
+ {
+ const int startPort = parsePort();
+ if (startPort < 0)
+ return false;
+
+ if (atEnd() || nextChar() != '-') {
+ m_portList.addPort(startPort);
+ return true;
+ }
+ ++m_pos;
+ const int endPort = parsePort();
+ if (endPort < 0)
+ return false;
+
+ if (endPort < startPort) {
+ qWarning("Invalid range (end < start).");
+ return false;
+ }
+ m_portList.addRange(startPort, endPort);
+ return true;
+ }
+
+ int parsePort()
+ {
+ if (atEnd()) {
+ qWarning("Empty port string.");
+ return -1;
+ }
+ int port = 0;
+ do {
+ const char next = nextChar();
+ if (!std::isdigit(next))
+ break;
+ port = 10*port + next - '0';
+ ++m_pos;
+ } while (!atEnd());
+ if (port == 0 || port >= 2 << 16) {
+ qWarning("Invalid port value.");
+ return -1;
+ }
+ return port;
+ }
+
+ bool atEnd() const { return m_pos == m_portsSpec.length(); }
+ char nextChar() const { return m_portsSpec.at(m_pos).toLatin1(); }
+
+ PortList m_portList;
+ int m_pos;
+ const QString &m_portsSpec;
+};
+
+} // anonymous namespace
+
+class PortListPrivate
+{
+public:
+ QList<Range> ranges;
+};
+
+} // namespace Internal
+
+PortList::PortList() : d(new Internal::PortListPrivate)
+{
+}
+
+PortList::PortList(const PortList &other) : d(new Internal::PortListPrivate(*other.d))
+{
+}
+
+PortList::~PortList()
+{
+ delete d;
+}
+
+PortList &PortList::operator=(const PortList &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+PortList PortList::fromString(const QString &portsSpec)
+{
+ Internal::PortsSpecParser p(portsSpec);
+ if (!p.parse()) {
+ qWarning("Could not parse string");
+ }
+ return p.portList();
+}
+
+void PortList::addPort(int port) { addRange(port, port); }
+
+void PortList::addRange(int startPort, int endPort)
+{
+ d->ranges << Internal::Range(startPort, endPort);
+}
+
+bool PortList::hasMore() const { return !d->ranges.isEmpty(); }
+
+bool PortList::contains(int port) const
+{
+ foreach (const Internal::Range &r, d->ranges) {
+ if (port >= r.first && port <= r.second)
+ return true;
+ }
+ return false;
+}
+
+int PortList::count() const
+{
+ int n = 0;
+ foreach (const Internal::Range &r, d->ranges)
+ n += r.second - r.first + 1;
+ return n;
+}
+
+int PortList::getNext()
+{
+ Q_ASSERT(!d->ranges.isEmpty());
+
+ Internal::Range &firstRange = d->ranges.first();
+ const int next = firstRange.first++;
+ if (firstRange.first > firstRange.second)
+ d->ranges.removeFirst();
+ return next;
+}
+
+QString PortList::toString() const
+{
+ QString stringRep;
+ foreach (const Internal::Range &range, d->ranges) {
+ stringRep += QString::number(range.first);
+ if (range.second != range.first)
+ stringRep += QLatin1Char('-') + QString::number(range.second);
+ stringRep += QLatin1Char(',');
+ }
+ if (!stringRep.isEmpty())
+ stringRep.remove(stringRep.length() - 1, 1); // Trailing comma.
+ return stringRep;
+}
+
+QString PortList::regularExpression()
+{
+ const QLatin1String portExpr("(\\d)+");
+ const QString listElemExpr = QString::fromLatin1("%1(-%1)?").arg(portExpr);
+ return QString::fromLatin1("((%1)(,%1)*)?").arg(listElemExpr);
+}
+
+} // namespace Utils
diff --git a/portlist.h b/portlist.h
new file mode 100644
index 0000000..2146c39
--- /dev/null
+++ b/portlist.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+** GNU Lesser General Public License Usage
+** 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef PORTLIST_H
+#define PORTLIST_H
+
+//#include "utils_global.h"
+
+class QString;
+
+namespace Utils {
+namespace Internal {
+class PortListPrivate;
+} // namespace Internal
+
+class PortList
+{
+public:
+ PortList();
+ PortList(const PortList &other);
+ PortList &operator=(const PortList &other);
+ ~PortList();
+
+ void addPort(int port);
+ void addRange(int startPort, int endPort);
+ bool hasMore() const;
+ bool contains(int port) const;
+ int count() const;
+ int getNext();
+ QString toString() const;
+
+ static PortList fromString(const QString &portsSpec);
+ static QString regularExpression();
+
+private:
+ Internal::PortListPrivate * const d;
+};
+
+} // namespace Utils
+
+#endif // PORTLIST_H