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