diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index a0c943df62..f526387eb4 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -47,6 +47,7 @@ #include "qthreadstorage.h" #include "qdir.h" #include "qdatetime.h" +#include #ifndef QT_NO_QOBJECT #include @@ -3011,6 +3012,53 @@ bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT #endif } +/*! + \relates + \since 5.5 + + Returns the numerical value of the environment variable \a varName. + If \a ok is not null, sets \c{*ok} to \c true or \c false depending + on the success of the conversion. + + Equivalent to + \code + qgetenv(varName).toInt() + \endcode + except that it's much faster, and can't throw exceptions. + + \sa qgetenv(), qEnvironmentVariableIsSet() +*/ +int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + // we provide a buffer that can hold any int value: + static const int NumBinaryDigitsPerOctalDigit = 3; + static const int MaxDigitsForOctalInt = + (std::numeric_limits::digits + NumBinaryDigitsPerOctalDigit - 1) / NumBinaryDigitsPerOctalDigit; + char buffer[MaxDigitsForOctalInt + 2]; // +1 for NUL +1 for optional '-' + size_t dummy; + if (getenv_s(&dummy, buffer, sizeof buffer, varName) != 0) { + if (ok) + *ok = false; + return 0; + } +#else + const char * const buffer = ::getenv(varName); + if (!buffer || !*buffer) { + if (ok) + *ok = false; + return 0; + } +#endif + const qlonglong value = qstrtoll(buffer, Q_NULLPTR, 0, ok); + if (int(value) != value) { // this is the check in QByteArray::toInt(), keep it in sync + if (ok) + *ok = false; + return 0; + } + return int(value); +} + /*! \relates \since 5.1 diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 9176d80f43..412996e462 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -1035,6 +1035,7 @@ Q_CORE_EXPORT bool qunsetenv(const char *varName); Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT; Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT; +Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=0) Q_DECL_NOEXCEPT; inline int qIntCast(double f) { return int(f); } inline int qIntCast(float f) { return int(f); } diff --git a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp index ef41ef8801..e95b86d239 100644 --- a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp +++ b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp @@ -49,14 +49,22 @@ class tst_QGetPutEnv : public QObject Q_OBJECT private slots: void getSetCheck(); + void intValue_data(); + void intValue(); }; void tst_QGetPutEnv::getSetCheck() { const char varName[] = "should_not_exist"; + bool ok; + QVERIFY(!qEnvironmentVariableIsSet(varName)); QVERIFY(qEnvironmentVariableIsEmpty(varName)); + ok = true; + QCOMPARE(qEnvironmentVariableIntValue(varName), 0); + QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); + QVERIFY(!ok); QByteArray result = qgetenv(varName); QCOMPARE(result, QByteArray()); @@ -65,12 +73,20 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(qEnvironmentVariableIsSet(varName)); QVERIFY(qEnvironmentVariableIsEmpty(varName)); + ok = true; + QCOMPARE(qEnvironmentVariableIntValue(varName), 0); + QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); + QVERIFY(!ok); #endif QVERIFY(qputenv(varName, QByteArray("supervalue"))); QVERIFY(qEnvironmentVariableIsSet(varName)); QVERIFY(!qEnvironmentVariableIsEmpty(varName)); + ok = true; + QCOMPARE(qEnvironmentVariableIntValue(varName), 0); + QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); + QVERIFY(!ok); result = qgetenv(varName); QVERIFY(result == "supervalue"); @@ -80,9 +96,61 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(qunsetenv(varName)); QVERIFY(!qEnvironmentVariableIsSet(varName)); QVERIFY(qEnvironmentVariableIsEmpty(varName)); + ok = true; + QCOMPARE(qEnvironmentVariableIntValue(varName), 0); + QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); + QVERIFY(!ok); result = qgetenv(varName); QCOMPARE(result, QByteArray()); } +void tst_QGetPutEnv::intValue_data() +{ + QTest::addColumn("value"); + QTest::addColumn("expected"); + QTest::addColumn("ok"); + + // most non-success cases already tested in getSetCheck() + +#define ROW(x, i, b) \ + QTest::newRow(#x) << QByteArray(#x) << (i) << (b) + ROW(auto, 0, false); + ROW(0, 0, true); + ROW(1, 1, true); + ROW(010, 8, true); + ROW(0x10, 16, true); + ROW(-1, -1, true); + ROW(-010, -8, true); + // ROW(0xffffffff, -1, true); // could be expected, but not how QByteArray::toInt() works + ROW(0xffffffff, 0, false); + const int bases[] = {10, 8, 16}; + for (size_t i = 0; i < sizeof bases / sizeof *bases; ++i) { + QTest::newRow(qPrintable(QString().sprintf("INT_MAX, base %d", bases[i]))) + << QByteArray::number(INT_MAX) << INT_MAX << true; + QTest::newRow(qPrintable(QString().sprintf("INT_MAX+1, base %d", bases[i]))) + << QByteArray::number(qlonglong(INT_MAX) + 1) << 0 << false; + QTest::newRow(qPrintable(QString().sprintf("INT_MIN, base %d", bases[i]))) + << QByteArray::number(INT_MIN) << INT_MIN << true; + QTest::newRow(qPrintable(QString().sprintf("INT_MIN-1, base %d", bases[i]))) + << QByteArray::number(qlonglong(INT_MIN) - 1) << 0 << false; + }; +} + +void tst_QGetPutEnv::intValue() +{ + const char varName[] = "should_not_exist"; + + QFETCH(QByteArray, value); + QFETCH(int, expected); + QFETCH(bool, ok); + + bool actualOk = !ok; + + QVERIFY(qputenv(varName, value)); + QCOMPARE(qEnvironmentVariableIntValue(varName), expected); + QCOMPARE(qEnvironmentVariableIntValue(varName, &actualOk), expected); + QCOMPARE(actualOk, ok); +} + QTEST_MAIN(tst_QGetPutEnv) #include "tst_qgetputenv.moc"