Skip to content

Commit

Permalink
Add new API for wstring conversion on windows and wrap get/set env. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
wengxt authored Feb 2, 2025
1 parent ed3ce70 commit cc1c9d8
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/lib/fcitx-utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ list(APPEND FCITX_UTILS_SOURCES
keydata.cpp
event.cpp
eventloopinterface.cpp
environ.cpp
)

set(FCITX_UTILS_HEADERS
Expand Down Expand Up @@ -101,6 +102,7 @@ set(FCITX_UTILS_HEADERS
log.h
testing.h
semver.h
environ.h
${CMAKE_CURRENT_BINARY_DIR}/fcitxutils_export.h
)

Expand Down
81 changes: 81 additions & 0 deletions src/lib/fcitx-utils/environ.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2025~2025 CSSlayer <[email protected]>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/

#include "environ.h"
#include <optional>
#include <string>
#include <string_view>
#include "fcitx-utils/utf8.h"
#include "stringutils.h"
#include "utf8.h"

#ifdef _WIN32
#include <windows.h>
#endif

namespace fcitx {

void setEnvironment(const char *variable, const char *value) {
#ifdef _WIN32
if (!variable || !value) {
return;
}

auto wname = utf8::UTF8ToUTF16(variable);
auto wvalue = utf8::UTF8ToUTF16(value);

auto assign = stringutils::concat(variable, "=", value);
auto wassign = utf8::UTF8ToUTF16(assign);

_wputenv(wassign.data());

SetEnvironmentVariableW(wname.data(), wvalue.data());
#else
setenv(variable, value, 1);
#endif
}

std::optional<std::string> getEnvironment(const char *variable) {
#ifdef _WIN32
if (!variable) {
return std::nullopt;
}
auto wname = utf8::UTF8ToUTF16(variable);
DWORD len = GetEnvironmentVariableW(wname.data(), nullptr, 0);

if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
return std::nullopt;
}
std::vector<wchar_t> wdata(len);
if (GetEnvironmentVariableW(wname.data(), wdata.data(), len) != len - 1) {
return std::nullopt;
}
std::wstring wvalue(wdata.data());

if (wvalue.find(L"%") != std::wstring::npos) {
len = ExpandEnvironmentStringsW(wvalue.data(), nullptr, 0);

if (len > 0) {
wdata.resize(len);
if (ExpandEnvironmentStringsW(wvalue.data(), wdata.data(), len) ==
len) {
wvalue = wdata.data();
}
}
}

return utf8::UTF16ToUTF8(wvalue);
#else
const char *value = getenv(variable);
if (value) {
return std::string(value);
}
return std::nullopt;
#endif
}

} // namespace fcitx
39 changes: 39 additions & 0 deletions src/lib/fcitx-utils/environ.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2025~2025 CSSlayer <[email protected]>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
#ifndef _FCITX_UTILS_SHIM_WIN32_H_
#define _FCITX_UTILS_SHIM_WIN32_H_

#include <optional>
#include <string>
#include "fcitx-utils/fcitxutils_export.h"

namespace fcitx {

/**
* Set environment variable
*
* @param variable variable name
* @return value
*
* @since 5.1.13
*/
FCITXUTILS_EXPORT void setEnvironment(const char *variable, const char *value);

/**
* Get environment variable value.
*
* @param variable variable name
* @return value
*
* @since 5.1.13
*/
FCITXUTILS_EXPORT std::optional<std::string>
getEnvironment(const char *variable);

} // namespace fcitx

#endif
49 changes: 49 additions & 0 deletions src/lib/fcitx-utils/utf8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,53 @@ std::string UCS4ToUTF8(uint32_t code) {
auto length = fcitx_ucs4_to_utf8(code, buf);
return {buf, buf + length};
}

#ifdef _WIN32

std::string UTF16ToUTF8(std::wstring_view data) {
std::string result;
for (size_t i = 0; i < data.size();) {
uint32_t ucs4 = 0;
uint16_t chr = data[i];
uint16_t chrNext = (i + 1 == data.size()) ? 0 : data[i + 1];
if (chr < 0xD800 || chr > 0xDFFF) {
ucs4 = chr;
i += 1;
} else if (0xD800 <= chr && chr <= 0xDBFF) {
if (!chrNext) {
return {};
}
if (0xDC00 <= chrNext && chrNext <= 0xDFFF) {
/* We have a valid surrogate pair. */
ucs4 = (((chr & 0x3FF) << 10) | (chrNext & 0x3FF)) + (1 << 16);
i += 2;
}
} else if (0xDC00 <= chr && chr <= 0xDFFF) {
return {};
}
result.append(utf8::UCS4ToUTF8(ucs4));
}
return result;
}

std::wstring UTF8ToUTF16(std::string_view str) {
if (!utf8::validate(str)) {
return {};
}
std::wstring result;
for (const auto ucs4 : utf8::MakeUTF8CharRange(str)) {
if (ucs4 < 0x10000) {
result.push_back(static_cast<uint16_t>(ucs4));
} else if (ucs4 < 0x110000) {
result.push_back(0xD800 | (((ucs4 - 0x10000) >> 10) & 0x3ff));
result.push_back(0xDC00 | (ucs4 & 0x3ff));
} else {
return {};
}
}
return result;
}

#endif

} // namespace fcitx::utf8
7 changes: 7 additions & 0 deletions src/lib/fcitx-utils/utf8.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,13 @@ auto MakeUTF8StringViewRange(const T &str) {
MakeUTF8StringViewIterator(std::end(str), std::end(str)));
}

#ifdef _WIN32

std::string UTF16ToUTF8(std::wstring_view data);
std::wstring UTF8ToUTF16(std::string_view str);

#endif

} // namespace fcitx::utf8

#endif // _FCITX_UTILS_UTF8_H_
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ set(FCITX_UTILS_TEST
testeventdispatcher
testrect
testfallbackuuid
testsemver)
testsemver
testenviron)

set(FCITX_UTILS_DBUS_TEST
testdbusmessage
Expand Down
18 changes: 18 additions & 0 deletions test/testenviron.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2025~2025 CSSlayer <[email protected]>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/

#include <optional>
#include "fcitx-utils/environ.h"
#include "fcitx-utils/log.h"

int main() {
fcitx::setEnvironment("TEST_VAR", "TEST_VALUE");
FCITX_ASSERT(fcitx::getEnvironment("TEST_VAR") == "TEST_VALUE");
FCITX_ASSERT(fcitx::getEnvironment("__BAD_VAR") == std::nullopt);

return 0;
}

0 comments on commit cc1c9d8

Please sign in to comment.