|
| 1 | +// Copyright (C) 2022 The Qt Company Ltd. |
| 2 | +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | + |
| 4 | +#include "qenvironmentvariables.h" |
| 5 | + |
| 6 | +#include <qplatformdefs.h> |
| 7 | +#include <QtCore/qbytearray.h> |
| 8 | +#include <QtCore/qmutex.h> |
| 9 | +#include <QtCore/qstring.h> |
| 10 | +#include <QtCore/qvarlengtharray.h> |
| 11 | + |
| 12 | +#include <QtCore/private/qlocking_p.h> |
| 13 | + |
| 14 | +QT_BEGIN_NAMESPACE |
| 15 | + |
| 16 | +// In the C runtime on all platforms access to the environment is not thread-safe. We |
| 17 | +// add thread-safety for the Qt wrappers. |
| 18 | +Q_CONSTINIT static QBasicMutex environmentMutex; |
| 19 | + |
| 20 | +/*! |
| 21 | + \relates <QEnvironmentVariables> |
| 22 | + \threadsafe |
| 23 | +
|
| 24 | + Returns the value of the environment variable with name \a varName as a |
| 25 | + QByteArray. If no variable by that name is found in the environment, this |
| 26 | + function returns a default-constructed QByteArray. |
| 27 | +
|
| 28 | + The Qt environment manipulation functions are thread-safe, but this |
| 29 | + requires that the C library equivalent functions like getenv and putenv are |
| 30 | + not directly called. |
| 31 | +
|
| 32 | + To convert the data to a QString use QString::fromLocal8Bit(). |
| 33 | +
|
| 34 | + \note on desktop Windows, qgetenv() may produce data loss if the |
| 35 | + original string contains Unicode characters not representable in the |
| 36 | + ANSI encoding. Use qEnvironmentVariable() instead. |
| 37 | + On Unix systems, this function is lossless. |
| 38 | +
|
| 39 | + \sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(), |
| 40 | + qEnvironmentVariableIsEmpty() |
| 41 | +*/ |
| 42 | +QByteArray qgetenv(const char *varName) |
| 43 | +{ |
| 44 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 45 | +#ifdef Q_CC_MSVC |
| 46 | + size_t requiredSize = 0; |
| 47 | + QByteArray buffer; |
| 48 | + getenv_s(&requiredSize, 0, 0, varName); |
| 49 | + if (requiredSize == 0) |
| 50 | + return buffer; |
| 51 | + buffer.resize(int(requiredSize)); |
| 52 | + getenv_s(&requiredSize, buffer.data(), requiredSize, varName); |
| 53 | + // requiredSize includes the terminating null, which we don't want. |
| 54 | + Q_ASSERT(buffer.endsWith('\0')); |
| 55 | + buffer.chop(1); |
| 56 | + return buffer; |
| 57 | +#else |
| 58 | + return QByteArray(::getenv(varName)); |
| 59 | +#endif |
| 60 | +} |
| 61 | + |
| 62 | +/*! |
| 63 | + \fn QString qEnvironmentVariable(const char *varName, const QString &defaultValue) |
| 64 | + \fn QString qEnvironmentVariable(const char *varName) |
| 65 | +
|
| 66 | + \relates <QEnvironmentVariables> |
| 67 | + \since 5.10 |
| 68 | +
|
| 69 | + These functions return the value of the environment variable, \a varName, as a |
| 70 | + QString. If no variable \a varName is found in the environment and \a defaultValue |
| 71 | + is provided, \a defaultValue is returned. Otherwise QString() is returned. |
| 72 | +
|
| 73 | + The Qt environment manipulation functions are thread-safe, but this |
| 74 | + requires that the C library equivalent functions like getenv and putenv are |
| 75 | + not directly called. |
| 76 | +
|
| 77 | + The following table describes how to choose between qgetenv() and |
| 78 | + qEnvironmentVariable(): |
| 79 | + \table |
| 80 | + \header \li Condition \li Recommendation |
| 81 | + \row |
| 82 | + \li Variable contains file paths or user text |
| 83 | + \li qEnvironmentVariable() |
| 84 | + \row |
| 85 | + \li Windows-specific code |
| 86 | + \li qEnvironmentVariable() |
| 87 | + \row |
| 88 | + \li Unix-specific code, destination variable is not QString and/or is |
| 89 | + used to interface with non-Qt APIs |
| 90 | + \li qgetenv() |
| 91 | + \row |
| 92 | + \li Destination variable is a QString |
| 93 | + \li qEnvironmentVariable() |
| 94 | + \row |
| 95 | + \li Destination variable is a QByteArray or std::string |
| 96 | + \li qgetenv() |
| 97 | + \endtable |
| 98 | +
|
| 99 | + \note on Unix systems, this function may produce data loss if the original |
| 100 | + string contains arbitrary binary data that cannot be decoded by the locale |
| 101 | + codec. Use qgetenv() instead for that case. On Windows, this function is |
| 102 | + lossless. |
| 103 | +
|
| 104 | + \note the variable name \a varName must contain only US-ASCII characters. |
| 105 | +
|
| 106 | + \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty() |
| 107 | +*/ |
| 108 | +QString qEnvironmentVariable(const char *varName, const QString &defaultValue) |
| 109 | +{ |
| 110 | +#if defined(Q_OS_WIN) |
| 111 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 112 | + QVarLengthArray<wchar_t, 32> wname(int(strlen(varName)) + 1); |
| 113 | + for (int i = 0; i < wname.size(); ++i) // wname.size() is correct: will copy terminating null |
| 114 | + wname[i] = uchar(varName[i]); |
| 115 | + size_t requiredSize = 0; |
| 116 | + QString buffer; |
| 117 | + _wgetenv_s(&requiredSize, 0, 0, wname.data()); |
| 118 | + if (requiredSize == 0) |
| 119 | + return defaultValue; |
| 120 | + buffer.resize(int(requiredSize)); |
| 121 | + _wgetenv_s(&requiredSize, reinterpret_cast<wchar_t *>(buffer.data()), requiredSize, |
| 122 | + wname.data()); |
| 123 | + // requiredSize includes the terminating null, which we don't want. |
| 124 | + Q_ASSERT(buffer.endsWith(QChar(u'\0'))); |
| 125 | + buffer.chop(1); |
| 126 | + return buffer; |
| 127 | +#else |
| 128 | + QByteArray value = qgetenv(varName); |
| 129 | + if (value.isNull()) |
| 130 | + return defaultValue; |
| 131 | +// duplicated in qfile.h (QFile::decodeName) |
| 132 | +#if defined(Q_OS_DARWIN) |
| 133 | + return QString::fromUtf8(value).normalized(QString::NormalizationForm_C); |
| 134 | +#else // other Unix |
| 135 | + return QString::fromLocal8Bit(value); |
| 136 | +#endif |
| 137 | +#endif |
| 138 | +} |
| 139 | + |
| 140 | +QString qEnvironmentVariable(const char *varName) |
| 141 | +{ |
| 142 | + return qEnvironmentVariable(varName, QString()); |
| 143 | +} |
| 144 | + |
| 145 | +/*! |
| 146 | + \relates <QEnvironmentVariables> |
| 147 | + \since 5.1 |
| 148 | +
|
| 149 | + Returns whether the environment variable \a varName is empty. |
| 150 | +
|
| 151 | + Equivalent to |
| 152 | + \snippet code/src_corelib_global_qglobal.cpp is-empty |
| 153 | + except that it's potentially much faster, and can't throw exceptions. |
| 154 | +
|
| 155 | + \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet() |
| 156 | +*/ |
| 157 | +bool qEnvironmentVariableIsEmpty(const char *varName) noexcept |
| 158 | +{ |
| 159 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 160 | +#ifdef Q_CC_MSVC |
| 161 | + // we provide a buffer that can only hold the empty string, so |
| 162 | + // when the env.var isn't empty, we'll get an ERANGE error (buffer |
| 163 | + // too small): |
| 164 | + size_t dummy; |
| 165 | + char buffer = '\0'; |
| 166 | + return getenv_s(&dummy, &buffer, 1, varName) != ERANGE; |
| 167 | +#else |
| 168 | + const char * const value = ::getenv(varName); |
| 169 | + return !value || !*value; |
| 170 | +#endif |
| 171 | +} |
| 172 | + |
| 173 | +/*! |
| 174 | + \relates <QEnvironmentVariables> |
| 175 | + \since 5.5 |
| 176 | +
|
| 177 | + Returns the numerical value of the environment variable \a varName. |
| 178 | + If \a ok is not null, sets \c{*ok} to \c true or \c false depending |
| 179 | + on the success of the conversion. |
| 180 | +
|
| 181 | + Equivalent to |
| 182 | + \snippet code/src_corelib_global_qglobal.cpp to-int |
| 183 | + except that it's much faster, and can't throw exceptions. |
| 184 | +
|
| 185 | + \note there's a limit on the length of the value, which is sufficient for |
| 186 | + all valid values of int, not counting leading zeroes or spaces. Values that |
| 187 | + are too long will either be truncated or this function will set \a ok to \c |
| 188 | + false. |
| 189 | +
|
| 190 | + \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet() |
| 191 | +*/ |
| 192 | +int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept |
| 193 | +{ |
| 194 | + static const int NumBinaryDigitsPerOctalDigit = 3; |
| 195 | + static const int MaxDigitsForOctalInt = |
| 196 | + (std::numeric_limits<uint>::digits + NumBinaryDigitsPerOctalDigit - 1) / NumBinaryDigitsPerOctalDigit; |
| 197 | + |
| 198 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 199 | + size_t size; |
| 200 | +#ifdef Q_CC_MSVC |
| 201 | + // we provide a buffer that can hold any int value: |
| 202 | + char buffer[MaxDigitsForOctalInt + 2]; // +1 for NUL +1 for optional '-' |
| 203 | + size_t dummy; |
| 204 | + if (getenv_s(&dummy, buffer, sizeof buffer, varName) != 0) { |
| 205 | + if (ok) |
| 206 | + *ok = false; |
| 207 | + return 0; |
| 208 | + } |
| 209 | + size = strlen(buffer); |
| 210 | +#else |
| 211 | + const char * const buffer = ::getenv(varName); |
| 212 | + if (!buffer || (size = strlen(buffer)) > MaxDigitsForOctalInt + 2) { |
| 213 | + if (ok) |
| 214 | + *ok = false; |
| 215 | + return 0; |
| 216 | + } |
| 217 | +#endif |
| 218 | + return QByteArrayView(buffer, size).toInt(ok, 0); |
| 219 | +} |
| 220 | + |
| 221 | +/*! |
| 222 | + \relates <QEnvironmentVariables> |
| 223 | + \since 5.1 |
| 224 | +
|
| 225 | + Returns whether the environment variable \a varName is set. |
| 226 | +
|
| 227 | + Equivalent to |
| 228 | + \snippet code/src_corelib_global_qglobal.cpp is-null |
| 229 | + except that it's potentially much faster, and can't throw exceptions. |
| 230 | +
|
| 231 | + \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty() |
| 232 | +*/ |
| 233 | +bool qEnvironmentVariableIsSet(const char *varName) noexcept |
| 234 | +{ |
| 235 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 236 | +#ifdef Q_CC_MSVC |
| 237 | + size_t requiredSize = 0; |
| 238 | + (void)getenv_s(&requiredSize, 0, 0, varName); |
| 239 | + return requiredSize != 0; |
| 240 | +#else |
| 241 | + return ::getenv(varName) != nullptr; |
| 242 | +#endif |
| 243 | +} |
| 244 | + |
| 245 | +/*! |
| 246 | + \relates <QEnvironmentVariables> |
| 247 | +
|
| 248 | + This function sets the \a value of the environment variable named |
| 249 | + \a varName. It will create the variable if it does not exist. It |
| 250 | + returns 0 if the variable could not be set. |
| 251 | +
|
| 252 | + Calling qputenv with an empty value removes the environment variable on |
| 253 | + Windows, and makes it set (but empty) on Unix. Prefer using qunsetenv() |
| 254 | + for fully portable behavior. |
| 255 | +
|
| 256 | + \note qputenv() was introduced because putenv() from the standard |
| 257 | + C library was deprecated in VC2005 (and later versions). qputenv() |
| 258 | + uses the replacement function in VC, and calls the standard C |
| 259 | + library's implementation on all other platforms. |
| 260 | +
|
| 261 | + \sa qgetenv(), qEnvironmentVariable() |
| 262 | +*/ |
| 263 | +bool qputenv(const char *varName, const QByteArray &value) |
| 264 | +{ |
| 265 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 266 | +#if defined(Q_CC_MSVC) |
| 267 | + return _putenv_s(varName, value.constData()) == 0; |
| 268 | +#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU) |
| 269 | + // POSIX.1-2001 has setenv |
| 270 | + return setenv(varName, value.constData(), true) == 0; |
| 271 | +#else |
| 272 | + QByteArray buffer(varName); |
| 273 | + buffer += '='; |
| 274 | + buffer += value; |
| 275 | + char *envVar = qstrdup(buffer.constData()); |
| 276 | + int result = putenv(envVar); |
| 277 | + if (result != 0) // error. we have to delete the string. |
| 278 | + delete[] envVar; |
| 279 | + return result == 0; |
| 280 | +#endif |
| 281 | +} |
| 282 | + |
| 283 | +/*! |
| 284 | + \relates <QEnvironmentVariables> |
| 285 | +
|
| 286 | + This function deletes the variable \a varName from the environment. |
| 287 | +
|
| 288 | + Returns \c true on success. |
| 289 | +
|
| 290 | + \since 5.1 |
| 291 | +
|
| 292 | + \sa qputenv(), qgetenv(), qEnvironmentVariable() |
| 293 | +*/ |
| 294 | +bool qunsetenv(const char *varName) |
| 295 | +{ |
| 296 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 297 | +#if defined(Q_CC_MSVC) |
| 298 | + return _putenv_s(varName, "") == 0; |
| 299 | +#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU) |
| 300 | + // POSIX.1-2001, BSD and Haiku have unsetenv |
| 301 | + return unsetenv(varName) == 0; |
| 302 | +#elif defined(Q_CC_MINGW) |
| 303 | + // On mingw, putenv("var=") removes "var" from the environment |
| 304 | + QByteArray buffer(varName); |
| 305 | + buffer += '='; |
| 306 | + return putenv(buffer.constData()) == 0; |
| 307 | +#else |
| 308 | + // Fallback to putenv("var=") which will insert an empty var into the |
| 309 | + // environment and leak it |
| 310 | + QByteArray buffer(varName); |
| 311 | + buffer += '='; |
| 312 | + char *envVar = qstrdup(buffer.constData()); |
| 313 | + return putenv(envVar) == 0; |
| 314 | +#endif |
| 315 | +} |
| 316 | + |
| 317 | +/* |
| 318 | + Wraps tzset(), which accesses the environment, so should only be called while |
| 319 | + we hold the lock on the environment mutex. |
| 320 | +*/ |
| 321 | +void qTzSet() |
| 322 | +{ |
| 323 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 324 | +#if defined(Q_OS_WIN) |
| 325 | + _tzset(); |
| 326 | +#else |
| 327 | + tzset(); |
| 328 | +#endif // Q_OS_WIN |
| 329 | +} |
| 330 | + |
| 331 | +/* |
| 332 | + Wrap mktime(), which is specified to behave as if it called tzset(), hence |
| 333 | + shares its implicit environment-dependence. |
| 334 | +*/ |
| 335 | +time_t qMkTime(struct tm *when) |
| 336 | +{ |
| 337 | + const auto locker = qt_scoped_lock(environmentMutex); |
| 338 | + return mktime(when); |
| 339 | +} |
| 340 | + |
| 341 | +QT_END_NAMESPACE |
0 commit comments