diff options
Diffstat (limited to 'src/corelib/tools/qregularexpression.cpp')
-rw-r--r-- | src/corelib/tools/qregularexpression.cpp | 565 |
1 files changed, 323 insertions, 242 deletions
diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 4a30daa72c..88b696f53a 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. -** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Copyright (C) 2016 Giuseppe D'Angelo <dangelog@gmail.com>. +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -45,7 +45,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qhashfunctions.h> -#include <QtCore/qmutex.h> +#include <QtCore/qreadwritelock.h> #include <QtCore/qvector.h> #include <QtCore/qstringlist.h> #include <QtCore/qdebug.h> @@ -54,7 +54,9 @@ #include <QtCore/qatomic.h> #include <QtCore/qdatastream.h> -#include <pcre.h> +#define PCRE2_CODE_UNIT_WIDTH 16 + +#include <pcre2.h> QT_BEGIN_NAMESPACE @@ -443,19 +445,25 @@ QT_BEGIN_NAMESPACE Other differences are outlined below. - \section2 Exact matching + \section2 Porting from QRegExp::exactMatch() QRegExp::exactMatch() in Qt 4 served two purposes: it exactly matched a regular expression against a subject string, and it implemented partial - matching. In fact, if an exact match was not found, one could still find - out how much of the subject string was matched by the regular expression - by calling QRegExp::matchedLength(). If the returned length was equal - to the subject string's length, then one could desume that a partial match - was found. + matching. - QRegularExpression supports partial matching explicitly by means of the - appropriate MatchType. If instead you simply want to be sure that the - subject string matches the regular expression exactly, you can wrap the + \section3 Porting from QRegExp's Exact Matching + + Exact matching indicates whether the regular expression matches the entire + subject string. For example, the classes yield on the subject string \c{"abc123"}: + + \table + \header \li \li QRegExp::exactMatch() \li QRegularExpressionMatch::hasMatch() + \row \li \c{"\\d+"} \li \b false \li \b true + \row \li \c{"[a-z]+\\d+"} \li \b true \li \b true + \endtable + + Exact matching is not reflected in QRegularExpression. If you want to be + sure that the subject string matches the regular expression exactly, you can wrap the pattern between a couple of anchoring expressions. Simply putting the pattern between the \c{^} and the \c{$} anchors is enough in most cases: @@ -477,6 +485,17 @@ QT_BEGIN_NAMESPACE Note the usage of the non-capturing group in order to preserve the meaning of the branch operator inside the pattern. + \section3 Porting from QRegExp's Partial Matching + + When using QRegExp::exactMatch(), if an exact match was not found, one + could still find out how much of the subject string was matched by the + regular expression by calling QRegExp::matchedLength(). If the returned length + was equal to the subject string's length, then one could conclude that a partial + match was found. + + QRegularExpression supports partial matching explicitly by means of the + appropriate MatchType. + \section2 Global matching Due to limitations of the QRegExp API it was impossible to implement global @@ -548,7 +567,7 @@ QT_BEGIN_NAMESPACE \inmodule QtCore \reentrant - \brief The QRegularExpressionMatch class provides the results of matching + \brief The QRegularExpressionMatch class provides the results of a matching a QRegularExpression against a string. \since 5.0 @@ -789,19 +808,19 @@ static int convertToPcreOptions(QRegularExpression::PatternOptions patternOption int options = 0; if (patternOptions & QRegularExpression::CaseInsensitiveOption) - options |= PCRE_CASELESS; + options |= PCRE2_CASELESS; if (patternOptions & QRegularExpression::DotMatchesEverythingOption) - options |= PCRE_DOTALL; + options |= PCRE2_DOTALL; if (patternOptions & QRegularExpression::MultilineOption) - options |= PCRE_MULTILINE; + options |= PCRE2_MULTILINE; if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) - options |= PCRE_EXTENDED; + options |= PCRE2_EXTENDED; if (patternOptions & QRegularExpression::InvertedGreedinessOption) - options |= PCRE_UNGREEDY; + options |= PCRE2_UNGREEDY; if (patternOptions & QRegularExpression::DontCaptureOption) - options |= PCRE_NO_AUTO_CAPTURE; + options |= PCRE2_NO_AUTO_CAPTURE; if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) - options |= PCRE_UCP; + options |= PCRE2_UCP; return options; } @@ -814,7 +833,9 @@ static int convertToPcreOptions(QRegularExpression::MatchOptions matchOptions) int options = 0; if (matchOptions & QRegularExpression::AnchoredMatchOption) - options |= PCRE_ANCHORED; + options |= PCRE2_ANCHORED; + if (matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) + options |= PCRE2_NO_UTF_CHECK; return options; } @@ -856,20 +877,16 @@ struct QRegularExpressionPrivate : QSharedData QRegularExpression::PatternOptions patternOptions; QString pattern; - // *All* of the following members are set managed while holding this mutex, + // *All* of the following members are managed while holding this mutex, // except for isDirty which is set to true by QRegularExpression setters // (right after a detach happened). - // On the other hand, after the compilation and studying, - // it's safe to *use* (i.e. read) them from multiple threads at the same time. - // Therefore, doMatch doesn't need to lock this mutex. - QMutex mutex; + mutable QReadWriteLock mutex; - // The PCRE pointers are reference-counted by the QRegularExpressionPrivate + // The PCRE code pointer is reference-counted by the QRegularExpressionPrivate // objects themselves; when the private is copied (i.e. a detach happened) // they are set to 0 - pcre16 *compiledPattern; - QAtomicPointer<pcre16_extra> studyData; - const char *errorString; + pcre2_code_16 *compiledPattern; + int errorCode; int errorOffset; int capturingCount; unsigned int usedCount; @@ -884,8 +901,7 @@ struct QRegularExpressionMatchPrivate : QSharedData int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount = 0); + QRegularExpression::MatchOptions matchOptions); QRegularExpressionMatch nextMatch() const; @@ -934,10 +950,13 @@ QRegularExpression::QRegularExpression(QRegularExpressionPrivate &dd) \internal */ QRegularExpressionPrivate::QRegularExpressionPrivate() - : patternOptions(0), pattern(), + : QSharedData(), + patternOptions(0), + pattern(), mutex(), - compiledPattern(0), studyData(0), - errorString(0), errorOffset(-1), + compiledPattern(0), + errorCode(0), + errorOffset(-1), capturingCount(0), usedCount(0), usingCrLfNewlines(false), @@ -964,13 +983,16 @@ QRegularExpressionPrivate::~QRegularExpressionPrivate() */ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPrivate &other) : QSharedData(other), - patternOptions(other.patternOptions), pattern(other.pattern), + patternOptions(other.patternOptions), + pattern(other.pattern), mutex(), - compiledPattern(0), studyData(0), - errorString(0), - errorOffset(-1), capturingCount(0), + compiledPattern(0), + errorCode(0), + errorOffset(-1), + capturingCount(0), usedCount(0), - usingCrLfNewlines(false), isDirty(true) + usingCrLfNewlines(false), + isDirty(true) { } @@ -979,14 +1001,13 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri */ void QRegularExpressionPrivate::cleanCompiledPattern() { - pcre16_free(compiledPattern); - pcre16_free_study(studyData.load()); - usedCount = 0; + pcre2_code_free_16(compiledPattern); compiledPattern = 0; - studyData.store(0); - usingCrLfNewlines = false; + errorCode = 0; errorOffset = -1; capturingCount = 0; + usedCount = 0; + usingCrLfNewlines = false; } /*! @@ -994,7 +1015,7 @@ void QRegularExpressionPrivate::cleanCompiledPattern() */ void QRegularExpressionPrivate::compilePattern() { - QMutexLocker lock(&mutex); + const QWriteLocker lock(&mutex); if (!isDirty) return; @@ -1003,18 +1024,23 @@ void QRegularExpressionPrivate::compilePattern() cleanCompiledPattern(); int options = convertToPcreOptions(patternOptions); - options |= PCRE_UTF16; + options |= PCRE2_UTF; - int errorCode; - compiledPattern = pcre16_compile2(pattern.utf16(), options, - &errorCode, &errorString, &errorOffset, 0); + PCRE2_SIZE patternErrorOffset; + compiledPattern = pcre2_compile_16(pattern.utf16(), + pattern.length(), + options, + &errorCode, + &patternErrorOffset, + NULL); - if (!compiledPattern) + if (!compiledPattern) { + errorOffset = static_cast<int>(patternErrorOffset); return; - - Q_ASSERT(errorCode == 0); - Q_ASSERT(studyData.load() == 0); // studying (=>optimizing) is always done later - errorOffset = -1; + } else { + // ignore whatever PCRE2 wrote into errorCode -- leave it to 0 to mean "no error" + errorCode = 0; + } getPatternInfo(); } @@ -1025,53 +1051,31 @@ void QRegularExpressionPrivate::compilePattern() void QRegularExpressionPrivate::getPatternInfo() { Q_ASSERT(compiledPattern); - Q_ASSERT(studyData.load() == 0); - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount); + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_CAPTURECOUNT, &capturingCount); // detect the settings for the newline - unsigned long int patternNewlineSetting; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_OPTIONS, &patternNewlineSetting); - patternNewlineSetting &= PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF - | PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; - if (patternNewlineSetting == 0) { + unsigned int patternNewlineSetting; + if (pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_NEWLINE, &patternNewlineSetting) != 0) { // no option was specified in the regexp, grab PCRE build defaults - int pcreNewlineSetting; - pcre16_config(PCRE_CONFIG_NEWLINE, &pcreNewlineSetting); - switch (pcreNewlineSetting) { - case 13: - patternNewlineSetting = PCRE_NEWLINE_CR; break; - case 10: - patternNewlineSetting = PCRE_NEWLINE_LF; break; - case 3338: // (13<<8 | 10) - patternNewlineSetting = PCRE_NEWLINE_CRLF; break; - case -2: - patternNewlineSetting = PCRE_NEWLINE_ANYCRLF; break; - case -1: - patternNewlineSetting = PCRE_NEWLINE_ANY; break; - default: - qWarning("QRegularExpressionPrivate::compilePattern(): " - "PCRE_CONFIG_NEWLINE returned an unknown newline"); - break; - } + pcre2_config_16(PCRE2_CONFIG_NEWLINE, &patternNewlineSetting); } - usingCrLfNewlines = (patternNewlineSetting == PCRE_NEWLINE_CRLF) || - (patternNewlineSetting == PCRE_NEWLINE_ANY) || - (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); + usingCrLfNewlines = (patternNewlineSetting == PCRE2_NEWLINE_CRLF) || + (patternNewlineSetting == PCRE2_NEWLINE_ANY) || + (patternNewlineSetting == PCRE2_NEWLINE_ANYCRLF); - int hasJOptionChanged; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_JCHANGED, &hasJOptionChanged); - if (hasJOptionChanged) { - qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n" - " is using the (?J) option; duplicate capturing group names are not supported by Qt", + unsigned int hasJOptionChanged; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_JCHANGED, &hasJOptionChanged); + if (Q_UNLIKELY(hasJOptionChanged)) { + qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n is using the (?J) option; duplicate capturing group names are not supported by Qt", qPrintable(pattern)); } } /* - Simple "smartpointer" wrapper around a pcre_jit_stack, to be used with + Simple "smartpointer" wrapper around a pcre2_jit_stack_16, to be used with QThreadStorage. */ class QPcreJitStackPointer @@ -1086,7 +1090,7 @@ public: { // The default JIT stack size in PCRE is 32K, // we allocate from 32K up to 512K. - stack = pcre16_jit_stack_alloc(32*1024, 512*1024); + stack = pcre2_jit_stack_create_16(32 * 1024, 512 * 1024, NULL); } /*! \internal @@ -1094,10 +1098,10 @@ public: ~QPcreJitStackPointer() { if (stack) - pcre16_jit_stack_free(stack); + pcre2_jit_stack_free_16(stack); } - pcre16_jit_stack *stack; + pcre2_jit_stack_16 *stack; }; Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) @@ -1105,7 +1109,7 @@ Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) /*! \internal */ -static pcre16_jit_stack *qtPcreCallback(void *) +static pcre2_jit_stack_16 *qtPcreCallback(void *) { if (jitStacks()->hasLocalData()) return jitStacks()->localData()->stack; @@ -1135,53 +1139,32 @@ static bool isJitEnabled() /*! \internal - The purpose of the function is to call pcre16_study (which allows some - optimizations to be performed, including JIT-compiling the pattern), and - setting the studyData member variable to the result of the study. It gets - called by doMatch() every time a match is performed. As of now, the - optimizations on the pattern are performed after a certain number of usages - (i.e. the qt_qregularexpression_optimize_after_use_count constant) unless - the DontAutomaticallyOptimizeOption option is set on the QRegularExpression - object, or anyhow by calling optimize() (which will pass - ImmediateOptimizeOption). + The purpose of the function is to call pcre2_jit_compile_16, which + JIT-compiles the pattern. - Notice that although the method is protected by a mutex, one thread may - invoke this function and return immediately (i.e. not study the pattern, - leaving studyData to NULL); but before calling pcre16_exec to perform the - match, another thread performs the studying and sets studyData to something - else. Although the assignment to studyData is itself atomic, the release of - the memory pointed by studyData isn't. Therefore, we work on a local copy - (localStudyData) before using storeRelease on studyData. In doMatch there's - the corresponding loadAcquire. + It gets called by doMatch() every time a match is performed. + + As of now, the optimizations on the pattern are performed after a certain + number of usages (i.e. the qt_qregularexpression_optimize_after_use_count + constant) unless the DontAutomaticallyOptimizeOption option is set on the + QRegularExpression object, or anyhow by calling optimize() (which will pass + ImmediateOptimizeOption). */ void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) { Q_ASSERT(compiledPattern); - QMutexLocker lock(&mutex); + static const bool enableJit = isJitEnabled(); - if (studyData.load()) // already optimized + if (!enableJit) return; + const QWriteLocker lock(&mutex); + if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count)) return; - static const bool enableJit = isJitEnabled(); - - int studyOptions = 0; - if (enableJit) - studyOptions |= (PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE | PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE); - - const char *err; - pcre16_extra * const localStudyData = pcre16_study(compiledPattern, studyOptions, &err); - - if (localStudyData && localStudyData->flags & PCRE_EXTRA_EXECUTABLE_JIT) - pcre16_assign_jit_stack(localStudyData, qtPcreCallback, 0); - - if (!localStudyData && err) - qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); - - studyData.storeRelease(localStudyData); + pcre2_jit_compile_16(compiledPattern, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD); } /*! @@ -1197,7 +1180,7 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const if (!compiledPattern) return -1; - int index = pcre16_get_stringnumber(compiledPattern, name.utf16()); + int index = pcre2_substring_number_from_name_16(compiledPattern, name.utf16()); if (index >= 0) return index; @@ -1207,24 +1190,25 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const /*! \internal - This is a simple wrapper for pcre16_exec for handling the case in which the + This is a simple wrapper for pcre2_match_16 for handling the case in which the JIT runs out of memory. In that case, we allocate a thread-local JIT stack - and re-run pcre16_exec. + and re-run pcre2_match_16. */ -static int pcre16SafeExec(const pcre16 *code, const pcre16_extra *extra, - const unsigned short *subject, int length, - int startOffset, int options, - int *ovector, int ovecsize) +static int safe_pcre2_match_16(const pcre2_code_16 *code, + const unsigned short *subject, int length, + int startOffset, int options, + pcre2_match_data_16 *matchData, + pcre2_match_context_16 *matchContext) { - int result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + int result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); - if (result == PCRE_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { + if (result == PCRE2_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { QPcreJitStackPointer *p = new QPcreJitStackPointer; jitStacks()->setLocalData(p); - result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); } return result; @@ -1273,29 +1257,24 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString QRegularExpression re(*const_cast<QRegularExpressionPrivate *>(this)); + QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, + subjectStart, subjectLength, + matchType, matchOptions); + if (offset < 0 || offset > subjectLength) - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; - if (!compiledPattern) { + if (Q_UNLIKELY(!compiledPattern)) { qWarning("QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; } // skip optimizing and doing the actual matching if NoMatch type was requested if (matchType == QRegularExpression::NoMatch) { - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions); priv->isValid = true; return priv; } - // capturingCount doesn't include the implicit "0" capturing group - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions, - capturingCount + 1); - if (!(patternOptions & QRegularExpression::DontAutomaticallyOptimizeOption)) { const OptimizePatternOption optimizePatternOption = (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) @@ -1306,22 +1285,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption); } - // work with a local copy of the study data, as we are running pcre_exec - // potentially more than once, and we don't want to run call it - // with different study data - const pcre16_extra * const currentStudyData = studyData.loadAcquire(); - int pcreOptions = convertToPcreOptions(matchOptions); if (matchType == QRegularExpression::PartialPreferCompleteMatch) - pcreOptions |= PCRE_PARTIAL_SOFT; + pcreOptions |= PCRE2_PARTIAL_SOFT; else if (matchType == QRegularExpression::PartialPreferFirstMatch) - pcreOptions |= PCRE_PARTIAL_HARD; + pcreOptions |= PCRE2_PARTIAL_HARD; - if (checkSubjectStringOption == DontCheckSubjectString - || matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) { - pcreOptions |= PCRE_NO_UTF16_CHECK; - } + if (checkSubjectStringOption == DontCheckSubjectString) + pcreOptions |= PCRE2_NO_UTF_CHECK; bool previousMatchWasEmpty = false; if (previous && previous->hasMatch && @@ -1329,25 +1301,28 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString previousMatchWasEmpty = true; } - int * const captureOffsets = priv->capturedOffsets.data(); - const int captureOffsetsCount = priv->capturedOffsets.size(); + pcre2_match_context_16 *matchContext = pcre2_match_context_create_16(NULL); + pcre2_jit_stack_assign_16(matchContext, &qtPcreCallback, NULL); + pcre2_match_data_16 *matchData = pcre2_match_data_create_from_pattern_16(compiledPattern, NULL); const unsigned short * const subjectUtf16 = subject.utf16() + subjectStart; int result; + QReadLocker lock(&mutex); + if (!previousMatchWasEmpty) { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } else { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions | PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED, + matchData, matchContext); - if (result == PCRE_ERROR_NOMATCH) { + if (result == PCRE2_ERROR_NOMATCH) { ++offset; if (usingCrLfNewlines @@ -1360,13 +1335,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString ++offset; } - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } } + lock.unlock(); + #ifdef QREGULAREXPRESSION_DEBUG qDebug() << "Matching" << pattern << "against" << subject << "starting at" << subjectStart << "len" << subjectLength @@ -1386,10 +1363,10 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString priv->capturedOffsets.resize(result * 2); } else { // no match, partial match or error - priv->hasPartialMatch = (result == PCRE_ERROR_PARTIAL); - priv->isValid = (result == PCRE_ERROR_NOMATCH || result == PCRE_ERROR_PARTIAL); + priv->hasPartialMatch = (result == PCRE2_ERROR_PARTIAL); + priv->isValid = (result == PCRE2_ERROR_NOMATCH || result == PCRE2_ERROR_PARTIAL); - if (result == PCRE_ERROR_PARTIAL) { + if (result == PCRE2_ERROR_PARTIAL) { // partial match: // leave the start and end capture offsets (i.e. cap(0)) priv->capturedCount = 1; @@ -1401,6 +1378,35 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString } } + // copy the captured substrings offsets, if any + if (priv->capturedCount) { + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(matchData); + int * const capturedOffsets = priv->capturedOffsets.data(); + + for (int i = 0; i < priv->capturedCount * 2; ++i) + capturedOffsets[i] = static_cast<int>(ovector[i]); + + // For partial matches, PCRE2 and PCRE1 differ in behavior when lookbehinds + // are involved. PCRE2 reports the real begin of the match and the maximum + // used lookbehind as distinct information; PCRE1 instead automatically + // adjusted ovector[0] to include the maximum lookbehind. + // + // For instance, given the pattern "\bstring\b", and the subject "a str": + // * PCRE1 reports partial, capturing " str" + // * PCRE2 reports partial, capturing "str" with a lookbehind of 1 + // + // To keep behavior, emulate PCRE1 here. + // (Eventually, we could expose the lookbehind info in a future patch.) + if (result == PCRE2_ERROR_PARTIAL) { + unsigned int maximumLookBehind; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_MAXLOOKBEHIND, &maximumLookBehind); + capturedOffsets[0] -= maximumLookBehind; + } + } + + pcre2_match_data_free_16(matchData); + pcre2_match_context_free_16(matchContext); + return priv; } @@ -1412,19 +1418,13 @@ QRegularExpressionMatchPrivate::QRegularExpressionMatchPrivate(const QRegularExp int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount) + QRegularExpression::MatchOptions matchOptions) : regularExpression(re), subject(subject), subjectStart(subjectStart), subjectLength(subjectLength), matchType(matchType), matchOptions(matchOptions), capturedCount(0), hasMatch(false), hasPartialMatch(false), isValid(false) { - Q_ASSERT(capturingCount >= 0); - if (capturingCount > 0) { - const int captureOffsetsCount = capturingCount * 3; - capturedOffsets.resize(captureOffsetsCount); - } } @@ -1632,13 +1632,13 @@ QStringList QRegularExpression::namedCaptureGroups() const // contains one ushort followed by the name, NUL terminated. // The ushort is the numerical index of the name in the pattern. // The length of each entry is namedCapturingTableEntrySize. - ushort *namedCapturingTable; - int namedCapturingTableEntryCount; - int namedCapturingTableEntrySize; + PCRE2_SPTR16 *namedCapturingTable; + unsigned int namedCapturingTableEntryCount; + unsigned int namedCapturingTableEntrySize; - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMETABLE, &namedCapturingTable); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMECOUNT, &namedCapturingTableEntryCount); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMETABLE, &namedCapturingTable); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMECOUNT, &namedCapturingTableEntryCount); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); QStringList result; @@ -1647,9 +1647,9 @@ QStringList QRegularExpression::namedCaptureGroups() const for (int i = 0; i < d->capturingCount + 1; ++i) result.append(QString()); - for (int i = 0; i < namedCapturingTableEntryCount; ++i) { - const ushort * const currentNamedCapturingTableRow = namedCapturingTable + - namedCapturingTableEntrySize * i; + for (unsigned int i = 0; i < namedCapturingTableEntryCount; ++i) { + const ushort * const currentNamedCapturingTableRow = + reinterpret_cast<const ushort *>(namedCapturingTable) + namedCapturingTableEntrySize * i; const int index = *currentNamedCapturingTableRow; result[index] = QString::fromUtf16(currentNamedCapturingTableRow + 1); @@ -1680,8 +1680,19 @@ bool QRegularExpression::isValid() const QString QRegularExpression::errorString() const { d.data()->compilePattern(); - if (d->errorString) - return QCoreApplication::translate("QRegularExpression", d->errorString); + if (d->errorCode) { + QString errorString; + int errorStringLength; + do { + errorString.resize(errorString.length() + 64); + errorStringLength = pcre2_get_error_message_16(d->errorCode, + reinterpret_cast<ushort *>(errorString.data()), + errorString.length()); + } while (errorStringLength < 0); + errorString.resize(errorStringLength); + + return QCoreApplication::translate("QRegularExpression", std::move(errorString).toLatin1().constData()); + } return QCoreApplication::translate("QRegularExpression", "no error"); } @@ -2583,7 +2594,8 @@ QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match) and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2012 University of Cambridge + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2015 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -2625,80 +2637,149 @@ static const char *pcreCompileErrorCodes[] = QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating ] for character class"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in character class"), QT_TRANSLATE_NOOP("QRegularExpression", "range out of order in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "nothing to repeat"), + QT_TRANSLATE_NOOP("QRegularExpression", "quantifier does not follow a repeatable item"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unexpected repeat"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (? or (?-"), QT_TRANSLATE_NOOP("QRegularExpression", "POSIX named classes are supported only within a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing )"), + QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "reference to non-existent subpattern"), - QT_TRANSLATE_NOOP("QRegularExpression", "erroffset passed as NULL"), - QT_TRANSLATE_NOOP("QRegularExpression", "unknown option bit(s) set"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern passed as NULL"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognised compile-time option bit(s)"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after (?# comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "failed to get memory"), - QT_TRANSLATE_NOOP("QRegularExpression", "unmatched parentheses"), + QT_TRANSLATE_NOOP("QRegularExpression", "failed to allocate heap memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unmatched closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: code overflow"), - QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?<"), + QT_TRANSLATE_NOOP("QRegularExpression", "letter or underscore expected after (?< or (?'"), QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is not fixed length"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed number or name after (?("), QT_TRANSLATE_NOOP("QRegularExpression", "conditional group contains more than two branches"), - QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?("), + QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?( or (?(?C)"), QT_TRANSLATE_NOOP("QRegularExpression", "(?R or (?[+-]digits must be followed by )"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown POSIX class name"), - QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UTF8 support"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\x{...} sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error in pcre2_study(): should not occur"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have Unicode support"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\x{} or \\o{} is too large"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid condition (?(0)"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\C not allowed in lookbehind assertion"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\C is not allowed in a lookbehind assertion"), QT_TRANSLATE_NOOP("QRegularExpression", "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u"), - QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is > 255"), - QT_TRANSLATE_NOOP("QRegularExpression", "closing ) for (?C expected"), - QT_TRANSLATE_NOOP("QRegularExpression", "recursive call could loop indefinitely"), + QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is greater than 255"), + QT_TRANSLATE_NOOP("QRegularExpression", "closing parenthesis for (?C expected"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in (*VERB) name"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?P"), QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in subpattern name (missing terminator)"), - QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-8 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "support for \\P, \\p, and \\X has not been compiled"), + QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name (PCRE2_DUPNAMES not set)"), + QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have support for \\P, \\p, or \\X"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed \\P or \\p sequence"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown property name after \\P or \\p"), - QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum 32 characters)"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum 10000)"), - QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 (not in UTF-8 mode)"), + QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum " "10000" " characters)"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum " "256" ")"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 in 8-bit non-UTF-8 mode"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: overran compiling workspace"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: previously-checked referenced subpattern not found"), QT_TRANSLATE_NOOP("QRegularExpression", "DEFINE group contains more than one branch"), - QT_TRANSLATE_NOOP("QRegularExpression", "repeating a DEFINE group is not allowed"), - QT_TRANSLATE_NOOP("QRegularExpression", "inconsistent NEWLINE options"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown newline setting"), QT_TRANSLATE_NOOP("QRegularExpression", "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number"), QT_TRANSLATE_NOOP("QRegularExpression", "a numbered reference must not be zero"), QT_TRANSLATE_NOOP("QRegularExpression", "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)"), - QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized"), + QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized or malformed"), QT_TRANSLATE_NOOP("QRegularExpression", "number is too big"), QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name expected"), QT_TRANSLATE_NOOP("QRegularExpression", "digit expected after (?+"), - QT_TRANSLATE_NOOP("QRegularExpression", "] is an invalid data character in JavaScript compatibility mode"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), QT_TRANSLATE_NOOP("QRegularExpression", "different names for subpatterns of the same number are not allowed"), QT_TRANSLATE_NOOP("QRegularExpression", "(*MARK) must have an argument"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UCP support"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by an ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a printable ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a letter or one of [\\]^_?"), QT_TRANSLATE_NOOP("QRegularExpression", "\\k is not followed by a braced, angle-bracketed, or quoted name"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in find_fixedlength()"), QT_TRANSLATE_NOOP("QRegularExpression", "\\N is not supported in a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many forward references"), + QT_TRANSLATE_NOOP("QRegularExpression", "SPARE ERROR"), QT_TRANSLATE_NOOP("QRegularExpression", "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-16 string"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UTF is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UCP is disabled by the application"), QT_TRANSLATE_NOOP("QRegularExpression", "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\u.... sequence is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-32 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "setting UTF is disabled by the application"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), - QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}") + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\u.... sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}"), + QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in (?(VERSION condition"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in auto_possessify()"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating delimiter for callout with string argument"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized string delimiter follows (?C"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "(?| and/or (?J: or (?x: parentheses are too deeply nested"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled in this PCRE2 library"), + QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too complicated"), + QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is too long"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern string is longer than the limit set by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "no error"), + QT_TRANSLATE_NOOP("QRegularExpression", "no match"), + QT_TRANSLATE_NOOP("QRegularExpression", "partial match"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 1 byte missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 2 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 3 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 4 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 2 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 3 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 4 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 5 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 6 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 6-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 2-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 3-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 4-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 5-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 6-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: isolated byte with 0x80 bit set"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: illegal byte (0xfe or 0xff)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: missing low surrogate at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: invalid low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: isolated low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad data value"), + QT_TRANSLATE_NOOP("QRegularExpression", "patterns do not all use the same character tables"), + QT_TRANSLATE_NOOP("QRegularExpression", "magic number missing"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern compiled in wrong mode: 8/16/32-bit error"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset value"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad option value"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset into UTF string"), + QT_TRANSLATE_NOOP("QRegularExpression", "callout error code"), /* Never returned by PCRE2 itself */ + QT_TRANSLATE_NOOP("QRegularExpression", "invalid data in workspace for DFA restart"), + QT_TRANSLATE_NOOP("QRegularExpression", "too much recursion for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "backreference condition or recursion test is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "function is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern contains an item that is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "workspace size exceeded in DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error - pattern overwritten?"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad JIT option"), + QT_TRANSLATE_NOOP("QRegularExpression", "JIT stack limit reached"), + QT_TRANSLATE_NOOP("QRegularExpression", "match limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "no more memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown substring"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-unique substring name"), + QT_TRANSLATE_NOOP("QRegularExpression", "NULL argument passed"), + QT_TRANSLATE_NOOP("QRegularExpression", "nested recursion at the same subject position"), + QT_TRANSLATE_NOOP("QRegularExpression", "recursion limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not available"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not set"), + QT_TRANSLATE_NOOP("QRegularExpression", "offset limit set without PCRE2_USE_OFFSET_LIMIT"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad escape sequence in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "expected closing curly bracket in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad substitution in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "match with end before start is not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many replacements (more than INT_MAX)") }; #endif // #if 0 |