Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtenvironmentvariables.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
7
8#include <qplatformdefs.h>
9#include <QtCore/qbytearray.h>
10#include <QtCore/qmutex.h>
11#include <QtCore/qstring.h>
12#include <QtCore/qvarlengtharray.h>
13
14#include <QtCore/private/qlocale_p.h>
15#include <QtCore/private/qlocking_p.h>
16
18
19// In the C runtime on all platforms access to the environment is not thread-safe. We
20// add thread-safety for the Qt wrappers.
21Q_CONSTINIT static QBasicMutex environmentMutex;
22
23/*!
24 \headerfile <QtEnvironmentVariables>
25 \inmodule QtCore
26 \ingroup funclists
27 \brief Helper functions for working with environment variables.
28
29 The functions in this header are used to manipulate the environment
30 variables.
31
32 \section2 Thread-safety
33
34 The Qt environment manipulation functions are thread-safe, but this requires
35 that the C library equivalent functions like \c {getenv} and \c {putenv}
36 are not directly called.
37
38 This is also relevant for the third-party libraries that are loaded by the
39 application. If such a library uses unsafe functions to manipulate the
40 environment from a secondary thread, it is recommended to do all the
41 environment setup in the main thread before loading the library and/or
42 creating secondary threads.
43*/
44
45/*!
46 \relates <QtEnvironmentVariables>
47 \threadsafe
48
49 Returns the value of the environment variable with name \a varName as a
50 QByteArray. If no variable by that name is found in the environment, this
51 function returns a default-constructed QByteArray.
52
53 The Qt environment manipulation functions are thread-safe, but this
54 requires that the C library equivalent functions like getenv and putenv are
55 not directly called.
56
57 To convert the data to a QString use QString::fromLocal8Bit().
58
59 \note on desktop Windows, qgetenv() may produce data loss if the
60 original string contains Unicode characters not representable in the
61 ANSI encoding. Use qEnvironmentVariable() instead.
62 On Unix systems, this function is lossless.
63
64 \sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(),
65 qEnvironmentVariableIsEmpty(), qEnvironmentVariableIntegerValue()
66*/
67QByteArray qgetenv(const char *varName)
68{
69 const auto locker = qt_scoped_lock(environmentMutex);
70 return QByteArray(::getenv(varName));
71}
72
73/*!
74 \fn QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
75 \fn QString qEnvironmentVariable(const char *varName)
76
77 \relates <QtEnvironmentVariables>
78 \since 5.10
79
80 These functions return the value of the environment variable, \a varName, as a
81 QString. If no variable \a varName is found in the environment and \a defaultValue
82 is provided, \a defaultValue is returned. Otherwise QString() is returned.
83
84 The Qt environment manipulation functions are thread-safe, but this
85 requires that the C library equivalent functions like getenv and putenv are
86 not directly called.
87
88 The following table describes how to choose between qgetenv() and
89 qEnvironmentVariable():
90 \table
91 \header \li Condition \li Recommendation
92 \row
93 \li Variable contains file paths or user text
94 \li qEnvironmentVariable()
95 \row
96 \li Windows-specific code
97 \li qEnvironmentVariable()
98 \row
99 \li Unix-specific code, destination variable is not QString and/or is
100 used to interface with non-Qt APIs
101 \li qgetenv()
102 \row
103 \li Destination variable is a QString
104 \li qEnvironmentVariable()
105 \row
106 \li Destination variable is a QByteArray or std::string
107 \li qgetenv()
108 \endtable
109
110 \note on Unix systems, this function may produce data loss if the original
111 string contains arbitrary binary data that cannot be decoded by the locale
112 codec. Use qgetenv() instead for that case. On Windows, this function is
113 lossless.
114
115 \note the variable name \a varName must contain only US-ASCII characters.
116
117 \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty(),
118 qEnvironmentVariableIntegerValue()
119*/
120QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
121{
122#if defined(Q_OS_WIN)
123 QVarLengthArray<wchar_t, 32> wname(qsizetype(strlen(varName)) + 1);
124 for (qsizetype i = 0; i < wname.size(); ++i) // wname.size() is correct: will copy terminating null
125 wname[i] = uchar(varName[i]);
126 size_t requiredSize = 0;
127 auto locker = qt_unique_lock(environmentMutex);
128 _wgetenv_s(&requiredSize, 0, 0, wname.data());
129 if (requiredSize == 0)
130 return defaultValue;
131 QString buffer(qsizetype(requiredSize), Qt::Uninitialized);
132 _wgetenv_s(&requiredSize, reinterpret_cast<wchar_t *>(buffer.data()), requiredSize,
133 wname.data());
134 locker.unlock();
135 // requiredSize includes the terminating null, which we don't want.
136 Q_ASSERT(buffer.endsWith(QChar(u'\0')));
137 buffer.chop(1);
138 return buffer;
139#else
140 const auto locker = qt_scoped_lock(environmentMutex);
141 const char *value = ::getenv(varName);
142 if (!value)
143 return defaultValue;
144// duplicated in qfile.h (QFile::decodeName)
145#if defined(Q_OS_DARWIN)
146 return QString::fromUtf8(value).normalized(QString::NormalizationForm_C);
147#else // other Unix
148 return QString::fromLocal8Bit(value);
149#endif
150#endif
151}
152
153QString qEnvironmentVariable(const char *varName)
154{
155 return qEnvironmentVariable(varName, QString());
156}
157
158/*!
159 \relates <QtEnvironmentVariables>
160 \since 5.1
161
162 Returns whether the environment variable \a varName is empty.
163
164 Equivalent to
165 \snippet code/src_corelib_global_qglobal.cpp is-empty
166 except that it's potentially much faster, and can't throw exceptions.
167
168 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
169*/
170bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
171{
172 const auto locker = qt_scoped_lock(environmentMutex);
173 const char * const value = ::getenv(varName);
174 return !value || !*value;
175}
176
177QT_WARNING_PUSH
178QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // older GCC don't like libstdc++'s std::optional
179/*!
180 \relates <QtEnvironmentVariables>
181 \since 5.5
182
183 Returns the numerical value of the environment variable \a varName.
184 If \a ok is not null, sets \c{*ok} to \c true or \c false depending
185 on the success of the conversion.
186
187 Equivalent to
188 \snippet code/src_corelib_global_qglobal.cpp to-int
189 except that it's much faster, and can't throw exceptions.
190
191 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
192*/
193int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept
194{
195 std::optional<qint64> value = qEnvironmentVariableIntegerValue(varName);
196 if (value && *value != int(*value))
197 value = std::nullopt;
198 if (ok)
199 *ok = bool(value);
200 return int(value.value_or(0));
201}
202QT_WARNING_POP
203
204/*!
205 \relates <QtEnvironmentVariables>
206 \since 6.10
207
208 Returns the numerical value of the environment variable \a varName. If the
209 variable is not set or could not be parsed as an integer, it returns
210 \c{std::nullopt}.
211
212 Similar to
213 \snippet code/src_corelib_global_qglobal.cpp to-int
214 except that it's much faster, and can't throw exceptions.
215
216 If a value of zero is semantically the same as an empty or unset variable,
217 applications can use
218 \snippet code/src_corelib_global_qglobal.cpp int-value_or
219 Do note in this case that failures to parse a value will also produce a
220 zero.
221
222 But if a value of zero can be used to disable some functionality,
223 applications can compare the returned \c{std::optional} to zero, which will
224 only be true if the variable was set and contained a number that parsed as
225 zero, as in:
226 \snippet code/src_corelib_global_qglobal.cpp int-eq0
227
228 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
229*/
230std::optional<qint64> qEnvironmentVariableIntegerValue(const char *varName) noexcept
231{
232 const auto locker = qt_scoped_lock(environmentMutex);
233 const char * const buffer = ::getenv(varName);
234 if (!buffer)
235 return std::nullopt;
236 auto r = QLocaleData::bytearrayToLongLong(buffer, 0);
237 if (!r.ok())
238 return std::nullopt;
239 return r.result;
240}
241
242/*!
243 \relates <QtEnvironmentVariables>
244 \since 5.1
245
246 Returns whether the environment variable \a varName is set.
247
248 Equivalent to
249 \snippet code/src_corelib_global_qglobal.cpp is-null
250 except that it's potentially much faster, and can't throw exceptions.
251
252 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty(),
253 qEnvironmentVariableIntegerValue()
254*/
255bool qEnvironmentVariableIsSet(const char *varName) noexcept
256{
257 const auto locker = qt_scoped_lock(environmentMutex);
258 return ::getenv(varName) != nullptr;
259}
260
261/*!
262 \fn bool qputenv(const char *varName, QByteArrayView value)
263 \relates <QtEnvironmentVariables>
264
265 This function sets the \a value of the environment variable named
266 \a varName. It will create the variable if it does not exist. It
267 returns 0 if the variable could not be set.
268
269 Calling qputenv with an empty value removes the environment variable on
270 Windows, and makes it set (but empty) on Unix. Prefer using qunsetenv()
271 for fully portable behavior.
272
273 \note qputenv() was introduced because putenv() from the standard
274 C library was deprecated in VC2005 (and later versions). qputenv()
275 uses the replacement function in VC, and calls the standard C
276 library's implementation on all other platforms.
277
278 \note In Qt versions prior to 6.5, the \a value argument was QByteArray,
279 not QByteArrayView.
280
281 \sa qgetenv(), qEnvironmentVariable()
282*/
283bool qputenv(const char *varName, QByteArrayView raw)
284{
285 auto protect = [](const char *str) { return str ? str : ""; };
286
287 std::string value{protect(raw.data()), size_t(raw.size())}; // NUL-terminates w/SSO
288
289#if defined(Q_CC_MSVC)
290 const auto locker = qt_scoped_lock(environmentMutex);
291 return _putenv_s(varName, value.data()) == 0;
292#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
293 // POSIX.1-2001 has setenv
294 const auto locker = qt_scoped_lock(environmentMutex);
295 return setenv(varName, value.data(), true) == 0;
296#else
297 std::string buffer;
298 buffer += protect(varName);
299 buffer += '=';
300 buffer += value;
301 char *envVar = qstrdup(buffer.data());
302 int result = [&] {
303 const auto locker = qt_scoped_lock(environmentMutex);
304 return putenv(envVar);
305 }();
306 if (result != 0) // error. we have to delete the string.
307 delete[] envVar;
308 return result == 0;
309#endif
310}
311
312/*!
313 \relates <QtEnvironmentVariables>
314
315 This function deletes the variable \a varName from the environment.
316
317 Returns \c true on success.
318
319 \since 5.1
320
321 \sa qputenv(), qgetenv(), qEnvironmentVariable()
322*/
323bool qunsetenv(const char *varName)
324{
325#if defined(Q_CC_MSVC)
326 const auto locker = qt_scoped_lock(environmentMutex);
327 return _putenv_s(varName, "") == 0;
328#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU)
329 // POSIX.1-2001, BSD and Haiku have unsetenv
330 const auto locker = qt_scoped_lock(environmentMutex);
331 return unsetenv(varName) == 0;
332#elif defined(Q_CC_MINGW)
333 // On mingw, putenv("var=") removes "var" from the environment
334 QByteArray buffer(varName);
335 buffer += '=';
336 const auto locker = qt_scoped_lock(environmentMutex);
337 return putenv(buffer.constData()) == 0;
338#else
339 // Fallback to putenv("var=") which will insert an empty var into the
340 // environment and leak it
341 QByteArray buffer(varName);
342 buffer += '=';
343 char *envVar = qstrdup(buffer.constData());
344 const auto locker = qt_scoped_lock(environmentMutex);
345 return putenv(envVar) == 0;
346#endif
347}
348
349/* Various time-related APIs that need to consult system settings also need
350 protection with the same lock as the environment, since those system settings
351 include part of the environment (principally TZ).
352
353 First, tzset(), which POSIX explicitly says accesses the environment.
354*/
355void qTzSet()
356{
357 const auto locker = qt_scoped_lock(environmentMutex);
358#if defined(Q_OS_WIN)
359 _tzset();
360#else
361 tzset();
362#endif // Q_OS_WIN
363}
364
365/* Wrap mktime(), which is specified to behave as if it called tzset(), hence
366 shares its implicit environment-dependence.
367*/
368time_t qMkTime(struct tm *when)
369{
370 const auto locker = qt_scoped_lock(environmentMutex);
371#if defined(Q_OS_WIN)
372 // QTBUG-83881 MS's mktime() seems to need _tzset() called first.
373 _tzset();
374#endif
375 return mktime(when);
376}
377
378/* For localtime(), POSIX mandates that it behave as if it called tzset().
379 For the alternatives to it, we need (if only for compatibility) to do the
380 same explicitly, which should ensure a re-parse of timezone info.
381*/
382bool qLocalTime(time_t utc, struct tm *local)
383{
384 const auto locker = qt_scoped_lock(environmentMutex);
385#if defined(Q_OS_WIN)
386 // The doc of localtime_s() says that it corrects for the same things
387 // _tzset() sets the globals for, but QTBUG-109974 reveals a need for an
388 // explicit call, all the same.
389 _tzset();
390 return !localtime_s(local, &utc);
391#elif QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
392 // Use the reentrant version of localtime() where available, as it is
393 // thread-safe and doesn't use a shared static data area.
394 // As localtime_r() is not specified to work as if it called tzset(),
395 // make an explicit call.
396 tzset();
397 if (tm *res = localtime_r(&utc, local)) {
398 Q_ASSERT(res == local);
399 Q_UNUSED(res);
400 return true;
401 }
402 return false;
403#else
404 // POSIX mandates that localtime() behaves as if it called tzset().
405 // Returns shared static data which may be overwritten at any time (albeit
406 // our lock probably keeps it safe). So copy the result promptly:
407 if (tm *res = localtime(&utc)) {
408 *local = *res;
409 return true;
410 }
411 return false;
412#endif
413}
414
415/* Access to the tzname[] global in one thread is UB if any other is calling
416 tzset() or anything that behaves as if it called tzset(). So also lock this
417 access to prevent such collisions.
418
419 Note that, on Windows, the return is a Microsoft-specific full name for the
420 zone, not an abbreviation.
421
422 Parameter dstIndex must be 1 for DST or 0 for standard time.
423 Returns the relevant form of the name of local-time's zone.
424*/
425QString qTzName(int dstIndex)
426{
427 Q_DECL_UNINITIALIZED
428 char name[512];
429 bool ok;
430 size_t size = 0;
431 {
432 const auto locker = qt_scoped_lock(environmentMutex);
433#if defined(_UCRT) // i.e., MSVC and MinGW-UCRT
434 ok = _get_tzname(&size, name, sizeof(name), dstIndex) == 0;
435 size -= 1; // It includes space for '\0' (or !ok => we're going to ignore it).
436 Q_ASSERT(!ok || size < sizeof(name));
437#else
438 const char *const src = tzname[dstIndex];
439 size = src ? strlen(src) : 0;
440 ok = src != nullptr && size < sizeof(name);
441 if (ok)
442 memcpy(name, src, size + 1);
443#endif // _UCRT
444 }
445 return ok ? QString::fromLocal8Bit(name, qsizetype(size)) : QString();
446}
447
448QT_END_NAMESPACE
Combined button and popup list for selecting options.
bool qLocalTime(time_t utc, struct tm *local)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
bool qputenv(const char *varName, QByteArrayView raw)
time_t qMkTime(struct tm *when)
void qTzSet()
QString qTzName(int dstIndex)
QString qEnvironmentVariable(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
Q_CORE_EXPORT bool qunsetenv(const char *varName)