diff options
Diffstat (limited to 'src/corelib/tools')
-rw-r--r-- | src/corelib/tools/qcommandlineoption.cpp | 30 | ||||
-rw-r--r-- | src/corelib/tools/qcommandlineoption.h | 3 | ||||
-rw-r--r-- | src/corelib/tools/qcommandlineparser.cpp | 49 | ||||
-rw-r--r-- | src/corelib/tools/qcommandlineparser.h | 6 | ||||
-rw-r--r-- | src/corelib/tools/qringbuffer_p.h | 62 | ||||
-rw-r--r-- | src/corelib/tools/qstring.cpp | 175 | ||||
-rw-r--r-- | src/corelib/tools/qvector.h | 1 |
7 files changed, 241 insertions, 85 deletions
diff --git a/src/corelib/tools/qcommandlineoption.cpp b/src/corelib/tools/qcommandlineoption.cpp index 7f898f68a8..51723f4a57 100644 --- a/src/corelib/tools/qcommandlineoption.cpp +++ b/src/corelib/tools/qcommandlineoption.cpp @@ -42,6 +42,7 @@ class QCommandLineOptionPrivate : public QSharedData { public: inline QCommandLineOptionPrivate() + : hidden(false) { } void setNames(const QStringList &nameList); @@ -58,6 +59,9 @@ public: //! The list of default values used for this option. QStringList defaultValues; + + //! Show or hide in --help + bool hidden; }; /*! @@ -362,4 +366,30 @@ QStringList QCommandLineOption::defaultValues() const return d->defaultValues; } +/*! + Sets whether to hide this option in the user-visible help output. + + All options are visible by default. Setting \a hidden to true for + a particular option makes it internal, i.e. not listed in the help output. + + \since 5.6 + \sa isHidden + */ +void QCommandLineOption::setHidden(bool hide) +{ + d->hidden = hide; +} + +/*! + Returns true if this option is omitted from the help output, + false if the option is listed. + + \since 5.6 + \sa setHidden() + */ +bool QCommandLineOption::isHidden() const +{ + return d->hidden; +} + QT_END_NAMESPACE diff --git a/src/corelib/tools/qcommandlineoption.h b/src/corelib/tools/qcommandlineoption.h index cf4160ecd2..85fc5ca6dd 100644 --- a/src/corelib/tools/qcommandlineoption.h +++ b/src/corelib/tools/qcommandlineoption.h @@ -77,6 +77,9 @@ public: void setDefaultValues(const QStringList &defaultValues); QStringList defaultValues() const; + void setHidden(bool hidden); + bool isHidden() const; + private: QSharedDataPointer<QCommandLineOptionPrivate> d; }; diff --git a/src/corelib/tools/qcommandlineparser.cpp b/src/corelib/tools/qcommandlineparser.cpp index 21bc14a272..7e49253f9b 100644 --- a/src/corelib/tools/qcommandlineparser.cpp +++ b/src/corelib/tools/qcommandlineparser.cpp @@ -53,6 +53,7 @@ class QCommandLineParserPrivate public: inline QCommandLineParserPrivate() : singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions), + optionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions), builtinVersionOption(false), builtinHelpOption(false), needsParsing(true) @@ -103,6 +104,9 @@ public: //! The parsing mode for "-abc" QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode; + //! How to parse "arg -option" + QCommandLineParser::OptionsAfterPositionalArgumentsMode optionsAfterPositionalArgumentsMode; + //! Whether addVersionOption was called bool builtinVersionOption; @@ -299,6 +303,41 @@ void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleD } /*! + \enum QCommandLineParser::OptionsAfterPositionalArgumentsMode + + This enum describes the way the parser interprets options that + occur after positional arguments. + + \value ParseAsOptions \c{application argument --opt -t} is interpreted as setting + the options \c{opt} and \c{t}, just like \c{application --opt -t argument} would do. + This is the default parsing mode. In order to specify that \c{--opt} and \c{-t} + are positional arguments instead, the user can use \c{--}, as in + \c{application argument -- --opt -t}. + + \value ParseAsPositionalArguments \c{application argument --opt} is interpreted as + having two positional arguments, \c{argument} and \c{--opt}. + This mode is useful for executables that aim to launch other executables + (e.g. wrappers, debugging tools, etc.) or that support internal commands + followed by options for the command. \c{argument} is the name of the command, + and all options occurring after it can be collected and parsed by another + command line parser, possibly in another executable. + + \sa setOptionsAfterPositionalArgumentsMode() + + \since 5.5 +*/ + +/*! + Sets the parsing mode to \a parsingMode. + This must be called before process() or parse(). + \since 5.5 +*/ +void QCommandLineParser::setOptionsAfterPositionalArgumentsMode(QCommandLineParser::OptionsAfterPositionalArgumentsMode parsingMode) +{ + d->optionsAfterPositionalArgumentsMode = parsingMode; +} + +/*! Adds the option \a option to look for while parsing. Returns \c true if adding the option was successful; otherwise returns \c false. @@ -640,7 +679,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args) const QLatin1Char dashChar('-'); const QLatin1Char assignChar('='); - bool doubleDashFound = false; + bool forcePositional = false; errorText.clear(); positionalArgumentList.clear(); optionNames.clear(); @@ -658,7 +697,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args) for (; argumentIterator != args.end() ; ++argumentIterator) { QString argument = *argumentIterator; - if (doubleDashFound) { + if (forcePositional) { positionalArgumentList.append(argument); } else if (argument.startsWith(doubleDashString)) { if (argument.length() > 2) { @@ -670,7 +709,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args) error = true; } } else { - doubleDashFound = true; + forcePositional = true; } } else if (argument.startsWith(dashChar)) { if (argument.size() == 1) { // single dash ("stdin") @@ -722,6 +761,8 @@ bool QCommandLineParserPrivate::parse(const QStringList &args) } } else { positionalArgumentList.append(argument); + if (optionsAfterPositionalArgumentsMode == QCommandLineParser::ParseAsPositionalArguments) + forcePositional = true; } if (argumentIterator == args.end()) break; @@ -1062,6 +1103,8 @@ QString QCommandLineParserPrivate::helpText() const ++longestOptionNameString; for (int i = 0; i < commandLineOptionList.count(); ++i) { const QCommandLineOption &option = commandLineOptionList.at(i); + if (option.isHidden()) + continue; text += wrapText(optionNameList.at(i), longestOptionNameString, option.description()); } if (!positionalArgumentDefinitions.isEmpty()) { diff --git a/src/corelib/tools/qcommandlineparser.h b/src/corelib/tools/qcommandlineparser.h index 91a799b4d5..8c528ba69e 100644 --- a/src/corelib/tools/qcommandlineparser.h +++ b/src/corelib/tools/qcommandlineparser.h @@ -57,6 +57,12 @@ public: }; void setSingleDashWordOptionMode(SingleDashWordOptionMode parsingMode); + enum OptionsAfterPositionalArgumentsMode { + ParseAsOptions, + ParseAsPositionalArguments + }; + void setOptionsAfterPositionalArgumentsMode(OptionsAfterPositionalArgumentsMode mode); + bool addOption(const QCommandLineOption &commandLineOption); bool addOptions(const QList<QCommandLineOption> &options); diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index ead85e9da0..6f4ebcf420 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -58,7 +58,7 @@ public: buffers.append(QByteArray()); } - inline int nextDataBlockSize() const { + inline qint64 nextDataBlockSize() const { return (tailBuffer == 0 ? tail : buffers.first().size()) - head; } @@ -86,9 +86,9 @@ public: return 0; } - inline void free(int bytes) { + inline void free(qint64 bytes) { while (bytes > 0) { - int blockSize = buffers.first().size() - head; + const qint64 blockSize = buffers.first().size() - head; if (tailBuffer == 0 || blockSize > bytes) { // keep a single block around if it does not exceed @@ -102,7 +102,8 @@ public: clear(); // try to minify/squeeze us } } else { - head += bytes; + Q_ASSERT(quint64(bytes) < QByteArray::MaxSize); + head += int(bytes); bufferSize -= bytes; } return; @@ -116,13 +117,15 @@ public: } } - inline char *reserve(int bytes) { - if (bytes <= 0) + inline char *reserve(qint64 bytes) { + if (bytes <= 0 || quint64(bytes) >= QByteArray::MaxSize) return 0; + const qint64 newSize = bytes + tail; // if need buffer reallocation - if (tail + bytes > buffers.last().size()) { - if (tail + bytes > buffers.last().capacity() && tail >= basicBlockSize) { + if (newSize > buffers.last().size()) { + if (newSize > buffers.last().capacity() && (tail >= basicBlockSize + || quint64(newSize) >= QByteArray::MaxSize)) { // shrink this buffer to its current size buffers.last().resize(tail); @@ -131,21 +134,22 @@ public: ++tailBuffer; tail = 0; } - buffers.last().resize(qMax(basicBlockSize, tail + bytes)); + buffers.last().resize(qMax(basicBlockSize, tail + int(bytes))); } char *writePtr = buffers.last().data() + tail; bufferSize += bytes; - tail += bytes; + Q_ASSERT(quint64(bytes) < QByteArray::MaxSize); + tail += int(bytes); return writePtr; } - inline void truncate(int pos) { + inline void truncate(qint64 pos) { if (pos < size()) chop(size() - pos); } - inline void chop(int bytes) { + inline void chop(qint64 bytes) { while (bytes > 0) { if (tailBuffer == 0 || tail > bytes) { // keep a single block around if it does not exceed @@ -159,7 +163,8 @@ public: clear(); // try to minify/squeeze us } } else { - tail -= bytes; + Q_ASSERT(quint64(bytes) < QByteArray::MaxSize); + tail -= int(bytes); bufferSize -= bytes; } return; @@ -206,7 +211,7 @@ public: ++bufferSize; } - inline int size() const { + inline qint64 size() const { return bufferSize; } @@ -219,9 +224,9 @@ public: bufferSize = 0; } - inline int indexOf(char c) const { - int index = 0; - int j = head; + inline qint64 indexOf(char c) const { + qint64 index = 0; + qint64 j = head; for (int i = 0; i < buffers.size(); ++i) { const char *ptr = buffers[i].constData() + j; j = index + (i == tailBuffer ? tail : buffers[i].size()) - j; @@ -236,9 +241,9 @@ public: return -1; } - inline int indexOf(char c, int maxLength) const { - int index = 0; - int j = head; + inline qint64 indexOf(char c, qint64 maxLength) const { + qint64 index = 0; + qint64 j = head; for (int i = 0; index < maxLength && i < buffers.size(); ++i) { const char *ptr = buffers[i].constData() + j; j = qMin(index + (i == tailBuffer ? tail : buffers[i].size()) - j, maxLength); @@ -253,11 +258,12 @@ public: return -1; } - inline int read(char *data, int maxLength) { - int bytesToRead = qMin(size(), maxLength); - int readSoFar = 0; + inline qint64 read(char *data, qint64 maxLength) { + const qint64 bytesToRead = qMin(size(), maxLength); + qint64 readSoFar = 0; while (readSoFar < bytesToRead) { - int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, nextDataBlockSize()); + const qint64 bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, + nextDataBlockSize()); if (data) memcpy(data + readSoFar, readPointer(), bytesToReadFromThisBlock); readSoFar += bytesToReadFromThisBlock; @@ -300,15 +306,15 @@ public: bufferSize += tail; } - inline int skip(int length) { + inline qint64 skip(qint64 length) { return read(0, length); } - inline int readLine(char *data, int maxLength) { + inline qint64 readLine(char *data, qint64 maxLength) { if (!data || --maxLength <= 0) return -1; - int i = indexOf('\n', maxLength); + qint64 i = indexOf('\n', maxLength); i = read(data, i >= 0 ? (i + 1) : maxLength); // Terminate it. @@ -325,7 +331,7 @@ private: int head, tail; int tailBuffer; // always buffers.size() - 1 const int basicBlockSize; - int bufferSize; + qint64 bufferSize; }; QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index e74a8eebee..8998b22be8 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -7600,86 +7600,155 @@ static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999) return -1; } +/* + Algorithm for multiArg: + + 1. Parse the string as a sequence of verbatim text and placeholders (%L?\d{,3}). + The L is parsed and accepted for compatibility with non-multi-arg, but since + multiArg only accepts strings as replacements, the localization request can + be safely ignored. + 2. The result of step (1) is a list of (string-ref,int)-tuples. The string-ref + either points at text to be copied verbatim (in which case the int is -1), + or, initially, at the textual representation of the placeholder. In that case, + the int contains the numerical number as parsed from the placeholder. + 3. Next, collect all the non-negative ints found, sort them in ascending order and + remove duplicates. + 3a. If the result has more entires than multiArg() was given replacement strings, + we have found placeholders we can't satisfy with replacement strings. That is + fine (there could be another .arg() call coming after this one), so just + truncate the result to the number of actual multiArg() replacement strings. + 3b. If the result has less entries than multiArg() was given replacement strings, + the string is missing placeholders. This is an error that the user should be + warned about. + 4. The result of step (3) is a mapping from the index of any replacement string to + placeholder number. This is the wrong way around, but since placeholder + numbers could get as large as 999, while we typically don't have more than 9 + replacement strings, we trade 4K of sparsely-used memory for doing a reverse lookup + each time we need to map a placeholder number to a replacement string index + (that's a linear search; but still *much* faster than using an associative container). + 5. Next, for each of the tuples found in step (1), do the following: + 5a. If the int is negative, do nothing. + 5b. Otherwise, if the int is found in the result of step (3) at index I, replace + the string-ref with a string-ref for the (complete) I'th replacement string. + 5c. Otherwise, do nothing. + 6. Concatenate all string refs into a single result string. +*/ + namespace { -class ArgMapper { - QVarLengthArray<int, 16> argPosToNumberMap; // maps from argument position to number -public: - void found(int n) { argPosToNumberMap.push_back(n); } +struct Part +{ + Part() : stringRef(), number(0) {} + Part(const QString &s, int pos, int len, int num = -1) Q_DECL_NOTHROW + : stringRef(&s, pos, len), number(num) {} - struct AssignmentResult { - int numArgs; - int lastNumber; - }; + QStringRef stringRef; + int number; +}; +} // unnamed namespace - AssignmentResult assignArgumentNumberToEachOfTheNs(int numArgs) - { - std::sort(argPosToNumberMap.begin(), argPosToNumberMap.end()); - argPosToNumberMap.erase(std::unique(argPosToNumberMap.begin(), argPosToNumberMap.end()), - argPosToNumberMap.end()); +template <> +class QTypeInfo<Part> : public QTypeInfoMerger<Part, QStringRef, int> {}; // Q_DECLARE_METATYPE - if (argPosToNumberMap.size() > numArgs) - argPosToNumberMap.resize(numArgs); - int lastNumber = argPosToNumberMap.empty() ? -1 : argPosToNumberMap.back(); - int arg = argPosToNumberMap.size(); +namespace { - const AssignmentResult result = {arg, lastNumber}; - return result; - } +enum { ExpectedParts = 32 }; - int numberToArgsIndex(int number) const - { - if (number != -1) { - const int * const it = std::find(argPosToNumberMap.begin(), argPosToNumberMap.end(), number); - return it == argPosToNumberMap.end() ? -1 : it - argPosToNumberMap.begin(); - } else { - return -1; - } - } -}; -} // unnamed namespace +typedef QVarLengthArray<Part, ExpectedParts> ParseResult; +typedef QVarLengthArray<int, ExpectedParts/2> ArgIndexToPlaceholderMap; -QString QString::multiArg(int numArgs, const QString **args) const +static ParseResult parseMultiArgFormatString(const QString &s) { - QString result; - ArgMapper mapper; - const QChar *uc = (const QChar *) d->data(); - const int len = d->size; + ParseResult result; + + const QChar *uc = s.constData(); + const int len = s.size(); const int end = len - 1; int i = 0; + int last = 0; - // populate the arg-mapper with the %n's that actually occur in the string while (i < end) { if (uc[i] == QLatin1Char('%')) { + int percent = i; int number = getEscape(uc, &i, len); if (number != -1) { - mapper.found(number); + if (last != percent) + result.push_back(Part(s, last, percent - last)); // literal text (incl. failed placeholders) + result.push_back(Part(s, percent, i - percent, number)); // parsed placeholder + last = i; continue; } } ++i; } - const ArgMapper::AssignmentResult r = mapper.assignArgumentNumberToEachOfTheNs(numArgs); + if (last < len) + result.push_back(Part(s, last, len - last)); // trailing literal text + + return result; +} - // sanity - if (numArgs > r.numArgs) { - qWarning("QString::arg: %d argument(s) missing in %s", numArgs - r.numArgs, toLocal8Bit().data()); - numArgs = r.numArgs; +static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult &parts) +{ + ArgIndexToPlaceholderMap result; + + for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) { + if (it->number >= 0) + result.push_back(it->number); } - i = 0; - while (i < len) { - if (uc[i] == QLatin1Char('%') && i != end) { - int number = getEscape(uc, &i, len, r.lastNumber); - int arg = mapper.numberToArgsIndex(number); - if (number != -1 && arg != -1) { - result += *args[arg]; - continue; - } + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), + result.end()); + + return result; +} + +static int resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[]) +{ + int totalSize = 0; + for (ParseResult::iterator pit = parts.begin(), end = parts.end(); pit != end; ++pit) { + if (pit->number != -1) { + const ArgIndexToPlaceholderMap::const_iterator ait + = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), pit->number); + if (ait != argIndexToPlaceholderMap.end()) + pit->stringRef = QStringRef(args[ait - argIndexToPlaceholderMap.begin()]); } - result += uc[i++]; + totalSize += pit->stringRef.size(); } + return totalSize; +} + +} // unnamed namespace + +QString QString::multiArg(int numArgs, const QString **args) const +{ + // Step 1-2 above + ParseResult parts = parseMultiArgFormatString(*this); + + // 3-4 + ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts); + + if (argIndexToPlaceholderMap.size() > numArgs) // 3a + argIndexToPlaceholderMap.resize(numArgs); + else if (argIndexToPlaceholderMap.size() < numArgs) // 3b + qWarning("QString::arg: %d argument(s) missing in %s", + numArgs - argIndexToPlaceholderMap.size(), toLocal8Bit().data()); + + // 5 + const int totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args); + + // 6: + QString result(totalSize, Qt::Uninitialized); + QChar *out = result.data(); + + for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) { + if (const int sz = it->stringRef.size()) { + memcpy(out, it->stringRef.constData(), sz * sizeof(QChar)); + out += sz; + } + } + return result; } diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 9d5b749e79..a48269cfaa 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -265,7 +265,6 @@ private: { return (i <= d->end()) && (d->begin() <= i); } - iterator c2m(const_iterator it) { return begin() + (it - cbegin()); } class AlignmentDummy { Data header; T array[1]; }; }; |