qputenv: port to QByteArrayView

The vast majority of in-tree users pass simple and short C string
literals as the value. By porting to QByteArrayView, we document that
we'll accept non-NUL-terminated data, and do the NUL-termination
internally, using SSO'ed std::string, saving memory allocations in the
common case of short strings.

I didn't bother to check which direction std::string takes for
nullptrs these days (there was a change accepted in that area for
C++20 or 23), so play it safe and protect against them.

Follow-up to

Task-number: QTBUG-105302
Change-Id: I2369acc62f1d5cbc26135396cfe0602d8c75300c
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
bb10
Marc Mutz 2022-08-03 19:23:28 +02:00
parent 5b62970b94
commit 5ff7163796
12 changed files with 43 additions and 29 deletions

View File

@ -285,6 +285,13 @@ QT_WARNING_POP
#if QT_CORE_REMOVED_SINCE(6, 5)
#include "qenvironmentvariables.h"
bool qputenv(const char *varName, const QByteArray &value)
{
return qputenv(varName, qToByteArrayViewIgnoringNull(value));
}
#include "qmetatype.h"
int QMetaType::idHelper() const

View File

@ -243,6 +243,7 @@ bool qEnvironmentVariableIsSet(const char *varName) noexcept
}
/*!
\fn bool qputenv(const char *varName, QByteArrayView value)
\relates <QEnvironmentVariables>
This function sets the \a value of the environment variable named
@ -258,30 +259,30 @@ bool qEnvironmentVariableIsSet(const char *varName) noexcept
uses the replacement function in VC, and calls the standard C
library's implementation on all other platforms.
\note In Qt versions prior to 6.5, the \a value argument was QByteArray,
not QByteArrayView.
\sa qgetenv(), qEnvironmentVariable()
*/
bool qputenv(const char *varName, const QByteArray &value)
bool qputenv(const char *varName, QByteArrayView raw)
{
// protect against non-NUL-terminated QByteArrays:
if (!const_cast<QByteArray&>(value).data_ptr()->isMutable()) {
QByteArray copy(value);
copy.reserve(copy.size() + 1); // ensures NUL termination (and isMutable() even for size==0
// (unlike detach()) to avoid infinite recursion)
return qputenv(varName, copy);
}
auto protect = [](const char *str) { return str ? str : ""; };
std::string value{protect(raw.data()), size_t(raw.size())}; // NUL-terminates w/SSO
#if defined(Q_CC_MSVC)
const auto locker = qt_scoped_lock(environmentMutex);
return _putenv_s(varName, value.constData()) == 0;
return _putenv_s(varName, value.data()) == 0;
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
// POSIX.1-2001 has setenv
const auto locker = qt_scoped_lock(environmentMutex);
return setenv(varName, value.constData(), true) == 0;
return setenv(varName, value.data(), true) == 0;
#else
QByteArray buffer(varName);
std::string buffer;
buffer += protect(varName);
buffer += '=';
buffer += value;
char *envVar = qstrdup(buffer.constData());
char *envVar = qstrdup(buffer.data());
int result = [&] {
const auto locker = qt_scoped_lock(environmentMutex);
return putenv(envVar);

View File

@ -5,6 +5,7 @@
#define QENVIRONMENTVARIABLES_H
#include <QtCore/qglobal.h>
#include <QtCore/qtdeprecationmarkers.h>
#if 0
#pragma qt_class(QEnvironmentVariables)
@ -14,11 +15,16 @@
QT_BEGIN_NAMESPACE
class QByteArray;
class QByteArrayView;
Q_CORE_EXPORT QByteArray qgetenv(const char *varName);
// need it as two functions because QString is only forward-declared here
Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName);
Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName, const QString &defaultValue);
#if QT_CORE_REMOVED_SINCE(6, 5)
Q_CORE_EXPORT bool qputenv(const char *varName, const QByteArray &value);
#endif
Q_CORE_EXPORT bool qputenv(const char *varName, QByteArrayView value);
Q_CORE_EXPORT bool qunsetenv(const char *varName);
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept;

View File

@ -124,7 +124,7 @@ struct QXcbSoftwareOpenGLEnforcer {
}
if (forceSoftwareOpenGL)
qputenv("LIBGL_ALWAYS_SOFTWARE", QByteArrayLiteral("1"));
qputenv("LIBGL_ALWAYS_SOFTWARE", "1");
}
~QXcbSoftwareOpenGLEnforcer() {

View File

@ -181,7 +181,7 @@ private slots:
void initTestCase()
{
qputenv("XDG_CONFIG_DIRS", "/does/not/exist");
qputenv("QT_MESSAGE_PATTERN", QByteArray("%{category}: %{type},%{message}"));
qputenv("QT_MESSAGE_PATTERN", "%{category}: %{type},%{message}");
oldMessageHandler = qInstallMessageHandler(myCustomMessageHandler);
// Create configuration
_config = new Configuration();

View File

@ -64,10 +64,10 @@ private:
qputenv("XDG_DATA_DIRS", QFile::encodeName(m_globalAppDir));
}
void setDefaultLocations() {
qputenv("XDG_CONFIG_HOME", QByteArray());
qputenv("XDG_CONFIG_DIRS", QByteArray());
qputenv("XDG_DATA_HOME", QByteArray());
qputenv("XDG_DATA_DIRS", QByteArray());
qputenv("XDG_CONFIG_HOME", nullptr);
qputenv("XDG_CONFIG_DIRS", nullptr);
qputenv("XDG_DATA_HOME", nullptr);
qputenv("XDG_DATA_DIRS", nullptr);
}
#endif

View File

@ -153,7 +153,7 @@ private:
{ reset(zone); }
void reset(const QByteArray &zone)
{
qputenv("TZ", zone.constData());
qputenv("TZ", zone);
qTzSet();
}
~TimeZoneRollback()
@ -161,7 +161,7 @@ private:
if (prior.isNull())
qunsetenv("TZ");
else
qputenv("TZ", prior.constData());
qputenv("TZ", prior);
qTzSet();
}
};

View File

@ -270,7 +270,7 @@ void tst_QHighDpi::screenDpiChange()
void tst_QHighDpi::environment_QT_SCALE_FACTOR()
{
qreal factor = 3.1415;
qputenv("QT_SCALE_FACTOR", QByteArray::number(factor));
qputenv("QT_SCALE_FACTOR", std::to_string(factor));
QList<qreal> dpiValues { 96, 144, 192 };
std::unique_ptr<QGuiApplication> app(createStandardOffscreenApp(dpiValues));

View File

@ -238,7 +238,7 @@ void tst_Http2::singleRequest()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@ -630,7 +630,7 @@ void tst_Http2::connectToHost()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@ -728,7 +728,7 @@ void tst_Http2::maxFrameSize()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@ -893,7 +893,7 @@ void tst_Http2::moreActivitySignals()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([]() { qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); });
#endif
@ -1004,7 +1004,7 @@ void tst_Http2::contentEncoding()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([]() { qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); });
#endif

View File

@ -55,7 +55,7 @@ class tst_QAccessibilityLinux : public QObject
public:
tst_QAccessibilityLinux() : m_window(0), root(0), rootApplication(0), mainWindow(0)
{
qputenv("QT_LINUX_ACCESSIBILITY_ALWAYS_ON", QByteArrayLiteral("1"));
qputenv("QT_LINUX_ACCESSIBILITY_ALWAYS_ON", "1");
dbus = new DBusConnection();
}
~tst_QAccessibilityLinux()

View File

@ -174,7 +174,7 @@ QString tst_QPrinterInfo::getOutputFromCommand(const QStringList &command)
{
// Forces the ouptut from the command to be in English
const QByteArray origSoftwareEnv = qgetenv("SOFTWARE");
qputenv("SOFTWARE", QByteArray());
qputenv("SOFTWARE", nullptr);
QString output = getOutputFromCommandInternal(command);
qputenv("SOFTWARE", origSoftwareEnv);
return output;

View File

@ -1119,7 +1119,7 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2()
<< QCoreApplication::applicationDirPath();
QVERIFY(isPathListIncluded(QCoreApplication::libraryPaths(), expected));
qputenv("QT_PLUGIN_PATH", QByteArray());
qputenv("QT_PLUGIN_PATH", nullptr);
}
}
#endif