diff options
author | Rainer Keller <rainer.keller@digia.com> | 2013-04-17 13:28:36 +0200 |
---|---|---|
committer | Rainer Keller <rainer.keller@digia.com> | 2013-04-17 13:28:36 +0200 |
commit | c47a4ed04da4d8d8f618187f671ed8dcb4ae824a (patch) | |
tree | 8831331988aa97f0e97623377df5076d8ece6a46 | |
parent | 1312d95b33edc640046abe519f2b6be464ddb6d7 (diff) |
Parse extended portlist
-rw-r--r-- | appcontroller.pro | 2 | ||||
-rw-r--r-- | main.cpp | 29 | ||||
-rw-r--r-- | portlist.cpp | 243 | ||||
-rw-r--r-- | portlist.h | 67 |
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 @@ -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 |